Compare commits
139 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da347492d3 | ||
|
|
2ec5d8db3c | ||
|
|
fd9c5040bf | ||
|
|
aa328f0add | ||
|
|
9743d7b87b | ||
|
|
98fb0c433e | ||
|
|
7c9fcd9f43 | ||
|
|
54c76d9968 | ||
|
|
40b3f0fedc | ||
|
|
dcfcf83430 | ||
|
|
e46b354643 | ||
|
|
f497e4e301 | ||
|
|
b65e4b3819 | ||
|
|
d166b036fc | ||
|
|
ddf5f22037 | ||
|
|
7d8a9f2b6d | ||
|
|
0a1695e3d7 | ||
|
|
4a653d4935 | ||
|
|
2bc31a10c5 | ||
|
|
e8d2c6214b | ||
|
|
3a0f2687e9 | ||
|
|
04c98326b2 | ||
|
|
eb22c7f303 | ||
|
|
d51a4d7a7e | ||
|
|
0fb705e1e2 | ||
|
|
10b849ef09 | ||
|
|
d7d3b23cea | ||
|
|
c3786d434e | ||
|
|
9e3b92014a | ||
|
|
f4e088131b | ||
|
|
e55e069fe3 | ||
|
|
d8d3767798 | ||
|
|
7e99b1ac78 | ||
|
|
6ff3a73bf2 | ||
|
|
2a43b52344 | ||
|
|
abff80ec23 | ||
|
|
a4edf86195 | ||
|
|
0d0da6bfec | ||
|
|
e0c8ece9b5 | ||
|
|
4d875bc3d4 | ||
|
|
3a6e23bcef | ||
|
|
efd0716707 | ||
|
|
c94a5fb743 | ||
|
|
047011f60b | ||
|
|
a54ed3a51a | ||
|
|
c37f09bfcd | ||
|
|
1c7042463d | ||
|
|
dcb003f9ab | ||
|
|
7dbda3cee7 | ||
|
|
26bee229a1 | ||
|
|
5bf2beb179 | ||
|
|
4a5c551678 | ||
|
|
277894215d | ||
|
|
684e08a3a1 | ||
|
|
19dbc2f9b9 | ||
|
|
833a1e06f0 | ||
|
|
daca0831a4 | ||
|
|
337889c5f1 | ||
|
|
244d2d3866 | ||
|
|
c0fed0ba4f | ||
|
|
affb107b9d | ||
|
|
f96073af99 | ||
|
|
496a0483d2 | ||
|
|
e11dca00bb | ||
|
|
fde39bf34e | ||
|
|
4f11bae238 | ||
|
|
f6282ba71f | ||
|
|
3edf1f4e1b | ||
|
|
41bc064083 | ||
|
|
eb8562e6b0 | ||
|
|
68fbdd92c3 | ||
|
|
02038a5d93 | ||
|
|
4fb8c2f4b2 | ||
|
|
7afffa60c3 | ||
|
|
0e6c860360 | ||
|
|
ebfbbfa08b | ||
|
|
b5f182dfec | ||
|
|
4cf28d0ad0 | ||
|
|
149bb049a5 | ||
|
|
124702f0a2 | ||
|
|
c1cebe578b | ||
|
|
e46c1ee849 | ||
|
|
70f1743114 | ||
|
|
54d520727e | ||
|
|
1f1e4db486 | ||
|
|
e536236634 | ||
|
|
140c236da5 | ||
|
|
69ede34274 | ||
|
|
fcf6e22132 | ||
|
|
7438ee8308 | ||
|
|
f01cf7fcb5 | ||
|
|
7a852f78e4 | ||
|
|
0923659a49 | ||
|
|
f9feb08607 | ||
|
|
3b43fe39e5 | ||
|
|
de30fa15b3 | ||
|
|
25a4d7c14d | ||
|
|
c8d3607efe | ||
|
|
a0e73a9aa9 | ||
|
|
eaccf237a4 | ||
|
|
5124266346 | ||
|
|
5cf2ea5a1e | ||
|
|
7a1af5914e | ||
|
|
e61f5eeb76 | ||
|
|
6d92106f9d | ||
|
|
8b06745e86 | ||
|
|
85ad999975 | ||
|
|
4c0f2d84cc | ||
|
|
6fc9803431 | ||
|
|
59a710bae5 | ||
|
|
98c642e1a8 | ||
|
|
e91f4470fb | ||
|
|
b33cc5284f | ||
|
|
eea6db6814 | ||
|
|
ba622c7edf | ||
|
|
b52e89d614 | ||
|
|
49cd3a0494 | ||
|
|
75cc16c5e0 | ||
|
|
e674d22ecd | ||
|
|
dca5011eb1 | ||
|
|
4d8e38b704 | ||
|
|
5ba4352641 | ||
|
|
1b2cc11a97 | ||
|
|
a3591e4bbb | ||
|
|
aa0f5639b1 | ||
|
|
f3abd0d9fc | ||
|
|
4a62aff7d2 | ||
|
|
c78e624eaf | ||
|
|
934cf5d21c | ||
|
|
f252d1395a | ||
|
|
2dc0472c69 | ||
|
|
3e09adc4d1 | ||
|
|
11750b9382 | ||
|
|
30a4c2199a | ||
|
|
406a9f996e | ||
|
|
5373579bd5 | ||
|
|
9b4cc201e7 | ||
|
|
6f2c96c2b6 | ||
|
|
1f6104de8b |
165
.github/workflows/build.yml
vendored
165
.github/workflows/build.yml
vendored
@@ -1,13 +1,14 @@
|
||||
name: Build APK
|
||||
|
||||
on:
|
||||
push:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
XRAY_CORE_VERSION:
|
||||
description: 'Xray core version or commit hash'
|
||||
release_tag:
|
||||
required: false
|
||||
|
||||
type: string
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -16,6 +17,102 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
fetch-depth: '0'
|
||||
|
||||
- name: Restore cached libtun2socks
|
||||
id: cache-libtun2socks-restore
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: ${{ github.workspace }}/libs
|
||||
key: libtun2socks-${{ runner.os }}-${{ hashFiles('.git/modules/badvpn/HEAD') }}-${{ hashFiles('.git/modules/libancillary/HEAD') }}
|
||||
|
||||
- name: Setup Android NDK
|
||||
uses: nttld/setup-ndk@v1
|
||||
id: setup-ndk
|
||||
# Same version as https://gitlab.com/fdroid/fdroiddata/metadata/com.v2ray.ang.yml
|
||||
with:
|
||||
ndk-version: r27
|
||||
add-to-path: true
|
||||
link-to-sdk: true
|
||||
local-cache: true
|
||||
|
||||
- name: Restore Android Symlinks
|
||||
run: |
|
||||
directory="${{ steps.setup-ndk.outputs.ndk-path }}/toolchains/llvm/prebuilt/linux-x86_64/bin"
|
||||
find "$directory" -type l | while read link; do
|
||||
current_target=$(readlink "$link")
|
||||
new_target="$directory/$(basename "$current_target")"
|
||||
ln -sf "$new_target" "$link"
|
||||
echo "Changed $(basename "$link") from $current_target to $new_target"
|
||||
done
|
||||
|
||||
- name: Build libtun2socks
|
||||
if: steps.cache-libtun2socks-restore.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
bash compile-tun2socks.sh
|
||||
tar -xvzf libtun2socks.so.tgz
|
||||
env:
|
||||
NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||
|
||||
- name: Save libtun2socks
|
||||
if: steps.cache-libtun2socks-restore.outputs.cache-hit != 'true'
|
||||
uses: actions/cache/save@v4
|
||||
with:
|
||||
path: ${{ github.workspace }}/libs
|
||||
key: libtun2socks-${{ runner.os }}-${{ hashFiles('.git/modules/badvpn/HEAD') }}-${{ hashFiles('.git/modules/libancillary/HEAD') }}
|
||||
|
||||
- name: Copy libtun2socks
|
||||
run: |
|
||||
cp -r ${{ github.workspace }}/libs ${{ github.workspace }}/V2rayNG/app
|
||||
|
||||
- name: Fetch AndroidLibXrayLite tag
|
||||
run: |
|
||||
pushd AndroidLibXrayLite
|
||||
CURRENT_TAG=$(git describe --tags --abbrev=0)
|
||||
echo "Current tag in this repo: $CURRENT_TAG"
|
||||
echo "CURRENT_TAG=$CURRENT_TAG" >> $GITHUB_ENV
|
||||
popd
|
||||
|
||||
- name: Download libv2ray
|
||||
uses: robinraju/release-downloader@v1
|
||||
with:
|
||||
repository: '2dust/AndroidLibXrayLite'
|
||||
tag: ${{ env.CURRENT_TAG }}
|
||||
fileName: 'libv2ray.aar'
|
||||
out-file-path: V2rayNG/app/libs/
|
||||
|
||||
- name: Restore cached libhysteria2
|
||||
id: cache-libhysteria2-restore
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: ${{ github.workspace }}/hysteria/libs
|
||||
key: libhysteria2-${{ runner.os }}-${{ hashFiles('.git/modules/hysteria/HEAD') }}-${{ hashFiles('libhysteria2.sh') }}
|
||||
|
||||
- name: Setup Golang
|
||||
if: steps.cache-libhysteria2-restore.outputs.cache-hit != 'true'
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: 'AndroidLibXrayLite/go.mod'
|
||||
|
||||
- name: Build libhysteria2
|
||||
if: steps.cache-libhysteria2-restore.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
bash libhysteria2.sh
|
||||
env:
|
||||
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||
|
||||
- name: Save libhysteria2
|
||||
if: steps.cache-libhysteria2-restore.outputs.cache-hit != 'true'
|
||||
uses: actions/cache/save@v4
|
||||
with:
|
||||
path: ${{ github.workspace }}/hysteria/libs
|
||||
key: libhysteria2-${{ runner.os }}-${{ hashFiles('.git/modules/hysteria/HEAD') }}-${{ hashFiles('libhysteria2.sh') }}
|
||||
|
||||
- name: Copy libhysteria2
|
||||
run: |
|
||||
cp -r ${{ github.workspace }}/hysteria/libs ${{ github.workspace }}/V2rayNG/app
|
||||
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
@@ -23,71 +120,51 @@ jobs:
|
||||
distribution: 'temurin'
|
||||
java-version: '21'
|
||||
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.23.2'
|
||||
cache: false
|
||||
|
||||
- name: Patch Go use 600296
|
||||
#https://go-review.googlesource.com/c/go/+/600296
|
||||
run: |
|
||||
cd "$(go env GOROOT)"
|
||||
curl "https://go-review.googlesource.com/changes/go~600296/revisions/5/patch" | base64 -d | patch --verbose -p 1
|
||||
|
||||
- name: Install gomobile
|
||||
run: |
|
||||
go install golang.org/x/mobile/cmd/gomobile@v0.0.0-20240806205939-81131f6468ab
|
||||
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 21 -ldflags='-s -w' ./
|
||||
cp *.aar ${{ github.workspace }}/V2rayNG/app/libs/
|
||||
- name: Decode Keystore
|
||||
uses: timheuer/base64-to-file@v1
|
||||
id: android_keystore
|
||||
with:
|
||||
fileName: "android_keystore.jks"
|
||||
encodedString: ${{ secrets.APP_KEYSTORE_BASE64 }}
|
||||
|
||||
- name: Build APK
|
||||
run: |
|
||||
cd ${{ github.workspace }}/V2rayNG
|
||||
chmod 755 gradlew
|
||||
./gradlew assembleDebug
|
||||
./gradlew licenseFdroidReleaseReport
|
||||
./gradlew assembleRelease -Pandroid.injected.signing.store.file=${{ steps.android_keystore.outputs.filePath }} -Pandroid.injected.signing.store.password=${{ secrets.APP_KEYSTORE_PASSWORD }} -Pandroid.injected.signing.key.alias=${{ secrets.APP_KEYSTORE_ALIAS }} -Pandroid.injected.signing.key.password=${{ secrets.APP_KEY_PASSWORD }}
|
||||
env:
|
||||
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||
|
||||
- name: Upload arm64-v8a APK
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ success() }}
|
||||
with:
|
||||
name: arm64-v8a
|
||||
path: ${{ github.workspace }}/V2rayNG/app/build/outputs/apk/debug/*arm64-v8a*.apk
|
||||
path: ${{ github.workspace }}/V2rayNG/app/build/outputs/apk/*/release/*arm64-v8a*.apk
|
||||
|
||||
- name: Upload armeabi-v7a APK
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ success() }}
|
||||
with:
|
||||
name: armeabi-v7a
|
||||
path: ${{ github.workspace }}/V2rayNG/app/build/outputs/apk/debug/*armeabi-v7a*.apk
|
||||
path: ${{ github.workspace }}/V2rayNG/app/build/outputs/apk/*/release/*armeabi-v7a*.apk
|
||||
|
||||
- name: Upload x86 APK
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ success() }}
|
||||
with:
|
||||
name: x86-apk
|
||||
path: ${{ github.workspace }}/V2rayNG/app/build/outputs/apk/debug/*x86*.apk
|
||||
path: ${{ github.workspace }}/V2rayNG/app/build/outputs/apk/*/release/*x86*.apk
|
||||
|
||||
- name: Upload Other APKs
|
||||
uses: actions/upload-artifact@v4
|
||||
- name: Upload to release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
if: github.event.inputs.release_tag != ''
|
||||
with:
|
||||
name: others-apk
|
||||
path: |
|
||||
${{ github.workspace }}/V2rayNG/app/build/outputs/apk/debug
|
||||
!${{ github.workspace }}/V2rayNG/app/build/outputs/apk/debug/*arm64-v8a*.apk
|
||||
!${{ github.workspace }}/V2rayNG/app/build/outputs/apk/debug/*armeabi-v7a*.apk
|
||||
!${{ github.workspace }}/V2rayNG/app/build/outputs/apk/debug/*x86*.apk
|
||||
file: ${{ github.workspace }}/V2rayNG/app/build/outputs/apk/*playstore*/release/*.apk
|
||||
tag: ${{ github.event.inputs.release_tag }}
|
||||
file_glob: true
|
||||
prerelease: true
|
||||
|
||||
16
.github/workflows/fastlane.yml
vendored
Normal file
16
.github/workflows/fastlane.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
name: Validate Fastlane metadata
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
|
||||
jobs:
|
||||
go:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Validate Fastlane Supply Metadata
|
||||
uses: ashutoshgngwr/validate-fastlane-supply-metadata@v2.0.0
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,3 +3,4 @@
|
||||
V2rayNG/app/release/output.json
|
||||
.idea/
|
||||
.gradle/
|
||||
*.so
|
||||
|
||||
12
.gitmodules
vendored
Normal file
12
.gitmodules
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
[submodule "hysteria"]
|
||||
path = hysteria
|
||||
url = https://github.com/apernet/hysteria
|
||||
[submodule "AndroidLibXrayLite"]
|
||||
path = AndroidLibXrayLite
|
||||
url = https://github.com/2dust/AndroidLibXrayLite
|
||||
[submodule "badvpn"]
|
||||
path = badvpn
|
||||
url = https://github.com/XTLS/badvpn
|
||||
[submodule "libancillary"]
|
||||
path = libancillary
|
||||
url = https://github.com/shadowsocks/libancillary
|
||||
@@ -1,20 +0,0 @@
|
||||
# AndroidLibV2rayLite
|
||||
|
||||
### Preparation
|
||||
- latest Ubuntu environment
|
||||
- At lease 30G free space
|
||||
- Get Repo [AndroidLibV2rayLite](https://github.com/2dust/AndroidLibV2rayLite) or [AndroidLibXrayLite](https://github.com/2dust/AndroidLibXrayLite)
|
||||
### Prepare Go
|
||||
- Go to https://golang.org/doc/install and install latest go
|
||||
- Make sure `go version` works as expected
|
||||
### Prepare gomobile
|
||||
- Go to https://pkg.go.dev/golang.org/x/mobile/cmd/gomobile and install gomobile
|
||||
- export PATH=$PATH:~/go/bin
|
||||
- Make sure `gomobile init` works as expected
|
||||
### Prepare NDK
|
||||
- Go to https://developer.android.com/ndk/downloads and install latest NDK
|
||||
- export PATH=$PATH:<wherever you ndk is located>
|
||||
- Make sure `ndk-build -v` works as expected
|
||||
### Make
|
||||
- sudo apt install make
|
||||
- Read and understand [build script](https://github.com/2dust/AndroidLibV2rayLite/blob/master/Makefile)
|
||||
1
AndroidLibXrayLite
Submodule
1
AndroidLibXrayLite
Submodule
Submodule AndroidLibXrayLite added at 08c4a038f0
@@ -3,7 +3,7 @@
|
||||
A V2Ray client for Android, support [Xray core](https://github.com/XTLS/Xray-core) and [v2fly core](https://github.com/v2fly/v2ray-core)
|
||||
|
||||
[](https://developer.android.com/about/versions/lollipop)
|
||||
[](https://kotlinlang.org)
|
||||
[](https://kotlinlang.org)
|
||||
[](https://github.com/2dust/v2rayNG/commits/master)
|
||||
[](https://www.codefactor.io/repository/github/2dust/v2rayng)
|
||||
[](https://github.com/2dust/v2rayNG/releases)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
id("com.jaredsburrows.license")
|
||||
}
|
||||
|
||||
android {
|
||||
@@ -11,20 +12,26 @@ android {
|
||||
applicationId = "com.v2ray.ang"
|
||||
minSdk = 21
|
||||
targetSdk = 35
|
||||
versionCode = 613
|
||||
versionName = "1.9.17"
|
||||
versionCode = 636
|
||||
versionName = "1.9.39"
|
||||
multiDexEnabled = true
|
||||
|
||||
val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';')
|
||||
splits {
|
||||
abi {
|
||||
isEnable = true
|
||||
reset()
|
||||
if (abiFilterList != null && abiFilterList.isNotEmpty()) {
|
||||
include(*abiFilterList.toTypedArray())
|
||||
} else {
|
||||
include(
|
||||
"arm64-v8a",
|
||||
"armeabi-v7a",
|
||||
"x86_64",
|
||||
"x86"
|
||||
)
|
||||
isUniversalApk = true
|
||||
}
|
||||
isUniversalApk = abiFilterList.isNullOrEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +48,19 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
flavorDimensions.add("distribution")
|
||||
productFlavors {
|
||||
create("fdroid") {
|
||||
dimension = "distribution"
|
||||
applicationIdSuffix = ".fdroid"
|
||||
buildConfigField("String", "DISTRIBUTION", "\"F-Droid\"")
|
||||
}
|
||||
create("playstore") {
|
||||
dimension = "distribution"
|
||||
buildConfigField("String", "DISTRIBUTION", "\"Play Store\"")
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
getByName("main") {
|
||||
jniLibs.srcDirs("libs")
|
||||
@@ -49,15 +69,35 @@ android {
|
||||
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
isCoreLibraryDesugaringEnabled = true
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_11.toString()
|
||||
jvmTarget = JavaVersion.VERSION_17.toString()
|
||||
}
|
||||
|
||||
applicationVariants.all {
|
||||
val variant = this
|
||||
val isFdroid = variant.productFlavors.any { it.name == "fdroid" }
|
||||
if (isFdroid) {
|
||||
val versionCodes =
|
||||
mapOf("armeabi-v7a" to 2, "arm64-v8a" to 1, "x86" to 4, "x86_64" to 3, "universal" to 0
|
||||
)
|
||||
|
||||
variant.outputs
|
||||
.map { it as com.android.build.gradle.internal.api.ApkVariantOutputImpl }
|
||||
.forEach { output ->
|
||||
val abi = output.getFilter("ABI") ?: "universal"
|
||||
output.outputFileName = "v2rayNG_${variant.versionName}-fdroid_${abi}.apk"
|
||||
if (versionCodes.containsKey(abi)) {
|
||||
output.versionCodeOverride =
|
||||
(100 * variant.versionCode + versionCodes[abi]!!).plus(5000000)
|
||||
} else {
|
||||
return@forEach
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val versionCodes =
|
||||
mapOf("armeabi-v7a" to 4, "arm64-v8a" to 4, "x86" to 4, "x86_64" to 4, "universal" to 4)
|
||||
|
||||
@@ -78,6 +118,7 @@ android {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
viewBinding = true
|
||||
@@ -103,6 +144,7 @@ dependencies {
|
||||
implementation(libs.androidx.constraintlayout)
|
||||
implementation(libs.preference.ktx)
|
||||
implementation(libs.recyclerview)
|
||||
implementation(libs.androidx.swiperefreshlayout)
|
||||
|
||||
// UI Libraries
|
||||
implementation(libs.material)
|
||||
@@ -115,16 +157,15 @@ dependencies {
|
||||
implementation(libs.gson)
|
||||
|
||||
// Reactive and Utility Libraries
|
||||
implementation(libs.rxjava)
|
||||
implementation(libs.rxandroid)
|
||||
implementation(libs.rxpermissions)
|
||||
implementation(libs.kotlinx.coroutines.android)
|
||||
implementation(libs.kotlinx.coroutines.core)
|
||||
|
||||
// Language and Processing Libraries
|
||||
implementation(libs.language.base)
|
||||
implementation(libs.language.json)
|
||||
|
||||
// Intent and Utility Libraries
|
||||
implementation(libs.quickie.bundled)
|
||||
implementation(libs.quickie.foss)
|
||||
implementation(libs.core)
|
||||
|
||||
// AndroidX Lifecycle and Architecture Components
|
||||
@@ -145,4 +186,5 @@ dependencies {
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
testImplementation(libs.org.mockito.mockito.inline)
|
||||
testImplementation(libs.mockito.kotlin)
|
||||
coreLibraryDesugaring(libs.desugar.jdk.libs)
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -99,10 +99,5 @@
|
||||
"domain": [
|
||||
"geosite:cn"
|
||||
]
|
||||
},
|
||||
{
|
||||
"remarks": "最终代理",
|
||||
"port": "0-65535",
|
||||
"outboundTag": "proxy"
|
||||
}
|
||||
]
|
||||
@@ -40,10 +40,5 @@
|
||||
"ip": [
|
||||
"geoip:ir"
|
||||
]
|
||||
},
|
||||
{
|
||||
"remarks": "Final Agent",
|
||||
"port": "0-65535",
|
||||
"outboundTag": "proxy"
|
||||
}
|
||||
]
|
||||
|
||||
1285
V2rayNG/app/src/main/assets/open_source_licenses.html
Normal file
1285
V2rayNG/app/src/main/assets/open_source_licenses.html
Normal file
File diff suppressed because it is too large
Load Diff
@@ -45,9 +45,4 @@ class AngApplication : MultiDexApplication() {
|
||||
SettingsManager.initRoutingRulesets(this)
|
||||
}
|
||||
|
||||
fun getPackageInfo(packageName: String) = packageManager.getPackageInfo(
|
||||
packageName, if (Build.VERSION.SDK_INT >= 28) PackageManager.GET_SIGNING_CERTIFICATES
|
||||
else @Suppress("DEPRECATION") PackageManager.GET_SIGNATURES
|
||||
)!!
|
||||
|
||||
}
|
||||
|
||||
@@ -22,8 +22,10 @@ object AppConfig {
|
||||
const val PREF_BYPASS_APPS = "pref_bypass_apps"
|
||||
const val PREF_LOCAL_DNS_ENABLED = "pref_local_dns_enabled"
|
||||
const val PREF_FAKE_DNS_ENABLED = "pref_fake_dns_enabled"
|
||||
const val PREF_APPEND_HTTP_PROXY = "pref_append_http_proxy"
|
||||
const val PREF_LOCAL_DNS_PORT = "pref_local_dns_port"
|
||||
const val PREF_VPN_DNS = "pref_vpn_dns"
|
||||
const val PREF_VPN_BYPASS_LAN = "pref_vpn_bypass_lan"
|
||||
const val PREF_ROUTING_DOMAIN_STRATEGY = "pref_routing_domain_strategy"
|
||||
const val PREF_ROUTING_RULESET = "pref_routing_ruleset"
|
||||
const val PREF_MUX_ENABLED = "pref_mux_enabled"
|
||||
@@ -47,9 +49,9 @@ object AppConfig {
|
||||
const val PREF_PROXY_SHARING = "pref_proxy_sharing_enabled"
|
||||
const val PREF_ALLOW_INSECURE = "pref_allow_insecure"
|
||||
const val PREF_SOCKS_PORT = "pref_socks_port"
|
||||
const val PREF_HTTP_PORT = "pref_http_port"
|
||||
const val PREF_REMOTE_DNS = "pref_remote_dns"
|
||||
const val PREF_DOMESTIC_DNS = "pref_domestic_dns"
|
||||
const val PREF_DNS_HOSTS = "pref_dns_hosts"
|
||||
const val PREF_DELAY_TEST_URL = "pref_delay_test_url"
|
||||
const val PREF_LOGLEVEL = "pref_core_loglevel"
|
||||
const val PREF_MODE = "pref_mode"
|
||||
@@ -111,7 +113,6 @@ object AppConfig {
|
||||
/** Ports and addresses for various services. */
|
||||
const val PORT_LOCAL_DNS = "10853"
|
||||
const val PORT_SOCKS = "10808"
|
||||
const val PORT_HTTP = "10809"
|
||||
const val WIREGUARD_LOCAL_ADDRESS_V4 = "172.16.0.2/32"
|
||||
const val WIREGUARD_LOCAL_ADDRESS_V6 = "2606:4700:110:8f81:d551:a0:532e:a2b3/128"
|
||||
const val WIREGUARD_LOCAL_MTU = "1420"
|
||||
|
||||
@@ -9,6 +9,7 @@ data class Hysteria2Bean(
|
||||
val http: Socks5Bean? = null,
|
||||
val tls: TlsBean? = null,
|
||||
val transport: TransportBean? = null,
|
||||
val bandwidth: BandwidthBean? = null,
|
||||
) {
|
||||
data class ObfsBean(
|
||||
val type: String?,
|
||||
@@ -37,4 +38,9 @@ data class Hysteria2Bean(
|
||||
val hopInterval: String?,
|
||||
)
|
||||
}
|
||||
|
||||
data class BandwidthBean(
|
||||
val down: String?,
|
||||
val up: String?,
|
||||
)
|
||||
}
|
||||
@@ -5,7 +5,6 @@ enum class NetworkType(val type: String) {
|
||||
KCP("kcp"),
|
||||
WS("ws"),
|
||||
HTTP_UPGRADE("httpupgrade"),
|
||||
SPLIT_HTTP("splithttp"),
|
||||
XHTTP("xhttp"),
|
||||
HTTP("http"),
|
||||
H2("h2"),
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.v2ray.ang.dto
|
||||
|
||||
import com.v2ray.ang.AppConfig.LOOPBACK
|
||||
import com.v2ray.ang.AppConfig.PORT_SOCKS
|
||||
import com.v2ray.ang.AppConfig.TAG_BLOCKED
|
||||
import com.v2ray.ang.AppConfig.TAG_DIRECT
|
||||
import com.v2ray.ang.AppConfig.TAG_PROXY
|
||||
@@ -44,6 +46,7 @@ data class ProfileItem(
|
||||
var spiderX: String? = null,
|
||||
|
||||
var secretKey: String? = null,
|
||||
var preSharedKey: String? = null,
|
||||
var localAddress: String? = null,
|
||||
var reserved: String? = null,
|
||||
var mtu: Int? = null,
|
||||
@@ -52,6 +55,8 @@ data class ProfileItem(
|
||||
var portHopping: String? = null,
|
||||
var portHoppingInterval: String? = null,
|
||||
var pinSHA256: String? = null,
|
||||
var bandwidthDown: String? = null,
|
||||
var bandwidthUp: String? = null,
|
||||
|
||||
) {
|
||||
companion object {
|
||||
@@ -65,6 +70,9 @@ data class ProfileItem(
|
||||
}
|
||||
|
||||
fun getServerAddressAndPort(): String {
|
||||
if (server.isNullOrEmpty() && configType == EConfigType.CUSTOM) {
|
||||
return "$LOOPBACK:$PORT_SOCKS"
|
||||
}
|
||||
return Utils.getIpv6Address(server) + ":" + serverPort
|
||||
}
|
||||
|
||||
|
||||
@@ -9,5 +9,5 @@ data class RulesetItem(
|
||||
var network: String? = null,
|
||||
var protocol: List<String>? = null,
|
||||
var enabled: Boolean = true,
|
||||
var looked: Boolean? = false,
|
||||
var locked: Boolean? = false,
|
||||
)
|
||||
@@ -195,13 +195,14 @@ data class V2rayConfig(
|
||||
|
||||
data class WireGuardBean(
|
||||
var publicKey: String = "",
|
||||
var preSharedKey: String = "",
|
||||
var endpoint: String = ""
|
||||
)
|
||||
}
|
||||
|
||||
data class StreamSettingsBean(
|
||||
var network: String = AppConfig.DEFAULT_NETWORK,
|
||||
var security: String = "",
|
||||
var security: String? = null,
|
||||
var tcpSettings: TcpSettingsBean? = null,
|
||||
var kcpSettings: KcpSettingsBean? = null,
|
||||
var wsSettings: WsSettingsBean? = null,
|
||||
@@ -356,7 +357,7 @@ data class V2rayConfig(
|
||||
authority: String?
|
||||
): String? {
|
||||
var sni: String? = null
|
||||
network = transport
|
||||
network = if (transport.isEmpty()) NetworkType.TCP.type else transport
|
||||
when (network) {
|
||||
NetworkType.TCP.type -> {
|
||||
val tcpSetting = TcpSettingsBean()
|
||||
@@ -367,11 +368,11 @@ data class V2rayConfig(
|
||||
requestObj.headers.Host = host.orEmpty().split(",").map { it.trim() }.filter { it.isNotEmpty() }
|
||||
requestObj.path = path.orEmpty().split(",").map { it.trim() }.filter { it.isNotEmpty() }
|
||||
tcpSetting.header.request = requestObj
|
||||
sni = requestObj.headers.Host?.getOrNull(0) ?: sni
|
||||
sni = requestObj.headers.Host?.getOrNull(0)
|
||||
}
|
||||
} else {
|
||||
tcpSetting.header.type = "none"
|
||||
sni = host.orEmpty()
|
||||
sni = host
|
||||
}
|
||||
tcpSettings = tcpSetting
|
||||
}
|
||||
@@ -390,7 +391,7 @@ data class V2rayConfig(
|
||||
NetworkType.WS.type -> {
|
||||
val wssetting = WsSettingsBean()
|
||||
wssetting.headers.Host = host.orEmpty()
|
||||
sni = wssetting.headers.Host
|
||||
sni = host
|
||||
wssetting.path = path ?: "/"
|
||||
wsSettings = wssetting
|
||||
}
|
||||
@@ -398,15 +399,15 @@ data class V2rayConfig(
|
||||
NetworkType.HTTP_UPGRADE.type -> {
|
||||
val httpupgradeSetting = HttpupgradeSettingsBean()
|
||||
httpupgradeSetting.host = host.orEmpty()
|
||||
sni = httpupgradeSetting.host
|
||||
sni = host
|
||||
httpupgradeSetting.path = path ?: "/"
|
||||
httpupgradeSettings = httpupgradeSetting
|
||||
}
|
||||
|
||||
NetworkType.SPLIT_HTTP.type, NetworkType.XHTTP.type -> {
|
||||
NetworkType.XHTTP.type -> {
|
||||
val xhttpSetting = XhttpSettingsBean()
|
||||
xhttpSetting.host = host.orEmpty()
|
||||
sni = xhttpSetting.host
|
||||
sni = host
|
||||
xhttpSetting.path = path ?: "/"
|
||||
xhttpSettings = xhttpSetting
|
||||
}
|
||||
@@ -415,7 +416,7 @@ data class V2rayConfig(
|
||||
network = NetworkType.H2.type
|
||||
val h2Setting = HttpSettingsBean()
|
||||
h2Setting.host = host.orEmpty().split(",").map { it.trim() }.filter { it.isNotEmpty() }
|
||||
sni = h2Setting.host.getOrNull(0) ?: sni
|
||||
sni = h2Setting.host.getOrNull(0)
|
||||
h2Setting.path = path ?: "/"
|
||||
httpSettings = h2Setting
|
||||
}
|
||||
@@ -435,7 +436,7 @@ data class V2rayConfig(
|
||||
grpcSetting.authority = authority.orEmpty()
|
||||
grpcSetting.idle_timeout = 60
|
||||
grpcSetting.health_check_timeout = 20
|
||||
sni = authority.orEmpty()
|
||||
sni = authority
|
||||
grpcSettings = grpcSetting
|
||||
}
|
||||
}
|
||||
@@ -452,15 +453,16 @@ data class V2rayConfig(
|
||||
shortId: String?,
|
||||
spiderX: String?
|
||||
) {
|
||||
security = streamSecurity
|
||||
security = if (streamSecurity.isEmpty()) null else streamSecurity
|
||||
if (security == null) return
|
||||
val tlsSetting = TlsSettingsBean(
|
||||
allowInsecure = allowInsecure,
|
||||
serverName = sni,
|
||||
fingerprint = fingerprint,
|
||||
serverName = if (sni.isNullOrEmpty()) null else sni,
|
||||
fingerprint = if (fingerprint.isNullOrEmpty()) null else fingerprint,
|
||||
alpn = if (alpns.isNullOrEmpty()) null else alpns.split(",").map { it.trim() }.filter { it.isNotEmpty() },
|
||||
publicKey = publicKey,
|
||||
shortId = shortId,
|
||||
spiderX = spiderX
|
||||
publicKey = if (publicKey.isNullOrEmpty()) null else publicKey,
|
||||
shortId = if (shortId.isNullOrEmpty()) null else shortId,
|
||||
spiderX = if (spiderX.isNullOrEmpty()) null else spiderX,
|
||||
)
|
||||
if (security == AppConfig.TLS) {
|
||||
tlsSettings = tlsSetting
|
||||
@@ -474,9 +476,9 @@ data class V2rayConfig(
|
||||
|
||||
data class MuxBean(
|
||||
var enabled: Boolean,
|
||||
var concurrency: Int = 8,
|
||||
var xudpConcurrency: Int = 8,
|
||||
var xudpProxyUDP443: String = "",
|
||||
var concurrency: Int? = null,
|
||||
var xudpConcurrency: Int? = null,
|
||||
var xudpProxyUDP443: String? = null,
|
||||
)
|
||||
|
||||
fun getServerAddress(): String? {
|
||||
@@ -594,7 +596,7 @@ data class V2rayConfig(
|
||||
)
|
||||
}
|
||||
|
||||
NetworkType.SPLIT_HTTP.type, NetworkType.XHTTP.type -> {
|
||||
NetworkType.XHTTP.type -> {
|
||||
val xhttpSettings = streamSettings?.xhttpSettings ?: return null
|
||||
listOf(
|
||||
"",
|
||||
|
||||
@@ -95,4 +95,4 @@ inline fun <reified T : Serializable> Intent.serializable(key: String): T? = whe
|
||||
else -> @Suppress("DEPRECATION") getSerializableExtra(key) as? T
|
||||
}
|
||||
|
||||
inline fun CharSequence?.isNotNullEmpty(): Boolean = (this != null && this.isNotEmpty())
|
||||
fun CharSequence?.isNotNullEmpty(): Boolean = (this != null && this.isNotEmpty())
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.v2ray.ang.fmt
|
||||
|
||||
import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.dto.NetworkType
|
||||
import com.v2ray.ang.dto.ProfileItem
|
||||
import com.v2ray.ang.extension.isNotNullEmpty
|
||||
@@ -31,8 +32,6 @@ open class FmtBase {
|
||||
|
||||
fun getItemFormQuery(config: ProfileItem, queryParam: Map<String, String>, allowInsecure: Boolean) {
|
||||
config.network = queryParam["type"] ?: NetworkType.TCP.type
|
||||
//TODO
|
||||
if (config.network == NetworkType.SPLIT_HTTP.type) config.network = NetworkType.XHTTP.type
|
||||
config.headerType = queryParam["headerType"]
|
||||
config.host = queryParam["host"]
|
||||
config.path = queryParam["path"]
|
||||
@@ -47,6 +46,9 @@ open class FmtBase {
|
||||
config.xhttpExtra = queryParam["extra"]
|
||||
|
||||
config.security = queryParam["security"]
|
||||
if (config.security != AppConfig.TLS && config.security != AppConfig.REALITY) {
|
||||
config.security = null
|
||||
}
|
||||
config.insecure = if (queryParam["allowInsecure"].isNullOrEmpty()) {
|
||||
allowInsecure
|
||||
} else {
|
||||
@@ -91,7 +93,7 @@ open class FmtBase {
|
||||
config.path.let { if (it.isNotNullEmpty()) dicQuery["path"] = it.orEmpty() }
|
||||
}
|
||||
|
||||
NetworkType.SPLIT_HTTP, NetworkType.XHTTP -> {
|
||||
NetworkType.XHTTP -> {
|
||||
config.host.let { if (it.isNotNullEmpty()) dicQuery["host"] = it.orEmpty() }
|
||||
config.path.let { if (it.isNotNullEmpty()) dicQuery["path"] = it.orEmpty() }
|
||||
config.xhttpMode.let { if (it.isNotNullEmpty()) dicQuery["mode"] = it.orEmpty() }
|
||||
|
||||
@@ -85,6 +85,12 @@ object Hysteria2Fmt : FmtBase() {
|
||||
)
|
||||
)
|
||||
|
||||
val bandwidth = if (config.bandwidthDown.isNullOrEmpty() || config.bandwidthUp.isNullOrEmpty()) null else
|
||||
Hysteria2Bean.BandwidthBean(
|
||||
down = config.bandwidthDown,
|
||||
up = config.bandwidthUp,
|
||||
)
|
||||
|
||||
val server =
|
||||
if (config.portHopping.isNullOrEmpty())
|
||||
config.getServerAddressAndPort()
|
||||
@@ -96,6 +102,7 @@ object Hysteria2Fmt : FmtBase() {
|
||||
auth = config.password,
|
||||
obfs = obfs,
|
||||
transport = transport,
|
||||
bandwidth = bandwidth,
|
||||
socks5 = Hysteria2Bean.Socks5Bean(
|
||||
listen = "$LOOPBACK:${socksPort}",
|
||||
),
|
||||
|
||||
@@ -37,12 +37,18 @@ object ShadowsocksFmt : FmtBase() {
|
||||
|
||||
if (!uri.rawQuery.isNullOrEmpty()) {
|
||||
val queryParam = getQueryParam(uri)
|
||||
|
||||
if (queryParam["plugin"] == "obfs-local" && queryParam["obfs"] == "http") {
|
||||
if (queryParam["plugin"]?.contains("obfs=http") == true) {
|
||||
val queryPairs = HashMap<String, String>()
|
||||
for (pair in queryParam["plugin"]?.split(";") ?: listOf()) {
|
||||
val idx = pair.split("=")
|
||||
if (idx.count() == 2) {
|
||||
queryPairs.put(idx.first(), idx.last())
|
||||
}
|
||||
}
|
||||
config.network = NetworkType.TCP.type
|
||||
config.headerType = "http"
|
||||
config.host = queryParam["obfs-host"]
|
||||
config.path = queryParam["path"]
|
||||
config.host = queryPairs["obfs-host"]
|
||||
config.path = queryPairs["path"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +108,30 @@ object ShadowsocksFmt : FmtBase() {
|
||||
server.method = profileItem.method
|
||||
}
|
||||
|
||||
val sni = outboundBean?.streamSettings?.populateTransportSettings(
|
||||
profileItem.network.orEmpty(),
|
||||
profileItem.headerType,
|
||||
profileItem.host,
|
||||
profileItem.path,
|
||||
profileItem.seed,
|
||||
profileItem.quicSecurity,
|
||||
profileItem.quicKey,
|
||||
profileItem.mode,
|
||||
profileItem.serviceName,
|
||||
profileItem.authority,
|
||||
)
|
||||
|
||||
outboundBean?.streamSettings?.populateTlsSettings(
|
||||
profileItem.security.orEmpty(),
|
||||
profileItem.insecure == true,
|
||||
if (profileItem.sni.isNullOrEmpty()) sni else profileItem.sni,
|
||||
profileItem.fingerPrint,
|
||||
profileItem.alpn,
|
||||
profileItem.publicKey,
|
||||
profileItem.shortId,
|
||||
profileItem.spiderX,
|
||||
)
|
||||
|
||||
return outboundBean
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ object TrojanFmt : FmtBase() {
|
||||
server.flow = profileItem.flow
|
||||
}
|
||||
|
||||
outboundBean?.streamSettings?.populateTransportSettings(
|
||||
val sni = outboundBean?.streamSettings?.populateTransportSettings(
|
||||
profileItem.network.orEmpty(),
|
||||
profileItem.headerType,
|
||||
profileItem.host,
|
||||
@@ -68,7 +68,7 @@ object TrojanFmt : FmtBase() {
|
||||
outboundBean?.streamSettings?.populateTlsSettings(
|
||||
profileItem.security.orEmpty(),
|
||||
profileItem.insecure == true,
|
||||
profileItem.sni,
|
||||
if (profileItem.sni.isNullOrEmpty()) sni else profileItem.sni,
|
||||
profileItem.fingerPrint,
|
||||
profileItem.alpn,
|
||||
profileItem.publicKey,
|
||||
|
||||
@@ -50,7 +50,7 @@ object VlessFmt : FmtBase() {
|
||||
vnext.users[0].flow = profileItem.flow
|
||||
}
|
||||
|
||||
outboundBean?.streamSettings?.populateTransportSettings(
|
||||
val sni = outboundBean?.streamSettings?.populateTransportSettings(
|
||||
profileItem.network.orEmpty(),
|
||||
profileItem.headerType,
|
||||
profileItem.host,
|
||||
@@ -68,7 +68,7 @@ object VlessFmt : FmtBase() {
|
||||
outboundBean?.streamSettings?.populateTlsSettings(
|
||||
profileItem.security.orEmpty(),
|
||||
profileItem.insecure == true,
|
||||
profileItem.sni,
|
||||
if (profileItem.sni.isNullOrEmpty()) sni else profileItem.sni,
|
||||
profileItem.fingerPrint,
|
||||
profileItem.alpn,
|
||||
profileItem.publicKey,
|
||||
|
||||
@@ -153,7 +153,7 @@ object VmessFmt : FmtBase() {
|
||||
vnext.users[0].security = profileItem.method
|
||||
}
|
||||
|
||||
outboundBean?.streamSettings?.populateTransportSettings(
|
||||
val sni = outboundBean?.streamSettings?.populateTransportSettings(
|
||||
profileItem.network.orEmpty(),
|
||||
profileItem.headerType,
|
||||
profileItem.host,
|
||||
@@ -169,7 +169,7 @@ object VmessFmt : FmtBase() {
|
||||
outboundBean?.streamSettings?.populateTlsSettings(
|
||||
profileItem.security.orEmpty(),
|
||||
profileItem.insecure == true,
|
||||
profileItem.sni,
|
||||
if (profileItem.sni.isNullOrEmpty()) sni else profileItem.sni,
|
||||
profileItem.fingerPrint,
|
||||
profileItem.alpn,
|
||||
null,
|
||||
|
||||
@@ -22,9 +22,10 @@ object WireguardFmt : FmtBase() {
|
||||
config.server = uri.idnHost
|
||||
config.serverPort = uri.port.toString()
|
||||
|
||||
config.secretKey = uri.userInfo
|
||||
config.secretKey = uri.userInfo.orEmpty()
|
||||
config.localAddress = (queryParam["address"] ?: WIREGUARD_LOCAL_ADDRESS_V4)
|
||||
config.publicKey = queryParam["publickey"].orEmpty()
|
||||
config.preSharedKey = queryParam["presharedkey"].orEmpty()
|
||||
config.mtu = Utils.parseInt(queryParam["mtu"] ?: AppConfig.WIREGUARD_LOCAL_MTU)
|
||||
config.reserved = (queryParam["reserved"] ?: "0,0,0")
|
||||
|
||||
@@ -33,33 +34,75 @@ object WireguardFmt : FmtBase() {
|
||||
|
||||
fun parseWireguardConfFile(str: String): ProfileItem? {
|
||||
val config = ProfileItem.create(EConfigType.WIREGUARD)
|
||||
val queryParam: MutableMap<String, String> = mutableMapOf()
|
||||
|
||||
val interfaceParams: MutableMap<String, String> = mutableMapOf()
|
||||
val peerParams: MutableMap<String, String> = mutableMapOf()
|
||||
|
||||
var currentSection: String? = null
|
||||
|
||||
str.lines().forEach { line ->
|
||||
val trimmedLine = line.trim()
|
||||
|
||||
if (trimmedLine.isEmpty() || trimmedLine.startsWith("#")) {
|
||||
return@forEach
|
||||
}
|
||||
|
||||
when {
|
||||
trimmedLine.startsWith("[Interface]", ignoreCase = true) -> currentSection = "Interface"
|
||||
trimmedLine.startsWith("[Peer]", ignoreCase = true) -> currentSection = "Peer"
|
||||
trimmedLine.isBlank() || trimmedLine.startsWith("#") -> Unit // Skip blank lines or comments
|
||||
currentSection != null -> {
|
||||
val (key, value) = trimmedLine.split("=").map { it.trim() }
|
||||
queryParam[key.lowercase()] = value // Store the key in lowercase for case-insensitivity
|
||||
else -> {
|
||||
if (currentSection != null) {
|
||||
val parts = trimmedLine.split("=", limit = 2).map { it.trim() }
|
||||
if (parts.size == 2) {
|
||||
val key = parts[0].lowercase()
|
||||
val value = parts[1]
|
||||
when (currentSection) {
|
||||
"Interface" -> interfaceParams[key] = value
|
||||
"Peer" -> peerParams[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config.secretKey = queryParam["privatekey"].orEmpty()
|
||||
config.localAddress = (queryParam["address"] ?: WIREGUARD_LOCAL_ADDRESS_V4)
|
||||
config.publicKey = queryParam["publickey"].orEmpty()
|
||||
config.mtu = Utils.parseInt(queryParam["mtu"] ?: AppConfig.WIREGUARD_LOCAL_MTU)
|
||||
config.reserved = (queryParam["reserved"] ?: "0,0,0")
|
||||
config.secretKey = interfaceParams["privatekey"].orEmpty()
|
||||
config.remarks = System.currentTimeMillis().toString()
|
||||
config.localAddress = interfaceParams["address"] ?: WIREGUARD_LOCAL_ADDRESS_V4
|
||||
config.mtu = Utils.parseInt(interfaceParams["mtu"] ?: AppConfig.WIREGUARD_LOCAL_MTU)
|
||||
config.publicKey = peerParams["publickey"].orEmpty()
|
||||
config.preSharedKey = peerParams["presharedkey"].orEmpty()
|
||||
val endpoint = peerParams["endpoint"].orEmpty()
|
||||
val endpointParts = endpoint.split(":", limit = 2)
|
||||
if (endpointParts.size == 2) {
|
||||
config.server = endpointParts[0]
|
||||
config.serverPort = endpointParts[1]
|
||||
} else {
|
||||
config.server = endpoint
|
||||
config.serverPort = ""
|
||||
}
|
||||
config.reserved = peerParams["reserved"] ?: "0,0,0"
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
fun toOutbound(profileItem: ProfileItem): OutboundBean? {
|
||||
val outboundBean = OutboundBean.create(EConfigType.WIREGUARD)
|
||||
|
||||
outboundBean?.settings?.let { wireguard ->
|
||||
wireguard.secretKey = profileItem.secretKey
|
||||
wireguard.address = (profileItem.localAddress ?: WIREGUARD_LOCAL_ADDRESS_V4).split(",")
|
||||
wireguard.peers?.firstOrNull()?.let { peer ->
|
||||
peer.publicKey = profileItem.publicKey.orEmpty()
|
||||
peer.preSharedKey = profileItem.preSharedKey.orEmpty()
|
||||
peer.endpoint = Utils.getIpv6Address(profileItem.server) + ":${profileItem.serverPort}"
|
||||
}
|
||||
wireguard.mtu = profileItem.mtu
|
||||
wireguard.reserved = profileItem.reserved?.split(",")?.map { it.toInt() }
|
||||
}
|
||||
|
||||
return outboundBean
|
||||
}
|
||||
|
||||
fun toUri(config: ProfileItem): String {
|
||||
val dicQuery = HashMap<String, String>()
|
||||
@@ -72,24 +115,10 @@ object WireguardFmt : FmtBase() {
|
||||
if (config.mtu != null) {
|
||||
dicQuery["mtu"] = config.mtu.toString()
|
||||
}
|
||||
if (config.preSharedKey != null) {
|
||||
dicQuery["presharedkey"] = Utils.removeWhiteSpace(config.preSharedKey).orEmpty()
|
||||
}
|
||||
|
||||
return toUri(config, config.secretKey, dicQuery)
|
||||
}
|
||||
|
||||
fun toOutbound(profileItem: ProfileItem): OutboundBean? {
|
||||
val outboundBean = OutboundBean.create(EConfigType.WIREGUARD)
|
||||
|
||||
outboundBean?.settings?.let { wireguard ->
|
||||
wireguard.secretKey = profileItem.secretKey
|
||||
wireguard.address = (profileItem.localAddress ?: WIREGUARD_LOCAL_ADDRESS_V4).split(",")
|
||||
wireguard.peers?.first()?.publicKey = profileItem.publicKey.orEmpty()
|
||||
wireguard.peers?.first()?.endpoint = Utils.getIpv6Address(profileItem.server) + ":${profileItem.serverPort}"
|
||||
wireguard.mtu = profileItem.mtu?.toInt()
|
||||
wireguard.reserved = profileItem.reserved?.split(",")?.map { it.toInt() }
|
||||
}
|
||||
|
||||
return outboundBean
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -138,11 +138,11 @@ object AngConfigManager {
|
||||
if (sb.count() > 0) {
|
||||
Utils.setClipboard(context, sb.toString())
|
||||
}
|
||||
return sb.lines().count()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,6 +170,13 @@ object AngConfigManager {
|
||||
if (guid == null) return -1
|
||||
val result = V2rayConfigManager.getV2rayConfig(context, guid)
|
||||
if (result.status) {
|
||||
val config = MmkvManager.decodeServerConfig(guid)
|
||||
if (config?.configType == EConfigType.HYSTERIA2) {
|
||||
val socksPort = Utils.findFreePort(listOf(100 + SettingsManager.getSocksPort(), 0))
|
||||
val hy2Config = Hysteria2Fmt.toNativeConfig(config, socksPort)
|
||||
Utils.setClipboard(context, JsonUtil.toJsonPretty(hy2Config) + "\n" + result.content)
|
||||
return 0
|
||||
}
|
||||
Utils.setClipboard(context, result.content)
|
||||
} else {
|
||||
return -1
|
||||
@@ -280,7 +287,7 @@ object AngConfigManager {
|
||||
val config = CustomFmt.parse(JsonUtil.toJson(srv)) ?: continue
|
||||
config.subscriptionId = subid
|
||||
val key = MmkvManager.encodeServerConfig("", config)
|
||||
MmkvManager.encodeServerRaw(key, JsonUtil.toJsonPretty(srv)?:"")
|
||||
MmkvManager.encodeServerRaw(key, JsonUtil.toJsonPretty(srv) ?: "")
|
||||
count += 1
|
||||
}
|
||||
return count
|
||||
|
||||
@@ -164,18 +164,22 @@ object MmkvManager {
|
||||
}
|
||||
}
|
||||
|
||||
fun removeAllServer() {
|
||||
fun removeAllServer(): Int {
|
||||
val count = profileFullStorage.allKeys()?.count() ?: 0
|
||||
mainStorage.clearAll()
|
||||
profileFullStorage.clearAll()
|
||||
//profileStorage.clearAll()
|
||||
serverAffStorage.clearAll()
|
||||
return count
|
||||
}
|
||||
|
||||
fun removeInvalidServer(guid: String) {
|
||||
fun removeInvalidServer(guid: String): Int {
|
||||
var count = 0
|
||||
if (guid.isNotEmpty()) {
|
||||
decodeServerAffiliationInfo(guid)?.let { aff ->
|
||||
if (aff.testDelayMillis < 0L) {
|
||||
removeServer(guid)
|
||||
count++
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -183,10 +187,12 @@ object MmkvManager {
|
||||
decodeServerAffiliationInfo(key)?.let { aff ->
|
||||
if (aff.testDelayMillis < 0L) {
|
||||
removeServer(key)
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
fun encodeServerRaw(guid: String, config: String) {
|
||||
@@ -336,17 +342,13 @@ object MmkvManager {
|
||||
}
|
||||
|
||||
fun decodeSettingsBool(key: String): Boolean {
|
||||
return settingsStorage.decodeBool(key,false)
|
||||
return settingsStorage.decodeBool(key, false)
|
||||
}
|
||||
|
||||
fun decodeSettingsBool(key: String, defaultValue: Boolean): Boolean {
|
||||
return settingsStorage.decodeBool(key, defaultValue)
|
||||
}
|
||||
|
||||
fun decodeSettingsInt(key: String, defaultValue: Int): Int {
|
||||
return settingsStorage.decodeInt(key, defaultValue)
|
||||
}
|
||||
|
||||
fun decodeSettingsStringSet(key: String): MutableSet<String>? {
|
||||
return settingsStorage.decodeStringSet(key)
|
||||
}
|
||||
|
||||
@@ -9,9 +9,11 @@ import com.v2ray.ang.AppConfig.ANG_PACKAGE
|
||||
import com.v2ray.ang.AppConfig.GEOIP_PRIVATE
|
||||
import com.v2ray.ang.AppConfig.GEOSITE_PRIVATE
|
||||
import com.v2ray.ang.AppConfig.TAG_DIRECT
|
||||
import com.v2ray.ang.dto.EConfigType
|
||||
import com.v2ray.ang.dto.ProfileItem
|
||||
import com.v2ray.ang.dto.RoutingType
|
||||
import com.v2ray.ang.dto.RulesetItem
|
||||
import com.v2ray.ang.dto.V2rayConfig
|
||||
import com.v2ray.ang.handler.MmkvManager.decodeServerConfig
|
||||
import com.v2ray.ang.handler.MmkvManager.decodeServerList
|
||||
import com.v2ray.ang.util.JsonUtil
|
||||
@@ -43,12 +45,12 @@ object SettingsManager {
|
||||
}
|
||||
|
||||
|
||||
fun resetRoutingRulesets(context: Context, index: Int) {
|
||||
fun resetRoutingRulesetsFromPresets(context: Context, index: Int) {
|
||||
val rulesetList = getPresetRoutingRulesets(context, index) ?: return
|
||||
resetRoutingRulesetsCommon(rulesetList)
|
||||
}
|
||||
|
||||
fun resetRoutingRulesetsFromClipboard(content: String?): Boolean {
|
||||
fun resetRoutingRulesets(content: String?): Boolean {
|
||||
if (content.isNullOrEmpty()) {
|
||||
return false
|
||||
}
|
||||
@@ -70,7 +72,7 @@ object SettingsManager {
|
||||
private fun resetRoutingRulesetsCommon(rulesetList: MutableList<RulesetItem>) {
|
||||
val rulesetNew: MutableList<RulesetItem> = mutableListOf()
|
||||
MmkvManager.decodeRoutingRulesets()?.forEach { key ->
|
||||
if (key.looked == true) {
|
||||
if (key.locked == true) {
|
||||
rulesetNew.add(key)
|
||||
}
|
||||
}
|
||||
@@ -91,11 +93,13 @@ object SettingsManager {
|
||||
fun saveRoutingRuleset(index: Int, ruleset: RulesetItem?) {
|
||||
if (ruleset == null) return
|
||||
|
||||
val rulesetList = MmkvManager.decodeRoutingRulesets()
|
||||
if (rulesetList.isNullOrEmpty()) return
|
||||
var rulesetList = MmkvManager.decodeRoutingRulesets()
|
||||
if (rulesetList.isNullOrEmpty()) {
|
||||
rulesetList = mutableListOf()
|
||||
}
|
||||
|
||||
if (index < 0 || index >= rulesetList.count()) {
|
||||
rulesetList.add(ruleset)
|
||||
rulesetList.add(0, ruleset)
|
||||
} else {
|
||||
rulesetList[index] = ruleset
|
||||
}
|
||||
@@ -113,6 +117,25 @@ object SettingsManager {
|
||||
}
|
||||
|
||||
fun routingRulesetsBypassLan(): Boolean {
|
||||
val vpnBypassLan = MmkvManager.decodeSettingsString(AppConfig.PREF_VPN_BYPASS_LAN) ?: "0"
|
||||
if (vpnBypassLan == "1") {
|
||||
return true
|
||||
} else if (vpnBypassLan == "2") {
|
||||
return false
|
||||
}
|
||||
|
||||
//Follow config
|
||||
val guid = MmkvManager.getSelectServer() ?: return false
|
||||
val config = MmkvManager.decodeServerConfig(guid) ?: return false
|
||||
if (config.configType == EConfigType.CUSTOM) {
|
||||
val raw = MmkvManager.decodeServerRaw(guid) ?: return false
|
||||
val v2rayConfig = JsonUtil.fromJson(raw, V2rayConfig::class.java)
|
||||
val exist = v2rayConfig.routing.rules.filter { it.outboundTag == TAG_DIRECT }?.any {
|
||||
it.domain?.contains(GEOSITE_PRIVATE) == true || it.ip?.contains(GEOIP_PRIVATE) == true
|
||||
}
|
||||
return exist == true
|
||||
}
|
||||
|
||||
val rulesetItems = MmkvManager.decodeRoutingRulesets()
|
||||
val exist = rulesetItems?.filter { it.enabled && it.outboundTag == TAG_DIRECT }?.any {
|
||||
it.domain?.contains(GEOSITE_PRIVATE) == true || it.ip?.contains(GEOIP_PRIVATE) == true
|
||||
@@ -155,7 +178,7 @@ object SettingsManager {
|
||||
}
|
||||
|
||||
fun getHttpPort(): Int {
|
||||
return parseInt(MmkvManager.decodeSettingsString(AppConfig.PREF_HTTP_PORT), AppConfig.PORT_HTTP.toInt())
|
||||
return getSocksPort() + (if (Utils.isXray()) 0 else 1)
|
||||
}
|
||||
|
||||
fun initAssets(context: Context, assets: AssetManager) {
|
||||
|
||||
@@ -34,10 +34,12 @@ import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_ADDRESS_V4
|
||||
import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_ADDRESS_V6
|
||||
import com.v2ray.ang.dto.ConfigResult
|
||||
import com.v2ray.ang.dto.EConfigType
|
||||
import com.v2ray.ang.dto.NetworkType
|
||||
import com.v2ray.ang.dto.ProfileItem
|
||||
import com.v2ray.ang.dto.RulesetItem
|
||||
import com.v2ray.ang.dto.V2rayConfig
|
||||
import com.v2ray.ang.dto.V2rayConfig.RoutingBean.RulesBean
|
||||
import com.v2ray.ang.extension.isNotNullEmpty
|
||||
import com.v2ray.ang.fmt.HttpFmt
|
||||
import com.v2ray.ang.fmt.Hysteria2Fmt
|
||||
import com.v2ray.ang.fmt.ShadowsocksFmt
|
||||
@@ -120,7 +122,6 @@ object V2rayConfigManager {
|
||||
private fun inbounds(v2rayConfig: V2rayConfig): Boolean {
|
||||
try {
|
||||
val socksPort = SettingsManager.getSocksPort()
|
||||
val httpPort = SettingsManager.getHttpPort()
|
||||
|
||||
v2rayConfig.inbounds.forEach { curInbound ->
|
||||
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_PROXY_SHARING) != true) {
|
||||
@@ -142,14 +143,13 @@ object V2rayConfigManager {
|
||||
v2rayConfig.inbounds[0].sniffing?.destOverride?.add("fakedns")
|
||||
}
|
||||
|
||||
if (Utils.isXray()) {
|
||||
v2rayConfig.inbounds.removeAt(1)
|
||||
} else {
|
||||
val httpPort = SettingsManager.getHttpPort()
|
||||
v2rayConfig.inbounds[1].port = httpPort
|
||||
}
|
||||
|
||||
// if (httpPort > 0) {
|
||||
// val httpCopy = v2rayConfig.inbounds[0].copy()
|
||||
// httpCopy.port = httpPort
|
||||
// httpCopy.protocol = "http"
|
||||
// v2rayConfig.inbounds.add(httpCopy)
|
||||
// }
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return false
|
||||
@@ -240,7 +240,7 @@ object V2rayConfigManager {
|
||||
|
||||
val rulesetItems = MmkvManager.decodeRoutingRulesets()
|
||||
rulesetItems?.forEach { key ->
|
||||
if (key != null && key.enabled && key.outboundTag == tag && !key.domain.isNullOrEmpty()) {
|
||||
if (key.enabled && key.outboundTag == tag && !key.domain.isNullOrEmpty()) {
|
||||
key.domain?.forEach {
|
||||
if (it != GEOSITE_PRIVATE
|
||||
&& (it.startsWith("geosite:") || it.startsWith("domain:"))
|
||||
@@ -334,7 +334,7 @@ object V2rayConfigManager {
|
||||
remoteDns.forEach {
|
||||
servers.add(it)
|
||||
}
|
||||
if (proxyDomain.size > 0) {
|
||||
if (proxyDomain.isNotEmpty()) {
|
||||
servers.add(
|
||||
V2rayConfig.DnsBean.ServersBean(
|
||||
address = remoteDns.first(),
|
||||
@@ -348,7 +348,7 @@ object V2rayConfigManager {
|
||||
val directDomain = userRule2Domain(TAG_DIRECT)
|
||||
val isCnRoutingMode = directDomain.contains(GEOSITE_CN)
|
||||
val geoipCn = arrayListOf(GEOIP_CN)
|
||||
if (directDomain.size > 0) {
|
||||
if (directDomain.isNotEmpty()) {
|
||||
servers.add(
|
||||
V2rayConfig.DnsBean.ServersBean(
|
||||
address = domesticDns.first(),
|
||||
@@ -370,9 +370,23 @@ object V2rayConfigManager {
|
||||
)
|
||||
}
|
||||
|
||||
//User DNS hosts
|
||||
try {
|
||||
val userHosts = MmkvManager.decodeSettingsString(AppConfig.PREF_DNS_HOSTS)
|
||||
if (userHosts.isNotNullEmpty()) {
|
||||
var userHostsMap = userHosts?.split(",")
|
||||
?.filter { it.isNotEmpty() }
|
||||
?.filter { it.contains(":") }
|
||||
?.associate { it.split(":").let { (k, v) -> k to v } }
|
||||
if (userHostsMap != null) hosts.putAll(userHostsMap)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
//block dns
|
||||
val blkDomain = userRule2Domain(TAG_BLOCKED)
|
||||
if (blkDomain.size > 0) {
|
||||
if (blkDomain.isNotEmpty()) {
|
||||
hosts.putAll(blkDomain.map { it to LOOPBACK })
|
||||
}
|
||||
|
||||
@@ -424,19 +438,18 @@ object V2rayConfigManager {
|
||||
|| protocol.equals(EConfigType.HYSTERIA2.name, true)
|
||||
) {
|
||||
muxEnabled = false
|
||||
} else if (protocol.equals(EConfigType.VLESS.name, true)
|
||||
&& outbound.settings?.vnext?.first()?.users?.first()?.flow?.isNotEmpty() == true
|
||||
) {
|
||||
} else if (outbound.streamSettings?.network == NetworkType.XHTTP.type) {
|
||||
muxEnabled = false
|
||||
}
|
||||
|
||||
if (muxEnabled == true) {
|
||||
outbound.mux?.enabled = true
|
||||
outbound.mux?.concurrency =
|
||||
MmkvManager.decodeSettingsInt(AppConfig.PREF_MUX_CONCURRENCY, 8)
|
||||
outbound.mux?.xudpConcurrency =
|
||||
MmkvManager.decodeSettingsInt(AppConfig.PREF_MUX_XUDP_CONCURRENCY, 16)
|
||||
outbound.mux?.xudpProxyUDP443 =
|
||||
MmkvManager.decodeSettingsString(AppConfig.PREF_MUX_XUDP_QUIC) ?: "reject"
|
||||
outbound.mux?.concurrency = MmkvManager.decodeSettingsString(AppConfig.PREF_MUX_CONCURRENCY, "8").orEmpty().toInt()
|
||||
outbound.mux?.xudpConcurrency = MmkvManager.decodeSettingsString(AppConfig.PREF_MUX_XUDP_CONCURRENCY, "16").orEmpty().toInt()
|
||||
outbound.mux?.xudpProxyUDP443 = MmkvManager.decodeSettingsString(AppConfig.PREF_MUX_XUDP_QUIC,"reject")
|
||||
if (protocol.equals(EConfigType.VLESS.name, true) && outbound.settings?.vnext?.first()?.users?.first()?.flow?.isNotEmpty() == true) {
|
||||
outbound.mux?.concurrency = -1
|
||||
}
|
||||
} else {
|
||||
outbound.mux?.enabled = false
|
||||
outbound.mux?.concurrency = -1
|
||||
|
||||
@@ -222,7 +222,7 @@ object PluginManager {
|
||||
return File(pluginDir, pluginId).absolutePath
|
||||
}
|
||||
|
||||
fun ComponentInfo.loadString(key: String) = when (val value = metaData.get(key)) {
|
||||
fun ComponentInfo.loadString(key: String) = when (val value = metaData.getString(key)) {
|
||||
is String -> value
|
||||
is Int -> AngApplication.application.packageManager.getResourcesForApplication(applicationInfo)
|
||||
.getString(value)
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
package com.v2ray.ang.plugin
|
||||
|
||||
import android.content.pm.ComponentInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.ResolveInfo
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
@@ -33,13 +34,18 @@ abstract class ResolvedPlugin(protected val resolveInfo: ResolveInfo) : Plugin()
|
||||
|
||||
override val id by lazy { componentInfo.loadString(PluginContract.METADATA_KEY_ID)!! }
|
||||
override val version by lazy {
|
||||
AngApplication.application.getPackageInfo(componentInfo.packageName).versionCode
|
||||
getPackageInfo(componentInfo.packageName).versionCode
|
||||
}
|
||||
override val versionName: String by lazy {
|
||||
AngApplication.application.getPackageInfo(componentInfo.packageName).versionName!!
|
||||
getPackageInfo(componentInfo.packageName).versionName!!
|
||||
}
|
||||
override val label: CharSequence get() = resolveInfo.loadLabel(AngApplication.application.packageManager)
|
||||
override val icon: Drawable get() = resolveInfo.loadIcon(AngApplication.application.packageManager)
|
||||
override val packageName: String get() = componentInfo.packageName
|
||||
override val directBootAware get() = Build.VERSION.SDK_INT < 24 || componentInfo.directBootAware
|
||||
|
||||
fun getPackageInfo(packageName: String) = AngApplication.application.packageManager.getPackageInfo(
|
||||
packageName, if (Build.VERSION.SDK_INT >= 28) PackageManager.GET_SIGNING_CERTIFICATES
|
||||
else @Suppress("DEPRECATION") PackageManager.GET_SIGNATURES
|
||||
)!!
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.app.Service
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
@@ -19,6 +20,7 @@ import com.v2ray.ang.AppConfig.ANG_PACKAGE
|
||||
import com.v2ray.ang.AppConfig.TAG_DIRECT
|
||||
import com.v2ray.ang.AppConfig.VPN
|
||||
import com.v2ray.ang.R
|
||||
import com.v2ray.ang.dto.EConfigType
|
||||
import com.v2ray.ang.dto.ProfileItem
|
||||
import com.v2ray.ang.extension.toSpeedString
|
||||
import com.v2ray.ang.extension.toast
|
||||
@@ -29,10 +31,11 @@ import com.v2ray.ang.util.MessageUtil
|
||||
import com.v2ray.ang.util.PluginUtil
|
||||
import com.v2ray.ang.util.Utils
|
||||
import go.Seq
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import io.reactivex.rxjava3.disposables.Disposable
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import libv2ray.Libv2ray
|
||||
import libv2ray.V2RayPoint
|
||||
@@ -44,6 +47,7 @@ object V2RayServiceManager {
|
||||
private const val NOTIFICATION_ID = 1
|
||||
private const val NOTIFICATION_PENDING_INTENT_CONTENT = 0
|
||||
private const val NOTIFICATION_PENDING_INTENT_STOP_V2RAY = 1
|
||||
private const val NOTIFICATION_PENDING_INTENT_RESTART_V2RAY = 2
|
||||
private const val NOTIFICATION_ICON_THRESHOLD = 3000
|
||||
|
||||
val v2rayPoint: V2RayPoint = Libv2ray.newV2RayPoint(V2RayCallback(), Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1)
|
||||
@@ -59,14 +63,17 @@ object V2RayServiceManager {
|
||||
|
||||
private var lastQueryTime = 0L
|
||||
private var mBuilder: NotificationCompat.Builder? = null
|
||||
private var mDisposable: Disposable? = null
|
||||
private var speedNotificationJob: Job? = null
|
||||
private var mNotificationManager: NotificationManager? = null
|
||||
|
||||
fun startV2Ray(context: Context) {
|
||||
if (v2rayPoint.isRunning) return
|
||||
val guid = MmkvManager.getSelectServer() ?: return
|
||||
val config = MmkvManager.decodeServerConfig(guid) ?: return
|
||||
if (!Utils.isValidUrl(config.server) && !Utils.isIpAddress(config.server)) return
|
||||
if (config.configType != EConfigType.CUSTOM
|
||||
&& !Utils.isValidUrl(config.server)
|
||||
&& !Utils.isIpAddress(config.server)
|
||||
) return
|
||||
// val result = V2rayConfigUtil.getV2rayConfig(context, guid)
|
||||
// if (!result.status) return
|
||||
|
||||
@@ -219,11 +226,15 @@ object V2RayServiceManager {
|
||||
}
|
||||
|
||||
AppConfig.MSG_STATE_STOP -> {
|
||||
Log.d(ANG_PACKAGE, "Stop Service")
|
||||
serviceControl.stopService()
|
||||
}
|
||||
|
||||
AppConfig.MSG_STATE_RESTART -> {
|
||||
startV2rayPoint()
|
||||
Log.d(ANG_PACKAGE, "Restart Service")
|
||||
serviceControl.stopService()
|
||||
Thread.sleep(500L)
|
||||
startV2Ray(serviceControl.getService())
|
||||
}
|
||||
|
||||
AppConfig.MSG_MEASURE_DELAY -> {
|
||||
@@ -278,30 +289,24 @@ object V2RayServiceManager {
|
||||
|
||||
private fun showNotification() {
|
||||
val service = serviceControl?.get()?.getService() ?: return
|
||||
val startMainIntent = Intent(service, MainActivity::class.java)
|
||||
val contentPendingIntent = PendingIntent.getActivity(
|
||||
service,
|
||||
NOTIFICATION_PENDING_INTENT_CONTENT, startMainIntent,
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||
} else {
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
}
|
||||
)
|
||||
|
||||
val startMainIntent = Intent(service, MainActivity::class.java)
|
||||
val contentPendingIntent = PendingIntent.getActivity(service, NOTIFICATION_PENDING_INTENT_CONTENT, startMainIntent, flags)
|
||||
|
||||
val stopV2RayIntent = Intent(AppConfig.BROADCAST_ACTION_SERVICE)
|
||||
stopV2RayIntent.`package` = ANG_PACKAGE
|
||||
stopV2RayIntent.putExtra("key", AppConfig.MSG_STATE_STOP)
|
||||
val stopV2RayPendingIntent = PendingIntent.getBroadcast(service, NOTIFICATION_PENDING_INTENT_STOP_V2RAY, stopV2RayIntent, flags)
|
||||
|
||||
val stopV2RayPendingIntent = PendingIntent.getBroadcast(
|
||||
service,
|
||||
NOTIFICATION_PENDING_INTENT_STOP_V2RAY, stopV2RayIntent,
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||
} else {
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
}
|
||||
)
|
||||
val restartV2RayIntent = Intent(AppConfig.BROADCAST_ACTION_SERVICE)
|
||||
restartV2RayIntent.`package` = ANG_PACKAGE
|
||||
restartV2RayIntent.putExtra("key", AppConfig.MSG_STATE_RESTART)
|
||||
val restartV2RayPendingIntent = PendingIntent.getBroadcast(service, NOTIFICATION_PENDING_INTENT_RESTART_V2RAY, restartV2RayIntent, flags)
|
||||
|
||||
val channelId =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
@@ -325,6 +330,11 @@ object V2RayServiceManager {
|
||||
service.getString(R.string.notification_action_stop_v2ray),
|
||||
stopV2RayPendingIntent
|
||||
)
|
||||
.addAction(
|
||||
R.drawable.ic_delete_24dp,
|
||||
service.getString(R.string.title_service_restart),
|
||||
restartV2RayPendingIntent
|
||||
)
|
||||
//.build()
|
||||
|
||||
//mBuilder?.setDefaults(NotificationCompat.FLAG_ONLY_ALERT_ONCE) //取消震动,铃声其他都不好使
|
||||
@@ -349,10 +359,15 @@ object V2RayServiceManager {
|
||||
|
||||
fun cancelNotification() {
|
||||
val service = serviceControl?.get()?.getService() ?: return
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
service.stopForeground(Service.STOP_FOREGROUND_REMOVE)
|
||||
} else {
|
||||
service.stopForeground(true)
|
||||
}
|
||||
|
||||
mBuilder = null
|
||||
mDisposable?.dispose()
|
||||
mDisposable = null
|
||||
speedNotificationJob?.cancel()
|
||||
speedNotificationJob = null
|
||||
}
|
||||
|
||||
private fun updateNotification(contentText: String?, proxyTraffic: Long, directTraffic: Long) {
|
||||
@@ -379,7 +394,7 @@ object V2RayServiceManager {
|
||||
}
|
||||
|
||||
private fun startSpeedNotification() {
|
||||
if (mDisposable == null &&
|
||||
if (speedNotificationJob == null &&
|
||||
v2rayPoint.isRunning &&
|
||||
MmkvManager.decodeSettingsBool(AppConfig.PREF_SPEED_ENABLED) == true
|
||||
) {
|
||||
@@ -387,8 +402,8 @@ object V2RayServiceManager {
|
||||
val outboundTags = currentConfig?.getAllOutboundTags()
|
||||
outboundTags?.remove(TAG_DIRECT)
|
||||
|
||||
mDisposable = Observable.interval(3, java.util.concurrent.TimeUnit.SECONDS)
|
||||
.subscribe {
|
||||
speedNotificationJob = CoroutineScope(Dispatchers.IO).launch {
|
||||
while (isActive) {
|
||||
val queryTime = System.currentTimeMillis()
|
||||
val sinceLastQueryInSeconds = (queryTime - lastQueryTime) / 1000.0
|
||||
var proxyTotal = 0L
|
||||
@@ -416,6 +431,8 @@ object V2RayServiceManager {
|
||||
}
|
||||
lastZeroSpeed = zeroSpeed
|
||||
lastQueryTime = queryTime
|
||||
delay(3000)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -431,11 +448,10 @@ object V2RayServiceManager {
|
||||
}
|
||||
|
||||
private fun stopSpeedNotification() {
|
||||
mDisposable?.let {
|
||||
it.dispose() //stop queryStats
|
||||
mDisposable = null
|
||||
speedNotificationJob?.let {
|
||||
it.cancel()
|
||||
speedNotificationJob = null
|
||||
updateNotification(currentConfig?.remarks, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import android.net.LocalSocketAddress
|
||||
import android.net.Network
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.NetworkRequest
|
||||
import android.net.ProxyInfo
|
||||
import android.net.VpnService
|
||||
import android.os.Build
|
||||
import android.os.ParcelFileDescriptor
|
||||
@@ -34,10 +35,10 @@ import java.lang.ref.SoftReference
|
||||
class V2RayVpnService : VpnService(), ServiceControl {
|
||||
companion object {
|
||||
private const val VPN_MTU = 1500
|
||||
private const val PRIVATE_VLAN4_CLIENT = "26.26.26.1"
|
||||
private const val PRIVATE_VLAN4_ROUTER = "26.26.26.2"
|
||||
private const val PRIVATE_VLAN6_CLIENT = "da26:2626::1"
|
||||
private const val PRIVATE_VLAN6_ROUTER = "da26:2626::2"
|
||||
private const val PRIVATE_VLAN4_CLIENT = "10.10.10.1"
|
||||
private const val PRIVATE_VLAN4_ROUTER = "10.10.10.2"
|
||||
private const val PRIVATE_VLAN6_CLIENT = "fc00::10:10:10:1"
|
||||
private const val PRIVATE_VLAN6_ROUTER = "fc00::10:10:10:2"
|
||||
private const val TUN2SOCKS = "libtun2socks.so"
|
||||
}
|
||||
|
||||
@@ -190,6 +191,9 @@ class V2RayVpnService : VpnService(), ServiceControl {
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
builder.setMetered(false)
|
||||
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_APPEND_HTTP_PROXY)) {
|
||||
builder.setHttpProxy(ProxyInfo.buildDirectProxy(LOOPBACK, SettingsManager.getHttpPort()))
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new interface using the builder and save the parameters.
|
||||
|
||||
@@ -5,15 +5,16 @@ import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.FileProvider
|
||||
import com.tbruyelle.rxpermissions3.RxPermissions
|
||||
import com.tencent.mmkv.MMKV
|
||||
import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.BuildConfig
|
||||
import com.v2ray.ang.R
|
||||
import com.v2ray.ang.databinding.ActivityAboutBinding
|
||||
import com.v2ray.ang.extension.toast
|
||||
import com.v2ray.ang.util.SpeedtestUtil
|
||||
import com.v2ray.ang.util.Utils
|
||||
import com.v2ray.ang.util.ZipUtil
|
||||
@@ -22,9 +23,23 @@ import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
class AboutActivity : BaseActivity() {
|
||||
|
||||
private val binding by lazy { ActivityAboutBinding.inflate(layoutInflater) }
|
||||
private val extDir by lazy { File(Utils.backupPath(this)) }
|
||||
|
||||
private val requestPermissionLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
|
||||
if (isGranted) {
|
||||
try {
|
||||
showFileChooser()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
} else {
|
||||
toast(R.string.toast_permission_denied)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(binding.root)
|
||||
@@ -32,6 +47,7 @@ class AboutActivity : BaseActivity() {
|
||||
title = getString(R.string.title_about)
|
||||
|
||||
binding.tvBackupSummary.text = this.getString(R.string.summary_configuration_backup, extDir)
|
||||
|
||||
binding.layoutBackup.setOnClickListener {
|
||||
val ret = backupConfiguration(extDir.absolutePath)
|
||||
if (ret.first) {
|
||||
@@ -49,7 +65,8 @@ class AboutActivity : BaseActivity() {
|
||||
Intent(Intent.ACTION_SEND).setType("application/zip")
|
||||
.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
.putExtra(
|
||||
Intent.EXTRA_STREAM, FileProvider.getUriForFile(
|
||||
Intent.EXTRA_STREAM,
|
||||
FileProvider.getUriForFile(
|
||||
this, BuildConfig.APPLICATION_ID + ".cache", File(ret.second)
|
||||
)
|
||||
), getString(R.string.title_configuration_share)
|
||||
@@ -61,22 +78,21 @@ class AboutActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
binding.layoutRestore.setOnClickListener {
|
||||
val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
val permission =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
Manifest.permission.READ_MEDIA_IMAGES
|
||||
} else {
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
}
|
||||
RxPermissions(this)
|
||||
.request(permission)
|
||||
.subscribe {
|
||||
if (it) {
|
||||
|
||||
if (ContextCompat.checkSelfPermission(this, permission) == android.content.pm.PackageManager.PERMISSION_GRANTED) {
|
||||
try {
|
||||
showFileChooser()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
} else
|
||||
toast(R.string.toast_permission_denied)
|
||||
} else {
|
||||
requestPermissionLauncher.launch(permission)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +104,16 @@ class AboutActivity : BaseActivity() {
|
||||
Utils.openUri(this, AppConfig.v2rayNGIssues)
|
||||
}
|
||||
|
||||
binding.layoutOssLicenses.setOnClickListener {
|
||||
val webView = android.webkit.WebView(this);
|
||||
webView.loadUrl("file:///android_asset/open_source_licenses.html")
|
||||
android.app.AlertDialog.Builder(this)
|
||||
.setTitle("Open source licenses")
|
||||
.setView(webView)
|
||||
.setPositiveButton("OK") { dialog, _ -> dialog.dismiss() }
|
||||
.show()
|
||||
}
|
||||
|
||||
binding.layoutTgChannel.setOnClickListener {
|
||||
Utils.openUri(this, AppConfig.TgChannelUrl)
|
||||
}
|
||||
@@ -148,9 +174,9 @@ class AboutActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
private val chooseFile =
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
val uri = it.data?.data
|
||||
if (it.resultCode == RESULT_OK && uri != null) {
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
val uri = result.data?.data
|
||||
if (result.resultCode == RESULT_OK && uri != null) {
|
||||
try {
|
||||
val targetFile =
|
||||
File(this.cacheDir.absolutePath, "${System.currentTimeMillis()}.zip")
|
||||
@@ -171,4 +197,7 @@ class AboutActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun toast(messageResId: Int) {
|
||||
Toast.makeText(this, getString(messageResId), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
package com.v2ray.ang.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.text.method.ScrollingMovementMethod
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import com.v2ray.ang.AppConfig.ANG_PACKAGE
|
||||
import com.v2ray.ang.R
|
||||
import com.v2ray.ang.databinding.ActivityLogcatBinding
|
||||
@@ -18,10 +18,13 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.IOException
|
||||
|
||||
class LogcatActivity : BaseActivity() {
|
||||
private val binding by lazy {
|
||||
ActivityLogcatBinding.inflate(layoutInflater)
|
||||
}
|
||||
|
||||
class LogcatActivity : BaseActivity(), SwipeRefreshLayout.OnRefreshListener {
|
||||
private val binding by lazy { ActivityLogcatBinding.inflate(layoutInflater) }
|
||||
|
||||
var logsetsAll: MutableList<String> = mutableListOf()
|
||||
var logsets: MutableList<String> = mutableListOf()
|
||||
private val adapter by lazy { LogcatRecyclerAdapter(this) }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -29,63 +32,118 @@ class LogcatActivity : BaseActivity() {
|
||||
|
||||
title = getString(R.string.title_logcat)
|
||||
|
||||
logcat(false)
|
||||
binding.recyclerView.setHasFixedSize(true)
|
||||
binding.recyclerView.layoutManager = LinearLayoutManager(this)
|
||||
binding.recyclerView.adapter = adapter
|
||||
binding.recyclerView.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL))
|
||||
|
||||
binding.refreshLayout.setOnRefreshListener(this)
|
||||
|
||||
logsets.add(getString(R.string.pull_down_to_refresh))
|
||||
}
|
||||
|
||||
private fun logcat(shouldFlushLog: Boolean) {
|
||||
binding.pbWaiting.visibility = View.VISIBLE
|
||||
private fun getLogcat() {
|
||||
|
||||
try {
|
||||
binding.refreshLayout.isRefreshing = true
|
||||
|
||||
lifecycleScope.launch(Dispatchers.Default) {
|
||||
val lst = LinkedHashSet<String>()
|
||||
lst.add("logcat")
|
||||
lst.add("-d")
|
||||
lst.add("-v")
|
||||
lst.add("time")
|
||||
lst.add("-s")
|
||||
lst.add("GoLog,tun2socks,${ANG_PACKAGE},AndroidRuntime,System.err")
|
||||
val process = withContext(Dispatchers.IO) {
|
||||
Runtime.getRuntime().exec(lst.toTypedArray())
|
||||
}
|
||||
|
||||
val allText = process.inputStream.bufferedReader().use { it.readLines() }.reversed()
|
||||
launch(Dispatchers.Main) {
|
||||
logsetsAll = allText.toMutableList()
|
||||
logsets = allText.toMutableList()
|
||||
adapter.notifyDataSetChanged()
|
||||
binding.refreshLayout.isRefreshing = false
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearLogcat() {
|
||||
try {
|
||||
if (shouldFlushLog) {
|
||||
val lst = linkedSetOf("logcat", "-c")
|
||||
lifecycleScope.launch(Dispatchers.Default) {
|
||||
val lst = LinkedHashSet<String>()
|
||||
lst.add("logcat")
|
||||
lst.add("-c")
|
||||
withContext(Dispatchers.IO) {
|
||||
val process = Runtime.getRuntime().exec(lst.toTypedArray())
|
||||
process.waitFor()
|
||||
}
|
||||
launch(Dispatchers.Main) {
|
||||
logsetsAll.clear()
|
||||
logsets.clear()
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
val lst = linkedSetOf(
|
||||
"logcat", "-d", "-v", "time", "-s",
|
||||
"GoLog,tun2socks,$ANG_PACKAGE,AndroidRuntime,System.err"
|
||||
)
|
||||
val process = withContext(Dispatchers.IO) {
|
||||
Runtime.getRuntime().exec(lst.toTypedArray())
|
||||
}
|
||||
val allText = process.inputStream.bufferedReader().use { it.readText() }
|
||||
withContext(Dispatchers.Main) {
|
||||
binding.tvLogcat.text = allText
|
||||
binding.tvLogcat.movementMethod = ScrollingMovementMethod()
|
||||
binding.pbWaiting.visibility = View.GONE
|
||||
Handler(Looper.getMainLooper()).post { binding.svLogcat.fullScroll(View.FOCUS_DOWN) }
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
withContext(Dispatchers.Main) {
|
||||
binding.pbWaiting.visibility = View.GONE
|
||||
toast(R.string.toast_failure)
|
||||
}
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_logcat, menu)
|
||||
|
||||
val searchItem = menu.findItem(R.id.search_view)
|
||||
if (searchItem != null) {
|
||||
val searchView = searchItem.actionView as SearchView
|
||||
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String?): Boolean = false
|
||||
|
||||
override fun onQueryTextChange(newText: String?): Boolean {
|
||||
filterLogs(newText)
|
||||
return false
|
||||
}
|
||||
})
|
||||
searchView.setOnCloseListener {
|
||||
filterLogs("")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
return super.onCreateOptionsMenu(menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
|
||||
R.id.copy_all -> {
|
||||
Utils.setClipboard(this, binding.tvLogcat.text.toString())
|
||||
Utils.setClipboard(this, logsets.joinToString("\n"))
|
||||
toast(R.string.toast_success)
|
||||
true
|
||||
}
|
||||
|
||||
R.id.clear_all -> {
|
||||
logcat(true)
|
||||
clearLogcat()
|
||||
true
|
||||
}
|
||||
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun filterLogs(content: String?): Boolean {
|
||||
val key = content?.trim()
|
||||
logsets = if (key.isNullOrEmpty()) {
|
||||
logsetsAll.toMutableList()
|
||||
} else {
|
||||
logsetsAll.filter { it.contains(key) }.toMutableList()
|
||||
}
|
||||
|
||||
adapter?.notifyDataSetChanged()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onRefresh() {
|
||||
getLogcat()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.v2ray.ang.ui
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.v2ray.ang.databinding.ItemRecyclerLogcatBinding
|
||||
|
||||
class LogcatRecyclerAdapter(val activity: LogcatActivity) : RecyclerView.Adapter<LogcatRecyclerAdapter.MainViewHolder>() {
|
||||
private var mActivity: LogcatActivity = activity
|
||||
|
||||
override fun getItemCount() = mActivity.logsets.size
|
||||
|
||||
override fun onBindViewHolder(holder: MainViewHolder, position: Int) {
|
||||
val content = mActivity.logsets[position]
|
||||
holder.itemSubSettingBinding.logContent.text = content
|
||||
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainViewHolder {
|
||||
return MainViewHolder(
|
||||
ItemRecyclerLogcatBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
class MainViewHolder(val itemSubSettingBinding: ItemRecyclerLogcatBinding) : RecyclerView.ViewHolder(itemSubSettingBinding.root)
|
||||
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.v2ray.ang.ui
|
||||
import android.Manifest
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.ColorStateList
|
||||
import android.net.Uri
|
||||
import android.net.VpnService
|
||||
@@ -27,7 +28,6 @@ import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.navigation.NavigationView
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.tbruyelle.rxpermissions3.RxPermissions
|
||||
import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.AppConfig.VPN
|
||||
import com.v2ray.ang.R
|
||||
@@ -41,8 +41,6 @@ import com.v2ray.ang.helper.SimpleItemTouchHelperCallback
|
||||
import com.v2ray.ang.service.V2RayServiceManager
|
||||
import com.v2ray.ang.util.Utils
|
||||
import com.v2ray.ang.viewmodel.MainViewModel
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -81,6 +79,60 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
private var mItemTouchHelper: ItemTouchHelper? = null
|
||||
val mainViewModel: MainViewModel by viewModels()
|
||||
|
||||
// register activity result for requesting permission
|
||||
private val requestPermissionLauncher =
|
||||
registerForActivityResult(
|
||||
ActivityResultContracts.RequestPermission()
|
||||
) { isGranted: Boolean ->
|
||||
if (isGranted) {
|
||||
when (pendingAction) {
|
||||
Action.IMPORT_QR_CODE_CONFIG ->
|
||||
scanQRCodeForConfig.launch(Intent(this, ScannerActivity::class.java))
|
||||
Action.IMPORT_QR_CODE_URL ->
|
||||
scanQRCodeForUrlToCustomConfig.launch(Intent(this, ScannerActivity::class.java))
|
||||
Action.READ_CONTENT_FROM_URI ->
|
||||
chooseFileForCustomConfig.launch(Intent.createChooser(Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||
type = "*/*"
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
}, getString(R.string.title_file_chooser)))
|
||||
Action.POST_NOTIFICATIONS -> {}
|
||||
else -> {}
|
||||
}
|
||||
} else {
|
||||
toast(R.string.toast_permission_denied)
|
||||
}
|
||||
pendingAction = Action.NONE
|
||||
}
|
||||
|
||||
private var pendingAction: Action = Action.NONE
|
||||
|
||||
enum class Action {
|
||||
NONE,
|
||||
IMPORT_QR_CODE_CONFIG,
|
||||
IMPORT_QR_CODE_URL,
|
||||
READ_CONTENT_FROM_URI,
|
||||
POST_NOTIFICATIONS
|
||||
}
|
||||
|
||||
private val chooseFileForCustomConfig = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
val uri = it.data?.data
|
||||
if (it.resultCode == RESULT_OK && uri != null) {
|
||||
readContentFromUri(uri)
|
||||
}
|
||||
}
|
||||
|
||||
private val scanQRCodeForConfig = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (it.resultCode == RESULT_OK) {
|
||||
importBatchConfig(it.data?.getStringExtra("SCAN_RESULT"))
|
||||
}
|
||||
}
|
||||
|
||||
private val scanQRCodeForUrlToCustomConfig = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (it.resultCode == RESULT_OK) {
|
||||
importConfigCustomUrl(it.data?.getStringExtra("SCAN_RESULT"))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(binding.root)
|
||||
@@ -129,11 +181,9 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
migrateLegacy()
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
RxPermissions(this)
|
||||
.request(Manifest.permission.POST_NOTIFICATIONS)
|
||||
.subscribe {
|
||||
if (!it)
|
||||
toast(R.string.toast_permission_denied_notification)
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||
pendingAction = Action.POST_NOTIFICATIONS
|
||||
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,9 +276,8 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
if (mainViewModel.isRunning.value == true) {
|
||||
Utils.stopVService(this)
|
||||
}
|
||||
Observable.timer(500, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
lifecycleScope.launch {
|
||||
delay(500)
|
||||
startV2Ray()
|
||||
}
|
||||
}
|
||||
@@ -346,8 +395,8 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val ret = mainViewModel.exportAllServer()
|
||||
launch(Dispatchers.Main) {
|
||||
if (ret == 0)
|
||||
toast(R.string.toast_success)
|
||||
if (ret > 0)
|
||||
toast(getString(R.string.title_export_config_count, ret))
|
||||
else
|
||||
toast(R.string.toast_failure)
|
||||
binding.pbWaiting.hide()
|
||||
@@ -358,13 +407,13 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
}
|
||||
|
||||
R.id.ping_all -> {
|
||||
toast(R.string.connection_test_testing)
|
||||
toast(getString(R.string.connection_test_testing_count, mainViewModel.serversCache.count()))
|
||||
mainViewModel.testAllTcping()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.real_ping_all -> {
|
||||
toast(R.string.connection_test_testing)
|
||||
toast(getString(R.string.connection_test_testing_count, mainViewModel.serversCache.count()))
|
||||
mainViewModel.testAllRealPing()
|
||||
true
|
||||
}
|
||||
@@ -379,14 +428,15 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
binding.pbWaiting.show()
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
mainViewModel.removeAllServer()
|
||||
val ret = mainViewModel.removeAllServer()
|
||||
launch(Dispatchers.Main) {
|
||||
mainViewModel.reloadServerList()
|
||||
toast(getString(R.string.title_del_config_count, ret))
|
||||
binding.pbWaiting.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
.setNegativeButton(android.R.string.no) { _, _ ->
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||
//do noting
|
||||
}
|
||||
.show()
|
||||
@@ -406,7 +456,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
}
|
||||
}
|
||||
}
|
||||
.setNegativeButton(android.R.string.no) { _, _ ->
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||
//do noting
|
||||
}
|
||||
.show()
|
||||
@@ -418,14 +468,15 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
binding.pbWaiting.show()
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
mainViewModel.removeInvalidServer()
|
||||
val ret = mainViewModel.removeInvalidServer()
|
||||
launch(Dispatchers.Main) {
|
||||
mainViewModel.reloadServerList()
|
||||
toast(getString(R.string.title_del_config_count, ret))
|
||||
binding.pbWaiting.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
.setNegativeButton(android.R.string.no) { _, _ ->
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||
//do noting
|
||||
}
|
||||
.show()
|
||||
@@ -460,38 +511,20 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
* import config from qrcode
|
||||
*/
|
||||
private fun importQRcode(forConfig: Boolean): Boolean {
|
||||
// try {
|
||||
// startActivityForResult(Intent("com.google.zxing.client.android.SCAN")
|
||||
// .addCategory(Intent.CATEGORY_DEFAULT)
|
||||
// .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP), requestCode)
|
||||
// } catch (e: Exception) {
|
||||
RxPermissions(this)
|
||||
.request(Manifest.permission.CAMERA)
|
||||
.subscribe {
|
||||
if (it)
|
||||
if (forConfig)
|
||||
val permission = Manifest.permission.CAMERA
|
||||
if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) {
|
||||
if (forConfig) {
|
||||
scanQRCodeForConfig.launch(Intent(this, ScannerActivity::class.java))
|
||||
else
|
||||
} else {
|
||||
scanQRCodeForUrlToCustomConfig.launch(Intent(this, ScannerActivity::class.java))
|
||||
else
|
||||
toast(R.string.toast_permission_denied)
|
||||
}
|
||||
// }
|
||||
} else {
|
||||
pendingAction = if (forConfig) Action.IMPORT_QR_CODE_CONFIG else Action.IMPORT_QR_CODE_URL
|
||||
requestPermissionLauncher.launch(permission)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private val scanQRCodeForConfig = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (it.resultCode == RESULT_OK) {
|
||||
importBatchConfig(it.data?.getStringExtra("SCAN_RESULT"))
|
||||
}
|
||||
}
|
||||
|
||||
private val scanQRCodeForUrlToCustomConfig = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (it.resultCode == RESULT_OK) {
|
||||
importConfigCustomUrl(it.data?.getStringExtra("SCAN_RESULT"))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* import config from clipboard
|
||||
*/
|
||||
@@ -517,7 +550,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
withContext(Dispatchers.Main) {
|
||||
when {
|
||||
count > 0 -> {
|
||||
toast(R.string.toast_success)
|
||||
toast(getString(R.string.title_import_config_count, count))
|
||||
mainViewModel.reloadServerList()
|
||||
}
|
||||
|
||||
@@ -612,10 +645,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
* import config from sub
|
||||
*/
|
||||
private fun importConfigViaSub(): Boolean {
|
||||
// val dialog = AlertDialog.Builder(this)
|
||||
// .setView(LayoutProgressBinding.inflate(layoutInflater).root)
|
||||
// .setCancelable(false)
|
||||
// .show()
|
||||
binding.pbWaiting.show()
|
||||
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
@@ -623,12 +652,11 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
delay(500L)
|
||||
launch(Dispatchers.Main) {
|
||||
if (count > 0) {
|
||||
toast(R.string.toast_success)
|
||||
toast(getString(R.string.title_update_config_count, count))
|
||||
mainViewModel.reloadServerList()
|
||||
} else {
|
||||
toast(R.string.toast_failure)
|
||||
}
|
||||
//dialog.dismiss()
|
||||
binding.pbWaiting.hide()
|
||||
}
|
||||
}
|
||||
@@ -643,17 +671,17 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
intent.type = "*/*"
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
|
||||
try {
|
||||
chooseFileForCustomConfig.launch(Intent.createChooser(intent, getString(R.string.title_file_chooser)))
|
||||
} catch (ex: ActivityNotFoundException) {
|
||||
toast(R.string.toast_require_file_manager)
|
||||
}
|
||||
val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
Manifest.permission.READ_MEDIA_IMAGES
|
||||
} else {
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
}
|
||||
|
||||
private val chooseFileForCustomConfig = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
val uri = it.data?.data
|
||||
if (it.resultCode == RESULT_OK && uri != null) {
|
||||
readContentFromUri(uri)
|
||||
if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) {
|
||||
pendingAction = Action.READ_CONTENT_FROM_URI
|
||||
chooseFileForCustomConfig.launch(Intent.createChooser(intent, getString(R.string.title_file_chooser)))
|
||||
} else {
|
||||
requestPermissionLauncher.launch(permission)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -666,10 +694,8 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
} else {
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
}
|
||||
RxPermissions(this)
|
||||
.request(permission)
|
||||
.subscribe {
|
||||
if (it) {
|
||||
|
||||
if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) {
|
||||
try {
|
||||
contentResolver.openInputStream(uri).use { input ->
|
||||
importCustomizeConfig(input?.bufferedReader()?.readText())
|
||||
@@ -677,8 +703,8 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
} else
|
||||
toast(R.string.toast_permission_denied)
|
||||
} else {
|
||||
requestPermissionLauncher.launch(permission)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.v2ray.ang.AngApplication.Companion.application
|
||||
import com.v2ray.ang.AppConfig
|
||||
@@ -23,9 +24,9 @@ import com.v2ray.ang.helper.ItemTouchHelperAdapter
|
||||
import com.v2ray.ang.helper.ItemTouchHelperViewHolder
|
||||
import com.v2ray.ang.service.V2RayServiceManager
|
||||
import com.v2ray.ang.util.Utils
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.*
|
||||
|
||||
class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<MainRecyclerAdapter.BaseViewHolder>(), ItemTouchHelperAdapter {
|
||||
companion object {
|
||||
@@ -143,7 +144,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
removeServer(guid, position)
|
||||
}
|
||||
.setNegativeButton(android.R.string.no) { _, _ ->
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||
//do noting
|
||||
}
|
||||
.show()
|
||||
@@ -165,10 +166,13 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
|
||||
notifyItemChanged(mActivity.mainViewModel.getPosition(guid))
|
||||
if (isRunning) {
|
||||
Utils.stopVService(mActivity)
|
||||
Observable.timer(500, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
mActivity.lifecycleScope.launch {
|
||||
try {
|
||||
delay(500)
|
||||
V2RayServiceManager.startV2Ray(mActivity)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,9 @@ import com.v2ray.ang.extension.v2RayApplication
|
||||
import com.v2ray.ang.handler.MmkvManager
|
||||
import com.v2ray.ang.util.AppManagerUtil
|
||||
import com.v2ray.ang.util.Utils
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.text.Collator
|
||||
|
||||
class PerAppProxyActivity : BaseActivity() {
|
||||
@@ -43,94 +42,40 @@ class PerAppProxyActivity : BaseActivity() {
|
||||
|
||||
val blacklist = MmkvManager.decodeSettingsStringSet(AppConfig.PREF_PER_APP_PROXY_SET)
|
||||
|
||||
AppManagerUtil.rxLoadNetworkAppList(this)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.map {
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
binding.pbWaiting.visibility = View.VISIBLE
|
||||
val blacklist = MmkvManager.decodeSettingsStringSet(AppConfig.PREF_PER_APP_PROXY_SET)
|
||||
val apps = withContext(Dispatchers.IO) {
|
||||
val appsList = AppManagerUtil.loadNetworkAppList(this@PerAppProxyActivity)
|
||||
|
||||
if (blacklist != null) {
|
||||
it.forEach { one ->
|
||||
if (blacklist.contains(one.packageName)) {
|
||||
one.isSelected = 1
|
||||
} else {
|
||||
one.isSelected = 0
|
||||
appsList.forEach { app ->
|
||||
app.isSelected = if (blacklist.contains(app.packageName)) 1 else 0
|
||||
}
|
||||
}
|
||||
val comparator = Comparator<AppInfo> { p1, p2 ->
|
||||
appsList.sortedWith(Comparator { p1, p2 ->
|
||||
when {
|
||||
p1.isSelected > p2.isSelected -> -1
|
||||
p1.isSelected == p2.isSelected -> 0
|
||||
else -> 1
|
||||
}
|
||||
}
|
||||
it.sortedWith(comparator)
|
||||
})
|
||||
} else {
|
||||
val comparator = object : Comparator<AppInfo> {
|
||||
val collator = Collator.getInstance()
|
||||
override fun compare(o1: AppInfo, o2: AppInfo) = collator.compare(o1.appName, o2.appName)
|
||||
}
|
||||
it.sortedWith(comparator)
|
||||
appsList.sortedWith(compareBy(collator) { it.appName })
|
||||
}
|
||||
}
|
||||
// .map {
|
||||
// val comparator = object : Comparator<AppInfo> {
|
||||
// val collator = Collator.getInstance()
|
||||
// override fun compare(o1: AppInfo, o2: AppInfo) = collator.compare(o1.appName, o2.appName)
|
||||
// }
|
||||
// it.sortedWith(comparator)
|
||||
// }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
appsAll = it
|
||||
adapter = PerAppProxyAdapter(this, it, blacklist)
|
||||
|
||||
appsAll = apps
|
||||
adapter = PerAppProxyAdapter(this@PerAppProxyActivity, apps, blacklist)
|
||||
binding.recyclerView.adapter = adapter
|
||||
binding.pbWaiting.visibility = View.GONE
|
||||
}
|
||||
/***
|
||||
recycler_view.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
var dst = 0
|
||||
val threshold = resources.getDimensionPixelSize(R.dimen.bypass_list_header_height) * 2
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
dst += dy
|
||||
if (dst > threshold) {
|
||||
header_view.hide()
|
||||
dst = 0
|
||||
} else if (dst < -20) {
|
||||
header_view.show()
|
||||
dst = 0
|
||||
} catch (e: Exception) {
|
||||
binding.pbWaiting.visibility = View.GONE
|
||||
Log.e(ANG_PACKAGE, "Error loading apps", e)
|
||||
}
|
||||
}
|
||||
|
||||
var hiding = false
|
||||
fun View.hide() {
|
||||
val target = -height.toFloat()
|
||||
if (hiding || translationY == target) return
|
||||
animate()
|
||||
.translationY(target)
|
||||
.setInterpolator(AccelerateInterpolator(2F))
|
||||
.setListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator?) {
|
||||
hiding = false
|
||||
}
|
||||
})
|
||||
hiding = true
|
||||
}
|
||||
|
||||
var showing = false
|
||||
fun View.show() {
|
||||
val target = 0f
|
||||
if (showing || translationY == target) return
|
||||
animate()
|
||||
.translationY(target)
|
||||
.setInterpolator(DecelerateInterpolator(2F))
|
||||
.setListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator?) {
|
||||
showing = false
|
||||
}
|
||||
})
|
||||
showing = true
|
||||
}
|
||||
})
|
||||
***/
|
||||
|
||||
binding.switchPerAppProxy.setOnCheckedChangeListener { _, isChecked ->
|
||||
MmkvManager.encodeSettings(AppConfig.PREF_PER_APP_PROXY, isChecked)
|
||||
}
|
||||
@@ -140,36 +85,6 @@ class PerAppProxyActivity : BaseActivity() {
|
||||
MmkvManager.encodeSettings(AppConfig.PREF_BYPASS_APPS, isChecked)
|
||||
}
|
||||
binding.switchBypassApps.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_BYPASS_APPS, false)
|
||||
|
||||
/***
|
||||
et_search.setOnEditorActionListener { v, actionId, event ->
|
||||
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
|
||||
//hide
|
||||
var imm: InputMethodManager = v.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS)
|
||||
|
||||
val key = v.text.toString().toUpperCase()
|
||||
val apps = ArrayList<AppInfo>()
|
||||
if (TextUtils.isEmpty(key)) {
|
||||
appsAll?.forEach {
|
||||
apps.add(it)
|
||||
}
|
||||
} else {
|
||||
appsAll?.forEach {
|
||||
if (it.appName.toUpperCase().indexOf(key) >= 0) {
|
||||
apps.add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
adapter = PerAppProxyAdapter(this, apps, adapter?.blacklist)
|
||||
recycler_view.adapter = adapter
|
||||
adapter?.notifyDataSetChanged()
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
***/
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
|
||||
@@ -37,7 +37,7 @@ class RoutingEditActivity : BaseActivity() {
|
||||
|
||||
private fun bindingServer(rulesetItem: RulesetItem): Boolean {
|
||||
binding.etRemarks.text = Utils.getEditable(rulesetItem.remarks)
|
||||
binding.chkLocked.isChecked = rulesetItem.looked == true
|
||||
binding.chkLocked.isChecked = rulesetItem.locked == true
|
||||
binding.etDomain.text = Utils.getEditable(rulesetItem.domain?.joinToString(","))
|
||||
binding.etIp.text = Utils.getEditable(rulesetItem.ip?.joinToString(","))
|
||||
binding.etPort.text = Utils.getEditable(rulesetItem.port)
|
||||
@@ -60,7 +60,7 @@ class RoutingEditActivity : BaseActivity() {
|
||||
|
||||
rulesetItem.apply {
|
||||
remarks = binding.etRemarks.text.toString()
|
||||
looked = binding.chkLocked.isChecked
|
||||
locked = binding.chkLocked.isChecked
|
||||
domain = binding.etDomain.text.toString().takeIf { it.isNotEmpty() }
|
||||
?.split(",")?.map { it.trim() }?.filter { it.isNotEmpty() }
|
||||
ip = binding.etIp.text.toString().takeIf { it.isNotEmpty() }
|
||||
@@ -95,7 +95,7 @@ class RoutingEditActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
.setNegativeButton(android.R.string.no) { _, _ ->
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||
// do nothing
|
||||
}
|
||||
.show()
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package com.v2ray.ang.ui
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.AdapterView
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
@@ -37,6 +39,16 @@ class RoutingSettingActivity : BaseActivity() {
|
||||
resources.getStringArray(R.array.preset_rulesets)
|
||||
}
|
||||
|
||||
private val requestCameraPermissionLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.RequestPermission()
|
||||
) { isGranted: Boolean ->
|
||||
if (isGranted) {
|
||||
scanQRcodeForRulesets.launch(Intent(this, ScannerActivity::class.java))
|
||||
} else {
|
||||
toast(R.string.toast_permission_denied)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(binding.root)
|
||||
@@ -83,13 +95,13 @@ class RoutingSettingActivity : BaseActivity() {
|
||||
true
|
||||
}
|
||||
|
||||
R.id.import_rulesets -> {
|
||||
R.id.import_predefined_rulesets -> {
|
||||
AlertDialog.Builder(this).setMessage(R.string.routing_settings_import_rulesets_tip)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
AlertDialog.Builder(this).setItems(preset_rulesets.asList().toTypedArray()) { _, i ->
|
||||
try {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
SettingsManager.resetRoutingRulesets(this@RoutingSettingActivity, i)
|
||||
SettingsManager.resetRoutingRulesetsFromPresets(this@RoutingSettingActivity, i)
|
||||
launch(Dispatchers.Main) {
|
||||
refreshData()
|
||||
toast(R.string.toast_success)
|
||||
@@ -99,11 +111,9 @@ class RoutingSettingActivity : BaseActivity() {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}.show()
|
||||
|
||||
|
||||
}
|
||||
.setNegativeButton(android.R.string.no) { _, _ ->
|
||||
//do noting
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||
//do nothing
|
||||
}
|
||||
.show()
|
||||
true
|
||||
@@ -120,7 +130,7 @@ class RoutingSettingActivity : BaseActivity() {
|
||||
return@setPositiveButton
|
||||
}
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val result = SettingsManager.resetRoutingRulesetsFromClipboard(clipboard)
|
||||
val result = SettingsManager.resetRoutingRulesets(clipboard)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (result) {
|
||||
refreshData()
|
||||
@@ -131,13 +141,17 @@ class RoutingSettingActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
.setNegativeButton(android.R.string.no) { _, _ ->
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||
//do nothing
|
||||
}
|
||||
.show()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.import_rulesets_from_qrcode -> {
|
||||
requestCameraPermissionLauncher.launch(Manifest.permission.CAMERA)
|
||||
true
|
||||
}
|
||||
|
||||
R.id.export_rulesets_to_clipboard -> {
|
||||
val rulesetList = MmkvManager.decodeRoutingRulesets()
|
||||
@@ -153,10 +167,37 @@ class RoutingSettingActivity : BaseActivity() {
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private val scanQRcodeForRulesets = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (it.resultCode == RESULT_OK) {
|
||||
importRulesetsFromQRcode(it.data?.getStringExtra("SCAN_RESULT"))
|
||||
}
|
||||
}
|
||||
|
||||
private fun importRulesetsFromQRcode(qrcode: String?): Boolean {
|
||||
AlertDialog.Builder(this).setMessage(R.string.routing_settings_import_rulesets_tip)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val result = SettingsManager.resetRoutingRulesets(qrcode)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (result) {
|
||||
refreshData()
|
||||
toast(R.string.toast_success)
|
||||
} else {
|
||||
toast(R.string.toast_failure)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||
//do nothing
|
||||
}
|
||||
.show()
|
||||
return true
|
||||
}
|
||||
|
||||
fun refreshData() {
|
||||
rulesets.clear()
|
||||
rulesets.addAll(MmkvManager.decodeRoutingRulesets() ?: mutableListOf())
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,7 +25,7 @@ class RoutingSettingRecyclerAdapter(val activity: RoutingSettingActivity) : Recy
|
||||
holder.itemRoutingSettingBinding.domainIp.text = (ruleset.domain ?: ruleset.ip ?: ruleset.port)?.toString()
|
||||
holder.itemRoutingSettingBinding.outboundTag.text = ruleset.outboundTag
|
||||
holder.itemRoutingSettingBinding.chkEnable.isChecked = ruleset.enabled
|
||||
holder.itemRoutingSettingBinding.imgLocked.isVisible = ruleset.looked == true
|
||||
holder.itemRoutingSettingBinding.imgLocked.isVisible = ruleset.locked == true
|
||||
holder.itemView.setBackgroundColor(Color.TRANSPARENT)
|
||||
|
||||
holder.itemRoutingSettingBinding.layoutEdit.setOnClickListener {
|
||||
|
||||
@@ -4,13 +4,23 @@ import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import com.tbruyelle.rxpermissions3.RxPermissions
|
||||
import com.v2ray.ang.R
|
||||
import com.v2ray.ang.extension.toast
|
||||
import com.v2ray.ang.handler.AngConfigManager
|
||||
|
||||
class ScScannerActivity : BaseActivity() {
|
||||
|
||||
private val requestCameraPermissionLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.RequestPermission()
|
||||
) { isGranted: Boolean ->
|
||||
if (isGranted) {
|
||||
scanQRCode.launch(Intent(this, ScannerActivity::class.java))
|
||||
} else {
|
||||
toast(R.string.toast_permission_denied)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_none)
|
||||
@@ -18,19 +28,10 @@ class ScScannerActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
fun importQRcode(): Boolean {
|
||||
RxPermissions(this)
|
||||
.request(Manifest.permission.CAMERA)
|
||||
.subscribe { granted ->
|
||||
if (granted) {
|
||||
scanQRCode.launch(Intent(this, ScannerActivity::class.java))
|
||||
} else {
|
||||
toast(R.string.toast_permission_denied)
|
||||
}
|
||||
}
|
||||
requestCameraPermissionLauncher.launch(Manifest.permission.CAMERA)
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
private val scanQRCode = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (it.resultCode == RESULT_OK) {
|
||||
val scanResult = it.data?.getStringExtra("SCAN_RESULT").orEmpty()
|
||||
@@ -46,5 +47,4 @@ class ScScannerActivity : BaseActivity() {
|
||||
}
|
||||
finish()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,13 +2,15 @@ package com.v2ray.ang.ui
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.BitmapFactory
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import com.tbruyelle.rxpermissions3.RxPermissions
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.R
|
||||
import com.v2ray.ang.extension.toast
|
||||
@@ -21,6 +23,37 @@ import io.github.g00fy2.quickie.config.ScannerConfig
|
||||
class ScannerActivity : BaseActivity() {
|
||||
|
||||
private val scanQrCode = registerForActivityResult(ScanCustomCode(), ::handleResult)
|
||||
private val chooseFile = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
val uri = it.data?.data
|
||||
if (it.resultCode == RESULT_OK && uri != null) {
|
||||
try {
|
||||
val inputStream = contentResolver.openInputStream(uri)
|
||||
val bitmap = BitmapFactory.decodeStream(inputStream)
|
||||
inputStream?.close()
|
||||
|
||||
val text = QRCodeDecoder.syncDecodeQRCode(bitmap)
|
||||
if (text.isNullOrEmpty()) {
|
||||
toast(R.string.toast_decoding_failed)
|
||||
} else {
|
||||
finished(text)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
toast(R.string.toast_decoding_failed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val requestPermissionLauncher =
|
||||
registerForActivityResult(
|
||||
ActivityResultContracts.RequestPermission()
|
||||
) { isGranted: Boolean ->
|
||||
if (isGranted) {
|
||||
showFileChooser()
|
||||
} else {
|
||||
toast(R.string.toast_permission_denied)
|
||||
}
|
||||
}
|
||||
|
||||
public override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -72,14 +105,11 @@ class ScannerActivity : BaseActivity() {
|
||||
} else {
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
}
|
||||
RxPermissions(this)
|
||||
.request(permission)
|
||||
.subscribe { granted ->
|
||||
if (granted) {
|
||||
|
||||
if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) {
|
||||
showFileChooser()
|
||||
} else {
|
||||
toast(R.string.toast_permission_denied)
|
||||
}
|
||||
requestPermissionLauncher.launch(permission)
|
||||
}
|
||||
true
|
||||
}
|
||||
@@ -100,26 +130,4 @@ class ScannerActivity : BaseActivity() {
|
||||
toast(R.string.toast_require_file_manager)
|
||||
}
|
||||
}
|
||||
|
||||
private val chooseFile = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
val uri = it.data?.data
|
||||
if (it.resultCode == RESULT_OK && uri != null) {
|
||||
try {
|
||||
val inputStream = contentResolver.openInputStream(uri)
|
||||
val bitmap = BitmapFactory.decodeStream(inputStream)
|
||||
inputStream?.close()
|
||||
|
||||
val text = QRCodeDecoder.syncDecodeQRCode(bitmap)
|
||||
if (text.isNullOrEmpty()) {
|
||||
toast(R.string.toast_decoding_failed)
|
||||
} else {
|
||||
finished(text)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
toast(R.string.toast_decoding_failed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import com.v2ray.ang.R
|
||||
import com.v2ray.ang.dto.EConfigType
|
||||
import com.v2ray.ang.dto.NetworkType
|
||||
import com.v2ray.ang.dto.ProfileItem
|
||||
import com.v2ray.ang.extension.isNotNullEmpty
|
||||
import com.v2ray.ang.extension.toast
|
||||
import com.v2ray.ang.handler.MmkvManager
|
||||
import com.v2ray.ang.util.JsonUtil
|
||||
@@ -112,6 +113,7 @@ class ServerActivity : BaseActivity() {
|
||||
private val sp_stream_alpn: Spinner? by lazy { findViewById(R.id.sp_stream_alpn) } //uTLS
|
||||
private val container_alpn: LinearLayout? by lazy { findViewById(R.id.lay_stream_alpn) }
|
||||
private val et_public_key: EditText? by lazy { findViewById(R.id.et_public_key) }
|
||||
private val et_preshared_key: EditText? by lazy { findViewById(R.id.et_preshared_key) }
|
||||
private val container_public_key: LinearLayout? by lazy { findViewById(R.id.lay_public_key) }
|
||||
private val et_short_id: EditText? by lazy { findViewById(R.id.et_short_id) }
|
||||
private val container_short_id: LinearLayout? by lazy { findViewById(R.id.lay_short_id) }
|
||||
@@ -124,6 +126,8 @@ class ServerActivity : BaseActivity() {
|
||||
private val et_port_hop: EditText? by lazy { findViewById(R.id.et_port_hop) }
|
||||
private val et_port_hop_interval: EditText? by lazy { findViewById(R.id.et_port_hop_interval) }
|
||||
private val et_pinsha256: EditText? by lazy { findViewById(R.id.et_pinsha256) }
|
||||
private val et_bandwidth_down: EditText? by lazy { findViewById(R.id.et_bandwidth_down) }
|
||||
private val et_bandwidth_up: EditText? by lazy { findViewById(R.id.et_bandwidth_up) }
|
||||
private val et_extra: EditText? by lazy { findViewById(R.id.et_extra) }
|
||||
private val layout_extra: LinearLayout? by lazy { findViewById(R.id.layout_extra) }
|
||||
|
||||
@@ -149,7 +153,7 @@ class ServerActivity : BaseActivity() {
|
||||
parent: AdapterView<*>?,
|
||||
view: View?,
|
||||
position: Int,
|
||||
id: Long
|
||||
id: Long,
|
||||
) {
|
||||
val types = transportTypes(networks[position])
|
||||
sp_header_type?.isEnabled = types.size > 1
|
||||
@@ -160,7 +164,7 @@ class ServerActivity : BaseActivity() {
|
||||
sp_header_type_title?.text =
|
||||
when (networks[position]) {
|
||||
NetworkType.GRPC.type -> getString(R.string.server_lab_mode_type)
|
||||
NetworkType.SPLIT_HTTP.type, NetworkType.XHTTP.type -> getString(R.string.server_lab_xhttp_mode)
|
||||
NetworkType.XHTTP.type -> getString(R.string.server_lab_xhttp_mode)
|
||||
else -> getString(R.string.server_lab_head_type)
|
||||
}.orEmpty()
|
||||
sp_header_type?.setSelection(
|
||||
@@ -168,7 +172,7 @@ class ServerActivity : BaseActivity() {
|
||||
types,
|
||||
when (networks[position]) {
|
||||
NetworkType.GRPC.type -> config?.mode
|
||||
NetworkType.SPLIT_HTTP.type, NetworkType.XHTTP.type -> config?.xhttpMode
|
||||
NetworkType.XHTTP.type -> config?.xhttpMode
|
||||
else -> config?.headerType
|
||||
}.orEmpty()
|
||||
)
|
||||
@@ -196,7 +200,7 @@ class ServerActivity : BaseActivity() {
|
||||
NetworkType.TCP.type -> R.string.server_lab_request_host_http
|
||||
NetworkType.WS.type -> R.string.server_lab_request_host_ws
|
||||
NetworkType.HTTP_UPGRADE.type -> R.string.server_lab_request_host_httpupgrade
|
||||
NetworkType.SPLIT_HTTP.type, NetworkType.XHTTP.type -> R.string.server_lab_request_host_xhttp
|
||||
NetworkType.XHTTP.type -> R.string.server_lab_request_host_xhttp
|
||||
NetworkType.H2.type -> R.string.server_lab_request_host_h2
|
||||
//"quic" -> R.string.server_lab_request_host_quic
|
||||
NetworkType.GRPC.type -> R.string.server_lab_request_host_grpc
|
||||
@@ -211,7 +215,7 @@ class ServerActivity : BaseActivity() {
|
||||
NetworkType.KCP.type -> R.string.server_lab_path_kcp
|
||||
NetworkType.WS.type -> R.string.server_lab_path_ws
|
||||
NetworkType.HTTP_UPGRADE.type -> R.string.server_lab_path_httpupgrade
|
||||
NetworkType.SPLIT_HTTP.type, NetworkType.XHTTP.type -> R.string.server_lab_path_xhttp
|
||||
NetworkType.XHTTP.type -> R.string.server_lab_path_xhttp
|
||||
NetworkType.H2.type -> R.string.server_lab_path_h2
|
||||
//"quic" -> R.string.server_lab_path_quic
|
||||
NetworkType.GRPC.type -> R.string.server_lab_path_grpc
|
||||
@@ -221,14 +225,14 @@ class ServerActivity : BaseActivity() {
|
||||
)
|
||||
et_extra?.text = Utils.getEditable(
|
||||
when (networks[position]) {
|
||||
NetworkType.SPLIT_HTTP.type, NetworkType.XHTTP.type -> JsonUtil.toJsonPretty(JsonUtil.parseString(config?.xhttpExtra))
|
||||
NetworkType.XHTTP.type -> config?.xhttpExtra
|
||||
else -> null
|
||||
}.orEmpty()
|
||||
)
|
||||
|
||||
layout_extra?.visibility =
|
||||
when (networks[position]) {
|
||||
NetworkType.SPLIT_HTTP.type, NetworkType.XHTTP.type -> View.VISIBLE
|
||||
NetworkType.XHTTP.type -> View.VISIBLE
|
||||
else -> View.GONE
|
||||
}
|
||||
}
|
||||
@@ -242,7 +246,7 @@ class ServerActivity : BaseActivity() {
|
||||
parent: AdapterView<*>?,
|
||||
view: View?,
|
||||
position: Int,
|
||||
id: Long
|
||||
id: Long,
|
||||
) {
|
||||
val isBlank = streamSecuritys[position].isBlank()
|
||||
val isTLS = streamSecuritys[position] == TLS
|
||||
@@ -320,29 +324,23 @@ class ServerActivity : BaseActivity() {
|
||||
} else if (config.configType == EConfigType.WIREGUARD) {
|
||||
et_id.text = Utils.getEditable(config.secretKey.orEmpty())
|
||||
et_public_key?.text = Utils.getEditable(config.publicKey.orEmpty())
|
||||
if (config.reserved == null) {
|
||||
et_reserved1?.text = Utils.getEditable("0,0,0")
|
||||
} else {
|
||||
et_reserved1?.text = Utils.getEditable(config.reserved?.toString())
|
||||
}
|
||||
if (config.localAddress == null) {
|
||||
et_local_address?.text = Utils.getEditable("${WIREGUARD_LOCAL_ADDRESS_V4},${WIREGUARD_LOCAL_ADDRESS_V6}")
|
||||
} else {
|
||||
et_local_address?.text = Utils.getEditable(config.localAddress)
|
||||
}
|
||||
if (config.mtu == null) {
|
||||
et_local_mtu?.text = Utils.getEditable(WIREGUARD_LOCAL_MTU)
|
||||
} else {
|
||||
et_local_mtu?.text = Utils.getEditable(config.mtu.toString())
|
||||
}
|
||||
et_preshared_key?.visibility = View.VISIBLE
|
||||
et_preshared_key?.text = Utils.getEditable(config.preSharedKey.orEmpty())
|
||||
et_reserved1?.text = Utils.getEditable(config.reserved ?: "0,0,0")
|
||||
et_local_address?.text = Utils.getEditable(
|
||||
config.localAddress ?: "$WIREGUARD_LOCAL_ADDRESS_V4,$WIREGUARD_LOCAL_ADDRESS_V6"
|
||||
)
|
||||
et_local_mtu?.text = Utils.getEditable(config.mtu?.toString() ?: WIREGUARD_LOCAL_MTU)
|
||||
} else if (config.configType == EConfigType.HYSTERIA2) {
|
||||
et_obfs_password?.text = Utils.getEditable(config.obfsPassword)
|
||||
et_port_hop?.text = Utils.getEditable(config.portHopping)
|
||||
et_port_hop_interval?.text = Utils.getEditable(config.portHoppingInterval)
|
||||
et_pinsha256?.text = Utils.getEditable(config.pinSHA256)
|
||||
et_bandwidth_down?.text = Utils.getEditable(config.bandwidthDown)
|
||||
et_bandwidth_up?.text = Utils.getEditable(config.bandwidthUp)
|
||||
}
|
||||
|
||||
val securityEncryptions = if (config.configType == EConfigType.SHADOWSOCKS) shadowsocksSecuritys else securitys
|
||||
val securityEncryptions =
|
||||
if (config.configType == EConfigType.SHADOWSOCKS) shadowsocksSecuritys else securitys
|
||||
val security = Utils.arrayFind(securityEncryptions, config.method.orEmpty())
|
||||
if (security >= 0) {
|
||||
sp_security?.setSelection(security)
|
||||
@@ -358,11 +356,11 @@ class ServerActivity : BaseActivity() {
|
||||
et_sni?.text = Utils.getEditable(config.sni)
|
||||
config.fingerPrint?.let {
|
||||
val utlsIndex = Utils.arrayFind(uTlsItems, it)
|
||||
sp_stream_fingerprint?.setSelection(utlsIndex)
|
||||
utlsIndex.let { sp_stream_fingerprint?.setSelection(if (it >= 0) it else 0) }
|
||||
}
|
||||
config.alpn?.let {
|
||||
val alpnIndex = Utils.arrayFind(alpns, it)
|
||||
sp_stream_alpn?.setSelection(alpnIndex)
|
||||
alpnIndex.let { sp_stream_alpn?.setSelection(if (it >= 0) it else 0) }
|
||||
}
|
||||
if (config.security == TLS) {
|
||||
container_allow_insecure?.visibility = View.VISIBLE
|
||||
@@ -373,7 +371,7 @@ class ServerActivity : BaseActivity() {
|
||||
container_public_key?.visibility = View.GONE
|
||||
container_short_id?.visibility = View.GONE
|
||||
container_spider_x?.visibility = View.GONE
|
||||
} else if (config.security == REALITY) { // reality settings
|
||||
} else if (config.security == REALITY) {
|
||||
container_public_key?.visibility = View.VISIBLE
|
||||
et_public_key?.text = Utils.getEditable(config.publicKey.orEmpty())
|
||||
container_short_id?.visibility = View.VISIBLE
|
||||
@@ -393,7 +391,6 @@ class ServerActivity : BaseActivity() {
|
||||
container_short_id?.visibility = View.GONE
|
||||
container_spider_x?.visibility = View.GONE
|
||||
}
|
||||
|
||||
val network = Utils.arrayFind(networks, config.network.orEmpty())
|
||||
if (network >= 0) {
|
||||
sp_network?.setSelection(network)
|
||||
@@ -447,7 +444,8 @@ class ServerActivity : BaseActivity() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
val config = MmkvManager.decodeServerConfig(editGuid) ?: ProfileItem.create(createConfigType)
|
||||
val config =
|
||||
MmkvManager.decodeServerConfig(editGuid) ?: ProfileItem.create(createConfigType)
|
||||
if (config.configType != EConfigType.SOCKS
|
||||
&& config.configType != EConfigType.HTTP
|
||||
&& TextUtils.isEmpty(et_id.text.toString())
|
||||
@@ -468,6 +466,12 @@ class ServerActivity : BaseActivity() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if (et_extra?.text?.toString().isNotNullEmpty()) {
|
||||
if (JsonUtil.parseString(et_extra?.text?.toString()) == null) {
|
||||
toast(R.string.server_lab_xhttp_extra)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
saveCommon(config)
|
||||
saveStreamSettings(config)
|
||||
@@ -504,6 +508,7 @@ class ServerActivity : BaseActivity() {
|
||||
} else if (config.configType == EConfigType.WIREGUARD) {
|
||||
config.secretKey = et_id.text.toString().trim()
|
||||
config.publicKey = et_public_key?.text.toString().trim()
|
||||
config.preSharedKey = et_preshared_key?.text.toString().trim()
|
||||
config.reserved = et_reserved1?.text.toString().trim()
|
||||
config.localAddress = et_local_address?.text.toString().trim()
|
||||
config.mtu = Utils.parseInt(et_local_mtu?.text.toString())
|
||||
@@ -512,6 +517,8 @@ class ServerActivity : BaseActivity() {
|
||||
config.portHopping = et_port_hop?.text?.toString()
|
||||
config.portHoppingInterval = et_port_hop_interval?.text?.toString()
|
||||
config.pinSHA256 = et_pinsha256?.text?.toString()
|
||||
config.bandwidthDown = et_bandwidth_down?.text?.toString()
|
||||
config.bandwidthUp = et_bandwidth_up?.text?.toString()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -548,7 +555,7 @@ class ServerActivity : BaseActivity() {
|
||||
|
||||
val allowInsecure =
|
||||
if (allowInsecureField == null || allowinsecures[allowInsecureField].isBlank()) {
|
||||
MmkvManager.decodeSettingsBool(PREF_ALLOW_INSECURE) == true
|
||||
MmkvManager.decodeSettingsBool(PREF_ALLOW_INSECURE)
|
||||
} else {
|
||||
allowinsecures[allowInsecureField].toBoolean()
|
||||
}
|
||||
@@ -577,7 +584,7 @@ class ServerActivity : BaseActivity() {
|
||||
grpcModes
|
||||
}
|
||||
|
||||
NetworkType.SPLIT_HTTP.type, NetworkType.XHTTP.type -> {
|
||||
NetworkType.XHTTP.type -> {
|
||||
xhttpMode
|
||||
}
|
||||
|
||||
@@ -599,7 +606,7 @@ class ServerActivity : BaseActivity() {
|
||||
MmkvManager.removeServer(editGuid)
|
||||
finish()
|
||||
}
|
||||
.setNegativeButton(android.R.string.no) { _, _ ->
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||
// do nothing
|
||||
}
|
||||
.show()
|
||||
|
||||
@@ -106,7 +106,7 @@ class ServerCustomConfigActivity : BaseActivity() {
|
||||
MmkvManager.removeServer(editGuid)
|
||||
finish()
|
||||
}
|
||||
.setNegativeButton(android.R.string.no) { _, _ ->
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||
// do nothing
|
||||
}
|
||||
.show()
|
||||
|
||||
@@ -40,8 +40,10 @@ class SettingsActivity : BaseActivity() {
|
||||
private val perAppProxy by lazy { findPreference<CheckBoxPreference>(AppConfig.PREF_PER_APP_PROXY) }
|
||||
private val localDns by lazy { findPreference<CheckBoxPreference>(AppConfig.PREF_LOCAL_DNS_ENABLED) }
|
||||
private val fakeDns by lazy { findPreference<CheckBoxPreference>(AppConfig.PREF_FAKE_DNS_ENABLED) }
|
||||
private val appendHttpProxy by lazy { findPreference<CheckBoxPreference>(AppConfig.PREF_APPEND_HTTP_PROXY) }
|
||||
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 vpnBypassLan by lazy { findPreference<ListPreference>(AppConfig.PREF_VPN_BYPASS_LAN) }
|
||||
|
||||
private val mux by lazy { findPreference<CheckBoxPreference>(AppConfig.PREF_MUX_ENABLED) }
|
||||
private val muxConcurrency by lazy { findPreference<EditTextPreference>(AppConfig.PREF_MUX_CONCURRENCY) }
|
||||
@@ -57,9 +59,9 @@ class SettingsActivity : BaseActivity() {
|
||||
private val autoUpdateInterval by lazy { findPreference<EditTextPreference>(AppConfig.SUBSCRIPTION_AUTO_UPDATE_INTERVAL) }
|
||||
|
||||
private val socksPort by lazy { findPreference<EditTextPreference>(AppConfig.PREF_SOCKS_PORT) }
|
||||
private val httpPort by lazy { findPreference<EditTextPreference>(AppConfig.PREF_HTTP_PORT) }
|
||||
private val remoteDns by lazy { findPreference<EditTextPreference>(AppConfig.PREF_REMOTE_DNS) }
|
||||
private val domesticDns by lazy { findPreference<EditTextPreference>(AppConfig.PREF_DOMESTIC_DNS) }
|
||||
private val dnsHosts by lazy { findPreference<EditTextPreference>(AppConfig.PREF_DNS_HOSTS) }
|
||||
private val delayTestUrl by lazy { findPreference<EditTextPreference>(AppConfig.PREF_DELAY_TEST_URL) }
|
||||
private val mode by lazy { findPreference<ListPreference>(AppConfig.PREF_MODE) }
|
||||
|
||||
@@ -141,11 +143,7 @@ class SettingsActivity : BaseActivity() {
|
||||
socksPort?.summary = if (TextUtils.isEmpty(nval)) AppConfig.PORT_SOCKS else nval
|
||||
true
|
||||
}
|
||||
httpPort?.setOnPreferenceChangeListener { _, any ->
|
||||
val nval = any as String
|
||||
httpPort?.summary = if (TextUtils.isEmpty(nval)) AppConfig.PORT_HTTP else nval
|
||||
true
|
||||
}
|
||||
|
||||
remoteDns?.setOnPreferenceChangeListener { _, any ->
|
||||
val nval = any as String
|
||||
remoteDns?.summary = if (nval == "") AppConfig.DNS_PROXY else nval
|
||||
@@ -156,6 +154,11 @@ class SettingsActivity : BaseActivity() {
|
||||
domesticDns?.summary = if (nval == "") AppConfig.DNS_DIRECT else nval
|
||||
true
|
||||
}
|
||||
dnsHosts?.setOnPreferenceChangeListener { _, any ->
|
||||
val nval = any as String
|
||||
dnsHosts?.summary = nval
|
||||
true
|
||||
}
|
||||
delayTestUrl?.setOnPreferenceChangeListener { _, any ->
|
||||
val nval = any as String
|
||||
delayTestUrl?.summary = if (nval == "") AppConfig.DelayTestUrl else nval
|
||||
@@ -175,6 +178,7 @@ class SettingsActivity : BaseActivity() {
|
||||
updateMode(MmkvManager.decodeSettingsString(AppConfig.PREF_MODE, VPN))
|
||||
localDns?.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_LOCAL_DNS_ENABLED, false)
|
||||
fakeDns?.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_FAKE_DNS_ENABLED, false)
|
||||
appendHttpProxy?.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_APPEND_HTTP_PROXY, false)
|
||||
localDnsPort?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_LOCAL_DNS_PORT, AppConfig.PORT_LOCAL_DNS)
|
||||
vpnDns?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_VPN_DNS, AppConfig.DNS_VPN)
|
||||
|
||||
@@ -195,9 +199,9 @@ class SettingsActivity : BaseActivity() {
|
||||
autoUpdateInterval?.isEnabled = MmkvManager.decodeSettingsBool(AppConfig.SUBSCRIPTION_AUTO_UPDATE, false)
|
||||
|
||||
socksPort?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_SOCKS_PORT, AppConfig.PORT_SOCKS)
|
||||
httpPort?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_HTTP_PORT, AppConfig.PORT_HTTP)
|
||||
remoteDns?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_REMOTE_DNS, AppConfig.DNS_PROXY)
|
||||
domesticDns?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_DOMESTIC_DNS, AppConfig.DNS_DIRECT)
|
||||
dnsHosts?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_DNS_HOSTS)
|
||||
delayTestUrl?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_DELAY_TEST_URL, AppConfig.DelayTestUrl)
|
||||
|
||||
initSharedPreference()
|
||||
@@ -213,7 +217,6 @@ class SettingsActivity : BaseActivity() {
|
||||
fragmentInterval,
|
||||
autoUpdateInterval,
|
||||
socksPort,
|
||||
httpPort,
|
||||
remoteDns,
|
||||
domesticDns,
|
||||
delayTestUrl
|
||||
@@ -244,6 +247,7 @@ class SettingsActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
listOf(
|
||||
AppConfig.PREF_VPN_BYPASS_LAN,
|
||||
AppConfig.PREF_ROUTING_DOMAIN_STRATEGY,
|
||||
AppConfig.PREF_MUX_XUDP_QUIC,
|
||||
AppConfig.PREF_FRAGMENT_PACKETS,
|
||||
@@ -264,8 +268,11 @@ class SettingsActivity : BaseActivity() {
|
||||
perAppProxy?.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_PER_APP_PROXY, false)
|
||||
localDns?.isEnabled = vpn
|
||||
fakeDns?.isEnabled = vpn
|
||||
appendHttpProxy?.isEnabled = vpn
|
||||
localDnsPort?.isEnabled = vpn
|
||||
vpnDns?.isEnabled = vpn
|
||||
vpnBypassLan?.isEnabled = vpn
|
||||
vpn
|
||||
if (vpn) {
|
||||
updateLocalDns(
|
||||
MmkvManager.decodeSettingsBool(
|
||||
|
||||
@@ -113,7 +113,7 @@ class SubEditActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
.setNegativeButton(android.R.string.no) { _, _ ->
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||
// do nothing
|
||||
}
|
||||
.show()
|
||||
|
||||
@@ -4,10 +4,15 @@ import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.v2ray.ang.R
|
||||
import com.v2ray.ang.databinding.ActivityLogcatBinding
|
||||
import com.v2ray.ang.extension.toast
|
||||
import com.v2ray.ang.handler.AngConfigManager
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.net.URLDecoder
|
||||
|
||||
class UrlSchemeActivity : BaseActivity() {
|
||||
@@ -66,7 +71,9 @@ class UrlSchemeActivity : BaseActivity() {
|
||||
decodedUrl += "#${fragment}"
|
||||
}
|
||||
Log.d("UrlScheme-decodedUrl", decodedUrl)
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val (count, countSub) = AngConfigManager.importBatchConfig(decodedUrl, "", false)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (count + countSub > 0) {
|
||||
toast(R.string.import_subscription_success)
|
||||
} else {
|
||||
@@ -74,4 +81,6 @@ class UrlSchemeActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@ import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.tbruyelle.rxpermissions3.RxPermissions
|
||||
import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.AppConfig.LOOPBACK
|
||||
import com.v2ray.ang.R
|
||||
@@ -50,6 +49,38 @@ class UserAssetActivity : BaseActivity() {
|
||||
val extDir by lazy { File(Utils.userAssetPath(this)) }
|
||||
val builtInGeoFiles = arrayOf("geosite.dat", "geoip.dat")
|
||||
|
||||
private val requestStoragePermissionLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.RequestPermission()
|
||||
) { isGranted: Boolean ->
|
||||
if (isGranted) {
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT)
|
||||
intent.type = "*/*"
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
|
||||
try {
|
||||
chooseFile.launch(
|
||||
Intent.createChooser(
|
||||
intent,
|
||||
getString(R.string.title_file_chooser)
|
||||
)
|
||||
)
|
||||
} catch (ex: android.content.ActivityNotFoundException) {
|
||||
toast(R.string.toast_require_file_manager)
|
||||
}
|
||||
} else {
|
||||
toast(R.string.toast_permission_denied)
|
||||
}
|
||||
}
|
||||
|
||||
private val requestCameraPermissionLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.RequestPermission()
|
||||
) { isGranted: Boolean ->
|
||||
if (isGranted) {
|
||||
scanQRCodeForAssetURL.launch(Intent(this, ScannerActivity::class.java))
|
||||
} else {
|
||||
toast(R.string.toast_permission_denied)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -86,27 +117,7 @@ class UserAssetActivity : BaseActivity() {
|
||||
} else {
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
}
|
||||
RxPermissions(this)
|
||||
.request(permission)
|
||||
.subscribe {
|
||||
if (it) {
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT)
|
||||
intent.type = "*/*"
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
|
||||
try {
|
||||
chooseFile.launch(
|
||||
Intent.createChooser(
|
||||
intent,
|
||||
getString(R.string.title_file_chooser)
|
||||
)
|
||||
)
|
||||
} catch (ex: android.content.ActivityNotFoundException) {
|
||||
toast(R.string.toast_require_file_manager)
|
||||
}
|
||||
} else
|
||||
toast(R.string.toast_permission_denied)
|
||||
}
|
||||
requestStoragePermissionLauncher.launch(permission)
|
||||
}
|
||||
|
||||
val chooseFile = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
@@ -158,14 +169,7 @@ class UserAssetActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
private fun importAssetFromQRcode(): Boolean {
|
||||
RxPermissions(this)
|
||||
.request(Manifest.permission.CAMERA)
|
||||
.subscribe {
|
||||
if (it)
|
||||
scanQRCodeForAssetURL.launch(Intent(this, ScannerActivity::class.java))
|
||||
else
|
||||
toast(R.string.toast_permission_denied)
|
||||
}
|
||||
requestCameraPermissionLauncher.launch(Manifest.permission.CAMERA)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -333,7 +337,7 @@ class UserAssetActivity : BaseActivity() {
|
||||
MmkvManager.removeAssetUrl(item.first)
|
||||
initAssets()
|
||||
}
|
||||
.setNegativeButton(android.R.string.no) { _, _ ->
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||
//do noting
|
||||
}
|
||||
.show()
|
||||
|
||||
@@ -116,7 +116,7 @@ class UserAssetUrlActivity : BaseActivity() {
|
||||
MmkvManager.removeAssetUrl(editAssetId)
|
||||
finish()
|
||||
}
|
||||
.setNegativeButton(android.R.string.no) { _, _ ->
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||
// do nothing
|
||||
}
|
||||
.show()
|
||||
|
||||
@@ -4,11 +4,13 @@ import android.content.Context
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageManager
|
||||
import com.v2ray.ang.dto.AppInfo
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
||||
object AppManagerUtil {
|
||||
private fun loadNetworkAppList(ctx: Context): ArrayList<AppInfo> {
|
||||
val packageManager = ctx.packageManager
|
||||
suspend fun loadNetworkAppList(context: Context): ArrayList<AppInfo> =
|
||||
withContext(Dispatchers.IO) {
|
||||
val packageManager = context.packageManager
|
||||
val packages = packageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS)
|
||||
val apps = ArrayList<AppInfo>()
|
||||
|
||||
@@ -23,17 +25,6 @@ object AppManagerUtil {
|
||||
apps.add(appInfo)
|
||||
}
|
||||
|
||||
return apps
|
||||
return@withContext apps
|
||||
}
|
||||
|
||||
fun rxLoadNetworkAppList(ctx: Context): Observable<ArrayList<AppInfo>> =
|
||||
Observable.unsafeCreate {
|
||||
it.onNext(loadNetworkAppList(ctx))
|
||||
}
|
||||
|
||||
// val PackageInfo.hasInternetPermission: Boolean
|
||||
// get() {
|
||||
// val permissions = requestedPermissions
|
||||
// return permissions?.any { it == Manifest.permission.INTERNET } ?: false
|
||||
// }
|
||||
}
|
||||
@@ -42,7 +42,11 @@ object JsonUtil {
|
||||
fun parseString(src: String?): JsonObject? {
|
||||
if (src == null)
|
||||
return null
|
||||
try {
|
||||
return JsonParser.parseString(src).getAsJsonObject()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -498,5 +498,7 @@ object Utils {
|
||||
ContextCompat.RECEIVER_NOT_EXPORTED
|
||||
}
|
||||
|
||||
fun isXray(): Boolean = (ANG_PACKAGE.startsWith("com.v2ray.ang"))
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.res.AssetManager
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
@@ -274,7 +273,8 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
||||
return deleteServer.count()
|
||||
}
|
||||
|
||||
fun removeAllServer() {
|
||||
fun removeAllServer(): Int {
|
||||
val count =
|
||||
if (subscriptionId.isEmpty() && keywordFilter.isEmpty()) {
|
||||
MmkvManager.removeAllServer()
|
||||
} else {
|
||||
@@ -282,18 +282,22 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
||||
for (item in serversCopy) {
|
||||
MmkvManager.removeServer(item.guid)
|
||||
}
|
||||
serversCache.toList().count()
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
fun removeInvalidServer() {
|
||||
fun removeInvalidServer(): Int {
|
||||
var count = 0
|
||||
if (subscriptionId.isEmpty() && keywordFilter.isEmpty()) {
|
||||
MmkvManager.removeInvalidServer("")
|
||||
count += MmkvManager.removeInvalidServer("")
|
||||
} else {
|
||||
val serversCopy = serversCache.toList()
|
||||
for (item in serversCopy) {
|
||||
MmkvManager.removeInvalidServer(item.guid)
|
||||
count += MmkvManager.removeInvalidServer(item.guid)
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
fun sortByTestResults() {
|
||||
|
||||
@@ -29,12 +29,13 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
|
||||
when (key) {
|
||||
AppConfig.PREF_MODE,
|
||||
AppConfig.PREF_VPN_DNS,
|
||||
AppConfig.PREF_VPN_BYPASS_LAN,
|
||||
AppConfig.PREF_REMOTE_DNS,
|
||||
AppConfig.PREF_DOMESTIC_DNS,
|
||||
AppConfig.PREF_DNS_HOSTS,
|
||||
AppConfig.PREF_DELAY_TEST_URL,
|
||||
AppConfig.PREF_LOCAL_DNS_PORT,
|
||||
AppConfig.PREF_SOCKS_PORT,
|
||||
AppConfig.PREF_HTTP_PORT,
|
||||
AppConfig.PREF_LOGLEVEL,
|
||||
AppConfig.PREF_LANGUAGE,
|
||||
AppConfig.PREF_UI_MODE_NIGHT,
|
||||
@@ -54,6 +55,7 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
|
||||
AppConfig.PREF_PROXY_SHARING,
|
||||
AppConfig.PREF_LOCAL_DNS_ENABLED,
|
||||
AppConfig.PREF_FAKE_DNS_ENABLED,
|
||||
AppConfig.PREF_APPEND_HTTP_PROXY,
|
||||
AppConfig.PREF_ALLOW_INSECURE,
|
||||
AppConfig.PREF_PREFER_IPV6,
|
||||
AppConfig.PREF_PER_APP_PROXY,
|
||||
|
||||
10
V2rayNG/app/src/main/res/drawable/license_24px.xml
Normal file
10
V2rayNG/app/src/main/res/drawable/license_24px.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M480,520Q430,520 395,485Q360,450 360,400Q360,350 395,315Q430,280 480,280Q530,280 565,315Q600,350 600,400Q600,450 565,485Q530,520 480,520ZM240,920L240,611Q202,569 181,515Q160,461 160,400Q160,266 253,173Q346,80 480,80Q614,80 707,173Q800,266 800,400Q800,461 779,515Q758,569 720,611L720,920L480,840L240,920ZM480,640Q580,640 650,570Q720,500 720,400Q720,300 650,230Q580,160 480,160Q380,160 310,230Q240,300 240,400Q240,500 310,570Q380,640 480,640ZM320,801L480,760L640,801L640,677Q605,697 564.5,708.5Q524,720 480,720Q436,720 395.5,708.5Q355,697 320,677L320,801ZM480,739L480,739Q480,739 480,739Q480,739 480,739Q480,739 480,739Q480,739 480,739L480,739L480,739Z"/>
|
||||
</vector>
|
||||
@@ -135,6 +135,30 @@
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_oss_licenses"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/server_height"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center|start"
|
||||
android:orientation="horizontal"
|
||||
android:padding="@dimen/padding">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/png_height"
|
||||
android:layout_height="@dimen/png_height"
|
||||
app:srcCompat="@drawable/license_24px" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/padding_start"
|
||||
android:text="@string/title_oss_license"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_feedback"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -77,6 +77,7 @@
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:scrollbars="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
|
||||
@@ -1,40 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
android:paddingStart="@dimen/padding_start"
|
||||
android:paddingEnd="@dimen/padding_end"
|
||||
tools:context=".ui.LogcatActivity">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/pb_waiting"
|
||||
style="@android:style/Widget.DeviceDefault.ProgressBar"
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/refreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_centerVertical="true" />
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/sv_logcat"
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:scrollbars="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:fillViewport="false"
|
||||
android:foregroundGravity="bottom">
|
||||
android:layout_height="match_parent"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_logcat"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="bottom"
|
||||
android:maxLines="65536"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</ScrollView>
|
||||
</RelativeLayout>
|
||||
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:scrollbars="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
|
||||
@@ -90,6 +90,44 @@
|
||||
|
||||
</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_bandwidth_down" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_bandwidth_down"
|
||||
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_bandwidth_up" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_bandwidth_up"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/edit_height"
|
||||
android:inputType="text" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<include layout="@layout/layout_tls_hysteria2" />
|
||||
|
||||
<LinearLayout
|
||||
|
||||
@@ -51,6 +51,25 @@
|
||||
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_preshared_key" />
|
||||
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_preshared_key"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/edit_height"
|
||||
android:inputType="text" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@@ -16,18 +17,16 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:checked="true" />
|
||||
android:checked="true"
|
||||
app:theme="@style/BrandedSwitch" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toStartOf="@id/switch_start_service"
|
||||
android:layout_toLeftOf="@id/switch_start_service"
|
||||
android:text="@string/tasker_start_service"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
|
||||
|
||||
|
||||
24
V2rayNG/app/src/main/res/layout/item_recycler_logcat.xml
Normal file
24
V2rayNG/app/src/main/res/layout/item_recycler_logcat.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/layout_margin_spacing">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/log_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -44,30 +44,23 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/layout_margin_spacing"
|
||||
android:lines="1"
|
||||
android:lines="2"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/layout_margin_spacing"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/chk_enable"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/sub_setting_enable"
|
||||
app:theme="@style/BrandedSwitch" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/sub_height"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="@dimen/padding_start"
|
||||
android:paddingEnd="@dimen/padding_end">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
@@ -108,6 +101,19 @@
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/layout_margin_top_height"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/chk_enable"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:theme="@style/BrandedSwitch" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
android:id="@+id/del_config"
|
||||
android:icon="@drawable/ic_delete_24dp"
|
||||
android:title="@string/menu_item_del_config"
|
||||
app:showAsAction="always" />
|
||||
app:showAsAction="ifRoom" />
|
||||
<item
|
||||
android:id="@+id/save_config"
|
||||
android:icon="@drawable/ic_action_done"
|
||||
android:title="@string/menu_item_save_config"
|
||||
app:showAsAction="always" />
|
||||
app:showAsAction="ifRoom" />
|
||||
</menu>
|
||||
@@ -5,10 +5,10 @@
|
||||
android:id="@+id/add_config"
|
||||
android:icon="@drawable/ic_add_24dp"
|
||||
android:title="@string/menu_item_add_config"
|
||||
app:showAsAction="always" />
|
||||
app:showAsAction="ifRoom" />
|
||||
<item
|
||||
android:id="@+id/sub_update"
|
||||
android:icon="@drawable/ic_restore_24dp"
|
||||
android:title="@string/title_sub_update"
|
||||
app:showAsAction="always" />
|
||||
app:showAsAction="ifRoom" />
|
||||
</menu>
|
||||
@@ -24,5 +24,5 @@
|
||||
android:id="@+id/download_file"
|
||||
android:icon="@drawable/ic_cloud_download_24dp"
|
||||
android:title="@string/menu_item_download_file"
|
||||
app:showAsAction="always" />
|
||||
app:showAsAction="ifRoom" />
|
||||
</menu>
|
||||
@@ -6,7 +6,7 @@
|
||||
android:icon="@drawable/ic_description_24dp"
|
||||
android:title="@string/menu_item_search"
|
||||
app:actionViewClass="androidx.appcompat.widget.SearchView"
|
||||
app:showAsAction="always" />
|
||||
app:showAsAction="ifRoom" />
|
||||
<item
|
||||
android:id="@+id/select_all"
|
||||
android:icon="@drawable/ic_select_all_24dp"
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/search_view"
|
||||
android:icon="@drawable/ic_outline_filter_alt_24"
|
||||
android:title="@string/menu_item_search"
|
||||
app:actionViewClass="androidx.appcompat.widget.SearchView"
|
||||
app:showAsAction="always|collapseActionView" />
|
||||
<item
|
||||
android:id="@+id/clear_all"
|
||||
android:icon="@drawable/ic_delete_24dp"
|
||||
android:title="@string/logcat_clear"
|
||||
app:showAsAction="always" />
|
||||
app:showAsAction="ifRoom" />
|
||||
<item
|
||||
android:id="@+id/copy_all"
|
||||
android:icon="@drawable/ic_copy"
|
||||
android:title="@string/logcat_copy"
|
||||
app:showAsAction="always" />
|
||||
app:showAsAction="ifRoom" />
|
||||
</menu>
|
||||
@@ -55,7 +55,7 @@
|
||||
|
||||
<item
|
||||
android:title="@string/menu_item_import_config_custom"
|
||||
app:showAsAction="always">
|
||||
app:showAsAction="ifRoom">
|
||||
<menu>
|
||||
<item
|
||||
android:id="@+id/import_config_custom_clipboard"
|
||||
|
||||
@@ -11,13 +11,17 @@
|
||||
android:icon="@drawable/ic_file_24dp"
|
||||
android:title="@string/title_user_asset_setting" />
|
||||
<item
|
||||
android:id="@+id/import_rulesets"
|
||||
android:title="@string/routing_settings_import_rulesets"
|
||||
android:id="@+id/import_predefined_rulesets"
|
||||
android:title="@string/routing_settings_import_predefined_rulesets"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/import_rulesets_from_clipboard"
|
||||
android:title="@string/routing_settings_import_rulesets_from_clipboard"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/import_rulesets_from_qrcode"
|
||||
android:title="@string/routing_settings_import_rulesets_from_qrcode"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/export_rulesets_to_clipboard"
|
||||
android:title="@string/routing_settings_export_rulesets_to_clipboard"
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
android:id="@+id/scan_code"
|
||||
android:icon="@drawable/ic_scan_24dp"
|
||||
android:title=""
|
||||
app:showAsAction="always" />
|
||||
app:showAsAction="ifRoom" />
|
||||
<item
|
||||
android:id="@+id/select_photo"
|
||||
android:icon="@drawable/ic_image_24dp"
|
||||
android:title=""
|
||||
app:showAsAction="always" />
|
||||
app:showAsAction="ifRoom" />
|
||||
</menu>
|
||||
@@ -8,6 +8,7 @@
|
||||
<string name="navigation_drawer_close">إغلاق درج التنقل</string>
|
||||
<string name="migration_success">نجحت عملية ترحيل البيانات!</string>
|
||||
<string name="migration_fail">فشلت عملية ترحيل البيانات!</string>
|
||||
<string name="pull_down_to_refresh">Please pull down to refresh!</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
<string name="notification_action_stop_v2ray">إيقاف</string>
|
||||
@@ -82,6 +83,7 @@
|
||||
<string name="server_lab_encryption">التشفير</string>
|
||||
<string name="server_lab_flow">التدفق</string>
|
||||
<string name="server_lab_public_key" translatable="false">المفتاح العام</string>
|
||||
<string name="server_lab_preshared_key">PreSharedKey(optional)</string>
|
||||
<string name="server_lab_short_id" translatable="false">المعرّف القصير</string>
|
||||
<string name="server_lab_spider_x" translatable="false">SpiderX</string>
|
||||
<string name="server_lab_secret_key" translatable="false">المفتاح السري</string>
|
||||
@@ -119,6 +121,8 @@
|
||||
<string name="server_lab_port_hop">Port Hopping</string>
|
||||
<string name="server_lab_port_hop_interval">Port Hopping Interval</string>
|
||||
<string name="server_lab_stream_pinsha256">pinSHA256</string>
|
||||
<string name="server_lab_bandwidth_down">Bandwidth down (units)</string>
|
||||
<string name="server_lab_bandwidth_up">Bandwidth up (units)</string>
|
||||
<string name="server_lab_xhttp_mode">XHTTP Mode</string>
|
||||
<string name="server_lab_xhttp_extra">XHTTP Extra raw JSON, format: { XHTTPObject }</string>
|
||||
|
||||
@@ -175,10 +179,14 @@
|
||||
<string name="summary_pref_remote_dns">DNS</string>
|
||||
|
||||
<string name="title_pref_vpn_dns">VPN DNS (IPv4/v6 فقط)</string>
|
||||
<string name="title_pref_vpn_bypass_lan">Does VPN bypass LAN</string>
|
||||
|
||||
<string name="title_pref_domestic_dns">DNS المحلي (اختياري)</string>
|
||||
<string name="summary_pref_domestic_dns">DNS</string>
|
||||
|
||||
<string name="title_pref_dns_hosts">DNS hosts (Format: domain:address,…)</string>
|
||||
<string name="summary_pref_dns_hosts">domain:address,…</string>
|
||||
|
||||
<string name="title_pref_delay_test_url">True delay test url (http/https)</string>
|
||||
<string name="summary_pref_delay_test_url">Url</string>
|
||||
|
||||
@@ -189,11 +197,8 @@
|
||||
<string name="title_pref_allow_insecure">السماح غير الآمن</string>
|
||||
<string name="summary_pref_allow_insecure">عند TLS، الافتراضي هو السماح غير الآمن</string>
|
||||
|
||||
<string name="title_pref_socks_port">منفذ بروكسي SOCKS5</string>
|
||||
<string name="summary_pref_socks_port">منفذ بروكسي SOCKS5</string>
|
||||
|
||||
<string name="title_pref_http_port">منفذ بروكسي HTTP</string>
|
||||
<string name="summary_pref_http_port">منفذ بروكسي HTTP</string>
|
||||
<string name="title_pref_socks_port">منفذ بروكسي Local</string>
|
||||
<string name="summary_pref_socks_port">منفذ بروكسي Local</string>
|
||||
|
||||
<string name="title_pref_local_dns_port">منفذ DNS المحلي</string>
|
||||
<string name="summary_pref_local_dns_port">منفذ DNS المحلي</string>
|
||||
@@ -204,6 +209,9 @@
|
||||
<string name="title_pref_start_scan_immediate">بدء المسح الضوئي على الفور</string>
|
||||
<string name="summary_pref_start_scan_immediate">افتح الكاميرا لمسح الرمز ضوئيًا على الفور عند بدء التشغيل، وإلا يمكنك اختيار مسح الرمز ضوئيًا أو تحديد صورة في شريط الأدوات</string>
|
||||
|
||||
<string name="title_pref_append_http_proxy">Append HTTP Proxy to VPN</string>
|
||||
<string name="summary_pref_append_http_proxy">HTTP proxy will be used directly from (browser/ some supported apps), without going through the virtual NIC device (Android 10+)</string>
|
||||
|
||||
<string name="title_pref_feedback">ملاحظات</string>
|
||||
<string name="summary_pref_feedback">ملاحظات التحسينات أو الأخطاء إلى GitHub</string>
|
||||
<string name="summary_pref_tg_group">الانضمام إلى مجموعة Telegram</string>
|
||||
@@ -211,6 +219,7 @@
|
||||
<string name="title_privacy_policy">سياسة الخصوصية</string>
|
||||
<string name="title_about">حول\nترجمة م. ابراهيم قاسم</string>
|
||||
<string name="title_source_code">الكود المصدري</string>
|
||||
<string name="title_oss_license">Open Source licenses</string>
|
||||
<string name="title_tg_channel">قناة Telegram</string>
|
||||
<string name="title_configuration_backup">نسخ التكوين احتياطيًا</string>
|
||||
<string name="summary_configuration_backup">موقع التخزين: [%s]، سيتم مسح النسخة الاحتياطية بعد إلغاء تثبيت التطبيق أو مسح التخزين</string>
|
||||
@@ -257,6 +266,10 @@
|
||||
<string name="filter_config_all">جميع مجموعات الاشتراك</string>
|
||||
<string name="title_del_duplicate_config_count">حذف %d من الإعدادات المكررة</string>
|
||||
|
||||
<string name="title_del_config_count">Delete %d configurations</string>
|
||||
<string name="title_import_config_count">Import %d configurations</string>
|
||||
<string name="title_export_config_count">Export %d configurations</string>
|
||||
<string name="title_update_config_count">Update %d configurations</string>
|
||||
<string name="tasker_start_service">بدء الخدمة</string>
|
||||
<string name="tasker_setting_confirm">تأكيد</string>
|
||||
|
||||
@@ -267,14 +280,16 @@
|
||||
<string name="routing_settings_delete">مسح</string>
|
||||
<string name="routing_settings_rule_title">Routing Rule Settings</string>
|
||||
<string name="routing_settings_add_rule">Add rule</string>
|
||||
<string name="routing_settings_import_rulesets">Import ruleset</string>
|
||||
<string name="routing_settings_import_predefined_rulesets">استيراد مجموعات قواعد محددة مسبقاً</string>
|
||||
<string name="routing_settings_import_rulesets_tip">Existing rulesets will be deleted, are you sure to continue?</string>
|
||||
<string name="routing_settings_import_rulesets_from_clipboard">Import ruleset from clipboard</string>
|
||||
<string name="routing_settings_import_rulesets_from_qrcode">استيراد مجموعة قواعد من رمز الاستجابة السريعة</string>
|
||||
<string name="routing_settings_export_rulesets_to_clipboard">Export ruleset to clipboard</string>
|
||||
<string name="routing_settings_locked">Locked, keep this rule when import presets</string>
|
||||
|
||||
<string name="connection_test_pending">التحقق من الاتصال</string>
|
||||
<string name="connection_test_testing">يجري الاختبار…</string>
|
||||
<string name="connection_test_testing_count">Testing %d configurations…</string>
|
||||
<string name="connection_test_available">نجاح: استغرق اتصال HTTP %dms</string>
|
||||
<string name="connection_test_error">فشل اكتشاف اتصال الإنترنت: %s</string>
|
||||
<string name="connection_test_fail">الإنترنت غير متاح</string>
|
||||
@@ -312,4 +327,10 @@
|
||||
<item>داكن</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="vpn_bypass_lan">
|
||||
<item>Follow config</item>
|
||||
<item>Bypass</item>
|
||||
<item>Not Bypass</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<string name="navigation_drawer_close">নেভিগেশন ড্রয়ার বন্ধ করুন</string>
|
||||
<string name="migration_success">ডেটা স্থানান্তর সফল!</string>
|
||||
<string name="migration_fail">ডেটা স্থানান্তর ব্যর্থ!</string>
|
||||
<string name="pull_down_to_refresh">Please pull down to refresh!</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
<string name="notification_action_stop_v2ray">বন্ধ করুন</string>
|
||||
@@ -81,6 +82,7 @@
|
||||
<string name="server_lab_encryption">এনক্রিপশন</string>
|
||||
<string name="server_lab_flow">ফ্লো</string>
|
||||
<string name="server_lab_public_key" translatable="false">পাবলিক কী</string>
|
||||
<string name="server_lab_preshared_key">PreSharedKey(optional)</string>
|
||||
<string name="server_lab_short_id" translatable="false">শর্ট আইডি</string>
|
||||
<string name="server_lab_spider_x" translatable="false">SpiderX</string>
|
||||
<string name="server_lab_secret_key" translatable="false">সিক্রেট কী</string>
|
||||
@@ -118,6 +120,8 @@
|
||||
<string name="server_lab_port_hop">Port Hopping</string>
|
||||
<string name="server_lab_port_hop_interval">Port Hopping Interval</string>
|
||||
<string name="server_lab_stream_pinsha256">pinSHA256</string>
|
||||
<string name="server_lab_bandwidth_down">Bandwidth down (units)</string>
|
||||
<string name="server_lab_bandwidth_up">Bandwidth up (units)</string>
|
||||
<string name="server_lab_xhttp_mode">XHTTP Mode</string>
|
||||
<string name="server_lab_xhttp_extra">XHTTP Extra raw JSON, format: { XHTTPObject }</string>
|
||||
|
||||
@@ -175,10 +179,14 @@
|
||||
<string name="summary_pref_remote_dns">DNS</string>
|
||||
|
||||
<string name="title_pref_vpn_dns">VPN DNS (শুধুমাত্র IPv4/v6)</string>
|
||||
<string name="title_pref_vpn_bypass_lan">Does VPN bypass LAN</string>
|
||||
|
||||
<string name="title_pref_domestic_dns">ঘরোয়া DNS (ঐচ্ছিক)</string>
|
||||
<string name="summary_pref_domestic_dns">DNS</string>
|
||||
|
||||
<string name="title_pref_dns_hosts">DNS hosts (Format: domain:address,…)</string>
|
||||
<string name="summary_pref_dns_hosts">domain:address,…</string>
|
||||
|
||||
<string name="title_pref_delay_test_url">সঠিক বিলম্ব পরীক্ষা ইউআরএল (http/https)</string>
|
||||
<string name="summary_pref_delay_test_url">ইউআরএল</string>
|
||||
|
||||
@@ -189,11 +197,8 @@
|
||||
<string name="title_pref_allow_insecure">allowInsecure</string>
|
||||
<string name="summary_pref_allow_insecure">যখন TLS, ডিফল্টভাবে allowInsecure</string>
|
||||
|
||||
<string name="title_pref_socks_port">SOCKS5 প্রক্সি পোর্ট</string>
|
||||
<string name="summary_pref_socks_port">SOCKS5 প্রক্সি পোর্ট</string>
|
||||
|
||||
<string name="title_pref_http_port">HTTP প্রক্সি পোর্ট</string>
|
||||
<string name="summary_pref_http_port">HTTP প্রক্সি পোর্ট</string>
|
||||
<string name="title_pref_socks_port">Local প্রক্সি পোর্ট</string>
|
||||
<string name="summary_pref_socks_port">Local প্রক্সি পোর্ট</string>
|
||||
|
||||
<string name="title_pref_local_dns_port">স্থানীয় DNS পোর্ট</string>
|
||||
<string name="summary_pref_local_dns_port">স্থানীয় DNS পোর্ট</string>
|
||||
@@ -203,6 +208,10 @@
|
||||
|
||||
<string name="title_pref_start_scan_immediate">তাত্ক্ষণিক স্ক্যান শুরু করুন</string>
|
||||
<string name="summary_pref_start_scan_immediate">শুরুতে তাত্ক্ষণিকভাবে স্ক্যান করতে ক্যামেরা খুলুন, অন্যথায় আপনি কোড স্ক্যান বা টুলবারে একটি ছবি নির্বাচন করতে পারেন</string>
|
||||
|
||||
<string name="title_pref_append_http_proxy">Append HTTP Proxy to VPN</string>
|
||||
<string name="summary_pref_append_http_proxy">HTTP proxy will be used directly from (browser/ some supported apps), without going through the virtual NIC device (Android 10+)</string>
|
||||
|
||||
<string name="title_pref_feedback">মতামত</string>
|
||||
<string name="summary_pref_feedback">মতামত উন্নয়ন বা বাগগুলি GitHub-এ পাঠান</string>
|
||||
<string name="summary_pref_tg_group">টেলিগ্রাম গ্রুপে যোগদান করুন</string>
|
||||
@@ -210,6 +219,7 @@
|
||||
<string name="title_privacy_policy">গোপনীয়তা নীতি</string>
|
||||
<string name="title_about">সম্পর্কিত</string>
|
||||
<string name="title_source_code">সোর্স কোড</string>
|
||||
<string name="title_oss_license">Open Source licenses</string>
|
||||
<string name="title_tg_channel">টেলিগ্রাম চ্যানেল</string>
|
||||
<string name="title_configuration_backup">কনফিগারেশন ব্যাকআপ</string>
|
||||
<string name="summary_configuration_backup">স্টোরেজ অবস্থান: [%s], অ্যাপ আনইনস্টল বা স্টোরেজ ক্লিয়ার করার পরে ব্যাকআপ মুছে যাবে</string>
|
||||
@@ -255,6 +265,10 @@
|
||||
<string name="title_filter_config">কনফিগারেশন ফাইল ফিল্টার করুন</string>
|
||||
<string name="filter_config_all">সব সাবস্ক্রিপশন গ্রুপ</string>
|
||||
<string name="title_del_duplicate_config_count">%d ডুপ্লিকেট কনফিগারেশন মুছে ফেলুন</string>
|
||||
<string name="title_del_config_count">Delete %d configurations</string>
|
||||
<string name="title_import_config_count">Import %d configurations</string>
|
||||
<string name="title_export_config_count">Export %d configurations</string>
|
||||
<string name="title_update_config_count">Update %d configurations</string>
|
||||
<string name="tasker_start_service">সার্ভিস শুরু করুন</string>
|
||||
<string name="tasker_setting_confirm">নিশ্চিত করুন</string>
|
||||
|
||||
@@ -265,14 +279,16 @@
|
||||
<string name="routing_settings_delete">মুছে ফেলুন</string>
|
||||
<string name="routing_settings_rule_title">Routing Rule Settings</string>
|
||||
<string name="routing_settings_add_rule">Add rule</string>
|
||||
<string name="routing_settings_import_rulesets">Import ruleset</string>
|
||||
<string name="routing_settings_import_predefined_rulesets">পূর্বনির্ধারিত নিয়মাবলী আমদানি করুন</string>
|
||||
<string name="routing_settings_import_rulesets_tip">Existing rulesets will be deleted, are you sure to continue?</string>
|
||||
<string name="routing_settings_import_rulesets_from_clipboard">Import ruleset from clipboard</string>
|
||||
<string name="routing_settings_import_rulesets_from_qrcode">QRcode থেকে রুলসেট আমদানি করুন</string>
|
||||
<string name="routing_settings_export_rulesets_to_clipboard">Export ruleset to clipboard</string>
|
||||
<string name="routing_settings_locked">Locked, keep this rule when import presets</string>
|
||||
|
||||
<string name="connection_test_pending">সংযোগ পরীক্ষা করুন</string>
|
||||
<string name="connection_test_testing">পরীক্ষা চলছে…</string>
|
||||
<string name="connection_test_testing_count">Testing %d configurations…</string>
|
||||
<string name="connection_test_available">সফল: HTTP সংযোগ নিয়েছে %dms</string>
|
||||
<string name="connection_test_error">ইন্টারনেট সংযোগ সনাক্ত করতে ব্যর্থ: %s</string>
|
||||
<string name="connection_test_fail">ইন্টারনেট উপলব্ধ নয়</string>
|
||||
@@ -315,5 +331,10 @@
|
||||
<item>ইরান হোয়াইটলিস্ট</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="vpn_bypass_lan">
|
||||
<item>Follow config</item>
|
||||
<item>Bypass</item>
|
||||
<item>Not Bypass</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
@@ -8,16 +8,17 @@
|
||||
<string name="navigation_drawer_close">بستن نومگه کشاری</string>
|
||||
<string name="migration_success">مووفقیت من جاگورویی داده</string>
|
||||
<string name="migration_fail">جاگورویی داده ٱنجوم نگرؽڌ</string>
|
||||
<string name="pull_down_to_refresh">Please pull down to refresh!</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
<string name="notification_action_stop_v2ray">واڌاشتن</string>
|
||||
<string name="toast_permission_denied">گرؽڌن موجوز مومکن نؽڌ</string>
|
||||
<string name="toast_permission_denied_notification">گرؽڌن موجوز وارسۊوی مومکن نؽڌ</string>
|
||||
<string name="notification_action_more">سی گرؽڌن دۉسمندیا بیشتر کیلیک کوݩ</string>
|
||||
<string name="toast_services_start">ره وستن خدمات</string>
|
||||
<string name="toast_services_start">ر وستن خدمات</string>
|
||||
<string name="toast_services_stop">واڌاشتن خدمات</string>
|
||||
<string name="toast_services_success">ره وستن خدمات وا مووفقیت ٱنجوم وابی</string>
|
||||
<string name="toast_services_failure">ره وستن خدمات وا مووفقیت ٱنجوم نوابی</string>
|
||||
<string name="toast_services_success">ر وستن خدمات وا مووفقیت ٱنجوم وابی</string>
|
||||
<string name="toast_services_failure">ر وستن خدمات وا مووفقیت ٱنجوم نوابی</string>
|
||||
|
||||
<!--ServerActivity-->
|
||||
<string name="title_server">فایل کانفیگ</string>
|
||||
@@ -40,16 +41,16 @@
|
||||
<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_invalid_config_comfirm">پؽش ز پاک کردن کانفیگ نا موئتبر قوۊل کوݩ! پاک کردن کانفیگن قوۊل اکۊنی؟</string>
|
||||
<string name="del_invalid_config_comfirm">پؽش ز پاک کردن کانفیگ نا موئتبر واجۊری کوݩ! پاک کردن کانفیگن قوۊل اکۊنی؟</string>
|
||||
<string name="server_lab_remarks">نیشتنا</string>
|
||||
<string name="server_lab_address">آدرس</string>
|
||||
<string name="server_lab_port">پورت</string>
|
||||
<string name="server_lab_id">نوم من توری</string>
|
||||
<string name="server_lab_alterid">alterId</string>
|
||||
<string name="server_lab_alterid">شناسه جایگۊزین</string>
|
||||
<string name="server_lab_security">ٱمنیت</string>
|
||||
<string name="server_lab_network">شبکه</string>
|
||||
<string name="server_lab_more_function">جاگورو</string>
|
||||
<string name="server_lab_head_type">نوء head</string>
|
||||
<string name="server_lab_head_type">نوء سر بلگ</string>
|
||||
<string name="server_lab_mode_type">هالت gRPC</string>
|
||||
<string name="server_lab_request_host">هاست</string>
|
||||
<string name="server_lab_request_host_http">هاست http</string>
|
||||
@@ -58,14 +59,14 @@
|
||||
<string name="server_lab_request_host_xhttp">هاست xhttp</string>
|
||||
<string name="server_lab_request_host_h2">هاست h2</string>
|
||||
<string name="server_lab_request_host_quic">ٱمنیت QUIC</string>
|
||||
<string name="server_lab_request_host_grpc">اختیار gRPC</string>
|
||||
<string name="server_lab_request_host_grpc">Authority gRPC</string>
|
||||
<string name="server_lab_path">تور</string>
|
||||
<string name="server_lab_path_ws">تور ws</string>
|
||||
<string name="server_lab_path_httpupgrade">تور httpupgrade</string>
|
||||
<string name="server_lab_path_xhttp">تور xhttp</string>
|
||||
<string name="server_lab_path_h2">تور h2</string>
|
||||
<string name="server_lab_path_quic">کیلیت QUIC</string>
|
||||
<string name="server_lab_path_kcp">سید kcp</string>
|
||||
<string name="server_lab_path_ws">تور WS</string>
|
||||
<string name="server_lab_path_httpupgrade">تور HTTPUpgrade</string>
|
||||
<string name="server_lab_path_xhttp">تور XHTTP</string>
|
||||
<string name="server_lab_path_h2">تور H2</string>
|
||||
<string name="server_lab_path_quic">تور QUIC</string>
|
||||
<string name="server_lab_path_kcp">KCP seed</string>
|
||||
<string name="server_lab_path_grpc">نوم خدمات gRPC</string>
|
||||
<string name="server_lab_stream_security">TLS</string>
|
||||
<string name="server_lab_stream_fingerprint" translatable="false">Fingerprint</string>
|
||||
@@ -80,13 +81,14 @@
|
||||
<string name="server_lab_security4">نوم من توری (اختیاری)</string>
|
||||
<string name="server_lab_encryption">رزم نگاری</string>
|
||||
<string name="server_lab_flow">جریان</string>
|
||||
<string name="server_lab_public_key">PublicKey</string>
|
||||
<string name="server_lab_short_id">ShortId</string>
|
||||
<string name="server_lab_public_key">کیلیت پوی وولاتی</string>
|
||||
<string name="server_lab_preshared_key">کیلیت رمز ناهاڌن ازاف (اختیاری)</string>
|
||||
<string name="server_lab_short_id">ShortID</string>
|
||||
<string name="server_lab_spider_x">SpiderX</string>
|
||||
<string name="server_lab_secret_key">SecretKey</string>
|
||||
<string name="server_lab_reserved">Reserved(اختیاری, وا کاما ز یک جوڌا ابۊن)</string>
|
||||
<string name="server_lab_local_address">آدرس مهلی (اختیاری IPv4/IPv6, وا کاما ز یک جوڌا ابۊن)</string>
|
||||
<string name="server_lab_local_mtu">Mtu(اختیاری، خوتکار 1420)</string>
|
||||
<string name="server_lab_secret_key">کیلیت سیخومی</string>
|
||||
<string name="server_lab_reserved">Reserved(اختیاری، وا کاما ز یک جوڌا ابۊن)</string>
|
||||
<string name="server_lab_local_address">آدرس مهلی (اختیاری IPv4/IPv6، وا کاما ز یک جوڌا ابۊن)</string>
|
||||
<string name="server_lab_local_mtu">Mtu(اختیاری، پؽش فرز 1420)</string>
|
||||
<string name="toast_success">وا مووفقیت ٱنجوم وابی</string>
|
||||
<string name="toast_failure">شکست خرد</string>
|
||||
<string name="toast_none_data">هیچ داده ای وۊجۊڌ نڌاره</string>
|
||||
@@ -106,7 +108,7 @@
|
||||
<string name="toast_asset_copy_failed">لف گیری فایل ٱنجوم نوابی، ز ی برنومه دؽوۉداری فایل هیاری بگرین</string>
|
||||
<string name="menu_item_add_asset">ازاف کردن دارایی</string>
|
||||
<string name="menu_item_add_file">ازاف کردن فایل</string>
|
||||
<string name="menu_item_add_url">ازاف کردن آدرس اینترنتی</string>
|
||||
<string name="menu_item_add_url">ازاف کردن لینگ</string>
|
||||
<string name="menu_item_scan_qrcode">اسکن QRcode</string>
|
||||
<string name="title_url">آدرس اینترنتی</string>
|
||||
<string name="menu_item_download_file">دانلود فایلا</string>
|
||||
@@ -114,15 +116,17 @@
|
||||
<string name="msg_file_not_found">فایلن نجوست</string>
|
||||
<string name="msg_remark_is_duplicate">ائزارات ز زیتر بیڌسۉݩ</string>
|
||||
<string name="toast_action_not_allowed">ای کار ممنۊ هڌ</string>
|
||||
<string name="server_obfs_password">رزم Obfs</string>
|
||||
<string name="server_obfs_password">رزم obfs</string>
|
||||
<string name="server_lab_port_hop">پورت گوم (درگا سرورن ز نۊ هؽل اکونه)</string>
|
||||
<string name="server_lab_port_hop_interval">فاسله پورت گوم (سانیه)</string>
|
||||
<string name="server_lab_stream_pinsha256">pinSHA256</string>
|
||||
<string name="server_lab_bandwidth_down">ب لم ٱووڌن پئنا باند (واهڌ)</string>
|
||||
<string name="server_lab_bandwidth_up">وا روء رئڌن پئنا باند (واهڌ)</string>
|
||||
<string name="server_lab_xhttp_mode">هالت XHTTP</string>
|
||||
<string name="server_lab_xhttp_extra">XHTTP قلوه خام JSON، قالوو: { XHTTPObject }</string>
|
||||
<string name="server_lab_xhttp_extra">XHTTP Extra خام JSON، قالوو: { XHTTPObject }</string>
|
||||
|
||||
<!-- PerAppProxyActivity -->
|
||||
<string name="msg_dialog_progress">هون بارونی بۊ</string>
|
||||
<string name="msg_dialog_progress">هون بار ونی بۊ</string>
|
||||
<string name="menu_item_search">پیتینیڌن</string>
|
||||
<string name="menu_item_select_all">پسند پوی</string>
|
||||
<string name="msg_enter_keywords">رزمان بزنین</string>
|
||||
@@ -138,13 +142,13 @@
|
||||
<string name="title_advanced">سامووا پؽش رئڌه</string>
|
||||
<string name="title_vpn_settings">سامووا VPN</string>
|
||||
<string name="title_pref_per_app_proxy">پروکسی و ری برنومه</string>
|
||||
<string name="summary_pref_per_app_proxy">پوی وولاتی: برنومه واجۊری بیڌه پروکسی هڌ، منپیز موستقیم بؽ نشووه هڌ. /n هالت دور زیڌن: برنومه نشووک ناڌه موستقیمن منپیز هڌ، پروکسی نشووک زیڌه نؽڌ. /n گۊزینه پسند خوتکار برنومه پروکسی من نومگه</string>
|
||||
<string name="title_pref_is_booted">منپیز خوتکار مجال ره ونی</string>
|
||||
<string name="summary_pref_is_booted">مجال ره وندن خوساخوس و سرور پسند بیڌه منپیز ابۊ که گاشڌ نا مووفق بۊ.</string>
|
||||
<string name="summary_pref_per_app_proxy">پوی وولاتی: برنومه واجۊری بیڌه پروکسی هڌ، منپیز موستقیم بؽ نشووه هڌ. هالت دور زیڌن: برنومه نشووک ناڌه موستقیمن منپیز هڌ، پروکسی نشووک زیڌه نؽڌ. گۊزینه پسند خوتکار برنومه پروکسی من نومگه</string>
|
||||
<string name="title_pref_is_booted">منپیز خوتکار مجال ر ونی</string>
|
||||
<string name="summary_pref_is_booted">مجال ر وندن، خوساخوس و سرور پسند بیڌه منپیز ابۊ که گاشڌ نا مووفق بۊ.</string>
|
||||
|
||||
<string name="title_mux_settings">سامووا Mux</string>
|
||||
<string name="title_pref_mux_enabled">ره وندن Mux</string>
|
||||
<string name="summary_pref_mux_enabled">زل تر، ٱما گاشڌ منپیز زی قت بۊ /n بارت دؽوۉداری، TCP، UDP و QUIC ن ای لم سفارشی کۊنین.</string>
|
||||
<string name="title_pref_mux_enabled">ر وندن Mux</string>
|
||||
<string name="summary_pref_mux_enabled">زل تر، ٱما گاشڌ منپیز زی قت بۊ بارت دؽوۉداری، 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 من تونل mux</string>
|
||||
@@ -154,46 +158,47 @@
|
||||
<item>گوم زیڌن</item>
|
||||
</string-array>
|
||||
|
||||
<string name="title_pref_speed_enabled">ره وندن نشۉݩ داڌن سورعت</string>
|
||||
<string name="summary_pref_speed_enabled">نشۉݩ داڌن سورعت هیم سکویی من وارسۊویا./nنماڌ وارسۊوی و ری و کار گرؽڌن آلشت ابۊ.</string>
|
||||
<string name="title_pref_speed_enabled">ر وندن نشۉݩ داڌن سورعت</string>
|
||||
<string name="summary_pref_speed_enabled">نشۉݩ داڌن سورعت هیم سکویی من وارسۊویا. نماڌ وارسۊوی و ری و کار گرؽڌن آلشت ابۊ.</string>
|
||||
|
||||
<string name="title_pref_sniffing_enabled">ره وندن Sniffing</string>
|
||||
<string name="title_pref_sniffing_enabled">ر وندن Sniffing</string>
|
||||
<string name="summary_pref_sniffing_enabled">دامنه sniff ن ز کتن امتهۉݩ کۊنین (پؽش فرز رۊشن)</string>
|
||||
<string name="title_pref_route_only_enabled">ره وندن routeOnly</string>
|
||||
<string name="title_pref_route_only_enabled">ر وندن routeOnly</string>
|
||||
<string name="summary_pref_route_only_enabled">ز نوم دامنه sniffed تینا سی تور جوستن استفاڌه کۊنین وو آدرس مورد نزرن و عونوان آدرس IP ووردارین.</string>
|
||||
|
||||
<string name="title_pref_local_dns_enabled">ره وندن DNS مهلی</string>
|
||||
<string name="title_pref_local_dns_enabled">ر وندن DNS مهلی</string>
|
||||
<string name="summary_pref_local_dns_enabled">DNS پردازشت وابیڌه و دس هسته ماژول DNS (پؽشنهاڌ ابۊ، ٱر نیاز هڌ ک جوستن تور وو ولات ٱسلین دور زنی)</string>
|
||||
|
||||
<string name="title_pref_fake_dns_enabled">ره وندن DNS جئلی</string>
|
||||
<string name="title_pref_fake_dns_enabled">ر وندن DNS جئلی</string>
|
||||
<string name="summary_pref_fake_dns_enabled">DNS مهلی آدرسا IP جئلی ن وورگنه (زل تر، ٱما گاشڌ من یقرد ز برنومیل کار نکونه)</string>
|
||||
|
||||
<string name="title_pref_prefer_ipv6">ترجی IPv6</string>
|
||||
<string name="summary_pref_prefer_ipv6">ترجی داڌن نشۊوی وو تورا IPv6</string>
|
||||
|
||||
<string name="title_pref_remote_dns">DNS ز ره دیر (اختیاری) (udp/tcp/https/quic)(اختیاری)</string>
|
||||
<string name="title_pref_remote_dns">DNS ز ر دیر (اختیاری) (udp/tcp/https/quic) (اختیاری)</string>
|
||||
<string name="summary_pref_remote_dns">DNS</string>
|
||||
|
||||
<string name="title_pref_vpn_dns">VPN DNS (تینا IPv4/v6)</string>
|
||||
<string name="title_pref_vpn_bypass_lan">VPN ز شبکه مهلی اگوڌرته؟</string>
|
||||
|
||||
<string name="title_pref_domestic_dns">DNS منی (اختیاری)</string>
|
||||
<string name="summary_pref_domestic_dns">DNS</string>
|
||||
|
||||
<string name="title_pref_delay_test_url">آدرس اینترنتی آزمایش تئخیر واقعی (http/https)</string>
|
||||
<string name="summary_pref_delay_test_url">Url</string>
|
||||
<string name="title_pref_dns_hosts">DNS هاست موستقیم (قالوو: دامنه: آدرس،...)</string>
|
||||
<string name="summary_pref_dns_hosts">دامنه:آدرس،...</string>
|
||||
|
||||
<string name="title_pref_proxy_sharing_enabled">هشتن منپیزا ز LAN</string>
|
||||
<string name="summary_pref_proxy_sharing_enabled">پوی دسگایل ترن وا آدرس IP ایسا، ز ره socks/http و پروکسی منپیز بۊن، تینا من شبکه قابل اعتماد فعال بۊ تا ز منپیز ؛یر موجاز جلو گری بۊ.</string>
|
||||
<string name="toast_warning_pref_proxysharing_short">منپیزا ز LAN ن موجار کۊنین، موتمعن بۊین ک من ی شبکه قابل ائتماڌ هڌین.</string>
|
||||
<string name="title_pref_delay_test_url">آدرس اینترنتی آزمایش تئخیر واقعی (http/https)</string>
|
||||
<string name="summary_pref_delay_test_url">نشۊوی اینترنتی</string>
|
||||
|
||||
<string name="title_pref_proxy_sharing_enabled">هشتن منپیزا ز شبکه مهلی</string>
|
||||
<string name="summary_pref_proxy_sharing_enabled">پوی دسگایل ترن وا آدرس IP ایسا، ز ر socks/http و پروکسی منپیز بۊن، تینا من شبکه قابل اعتماد فعال بۊ تا ز منپیز غیر موجاز جلو گری بۊ.</string>
|
||||
<string name="toast_warning_pref_proxysharing_short">منپیزا ز شبکه مهلی ن موجار کۊنین، موتمعن بۊین ک من ی شبکه قابل ائتماڌ هڌین.</string>
|
||||
|
||||
<string name="title_pref_allow_insecure">اجازه نا ٱمن</string>
|
||||
<string name="summary_pref_allow_insecure">مجال و کار بردن TLS ب تۉر پؽش فرز، موجوز نا ٱمن فعال هڌ.</string>
|
||||
<string name="summary_pref_allow_insecure">مجال و کار بوردن TLS ب تۉر پؽش فرز، موجوز نا ٱمن فعال هڌ.</string>
|
||||
|
||||
<string name="title_pref_socks_port">پورت پروکسی SOCKS5</string>
|
||||
<string name="summary_pref_socks_port">پورت پروکسی SOCKS5</string>
|
||||
|
||||
<string name="title_pref_http_port">پورت پروکسی HTTP</string>
|
||||
<string name="summary_pref_http_port">پورت پروکسی HTTP</string>
|
||||
<string name="title_pref_socks_port">پورت پروکسی مهلی</string>
|
||||
<string name="summary_pref_socks_port">پورت پروکسی مهلی</string>
|
||||
|
||||
<string name="title_pref_local_dns_port">پورت DNS مهلی</string>
|
||||
<string name="summary_pref_local_dns_port">پورت DNS مهلی</string>
|
||||
@@ -201,8 +206,11 @@
|
||||
<string name="title_pref_confirm_remove">قوۊل کردن پاک کردن کانفیگ</string>
|
||||
<string name="summary_pref_confirm_remove">سی پاک وابیڌن فایل کانفیگ نیاز به قوۊل کردن دووارته ز سمت منتور هڌ</string>
|
||||
|
||||
<string name="title_pref_start_scan_immediate">زی اسکنن ره ون</string>
|
||||
<string name="summary_pref_start_scan_immediate">شؽواتگرن سی اسکن، زی مجال ره وندن بۊگۊشین، اندی ترین کودن اسکن کۊنین یا شؽواتی ن منه نوار ٱوزار پسند کۊنین.</string>
|
||||
<string name="title_pref_start_scan_immediate">زی اسکنن ر ون</string>
|
||||
<string name="summary_pref_start_scan_immediate">شؽواتگرن سی اسکن، زی مجال ر وندن بۊگۊشین، اندی ترین کودن اسکن کۊنین یا شؽواتی ن منه نوار ٱوزار پسند کۊنین.</string>
|
||||
|
||||
<string name="title_pref_append_http_proxy">پروکسی HTTP ن و VPN ازاف کۊنین</string>
|
||||
<string name="summary_pref_append_http_proxy">پروکسی HTTP ن موسقیمن ز (مۊرۊرگر/ی قرد ز برنومیل لادراری بیڌه)، بؽ استفاڌه ز دسگا NIC مجازی (Android 10+) استفاڌه ابۊ.</string>
|
||||
|
||||
<string name="title_pref_feedback">فشناڌن منشڌ</string>
|
||||
<string name="summary_pref_feedback">فشناڌن منشڌ یا داسوو موشکلا من Github</string>
|
||||
@@ -211,14 +219,15 @@
|
||||
<string name="title_privacy_policy">هریم سیخومی</string>
|
||||
<string name="title_about">زبار</string>
|
||||
<string name="title_source_code">کود بونچک</string>
|
||||
<string name="title_oss_license">موجوزا کود بونچک</string>
|
||||
<string name="title_tg_channel">تورگه تلگرام</string>
|
||||
<string name="title_configuration_backup">لادراری گرؽڌن ز کانفیگ</string>
|
||||
<string name="summary_configuration_backup">جاگه زفت کردن: [%s]، بعڌ پاک کردن برنومه یا پاک کردن جاگه زفت کردن، نوسخه لادرار هم پاک ابۊ.</string>
|
||||
<string name="title_configuration_restore">وورگندن کانفیگ</string>
|
||||
<string name="title_configuration_share">یک رسۊوی کانفیگ</string>
|
||||
|
||||
<string name="title_pref_promotion">تبلیغات</string>
|
||||
<string name="summary_pref_promotion">تبلیغات، سی نیشتن جوزیات بیشتر کیلیک کۊنین. (هیاری مالی کۊنین تا پاک بۊ)</string>
|
||||
<string name="title_pref_promotion">تبلیقات</string>
|
||||
<string name="summary_pref_promotion">تبلیقات، سی نیشتن جوزیات بیشتر کیلیک کۊنین. (هیاری مالی کۊنین تا پاک بۊ)</string>
|
||||
|
||||
<string name="title_pref_auto_update_subscription">ورۊ کردن خوتکار اشتراکا</string>
|
||||
<string name="summary_pref_auto_update_subscription">اشتراکا خوتۉ ن و تۉر خوتکار وا فاسله زمۊوی من پس زمینه ورۊ کۊنین. ای ویژیی من پوی دسگایل گاشڌ همیشه کار نکونه</string>
|
||||
@@ -226,7 +235,7 @@
|
||||
|
||||
<string name="title_core_loglevel">سئت داسووا</string>
|
||||
<string name="title_mode">هالت</string>
|
||||
<string name="title_mode_help">سی هیاری بیشتر ری ای هؽل بزݩ</string>
|
||||
<string name="title_mode_help">سی دووسمندیا وو هیاری بیشتر، ری ای هؽل بزݩ</string>
|
||||
<string name="title_language">زۉݩ</string>
|
||||
<string name="title_ui_settings">سامووا رابت منتوری</string>
|
||||
<string name="title_pref_ui_mode_night">سامووا هالت رابت منتوری</string>
|
||||
@@ -234,7 +243,7 @@
|
||||
<string name="title_logcat">داسووا</string>
|
||||
<string name="logcat_copy">لف گیری</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_duplicate_config">پاک کردن کانفیگا تکراری جرگه سکویی</string>
|
||||
<string name="title_del_invalid_config">پاک کردن کانفیگا نا موئتبر جرگه سکویی</string>
|
||||
@@ -242,13 +251,13 @@
|
||||
<string name="title_sub_setting">سامووا جرگه اشتراک</string>
|
||||
<string name="sub_setting_remarks">نیشتنا</string>
|
||||
<string name="sub_setting_url">نشۊوی اینترنتی اختیاری</string>
|
||||
<string name="sub_setting_filter">نیشتنا فیلتر مئمۊلی</string>
|
||||
<string name="sub_setting_filter">نوم موستعار فیلتر</string>
|
||||
<string name="sub_setting_enable">فعال بیڌن ورۊ کردن</string>
|
||||
<string name="sub_auto_update">فعال بیڌن ورۊ کردن خوتکار</string>
|
||||
<string name="sub_setting_pre_profile">نیشتنا پروکسی پؽشی</string>
|
||||
<string name="sub_setting_next_profile">نیشتنا پروکسی نیایی</string>
|
||||
<string name="sub_setting_pre_profile_tip">نیشتنا هڌسۉݩ وو هرف نارن</string>
|
||||
<string name="title_sub_update">بهروزرسانی اشتراک گروه فعلی</string>
|
||||
<string name="sub_setting_pre_profile">نوم موستعار پروکسی دیندایی</string>
|
||||
<string name="sub_setting_next_profile">نوم موستعار پروکسی نیایی</string>
|
||||
<string name="sub_setting_pre_profile_tip">موتمعن بۊ ک نوم موستعار هڌس وو جۊرس نی</string>
|
||||
<string name="title_sub_update">ورۊ کردن اشتراک جرگه سکویی</string>
|
||||
<string name="title_ping_all_server">Tcping کانفیگا جرگه سکویی</string>
|
||||
<string name="title_real_ping_all_server">تئخیر واقعی کانفیگا جرگه سکویی</string>
|
||||
<string name="title_user_asset_setting">فایلا دارایی جوقرافیایی</string>
|
||||
@@ -257,19 +266,24 @@
|
||||
<string name="filter_config_all">پوی جرگیل</string>
|
||||
<string name="title_del_duplicate_config_count">پاک کردن %d کانفیگ تکراری</string>
|
||||
|
||||
<string name="tasker_start_service">ره وندن خدمات</string>
|
||||
<string name="title_del_config_count">پاک کردن %d کانفیگ</string>
|
||||
<string name="title_import_config_count">و من ٱووردن %d کانفیگ</string>
|
||||
<string name="title_export_config_count">و در کشیڌن %d کانفیگ</string>
|
||||
<string name="title_update_config_count">ورۊ کردن %d کانفیگ</string>
|
||||
<string name="tasker_start_service">ر وندن خدمات</string>
|
||||
<string name="tasker_setting_confirm">قوۊل</string>
|
||||
|
||||
<string name="routing_settings_domain_strategy">نشقه دامنه</string>
|
||||
<string name="routing_settings_title">سامووا تور جوستن</string>
|
||||
<string name="routing_settings_tips">وا کاما ز یک جوڌا ابۊن (,) پسند دامنه یا آی پی</string>
|
||||
<string name="routing_settings_tips">وا کاما ز یک جوڌا ابۊن (،) پسند دامنه یا آی پی</string>
|
||||
<string name="routing_settings_save">زفت کردن</string>
|
||||
<string name="routing_settings_delete">روفتن</string>
|
||||
<string name="routing_settings_rule_title">سامووا قانۉݩ تور جوستن</string>
|
||||
<string name="routing_settings_add_rule">ازاف کردن قانۉݩ</string>
|
||||
<string name="routing_settings_import_rulesets">و من ٱووردن قانووا</string>
|
||||
<string name="routing_settings_import_predefined_rulesets">و من ٱووردن قانووا</string>
|
||||
<string name="routing_settings_import_rulesets_tip">قانووایی ک هیم سکو هڌسۉݩ پاک ابۊن، هنی هم اخۊی پاکسۉݩ کۊنی؟</string>
|
||||
<string name="routing_settings_import_rulesets_from_clipboard">و من ٱووردن قانووا ز کلیپ بورد</string>
|
||||
<string name="routing_settings_import_rulesets_from_qrcode">و من ٱووردن قانووا ز QRcode</string>
|
||||
<string name="routing_settings_export_rulesets_to_clipboard">و در کشیڌن قانووا وو زفت من کلیپ بورد</string>
|
||||
<string name="routing_settings_locked">چفت هڌ، ای قانؤنن مجال و من ٱووردن ز پؽش سامووا زفت کۊنین</string>
|
||||
<string name="routing_settings_domain" translatable="false">domain</string>
|
||||
@@ -283,6 +297,7 @@
|
||||
|
||||
<string name="connection_test_pending">منپیزن واجۊری کوݩ</string>
|
||||
<string name="connection_test_testing">هونی آزمایش ابۊ…</string>
|
||||
<string name="connection_test_testing_count">%d کانفیگ هونی آزمایش ابۊ...</string>
|
||||
<string name="connection_test_available">مووفق بی: منپیز HTTP %dms تۊل کشی</string>
|
||||
<string name="connection_test_error">منپیز و اینترنتن نجوست: %s</string>
|
||||
<string name="connection_test_fail">اینترنت من دسرس نؽ</string>
|
||||
@@ -296,7 +311,7 @@
|
||||
<string name="title_pref_fragment_packets">Fragment Packets</string>
|
||||
<string name="title_pref_fragment_length">Fragment Length (min-max)</string>
|
||||
<string name="title_pref_fragment_interval">Fragment Interval (min-max)</string>
|
||||
<string name="title_pref_fragment_enabled">فعال کردن Fragment</string>
|
||||
<string name="title_pref_fragment_enabled">ر وندن Fragment</string>
|
||||
|
||||
<string-array name="share_method">
|
||||
<item>QRcode</item>
|
||||
@@ -315,7 +330,7 @@
|
||||
</string-array>
|
||||
|
||||
<string-array name="ui_mode_night">
|
||||
<item>و دین کردن سیستم</item>
|
||||
<item>و دین کردن سیستوم</item>
|
||||
<item>رۊشنا</item>
|
||||
<item>تاریک</item>
|
||||
</string-array>
|
||||
@@ -327,4 +342,10 @@
|
||||
<item>نومگه اسبؽڌ ایران</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="vpn_bypass_lan">
|
||||
<item>پؽش فرز کانفیگ</item>
|
||||
<item>دور زیڌه بۊ</item>
|
||||
<item>دور زیڌه نبۊ</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">v2rayNG</string>
|
||||
<string name="app_widget_name">تعویض</string>
|
||||
<string name="app_tile_name">تعویض</string>
|
||||
<string name="app_tile_first_use">برای اولین بار از این ویژگی استفاده میکنید، لطفا از برنامه برای افزودن سرور استفاده کنید</string>
|
||||
@@ -7,6 +8,7 @@
|
||||
<string name="navigation_drawer_close">بستن منو کشویی</string>
|
||||
<string name="migration_success">موفقیت در انتقال داده</string>
|
||||
<string name="migration_fail">انتقال داده انجام نشد!</string>
|
||||
<string name="pull_down_to_refresh">لطفاً برای تازه کردن، پایین بکشید!</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
<string name="notification_action_stop_v2ray">توقف</string>
|
||||
@@ -25,51 +27,51 @@
|
||||
<string name="menu_item_del_config">حذف کانفیگ</string>
|
||||
<string name="menu_item_import_config_qrcode">کانفیگ را از QRcode وارد کنید</string>
|
||||
<string name="menu_item_import_config_clipboard">کانفیگ را از کلیپ بورد وارد کنید</string>
|
||||
<string name="menu_item_import_config_manually_vmess">تایپ دستی[VMess]</string>
|
||||
<string name="menu_item_import_config_manually_vmess">تایپ دستی[VMESS]</string>
|
||||
<string name="menu_item_import_config_manually_vless">تایپ دستی[VLESS]</string>
|
||||
<string name="menu_item_import_config_manually_ss">تایپ دستی[Shadowsocks]</string>
|
||||
<string name="menu_item_import_config_manually_ss">تایپ دستی[SHADOWSOCKS]</string>
|
||||
<string name="menu_item_import_config_manually_socks">تایپ دستی[SOCKS]</string>
|
||||
<string name="menu_item_import_config_manually_http">Type manually[HTTP]</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_manually_hysteria2">Type manually[Hysteria2]</string>
|
||||
<string name="menu_item_import_config_manually_http">تایپ دستی[HTTP]</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_manually_hysteria2">تایپ دستی[HYSTERIA2]</string>
|
||||
<string name="menu_item_import_config_custom">کانفیگ سفارشی</string>
|
||||
<string name="menu_item_import_config_custom_clipboard">کانفیگ سفارشی را از کلیپ بورد وارد کنید</string>
|
||||
<string name="menu_item_import_config_custom_local">کانفیگ سفارشی را به صورت محلی وارد کنید</string>
|
||||
<string name="menu_item_import_config_custom_url">کانفیگ سفارشی را از طریق نشانی اینترنتی وارد کنید</string>
|
||||
<string name="menu_item_import_config_custom_url_scan">نشانی اینترنتی اسکن کانفیگ سفارشی را وارد کنید</string>
|
||||
<string name="del_config_comfirm">حذف شود؟</string>
|
||||
<string name="del_invalid_config_comfirm">لطفا قبل از حذف کانفیگ نامعتبر تایید کنید! حذف کانفیگ را تایید می کنید؟</string>
|
||||
<string name="del_invalid_config_comfirm">لطفا قبل از حذف کانفیگ نامعتبر بررسی کنید! حذف کانفیگ را تایید می کنید؟</string>
|
||||
<string name="server_lab_remarks">ملاحظات</string>
|
||||
<string name="server_lab_address">نشانی</string>
|
||||
<string name="server_lab_port">پورت</string>
|
||||
<string name="server_lab_id">شناسه</string>
|
||||
<string name="server_lab_alterid">alterId</string>
|
||||
<string name="server_lab_alterid">شناسه جایگزین</string>
|
||||
<string name="server_lab_security">امنیت</string>
|
||||
<string name="server_lab_network">شبکه</string>
|
||||
<string name="server_lab_more_function">انتقال</string>
|
||||
<string name="server_lab_head_type">نوع head</string>
|
||||
<string name="server_lab_head_type">نوع سربرگ</string>
|
||||
<string name="server_lab_mode_type">حالت gRPC</string>
|
||||
<string name="server_lab_request_host">host</string>
|
||||
<string name="server_lab_request_host_http">http host</string>
|
||||
<string name="server_lab_request_host_ws">ws host</string>
|
||||
<string name="server_lab_request_host_httpupgrade">httpupgrade host</string>
|
||||
<string name="server_lab_request_host_xhttp">xhttp host</string>
|
||||
<string name="server_lab_request_host_h2">h2 host</string>
|
||||
<string name="server_lab_request_host">هاست</string>
|
||||
<string name="server_lab_request_host_http">هاست HTTP</string>
|
||||
<string name="server_lab_request_host_ws">هاست WS</string>
|
||||
<string name="server_lab_request_host_httpupgrade">هاست HTTPUpgrade</string>
|
||||
<string name="server_lab_request_host_xhttp">هاست XHTTP</string>
|
||||
<string name="server_lab_request_host_h2">هاست H2</string>
|
||||
<string name="server_lab_request_host_quic">QUIC security</string>
|
||||
<string name="server_lab_request_host_grpc">gRPC Authority</string>
|
||||
<string name="server_lab_path">path</string>
|
||||
<string name="server_lab_path_ws">ws path</string>
|
||||
<string name="server_lab_path_httpupgrade">httpupgrade path</string>
|
||||
<string name="server_lab_path_xhttp">xhttp path </string>
|
||||
<string name="server_lab_path_h2">h2 path</string>
|
||||
<string name="server_lab_path_quic">QUIC key</string>
|
||||
<string name="server_lab_path_kcp">kcp seed</string>
|
||||
<string name="server_lab_path_grpc">gRPC serviceName</string>
|
||||
<string name="server_lab_path">مسیر</string>
|
||||
<string name="server_lab_path_ws">مسیر WS</string>
|
||||
<string name="server_lab_path_httpupgrade">مسیر HTTPUpgrade</string>
|
||||
<string name="server_lab_path_xhttp">مسیر XHTTP</string>
|
||||
<string name="server_lab_path_h2">مسیر H2</string>
|
||||
<string name="server_lab_path_quic">مسیر QUIC</string>
|
||||
<string name="server_lab_path_kcp">KCP seed</string>
|
||||
<string name="server_lab_path_grpc">gRPC ServiceName</string>
|
||||
<string name="server_lab_stream_security">TLS</string>
|
||||
<string name="server_lab_stream_fingerprint">اثرانگشت</string>
|
||||
<string name="server_lab_stream_alpn">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_address3">نشانی</string>
|
||||
<string name="server_lab_port3">پورت</string>
|
||||
@@ -79,13 +81,14 @@
|
||||
<string name="server_lab_security4">نام کاربری (اختیاری)</string>
|
||||
<string name="server_lab_encryption">رمزنگاری</string>
|
||||
<string name="server_lab_flow">جریان</string>
|
||||
<string name="server_lab_public_key">PublicKey</string>
|
||||
<string name="server_lab_short_id">ShortId</string>
|
||||
<string name="server_lab_public_key">کلید عمومی</string>
|
||||
<string name="server_lab_preshared_key">کلید رمزگذاری اضافی (اختیاری)</string>
|
||||
<string name="server_lab_short_id">ShortID</string>
|
||||
<string name="server_lab_spider_x">SpiderX</string>
|
||||
<string name="server_lab_secret_key">SecretKey</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="server_lab_secret_key">کلید خصوصی</string>
|
||||
<string name="server_lab_reserved">Reserved (اختیاری، جدا شده با کاما)</string>
|
||||
<string name="server_lab_local_address">آدرس محلی IPV4 (اختیاری)</string>
|
||||
<string name="server_lab_local_mtu">MTU (اختیاری، پیشفرض 1420)</string>
|
||||
<string name="toast_success">با موفقیت انجام شد</string>
|
||||
<string name="toast_failure">شکست</string>
|
||||
<string name="toast_none_data">هیچ داده ای وجود ندارد</string>
|
||||
@@ -96,34 +99,36 @@
|
||||
<string name="server_customize_config">کانفیگ سفارشی</string>
|
||||
<string name="toast_config_file_invalid">کانفیگ معتبر نیست</string>
|
||||
<string name="server_lab_content">محتوا</string>
|
||||
<string name="toast_none_data_clipboard">هیچ دادهای در کلیپ بورد وجود ندارد</string>
|
||||
<string name="toast_none_data_clipboard">هیچ داده ای در کلیپ بورد وجود ندارد</string>
|
||||
<string name="toast_invalid_url">نشانی اینترنتی معتبر نیست</string>
|
||||
<string name="toast_insecure_url_protocol">لطفاً از آدرس اشتراک پروتکل HTTP ناامن استفاده نکنید</string>
|
||||
<string name="server_lab_need_inbound">اطمینان حاصل کنید که پورت ورودی با تنظیمات مطابقت دارد</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="menu_item_add_file">افزودن فایل ها</string>
|
||||
<string name="menu_item_scan_qrcode">اسکن QRcode</string>
|
||||
<string name="title_url">URL</string>
|
||||
<string name="menu_item_download_file">دانلود فایل ها</string>
|
||||
<string name="toast_action_not_allowed">این عمل ممنوع است</string>
|
||||
<string name="server_obfs_password">رمز عبور Obfs</string>
|
||||
<string name="server_obfs_password">رمز عبور obfs</string>
|
||||
<string name="server_lab_port_hop">پورت پرش (درگاه سرور را بازنویسی می کند)</string>
|
||||
<string name="server_lab_port_hop_interval">فاصله پورت پرش (ثانیه)</string>
|
||||
<string name="server_lab_stream_pinsha256">pinSHA256</string>
|
||||
<string name="server_lab_bandwidth_down">کاهش پهنای باند (واحد)</string>
|
||||
<string name="server_lab_bandwidth_up">افزایش پهنای باند (واحد)</string>
|
||||
<string name="server_lab_xhttp_mode">حالت XHTTP</string>
|
||||
<string name="server_lab_xhttp_extra">جیسون خام XHTTP Extra، فرمت: { XHTTPObject }</string>
|
||||
<string name="server_lab_xhttp_extra">خام JSON XHTTP Extra، قالب: { XHTTPObject }</string>
|
||||
|
||||
<!-- PerAppProxyActivity -->
|
||||
<string name="title_user_asset_add_url">URL را اضافه کنید</string>
|
||||
<string name="title_user_asset_add_url">آدرس اینترنتی را اضافه کنید</string>
|
||||
<string name="msg_file_not_found">فایل پیدا نشد</string>
|
||||
<string name="msg_remark_is_duplicate">نام قبلاً وجود دارد</string>
|
||||
<string name="msg_dialog_progress">بارگذاری</string>
|
||||
<string name="menu_item_search">جستجو</string>
|
||||
<string name="menu_item_select_all">انتخاب همه</string>
|
||||
<string name="msg_enter_keywords">کلیدواژه ها را وارد کنید</string>
|
||||
<string name="switch_bypass_apps_mode">حالت Bypass</string>
|
||||
<string name="switch_bypass_apps_mode">حالت دور زدن</string>
|
||||
<string name="menu_item_select_proxy_app">انتخاب خودکار پروکسی برنامه</string>
|
||||
<string name="msg_downloading_content">در حال دانلود محتوا</string>
|
||||
<string name="menu_item_export_proxy_app">خروجی گرفتن در کلیپ بورد</string>
|
||||
@@ -134,16 +139,16 @@
|
||||
<string name="title_advanced">تنظیمات پیشرفته</string>
|
||||
<string name="title_vpn_settings">تنظیمات VPN</string>
|
||||
<string name="title_pref_per_app_proxy">پروکسی به تفکیک برنامه</string>
|
||||
<string name="summary_pref_per_app_proxy">عمومی: برنامه بررسی شده پروکسی است، اتصال مستقیم بدون بررسی است. \nحالت bypass: برنامه بررسی شده مستقیما متصل است، پراکسی بررسی نشده است. \nگزینهای برای انتخاب خودکار پروکسی برنامه در منو است.</string>
|
||||
<string name="summary_pref_per_app_proxy">عمومی: برنامه بررسی شده پروکسی است، اتصال مستقیم بدون بررسی است. \nحالت دور زدن: برنامه بررسی شده مستقیما متصل است، پراکسی بررسی نشده است. \nگزینهای برای انتخاب خودکار پروکسی برنامه در منو است.</string>
|
||||
<string name="title_pref_is_booted">اتصال خودکار هنگام راه اندازی</string>
|
||||
<string name="summary_pref_is_booted">هنگام راه اندازی به طور خودکار به سرور انتخابی متصل می شود که ممکن است ناموفق باشد.</string>
|
||||
|
||||
<string name="title_mux_settings">تنظیمات Mux</string>
|
||||
<string name="title_pref_mux_enabled">فعال کردن Mux</string>
|
||||
<string name="title_mux_settings">تنظیمات MUX</string>
|
||||
<string name="title_pref_mux_enabled">فعال کردن MUX</string>
|
||||
<string name="summary_pref_mux_enabled">سریعتر است، اما ممکن است باعث اتصال ناپایدار شود\nمخزن ترافیک TCP با 8 اتصال پیشفرض، نحوه مدیریت UDP و QUIC را در زیر سفارشی کنید.</string>
|
||||
<string name="title_pref_mux_concurency">اتصالات TCP (محدوده -1 تا 1024)</string>
|
||||
<string name="title_pref_mux_xudp_concurency">اتصالات XUDP (محدوده -1 تا 1024)</string>
|
||||
<string name="title_pref_mux_xudp_quic">مدیریت QUIC در تونل mux</string>
|
||||
<string name="title_pref_mux_xudp_quic">مدیریت QUIC در تونل MUX</string>
|
||||
<string-array name="mux_xudp_quic_entries">
|
||||
<item>رد کردن</item>
|
||||
<item>مجاز</item>
|
||||
@@ -151,12 +156,12 @@
|
||||
</string-array>
|
||||
|
||||
<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="summary_pref_sniffing_enabled">دامنه sniff را از بسته امتحان کنید (پیشفرض روشن)</string>
|
||||
<string name="title_pref_route_only_enabled">فعال کردن routeOnly</string>
|
||||
<string name="summary_pref_route_only_enabled">از نام دامنه sniffed فقط برای مسیریابی استفاده کنید و آدرس مورد نظر را به عنوان آدرس IP نگه دارید.</string>
|
||||
<string name="title_pref_sniffing_enabled">فعال کردن تجزیه و تحلیل بسته ها (Sniffing)</string>
|
||||
<string name="summary_pref_sniffing_enabled">استفاده از تشخیص نام دامنه (Sniff) در بسته ها (به طور پیش فرض فعال است)</string>
|
||||
<string name="title_pref_route_only_enabled">فعال کردن دامنه فقط مسیر یابی (RouteOnly)</string>
|
||||
<string name="summary_pref_route_only_enabled">از نام دامنه (Snnifed) فقط برای مسیریابی استفاده کنید و آدرس مقصد را به عنوان IP ذخیره کنید.</string>
|
||||
|
||||
|
||||
<string name="title_pref_local_dns_enabled">فعال کردن DNS محلی</string>
|
||||
@@ -165,32 +170,33 @@
|
||||
<string name="title_pref_fake_dns_enabled">فعال کردن DNS جعلی</string>
|
||||
<string name="summary_pref_fake_dns_enabled">دی ان اس محلی آدرس های آیپی جعلی را بر می گرداند (سریع تر می باشد و تاخیر را کاهش می دهد اما ممکن است برای برخی از برنامه ها کار نکند)</string>
|
||||
|
||||
<string name="title_pref_prefer_ipv6">ترجیح دادن IPv6</string>
|
||||
<string name="title_pref_prefer_ipv6">ترجیح دادن IPV6</string>
|
||||
<string name="summary_pref_prefer_ipv6">ترجیح دادن نشانی و مسیر های IPv6</string>
|
||||
|
||||
<string name="title_pref_remote_dns">DNS از راه دور (اختیاری)</string>
|
||||
<string name="title_pref_remote_dns">DNS از راه دور (اختیاری) (udp/tcp/https/quic)</string>
|
||||
<string name="summary_pref_remote_dns">DNS</string>
|
||||
|
||||
<string name="title_pref_vpn_dns">VPN DNS (فقط IPv4/v6)</string>
|
||||
<string name="title_pref_vpn_bypass_lan">آیا VPN از شبکه محلی عبور می کند؟</string>
|
||||
|
||||
<string name="title_pref_domestic_dns">DNS داخلی (اختیاری)</string>
|
||||
<string name="summary_pref_domestic_dns">DNS</string>
|
||||
|
||||
<string name="title_pref_delay_test_url">آدرس اینترنتی آزمایش تاخیر واقعی کانفیگ ها (http/https)</string>
|
||||
<string name="summary_pref_delay_test_url">Url</string>
|
||||
<string name="title_pref_dns_hosts">DNS مستقیم هاست (فرمت: دامنه:آدرس،…)</string>
|
||||
<string name="summary_pref_dns_hosts">دامنه:آدرس،…</string>
|
||||
|
||||
<string name="title_pref_proxy_sharing_enabled">اجازه اتصالات از طریق LAN</string>
|
||||
<string name="summary_pref_proxy_sharing_enabled">دستگاه های دیگر میتوانند از طریق socks/http به پراکسی توسط نشانی آیپی شما متصل شوند، فقط در شبکه مورد اعتماد فعال میشوند تا از اتصال غیرمجاز جلوگیری کنند.</string>
|
||||
<string name="toast_warning_pref_proxysharing_short">اتصالات از طریق LAN را مجاز کنید، مطمئن شوید که در یک شبکه قابل اعتماد هستید</string>
|
||||
<string name="title_pref_delay_test_url">آدرس اینترنتی آزمایش تاخیر واقعی کانفیگ ها (HTTP/HTTPS)</string>
|
||||
<string name="summary_pref_delay_test_url">URL</string>
|
||||
|
||||
<string name="title_pref_allow_insecure">مجوز ناامن</string>
|
||||
<string name="title_pref_proxy_sharing_enabled">اجازه اتصالات از طریق شبکه محلی</string>
|
||||
<string name="summary_pref_proxy_sharing_enabled">سایر دستگاه ها می توانند با استفاده از آدرس آیپی شما برای استفاده از یک پروکسی محلی متصل شوند. فقط در یک شبکه قابل اعتماد برای جلوگیری از اتصالات غیرمجاز استفاده کنید.</string>
|
||||
<string name="toast_warning_pref_proxysharing_short">اتصالات از طریق شبکه محلی را مجاز کنید، مطمئن شوید که در یک شبکه قابل اعتماد هستید.</string>
|
||||
|
||||
<string name="title_pref_allow_insecure">اعطای مجوز ناامن</string>
|
||||
<string name="summary_pref_allow_insecure">هنگام استفاده از TLS، به طور پیش فرض مجوز ناامن فعال است.</string>
|
||||
|
||||
<string name="title_pref_socks_port">پورت پروکسی SOCKS5</string>
|
||||
<string name="summary_pref_socks_port">پورت پروکسی SOCKS5</string>
|
||||
|
||||
<string name="title_pref_http_port">پورت پروکسی HTTP</string>
|
||||
<string name="summary_pref_http_port">پورت پروکسی HTTP</string>
|
||||
<string name="title_pref_socks_port">پورت پروکسی محلی</string>
|
||||
<string name="summary_pref_socks_port">پورت پروکسی محلی</string>
|
||||
|
||||
<string name="title_pref_local_dns_port">پورت DNS محلی</string>
|
||||
<string name="summary_pref_local_dns_port">پورت DNS محلی</string>
|
||||
@@ -201,6 +207,9 @@
|
||||
<string name="title_pref_start_scan_immediate">فورا اسکن را شروع کن</string>
|
||||
<string name="summary_pref_start_scan_immediate">دوربین را برای اسکن بلافاصله در هنگام راه اندازی باز کنید، در غیر این صورت می توانید کد را اسکن کنید یا عکسی را در نوار ابزار انتخاب کنید.</string>
|
||||
|
||||
<string name="title_pref_append_http_proxy">پروکسی HTTP را به VPN اضافه کنید</string>
|
||||
<string name="summary_pref_append_http_proxy">پروکسی HTTP مستقیماً از (مرورگر/برخی برنامههای پشتیبانیشده)، بدون استفاده از دستگاه NIC مجازی (Android 10+) استفاده میشود.</string>
|
||||
|
||||
<string name="title_pref_feedback">بازخورد</string>
|
||||
<string name="summary_pref_feedback">بازخورد یا گزارش اشکالات در گیت هاب</string>
|
||||
<string name="summary_pref_tg_group">عضویت در گروه تلگرام</string>
|
||||
@@ -209,6 +218,7 @@
|
||||
<string name="title_privacy_policy">حریم خصوصی</string>
|
||||
<string name="title_about">درباره</string>
|
||||
<string name="title_source_code">کد منبع</string>
|
||||
<string name="title_oss_license">مجوز های منبع باز</string>
|
||||
<string name="title_tg_channel">کانال تلگرام</string>
|
||||
<string name="title_configuration_backup">پشتیبان گیری از پیکربندی</string>
|
||||
<string name="summary_configuration_backup">محل ذخیره سازی: [%s], پس از حذف نصب برنامه یا پاک کردن فضای ذخیره سازی، نسخه پشتیبان پاک می شود</string>
|
||||
@@ -217,12 +227,12 @@
|
||||
<string name="title_pref_promotion">تبلیغات</string>
|
||||
<string name="summary_pref_promotion">تبلیغات، برای جزئیات بیشتر کلیک کنید (کمک مالی کنید تا حذف شود)</string>
|
||||
|
||||
<string name="title_pref_auto_update_subscription">بهروزرسانی خودکار اشتراک ها</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_pref_auto_update_interval">فاصله به روزرسانی خودکار ( حداقل مقدار ، 15 دقیقه )</string>
|
||||
<string name="title_core_loglevel">سطح گزارشات</string>
|
||||
<string name="title_mode">حالت</string>
|
||||
<string name="title_mode_help">برای راهنمایی بیشتر روی این متن، کلیک کنید</string>
|
||||
<string name="title_mode_help">برای اطلاعات و راهنمایی بیشتر، روی این متن کلیک کنید</string>
|
||||
<string name="title_language">زبان</string>
|
||||
<string name="title_ui_settings">تنظیمات رابط کاربری</string>
|
||||
<string name="title_pref_ui_mode_night">تنظیمات حالت رابط کاربری</string>
|
||||
@@ -238,14 +248,14 @@
|
||||
<string name="title_sub_setting">تنظیمات گروه اشتراک</string>
|
||||
<string name="sub_setting_remarks">ملاحظات</string>
|
||||
<string name="sub_setting_url">نشانی اینترنتی اختیاری</string>
|
||||
<string name="sub_setting_filter">Remarks regular filter</string>
|
||||
<string name="sub_setting_filter">نام مستعار فیلتر</string>
|
||||
<string name="sub_setting_enable">فعال کردن بهروزرسانی</string>
|
||||
<string name="sub_auto_update">فعال سازی بهروزرسانی خودکار</string>
|
||||
<string name="sub_setting_pre_profile">Previous proxy remarks</string>
|
||||
<string name="sub_setting_next_profile">Next proxy remarks</string>
|
||||
<string name="sub_setting_pre_profile_tip">The remarks exists and is unique</string>
|
||||
<string name="sub_setting_pre_profile">نام مستعار پروکسی قبلی</string>
|
||||
<string name="sub_setting_next_profile">نام مستعار پروکسی بعدی</string>
|
||||
<string name="sub_setting_pre_profile_tip">لطفاً مطمئن شوید که نام مستعار وجود دارد و منحصر به فرد است</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_user_asset_setting">فایل های دارایی جغرافیا</string>
|
||||
<string name="title_sort_by_test_results">مرتب سازی بر اساس نتایج آزمایش</string>
|
||||
@@ -253,6 +263,10 @@
|
||||
<string name="filter_config_all">همه گروههای اشتراک</string>
|
||||
<string name="title_del_duplicate_config_count">حذف %d کانفیگ تکراری</string>
|
||||
|
||||
<string name="title_del_config_count">حذف %d کانفیگ</string>
|
||||
<string name="title_import_config_count">وارد کردن %d کانفیگ</string>
|
||||
<string name="title_export_config_count">صادر کردن %d کانفیگ</string>
|
||||
<string name="title_update_config_count">آپدیت کردن %d کانفیگ</string>
|
||||
<string name="tasker_start_service">شروع خدمات</string>
|
||||
<string name="tasker_setting_confirm">تایید</string>
|
||||
|
||||
@@ -263,14 +277,24 @@
|
||||
<string name="routing_settings_delete">حذف</string>
|
||||
<string name="routing_settings_rule_title">تنظیمات قانون مسیریابی</string>
|
||||
<string name="routing_settings_add_rule">اضافه کردن قانون</string>
|
||||
<string name="routing_settings_import_rulesets">وارد کردن مجموعه قوانین</string>
|
||||
<string name="routing_settings_import_predefined_rulesets">وارد کردن مجموعه قوانین از پیش تعریف شده</string>
|
||||
<string name="routing_settings_import_rulesets_tip">مجموعه قوانین موجود حذف خواهند شد، آیا مطمئن هستید که ادامه می دهید؟</string>
|
||||
<string name="routing_settings_import_rulesets_from_clipboard">وارد کردن مجموعه قوانین از کلیپ بورد</string>
|
||||
<string name="routing_settings_import_rulesets_from_qrcode">وارد کردن مجموعه قوانین از QRcode</string>
|
||||
<string name="routing_settings_export_rulesets_to_clipboard">صادر کردن مجموعه قوانین به کلیپ بورد</string>
|
||||
<string name="routing_settings_locked">قفل است، این قانون را هنگام وارد کردن از پیش تنظیمها حفظ کنید</string>
|
||||
<string name="routing_settings_locked">قفل است، این قانون را هنگام وارد کردن از پیش تنظیم ها حفظ کنید</string>
|
||||
<string name="routing_settings_domain">دامنه</string>
|
||||
<string name="routing_settings_ip">آیپی</string>
|
||||
<string name="routing_settings_port">پورت</string>
|
||||
<string name="routing_settings_protocol">پورتکل</string>
|
||||
<string name="routing_settings_protocol_tip">[http,tls,bittorrent]</string>
|
||||
<string name="routing_settings_network">شبکه</string>
|
||||
<string name="routing_settings_network_tip">[udp|tcp]</string>
|
||||
<string name="routing_settings_outbound_tag">برچسب خروجی</string>
|
||||
|
||||
<string name="connection_test_pending">اتصال را بررسی کنید</string>
|
||||
<string name="connection_test_testing">در حال آزمایش...</string>
|
||||
<string name="connection_test_testing_count">تست کردن %d کانفیگ…</string>
|
||||
<string name="connection_test_available">موفقیت: اتصال HTTP %dms طول کشید</string>
|
||||
<string name="connection_test_error">اتصال به اینترنت شناسایی نشد: %s</string>
|
||||
<string name="connection_test_fail">اینترنت در دسترس نیست</string>
|
||||
@@ -300,7 +324,7 @@
|
||||
<item>VPN</item>
|
||||
<item>فقط پروکسی</item>
|
||||
</string-array>
|
||||
<string name="menu_item_add_asset">افزودن</string>
|
||||
<string name="menu_item_add_asset">افزودن منبع</string>
|
||||
<string name="menu_item_add_url">افزودن لینک</string>
|
||||
|
||||
<string-array name="ui_mode_night">
|
||||
@@ -312,8 +336,14 @@
|
||||
<string-array name="preset_rulesets">
|
||||
<item>لیست سفید چین</item>
|
||||
<item>لیست سیاه چین</item>
|
||||
<item>جهانی(Global)</item>
|
||||
<item>جهانی(GLOBAL)</item>
|
||||
<item>ایران</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="vpn_bypass_lan">
|
||||
<item>پیش فرض کانفیگ</item>
|
||||
<item>دور زده شود</item>
|
||||
<item>دور زده نشود</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<string name="navigation_drawer_close">Закрыть панель навигации</string>
|
||||
<string name="migration_success">Успешный перенос данных!</string>
|
||||
<string name="migration_fail">Перенос данных не выполнен!</string>
|
||||
<string name="pull_down_to_refresh">Потяните вниз для обновления!</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
<string name="notification_action_stop_v2ray">Остановить</string>
|
||||
@@ -54,14 +55,14 @@
|
||||
<string name="server_lab_request_host_http">Узел HTTP</string>
|
||||
<string name="server_lab_request_host_ws">Узел WS</string>
|
||||
<string name="server_lab_request_host_httpupgrade">Узел HTTPUpgrade</string>
|
||||
<string name="server_lab_request_host_xhttp">Узел SplitHTTP</string>
|
||||
<string name="server_lab_request_host_xhttp">Узел XHTTP</string>
|
||||
<string name="server_lab_request_host_h2">Узел H2</string>
|
||||
<string name="server_lab_request_host_quic">Шифрование QUIC</string>
|
||||
<string name="server_lab_request_host_grpc">Полномочия gRPC</string>
|
||||
<string name="server_lab_path">Путь</string>
|
||||
<string name="server_lab_path_ws">Путь WS</string>
|
||||
<string name="server_lab_path_httpupgrade">Путь HTTPUpgrade</string>
|
||||
<string name="server_lab_path_xhttp">Путь SplitHTTP</string>
|
||||
<string name="server_lab_path_xhttp">Путь XHTTP</string>
|
||||
<string name="server_lab_path_h2">Путь H2</string>
|
||||
<string name="server_lab_path_quic">Ключ QUIC</string>
|
||||
<string name="server_lab_path_kcp">Сид KCP</string>
|
||||
@@ -80,10 +81,11 @@
|
||||
<string name="server_lab_encryption">Шифрование</string>
|
||||
<string name="server_lab_flow">Поток</string>
|
||||
<string name="server_lab_public_key">Открытый ключ</string>
|
||||
<string name="server_lab_preshared_key">Дополнительный ключ шифрования (необязательно)</string>
|
||||
<string name="server_lab_short_id">ShortID</string>
|
||||
<string name="server_lab_spider_x">SpiderX</string>
|
||||
<string name="server_lab_secret_key">Закрытый ключ</string>
|
||||
<string name="server_lab_reserved">Reserved (необязательно)</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>
|
||||
@@ -106,7 +108,7 @@
|
||||
<string name="menu_item_add_asset">Добавить ресурс</string>
|
||||
<string name="menu_item_add_file">Добавить файлы</string>
|
||||
<string name="menu_item_add_url">Добавить URL</string>
|
||||
<string name="menu_item_scan_qrcode">Сканирование QR-кода</string>
|
||||
<string name="menu_item_scan_qrcode">Сканировать QR-код</string>
|
||||
<string name="title_url">URL</string>
|
||||
<string name="menu_item_download_file">Загрузить файлы</string>
|
||||
<string name="title_user_asset_add_url">Добавить URL ресурса</string>
|
||||
@@ -114,11 +116,13 @@
|
||||
<string name="msg_remark_is_duplicate">Название уже существует</string>
|
||||
<string name="toast_action_not_allowed">Это действие запрещено</string>
|
||||
<string name="server_obfs_password">Пароль obfs</string>
|
||||
<string name="server_lab_port_hop">Переключение портов</string>
|
||||
<string name="server_lab_port_hop_interval">Интервал переключения портов</string>
|
||||
<string name="server_lab_port_hop">Смена портов (переопределяет порт)</string>
|
||||
<string name="server_lab_port_hop_interval">Интервал смены портов</string>
|
||||
<string name="server_lab_stream_pinsha256">pinSHA256</string>
|
||||
<string name="server_lab_xhttp_mode">XHTTP Mode</string>
|
||||
<string name="server_lab_xhttp_extra">XHTTP Extra raw JSON, format: { XHTTPObject }</string>
|
||||
<string name="server_lab_bandwidth_down">Входящая пропускная способность (единицы)</string>
|
||||
<string name="server_lab_bandwidth_up">Исходящая пропускная способность (единицы)</string>
|
||||
<string name="server_lab_xhttp_mode">Режим XHTTP</string>
|
||||
<string name="server_lab_xhttp_extra">Необработанный JSON XHTTP Extra, формат: { XHTTPObject }</string>
|
||||
|
||||
<!-- PerAppProxyActivity -->
|
||||
<string name="msg_dialog_progress">Загрузка…</string>
|
||||
@@ -174,28 +178,29 @@
|
||||
<string name="summary_pref_remote_dns">DNS</string>
|
||||
|
||||
<string name="title_pref_vpn_dns">VPN DNS (только IPv4/v6)</string>
|
||||
<string name="title_pref_vpn_bypass_lan">VPN пропускает LAN</string>
|
||||
|
||||
<string name="title_pref_domestic_dns">Внутренняя DNS (необязательно)</string>
|
||||
<string name="summary_pref_domestic_dns">DNS</string>
|
||||
|
||||
<string name="title_pref_dns_hosts">Узлы DNS (формат: домен:адрес,…)</string>
|
||||
<string name="summary_pref_dns_hosts">домен:адрес,…</string>
|
||||
|
||||
<string name="title_pref_delay_test_url">Сервис проверки времени отклика (HTTP/HTTPS)</string>
|
||||
<string name="summary_pref_delay_test_url">URL</string>
|
||||
|
||||
<string name="title_pref_proxy_sharing_enabled">Разрешать подключения из LAN</string>
|
||||
<string name="summary_pref_proxy_sharing_enabled">Другие устройства могут подключаться, используя ваш IP-адрес, чтобы использовать прокси по протоколам SOCKS/HTTP. Используйте только в доверенной сети, чтобы избежать несанкционированного подключения.</string>
|
||||
<string name="summary_pref_proxy_sharing_enabled">Другие устройства могут подключаться, используя ваш IP-адрес, чтобы использовать локальный прокси. Используйте только в доверенной сети, чтобы избежать несанкционированного подключения.</string>
|
||||
<string name="toast_warning_pref_proxysharing_short">Доступ из LAN разрешён, убедитесь, что вы находитесь в доверенной сети</string>
|
||||
|
||||
<string name="title_pref_allow_insecure">Разрешать небезопасные</string>
|
||||
<string name="summary_pref_allow_insecure">Для TLS по умолчанию разрешены небезопасные соединения</string>
|
||||
|
||||
<string name="title_pref_socks_port">Порт SOCKS5-прокси</string>
|
||||
<string name="summary_pref_socks_port">Порт SOCKS5-прокси</string>
|
||||
<string name="title_pref_socks_port">Порт локального прокси</string>
|
||||
<string name="summary_pref_socks_port">Порт локального прокси</string>
|
||||
|
||||
<string name="title_pref_http_port">Порт HTTP-прокси</string>
|
||||
<string name="summary_pref_http_port">Порт HTTP-прокси</string>
|
||||
|
||||
<string name="title_pref_local_dns_port">Локальный порт DNS</string>
|
||||
<string name="summary_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="title_pref_confirm_remove">Подтверждение удаления профиля</string>
|
||||
<string name="summary_pref_confirm_remove">Требовать двойное подтверждение удаления профиля</string>
|
||||
@@ -203,6 +208,9 @@
|
||||
<string name="title_pref_start_scan_immediate">Сканирование при запуске</string>
|
||||
<string name="summary_pref_start_scan_immediate">Начинать сканирование сразу при запуске приложения или запускать функцию сканирования камерой или из изображения через панель инструментов</string>
|
||||
|
||||
<string name="title_pref_append_http_proxy">Дополнительный HTTP-прокси</string>
|
||||
<string name="summary_pref_append_http_proxy">HTTP-прокси будет использоваться напрямую (из браузера и других поддерживающих приложений), минуя виртуальный сетевой адаптер (Android 10+)</string>
|
||||
|
||||
<string name="title_pref_feedback">Обратная связь</string>
|
||||
<string name="summary_pref_feedback">Предложить улучшение или сообщить об ошибке на GitHub</string>
|
||||
<string name="summary_pref_tg_group">Присоединиться к группе в Telegram</string>
|
||||
@@ -210,6 +218,7 @@
|
||||
<string name="title_privacy_policy">Политика конфиденциальности</string>
|
||||
<string name="title_about">О приложении</string>
|
||||
<string name="title_source_code">Исходный код</string>
|
||||
<string name="title_oss_license">Лицензии открытого исходного кода</string>
|
||||
<string name="title_tg_channel">Telegram-канал</string>
|
||||
<string name="title_configuration_backup">Резервирование конфигурации</string>
|
||||
<string name="summary_configuration_backup">Путь: [%s]. Резервная копия будет стёрта при удалении приложения или очистке хранилища.</string>
|
||||
@@ -255,6 +264,10 @@
|
||||
<string name="title_filter_config">Фильтр групп</string>
|
||||
<string name="filter_config_all">Все группы</string>
|
||||
<string name="title_del_duplicate_config_count">Удалено дубликатов профилей: %d</string>
|
||||
<string name="title_del_config_count">Удалено профилей: %d</string>
|
||||
<string name="title_import_config_count">Импортировано профилей: %d</string>
|
||||
<string name="title_export_config_count">Экспортировано профилей: %d</string>
|
||||
<string name="title_update_config_count">Обновлено профилей: %d</string>
|
||||
|
||||
<string name="tasker_start_service">Запуск службы</string>
|
||||
<string name="tasker_setting_confirm">Подтвердить</string>
|
||||
@@ -266,9 +279,10 @@
|
||||
<string name="routing_settings_delete">Очистить</string>
|
||||
<string name="routing_settings_rule_title">Настройка правил маршрутизации</string>
|
||||
<string name="routing_settings_add_rule">Добавить правило</string>
|
||||
<string name="routing_settings_import_rulesets">Импорт правил</string>
|
||||
<string name="routing_settings_import_predefined_rulesets">Импорт набора правил</string>
|
||||
<string name="routing_settings_import_rulesets_tip">Существующие правила будут удалены. Продолжить?</string>
|
||||
<string name="routing_settings_import_rulesets_from_clipboard">Импорт правил из буфера обмена</string>
|
||||
<string name="routing_settings_import_rulesets_from_qrcode">Импорт правил из QR-кода</string>
|
||||
<string name="routing_settings_export_rulesets_to_clipboard">Экспорт правил в буфер обмена</string>
|
||||
<string name="routing_settings_locked">Постоянное (сохранится при импорте правил)</string>
|
||||
<string name="routing_settings_domain">Домен</string>
|
||||
@@ -276,12 +290,13 @@
|
||||
<string name="routing_settings_port">Порт</string>
|
||||
<string name="routing_settings_protocol">Протокол</string>
|
||||
<string name="routing_settings_protocol_tip">[http,tls,bittorrent]</string>
|
||||
<string name="routing_settings_network">Сеть</string>"
|
||||
<string name="routing_settings_network">Сеть</string>
|
||||
<string name="routing_settings_network_tip">[udp|tcp]</string>
|
||||
<string name="routing_settings_outbound_tag">Исходящее подключение</string>
|
||||
|
||||
<string name="connection_test_pending">Проверить подключение</string>
|
||||
<string name="connection_test_testing">Проверка…</string>
|
||||
<string name="connection_test_testing_count">Проверка профилей: %d</string>
|
||||
<string name="connection_test_available">Успешно: HTTP-соединение заняло %d мс</string>
|
||||
<string name="connection_test_error">Сбой проверки интернет-соединения: %s</string>
|
||||
<string name="connection_test_fail">Интернет недоступен</string>
|
||||
@@ -326,4 +341,10 @@
|
||||
<item>Белый список Ирана</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="vpn_bypass_lan">
|
||||
<item>Как в профиле</item>
|
||||
<item>Пропускает</item>
|
||||
<item>Не пропускает</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<string name="navigation_drawer_close">Đóng Menu ứng dụng</string>
|
||||
<string name="migration_success">Đã chuyển dữ liệu!</string>
|
||||
<string name="migration_fail">Không thể chuyển dữ liệu!</string>
|
||||
<string name="pull_down_to_refresh">Please pull down to refresh!</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
<string name="notification_action_stop_v2ray">Ngắt kết nối v2rayNG</string>
|
||||
@@ -80,6 +81,7 @@
|
||||
<string name="server_lab_encryption">Mã hóa</string>
|
||||
<string name="server_lab_flow">Kiểm soát lưu lượng (Flow)</string>
|
||||
<string name="server_lab_public_key">PublicKey</string>
|
||||
<string name="server_lab_preshared_key">PreSharedKey(optional)</string>
|
||||
<string name="server_lab_short_id">ShortId</string>
|
||||
<string name="server_lab_spider_x">SpiderX</string>
|
||||
<string name="server_lab_secret_key">SecretKey</string>
|
||||
@@ -112,6 +114,8 @@
|
||||
<string name="server_lab_port_hop">Port Hopping</string>
|
||||
<string name="server_lab_port_hop_interval">Port Hopping Interval</string>
|
||||
<string name="server_lab_stream_pinsha256">pinSHA256</string>
|
||||
<string name="server_lab_bandwidth_down">Bandwidth down (units)</string>
|
||||
<string name="server_lab_bandwidth_up">Bandwidth up (units)</string>
|
||||
<string name="server_lab_xhttp_mode">XHTTP Mode</string>
|
||||
<string name="server_lab_xhttp_extra">XHTTP Extra raw JSON, format: { XHTTPObject }</string>
|
||||
|
||||
@@ -174,10 +178,14 @@
|
||||
<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_bypass_lan">Does VPN bypass LAN</string>
|
||||
|
||||
<string name="title_pref_domestic_dns">DNS nội địa (Không bắt buộc)</string>
|
||||
<string name="summary_pref_domestic_dns">DNS</string>
|
||||
|
||||
<string name="title_pref_dns_hosts">DNS hosts (Format: domain:address,…)</string>
|
||||
<string name="summary_pref_dns_hosts">domain:address,…</string>
|
||||
|
||||
<string name="title_pref_delay_test_url">URL kiểm tra độ trễ thực (HTTP / HTTPS)</string>
|
||||
<string name="summary_pref_delay_test_url">URL</string>
|
||||
|
||||
@@ -188,11 +196,8 @@
|
||||
<string name="title_pref_allow_insecure">Bỏ qua xác minh chứng chỉ</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ỉ.</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="title_pref_http_port">Cổng Proxy HTTP</string>
|
||||
<string name="summary_pref_http_port">Cổng Proxy HTTP</string>
|
||||
<string name="title_pref_socks_port">Cổng Proxy Local</string>
|
||||
<string name="summary_pref_socks_port">Cổng Proxy Local</string>
|
||||
|
||||
<string name="title_pref_local_dns_port">Cổng Local DNS</string>
|
||||
<string name="summary_pref_local_dns_port">Cổng Local DNS</string>
|
||||
@@ -203,6 +208,9 @@
|
||||
<string name="title_pref_start_scan_immediate">Quét mã QR ngay lập tức</string>
|
||||
<string name="summary_pref_start_scan_immediate">Mở camera để quét mã QR ngay khi khởi động, nếu không, bạn cũng có thể chọn quét mã hoặc chọn ảnh từ thanh công cụ.</string>
|
||||
|
||||
<string name="title_pref_append_http_proxy">Append HTTP Proxy to VPN</string>
|
||||
<string name="summary_pref_append_http_proxy">HTTP proxy will be used directly from (browser/ some supported apps), without going through the virtual NIC device (Android 10+)</string>
|
||||
|
||||
<string name="title_pref_feedback">Phản hồi lỗi</string>
|
||||
<string name="summary_pref_feedback">Phản hồi cải tiến hoặc lỗi lên GitHub</string>
|
||||
<string name="summary_pref_tg_group">Tham gia nhóm Telegram</string>
|
||||
@@ -211,6 +219,7 @@
|
||||
<string name="title_privacy_policy">Chính sách bảo mật</string>
|
||||
<string name="title_about">Giới thiệu</string>
|
||||
<string name="title_source_code">Mã nguồn</string>
|
||||
<string name="title_oss_license">Open Source licenses</string>
|
||||
<string name="title_tg_channel">Kênh Telegram</string>
|
||||
<string name="title_configuration_backup">Sao lưu cấu hình</string>
|
||||
<string name="summary_configuration_backup">Nơi lưu trữ: [%s], bản backup sẽ được dọn dẹp sau khi xóa ứng dụng hoặc xóa bộ nhớ.</string>
|
||||
@@ -256,6 +265,10 @@
|
||||
<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_config_count">Delete %d configurations</string>
|
||||
<string name="title_import_config_count">Import %d configurations</string>
|
||||
<string name="title_export_config_count">Export %d configurations</string>
|
||||
<string name="title_update_config_count">Update %d configurations</string>
|
||||
<string name="tasker_start_service">Khởi động v2rayNG</string>
|
||||
<string name="tasker_setting_confirm">Xác nhận</string>
|
||||
|
||||
@@ -266,14 +279,16 @@
|
||||
<string name="routing_settings_delete">Xoá</string>
|
||||
<string name="routing_settings_rule_title">Routing Rule Settings</string>
|
||||
<string name="routing_settings_add_rule">Add rule</string>
|
||||
<string name="routing_settings_import_rulesets">Import ruleset</string>
|
||||
<string name="routing_settings_import_predefined_rulesets">Nhập các bộ quy tắc được xác định trước</string>
|
||||
<string name="routing_settings_import_rulesets_tip">Existing rulesets will be deleted, are you sure to continue?</string>
|
||||
<string name="routing_settings_import_rulesets_from_clipboard">Import ruleset from clipboard</string>
|
||||
<string name="routing_settings_import_rulesets_from_qrcode">Nhập bộ quy tắc từ QRcode</string>
|
||||
<string name="routing_settings_export_rulesets_to_clipboard">Export ruleset to clipboard</string>
|
||||
<string name="routing_settings_locked">Locked, keep this rule when import presets</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_count">Testing %d configurations…</string>
|
||||
<string name="connection_test_available">Kiểm tra thành công: thời gian truy cập Google là %d ms</string>
|
||||
<string name="connection_test_error">Lỗi kết nối mạng, hãy thử đổi cấu hình hoặc kiểm tra lại! Mã lỗi: %s</string>
|
||||
<string name="connection_test_fail">Không có kết nối mạng!</string>
|
||||
@@ -313,4 +328,10 @@
|
||||
<string name="title_pref_fragment_interval">Fragment Interval (min-max)</string>
|
||||
<string name="title_pref_fragment_enabled">Enable Fragment</string>
|
||||
|
||||
<string-array name="vpn_bypass_lan">
|
||||
<item>Follow config</item>
|
||||
<item>Bypass</item>
|
||||
<item>Not Bypass</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<string name="navigation_drawer_close">Close navigation drawer</string>
|
||||
<string name="migration_success">数据迁移成功!</string>
|
||||
<string name="migration_fail">数据迁移失败啦!</string>
|
||||
<string name="pull_down_to_refresh">请下拉刷新!</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
<string name="notification_action_stop_v2ray">停止</string>
|
||||
@@ -80,6 +81,7 @@
|
||||
<string name="server_lab_encryption">加密方式(encryption)</string>
|
||||
<string name="server_lab_flow">流控(flow)</string>
|
||||
<string name="server_lab_public_key">PublicKey</string>
|
||||
<string name="server_lab_preshared_key">PreSharedKey(optional)</string>
|
||||
<string name="server_lab_short_id">ShortId</string>
|
||||
<string name="server_lab_spider_x">SpiderX</string>
|
||||
<string name="server_lab_secret_key">SecretKey</string>
|
||||
@@ -112,6 +114,8 @@
|
||||
<string name="server_lab_port_hop">跳跃端口(会覆盖服务器端口)</string>
|
||||
<string name="server_lab_port_hop_interval">端口跳跃间隔(秒)</string>
|
||||
<string name="server_lab_stream_pinsha256">SHA256证书指纹</string>
|
||||
<string name="server_lab_bandwidth_down">带宽下行 (单位)</string>
|
||||
<string name="server_lab_bandwidth_up">带宽上行 (单位)</string>
|
||||
<string name="server_lab_xhttp_mode">XHTTP 模式</string>
|
||||
<string name="server_lab_xhttp_extra">XHTTP Extra 原始 JSON,格式: { XHTTPObject }</string>
|
||||
|
||||
@@ -171,10 +175,14 @@
|
||||
<string name="summary_pref_remote_dns">DNS</string>
|
||||
|
||||
<string name="title_pref_vpn_dns">VPN DNS (仅支持 IPv4/v6)</string>
|
||||
<string name="title_pref_vpn_bypass_lan">VPN是否绕过局域网</string>
|
||||
|
||||
<string name="title_pref_domestic_dns">境内DNS (可选)</string>
|
||||
<string name="summary_pref_domestic_dns">DNS</string>
|
||||
|
||||
<string name="title_pref_dns_hosts">DNS hosts (格式: 域名:地址,…)</string>
|
||||
<string name="summary_pref_dns_hosts">domain:address,…</string>
|
||||
|
||||
<string name="title_pref_delay_test_url">真连接延迟测试网址 (http/https)</string>
|
||||
<string name="summary_pref_delay_test_url">Url</string>
|
||||
|
||||
@@ -185,11 +193,8 @@
|
||||
<string name="title_pref_allow_insecure">跳过证书验证(allowInsecure)</string>
|
||||
<string name="summary_pref_allow_insecure">传输层安全选tls时,默认跳过证书验证(allowInsecure)</string>
|
||||
|
||||
<string name="title_pref_socks_port">SOCKS5代理端口</string>
|
||||
<string name="summary_pref_socks_port">SOCKS5代理端口</string>
|
||||
|
||||
<string name="title_pref_http_port">HTTP代理端口</string>
|
||||
<string name="summary_pref_http_port">HTTP代理端口</string>
|
||||
<string name="title_pref_socks_port">本地代理端口</string>
|
||||
<string name="summary_pref_socks_port">本地代理端口</string>
|
||||
|
||||
<string name="title_pref_local_dns_port">本地DNS端口</string>
|
||||
<string name="summary_pref_local_dns_port">本地DNS端口</string>
|
||||
@@ -200,6 +205,9 @@
|
||||
<string name="title_pref_start_scan_immediate">立即启动扫码</string>
|
||||
<string name="summary_pref_start_scan_immediate">启动时立即打开相机扫描,否则可在工具栏选择扫码或选照片</string>
|
||||
|
||||
<string name="title_pref_append_http_proxy">追加 HTTP 代理至 VPN</string>
|
||||
<string name="summary_pref_append_http_proxy">浏览器 / 一些支持的应用 将直接使用 HTTP 代理, 而不经过虚拟网卡设备 (Android 10+)</string>
|
||||
|
||||
<string name="title_pref_feedback">反馈</string>
|
||||
<string name="summary_pref_feedback">反馈改进或漏洞至 GitHub</string>
|
||||
<string name="summary_pref_tg_group">加入Telegram Group</string>
|
||||
@@ -207,6 +215,7 @@
|
||||
<string name="title_privacy_policy">隐私权政策</string>
|
||||
<string name="title_about">关于</string>
|
||||
<string name="title_source_code">源代码</string>
|
||||
<string name="title_oss_license">Open Source licenses</string>
|
||||
<string name="title_tg_channel">Telegram 频道</string>
|
||||
<string name="title_configuration_backup">备份配置</string>
|
||||
<string name="summary_configuration_backup">存储位置: [%s], 卸载App或清除存储后备份将被清除</string>
|
||||
@@ -253,6 +262,10 @@
|
||||
<string name="filter_config_all">所有分组</string>
|
||||
<string name="title_del_duplicate_config_count">删除 %d 个重复配置</string>
|
||||
|
||||
<string name="title_del_config_count">删除 %d 个配置</string>
|
||||
<string name="title_import_config_count">导入 %d 个配置</string>
|
||||
<string name="title_export_config_count">导出 %d 个配置</string>
|
||||
<string name="title_update_config_count">更新 %d 个配置</string>
|
||||
<string name="tasker_start_service">启动服务</string>
|
||||
<string name="tasker_setting_confirm">确定</string>
|
||||
|
||||
@@ -263,14 +276,16 @@
|
||||
<string name="routing_settings_delete">清空</string>
|
||||
<string name="routing_settings_rule_title">路由规则设置</string>
|
||||
<string name="routing_settings_add_rule">添加规则</string>
|
||||
<string name="routing_settings_import_rulesets">导入预设规则集</string>
|
||||
<string name="routing_settings_import_predefined_rulesets">导入预定义规则集</string>
|
||||
<string name="routing_settings_import_rulesets_tip">将删除现有的规则集,是否确定继续?</string>
|
||||
<string name="routing_settings_import_rulesets_from_clipboard">从剪贴板导入规则集</string>
|
||||
<string name="routing_settings_import_rulesets_from_qrcode">从 QRcode 导入规则集</string>
|
||||
<string name="routing_settings_export_rulesets_to_clipboard">导出规则集至剪贴板</string>
|
||||
<string name="routing_settings_locked">锁定中,导入预设时不删除此规则</string>
|
||||
|
||||
<string name="connection_test_pending">"检查网络连接"</string>
|
||||
<string name="connection_test_testing">"测试中…"</string>
|
||||
<string name="connection_test_testing_count">测试 %d 个配置中…</string>
|
||||
<string name="connection_test_available">"连接成功:延时 %d 毫秒"</string>
|
||||
<string name="connection_test_error">"失败:%s"</string>
|
||||
<string name="connection_test_fail">"无互联网连接"</string>
|
||||
@@ -317,4 +332,10 @@
|
||||
<item>伊朗(Iran)</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="vpn_bypass_lan">
|
||||
<item>跟随配置文件</item>
|
||||
<item>绕过</item>
|
||||
<item>不绕过</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<string name="navigation_drawer_close">關閉導覽匣</string>
|
||||
<string name="migration_success">資料遷移成功!</string>
|
||||
<string name="migration_fail">資料遷移失敗!</string>
|
||||
<string name="pull_down_to_refresh">請下拉刷新!</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
<string name="notification_action_stop_v2ray">停止</string>
|
||||
@@ -80,6 +81,7 @@
|
||||
<string name="server_lab_encryption">加密 (encryption)</string>
|
||||
<string name="server_lab_flow">流程 (flow)</string>
|
||||
<string name="server_lab_public_key">PublicKey</string>
|
||||
<string name="server_lab_preshared_key">PreSharedKey(optional)</string>
|
||||
<string name="server_lab_short_id">ShortId</string>
|
||||
<string name="server_lab_spider_x">SpiderX</string>
|
||||
<string name="server_lab_secret_key">SecretKey</string>
|
||||
@@ -112,6 +114,8 @@
|
||||
<string name="server_lab_port_hop">跳躍連接埠(會覆蓋伺服器連接埠)</string>
|
||||
<string name="server_lab_port_hop_interval">連接埠跳躍間隔(秒)</string>
|
||||
<string name="server_lab_stream_pinsha256">SHA256憑證指紋</string>
|
||||
<string name="server_lab_bandwidth_down">頻寬下行 (單位)</string>
|
||||
<string name="server_lab_bandwidth_up">頻寬上行 (單位)</string>
|
||||
<string name="server_lab_xhttp_mode">XHTTP 模式</string>
|
||||
<string name="server_lab_xhttp_extra">XHTTP Extra 原始 JSON,格式: { XHTTPObject }</string>
|
||||
|
||||
@@ -173,9 +177,10 @@
|
||||
<string name="summary_pref_remote_dns">DNS</string>
|
||||
|
||||
<string name="title_pref_vpn_dns">VPN DNS (僅支援 IPv4/v6)</string>
|
||||
<string name="title_pref_vpn_bypass_lan">VPN是否繞過區域網</string>
|
||||
|
||||
<string name="title_pref_domestic_dns">國內 DNS (可選)</string>
|
||||
<string name="summary_pref_domestic_dns">DNS</string>
|
||||
<string name="title_pref_dns_hosts">DNS hosts (格式: 網域:位址,…)</string>
|
||||
<string name="summary_pref_dns_hosts">domain:address,…</string>
|
||||
|
||||
<string name="title_pref_delay_test_url">真連線延遲測試網址 (http/https)</string>
|
||||
<string name="summary_pref_delay_test_url">Url</string>
|
||||
@@ -187,11 +192,8 @@
|
||||
<string name="title_pref_allow_insecure">跳過憑證驗證 (allowInsecure)</string>
|
||||
<string name="summary_pref_allow_insecure">傳輸層安全選 tls 時,預設跳過憑證驗證 (allowInsecure)</string>
|
||||
|
||||
<string name="title_pref_socks_port">SOCKS5 Proxy 埠</string>
|
||||
<string name="summary_pref_socks_port">SOCKS5 Proxy 埠</string>
|
||||
|
||||
<string name="title_pref_http_port">HTTP Proxy 埠</string>
|
||||
<string name="summary_pref_http_port">HTTP Proxy 埠</string>
|
||||
<string name="title_pref_socks_port">本地 Proxy 埠</string>
|
||||
<string name="summary_pref_socks_port">本地 Proxy 埠</string>
|
||||
|
||||
<string name="title_pref_local_dns_port">本機 DNS 埠</string>
|
||||
<string name="summary_pref_local_dns_port">本機 DNS 埠</string>
|
||||
@@ -202,6 +204,9 @@
|
||||
<string name="title_pref_start_scan_immediate">立即啟動掃碼</string>
|
||||
<string name="summary_pref_start_scan_immediate">啟動時立即打開相機掃描,否則可在工具欄選擇掃碼或選照片</string>
|
||||
|
||||
<string name="title_pref_append_http_proxy">追加 HTTP 代理至 VPN</string>
|
||||
<string name="summary_pref_append_http_proxy">瀏覽器 / 一些支援的應用 將直接使用 HTTP 代理, 而不經過虛擬網卡設備 (Android 10+)</string>
|
||||
|
||||
<string name="title_pref_feedback">意見回饋</string>
|
||||
<string name="summary_pref_feedback">前往 GitHub 回報錯誤</string>
|
||||
<string name="summary_pref_tg_group">加入 Telegram 群組</string>
|
||||
@@ -209,6 +214,7 @@
|
||||
<string name="title_privacy_policy">隱私權政策</string>
|
||||
<string name="title_about">關於</string>
|
||||
<string name="title_source_code">原始碼</string>
|
||||
<string name="title_oss_license">Open Source licenses</string>
|
||||
<string name="title_tg_channel">Telegram 頻道</string>
|
||||
<string name="title_configuration_backup">備份設定</string>
|
||||
<string name="summary_configuration_backup">儲存位置: [%s], 卸載App或清除儲存後備份將被清除</string>
|
||||
@@ -253,8 +259,12 @@
|
||||
<string name="title_sort_by_test_results">依偵測結果排序</string>
|
||||
<string name="title_filter_config">過濾設定</string>
|
||||
<string name="filter_config_all">所有分組</string>
|
||||
<string name="title_del_duplicate_config_count">Delete %d duplicate configurations</string>
|
||||
<string name="title_del_duplicate_config_count">删除 %d 个重复配置</string>
|
||||
|
||||
<string name="title_del_config_count">删除 %d 个配置</string>
|
||||
<string name="title_import_config_count">导入 %d 个配置</string>
|
||||
<string name="title_export_config_count">导出 %d 个配置</string>
|
||||
<string name="title_update_config_count">更新 %d 个配置</string>
|
||||
<string name="tasker_start_service">啟動服務</string>
|
||||
<string name="tasker_setting_confirm">確定</string>
|
||||
|
||||
@@ -265,14 +275,16 @@
|
||||
<string name="routing_settings_delete">清除</string>
|
||||
<string name="routing_settings_rule_title">路由規則設定</string>
|
||||
<string name="routing_settings_add_rule">新增規則</string>
|
||||
<string name="routing_settings_import_rulesets">匯入預設規則集</string>
|
||||
<string name="routing_settings_import_predefined_rulesets">匯入預先定義的規則集</string>
|
||||
<string name="routing_settings_import_rulesets_tip">將刪除現有的規則集,是否確定繼續? </string>
|
||||
<string name="routing_settings_import_rulesets_from_clipboard">從剪貼簿匯入規則集</string>
|
||||
<string name="routing_settings_import_rulesets_from_qrcode">從 QRcode 匯入規則集</string>
|
||||
<string name="routing_settings_export_rulesets_to_clipboard">匯出規則集至剪貼簿</string>
|
||||
<string name="routing_settings_locked">鎖定中,匯入預設時不刪除此規則</string>
|
||||
|
||||
<string name="connection_test_pending">"測試連線能力"</string>
|
||||
<string name="connection_test_testing">"測試中……"</string>
|
||||
<string name="connection_test_testing_count">測試 %d 个配置中…</string>
|
||||
<string name="connection_test_available">"成功:%d ms延遲"</string>
|
||||
<string name="connection_test_error">"測試網際網路連線失敗:%s"</string>
|
||||
<string name="connection_test_fail">"無法使用網際網路"</string>
|
||||
@@ -319,4 +331,10 @@
|
||||
<item>伊朗(Iran)</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="vpn_bypass_lan">
|
||||
<item>跟隨設定檔</item>
|
||||
<item>繞過</item>
|
||||
<item>不繞過</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
<item>ws</item>
|
||||
<item>httpupgrade</item>
|
||||
<item>xhttp</item>
|
||||
<item>splithttp</item>
|
||||
<item>h2</item>
|
||||
<item>grpc</item>
|
||||
</string-array>
|
||||
@@ -210,5 +209,13 @@
|
||||
<item>auto</item>
|
||||
<item>packet-up</item>
|
||||
<item>stream-up</item>
|
||||
<item>stream-one</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="vpn_bypass_lan_value" translatable="false">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
@@ -8,6 +8,7 @@
|
||||
<string name="navigation_drawer_close">Close navigation drawer</string>
|
||||
<string name="migration_success">Data migration success!</string>
|
||||
<string name="migration_fail">Data migration failed!</string>
|
||||
<string name="pull_down_to_refresh">Please pull down to refresh!</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
<string name="notification_action_stop_v2ray">Stop</string>
|
||||
@@ -81,6 +82,7 @@
|
||||
<string name="server_lab_encryption">encryption</string>
|
||||
<string name="server_lab_flow">flow</string>
|
||||
<string name="server_lab_public_key">PublicKey</string>
|
||||
<string name="server_lab_preshared_key">PreSharedKey(optional)</string>
|
||||
<string name="server_lab_short_id">ShortId</string>
|
||||
<string name="server_lab_spider_x">SpiderX</string>
|
||||
<string name="server_lab_secret_key">SecretKey</string>
|
||||
@@ -118,6 +120,8 @@
|
||||
<string name="server_lab_port_hop">Port Hopping(will override the port)</string>
|
||||
<string name="server_lab_port_hop_interval">Port Hopping Interval</string>
|
||||
<string name="server_lab_stream_pinsha256">pinSHA256</string>
|
||||
<string name="server_lab_bandwidth_down">Bandwidth down (units)</string>
|
||||
<string name="server_lab_bandwidth_up">Bandwidth up (units)</string>
|
||||
<string name="server_lab_xhttp_mode">XHTTP Mode</string>
|
||||
<string name="server_lab_xhttp_extra">XHTTP Extra raw JSON, format: { XHTTPObject }</string>
|
||||
|
||||
@@ -177,25 +181,26 @@
|
||||
<string name="summary_pref_remote_dns">DNS</string>
|
||||
|
||||
<string name="title_pref_vpn_dns">VPN DNS (only IPv4/v6)</string>
|
||||
<string name="title_pref_vpn_bypass_lan">Does VPN bypass LAN</string>
|
||||
|
||||
<string name="title_pref_domestic_dns">Domestic DNS (Optional)</string>
|
||||
<string name="summary_pref_domestic_dns">DNS</string>
|
||||
|
||||
<string name="title_pref_dns_hosts">DNS hosts (Format: domain:address,…)</string>
|
||||
<string name="summary_pref_dns_hosts">domain:address,…</string>
|
||||
|
||||
<string name="title_pref_delay_test_url">True delay test url (http/https)</string>
|
||||
<string name="summary_pref_delay_test_url">Url</string>
|
||||
|
||||
<string name="title_pref_proxy_sharing_enabled">Allow connections from the LAN</string>
|
||||
<string name="summary_pref_proxy_sharing_enabled">Other devices can connect to proxy by your ip address through socks/http, Only enable in trusted network to avoid unauthorized connection</string>
|
||||
<string name="summary_pref_proxy_sharing_enabled">Other devices can connect to proxy by your ip address through local proxy, Only enable in trusted network to avoid unauthorized connection</string>
|
||||
<string name="toast_warning_pref_proxysharing_short">Allow connections from the LAN, Make sure you are in a trusted network</string>
|
||||
|
||||
<string name="title_pref_allow_insecure">allowInsecure</string>
|
||||
<string name="summary_pref_allow_insecure">When TLS, the default allowInsecure</string>
|
||||
|
||||
<string name="title_pref_socks_port">SOCKS5 proxy port</string>
|
||||
<string name="summary_pref_socks_port">SOCKS5 proxy port</string>
|
||||
|
||||
<string name="title_pref_http_port">HTTP proxy port</string>
|
||||
<string name="summary_pref_http_port">HTTP proxy port</string>
|
||||
<string name="title_pref_socks_port">Local proxy port</string>
|
||||
<string name="summary_pref_socks_port">Local proxy port</string>
|
||||
|
||||
<string name="title_pref_local_dns_port">Local DNS port</string>
|
||||
<string name="summary_pref_local_dns_port">Local DNS port</string>
|
||||
@@ -206,6 +211,9 @@
|
||||
<string name="title_pref_start_scan_immediate">Start scanning immediately</string>
|
||||
<string name="summary_pref_start_scan_immediate">Open the camera to scan immediately at startup, otherwise you can choose to scan the code or select a photo in the toolbar</string>
|
||||
|
||||
<string name="title_pref_append_http_proxy">Append HTTP Proxy to VPN</string>
|
||||
<string name="summary_pref_append_http_proxy">HTTP proxy will be used directly from (browser/ some supported apps), without going through the virtual NIC device (Android 10+)</string>
|
||||
|
||||
<string name="title_pref_feedback">Feedback</string>
|
||||
<string name="summary_pref_feedback">Feedback enhancements or bugs to GitHub</string>
|
||||
<string name="summary_pref_tg_group">Join Telegram Group</string>
|
||||
@@ -213,6 +221,7 @@
|
||||
<string name="title_privacy_policy">Privacy policy</string>
|
||||
<string name="title_about">About</string>
|
||||
<string name="title_source_code">Source code</string>
|
||||
<string name="title_oss_license">Open Source licenses</string>
|
||||
<string name="title_tg_channel">Telegram channel</string>
|
||||
<string name="title_configuration_backup">Backup configuration</string>
|
||||
<string name="summary_configuration_backup">Storage location: [%s], The backup will be cleared after uninstalling the app or clearing the storage</string>
|
||||
@@ -258,6 +267,10 @@
|
||||
<string name="title_filter_config">Filter configuration file</string>
|
||||
<string name="filter_config_all">All groups</string>
|
||||
<string name="title_del_duplicate_config_count">Delete %d duplicate configurations</string>
|
||||
<string name="title_del_config_count">Delete %d configurations</string>
|
||||
<string name="title_import_config_count">Import %d configurations</string>
|
||||
<string name="title_export_config_count">Export %d configurations</string>
|
||||
<string name="title_update_config_count">Update %d configurations</string>
|
||||
|
||||
<string name="tasker_start_service">Start Service</string>
|
||||
<string name="tasker_setting_confirm">Confirm</string>
|
||||
@@ -269,9 +282,10 @@
|
||||
<string name="routing_settings_delete">Clear</string>
|
||||
<string name="routing_settings_rule_title">Routing Rule Settings</string>
|
||||
<string name="routing_settings_add_rule">Add rule</string>
|
||||
<string name="routing_settings_import_rulesets">Import ruleset</string>
|
||||
<string name="routing_settings_import_predefined_rulesets">Import predefined rulesets</string>
|
||||
<string name="routing_settings_import_rulesets_tip">Existing rulesets will be deleted, are you sure to continue?</string>
|
||||
<string name="routing_settings_import_rulesets_from_clipboard">Import ruleset from clipboard</string>
|
||||
<string name="routing_settings_import_rulesets_from_qrcode">Import ruleset from QRcode</string>
|
||||
<string name="routing_settings_export_rulesets_to_clipboard">Export ruleset to clipboard</string>
|
||||
<string name="routing_settings_locked">Locked, keep this rule when import presets</string>
|
||||
<string name="routing_settings_domain" translatable="false">domain</string>
|
||||
@@ -285,6 +299,7 @@
|
||||
|
||||
<string name="connection_test_pending">Check Connectivity</string>
|
||||
<string name="connection_test_testing">Testing…</string>
|
||||
<string name="connection_test_testing_count">Testing %d configurations…</string>
|
||||
<string name="connection_test_available">Success: HTTP connection took %dms</string>
|
||||
<string name="connection_test_error">Fail to detect internet connection: %s</string>
|
||||
<string name="connection_test_fail">Internet Unavailable</string>
|
||||
@@ -329,4 +344,10 @@
|
||||
<item>Iran Whitelist</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="vpn_bypass_lan">
|
||||
<item>Follow config</item>
|
||||
<item>Bypass</item>
|
||||
<item>Not Bypass</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -34,6 +34,11 @@
|
||||
android:summary="@string/summary_pref_fake_dns_enabled"
|
||||
android:title="@string/title_pref_fake_dns_enabled" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:key="pref_append_http_proxy"
|
||||
android:summary="@string/summary_pref_append_http_proxy"
|
||||
android:title="@string/title_pref_append_http_proxy" />
|
||||
|
||||
<EditTextPreference
|
||||
android:inputType="number"
|
||||
android:key="pref_local_dns_port"
|
||||
@@ -44,6 +49,14 @@
|
||||
android:key="pref_vpn_dns"
|
||||
android:summary="@string/summary_pref_remote_dns"
|
||||
android:title="@string/title_pref_vpn_dns" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="0"
|
||||
android:entries="@array/vpn_bypass_lan"
|
||||
android:entryValues="@array/vpn_bypass_lan_value"
|
||||
android:key="pref_vpn_bypass_lan"
|
||||
android:summary="%s"
|
||||
android:title="@string/title_pref_vpn_bypass_lan" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/title_ui_settings">
|
||||
@@ -175,12 +188,6 @@
|
||||
android:summary="10808"
|
||||
android:title="@string/title_pref_socks_port" />
|
||||
|
||||
<EditTextPreference
|
||||
android:inputType="number"
|
||||
android:key="pref_http_port"
|
||||
android:summary="10809"
|
||||
android:title="@string/title_pref_http_port" />
|
||||
|
||||
<EditTextPreference
|
||||
android:key="pref_remote_dns"
|
||||
android:summary="@string/summary_pref_remote_dns"
|
||||
@@ -191,6 +198,11 @@
|
||||
android:summary="@string/summary_pref_domestic_dns"
|
||||
android:title="@string/title_pref_domestic_dns" />
|
||||
|
||||
<EditTextPreference
|
||||
android:key="pref_dns_hosts"
|
||||
android:summary="@string/summary_pref_dns_hosts"
|
||||
android:title="@string/title_pref_dns_hosts" />
|
||||
|
||||
<EditTextPreference
|
||||
android:key="pref_delay_test_url"
|
||||
android:summary="@string/summary_pref_delay_test_url"
|
||||
|
||||
@@ -4,3 +4,10 @@ plugins {
|
||||
alias(libs.plugins.android.library) apply false
|
||||
alias(libs.plugins.kotlin.android) apply false
|
||||
}
|
||||
|
||||
buildscript {
|
||||
dependencies {
|
||||
classpath(libs.gradle.license.plugin)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. For more details, visit
|
||||
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
[versions]
|
||||
agp = "8.7.2"
|
||||
kotlin = "2.0.21"
|
||||
agp = "8.8.2"
|
||||
desugar_jdk_libs = "2.1.5"
|
||||
gradleLicensePlugin = "0.9.8"
|
||||
kotlin = "2.1.10"
|
||||
coreKtx = "1.15.0"
|
||||
junit = "4.13.2"
|
||||
junitVersion = "1.2.1"
|
||||
espressoCore = "3.6.1"
|
||||
appcompat = "1.7.0"
|
||||
material = "1.12.0"
|
||||
activity = "1.9.3"
|
||||
constraintlayout = "2.2.0"
|
||||
mmkvStatic = "1.3.9"
|
||||
activity = "1.10.1"
|
||||
constraintlayout = "2.2.1"
|
||||
mmkvStatic = "1.3.12"
|
||||
gson = "2.11.0"
|
||||
rxjava = "3.1.9"
|
||||
rxandroid = "3.0.2"
|
||||
rxpermissions = "0.12"
|
||||
quickieFoss = "1.13.1"
|
||||
kotlinx-coroutines-android = "1.10.1"
|
||||
kotlinx-coroutines-core = "1.10.1"
|
||||
swiperefreshlayout = "1.1.0"
|
||||
toastcompat = "1.1.0"
|
||||
editorkit = "2.9.0"
|
||||
quickieBundled = "1.10.0"
|
||||
core = "3.5.3"
|
||||
workRuntimeKtx = "2.10.0"
|
||||
lifecycleViewmodelKtx = "2.8.7"
|
||||
@@ -24,9 +26,12 @@ multidex = "2.0.1"
|
||||
mockitoMockitoInline = "4.0.0"
|
||||
flexbox = "3.0.0"
|
||||
preferenceKtx = "1.2.1"
|
||||
recyclerview = "1.3.2"
|
||||
recyclerview = "1.4.0"
|
||||
[libraries]
|
||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||
androidx-swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "swiperefreshlayout" }
|
||||
desugar_jdk_libs = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugar_jdk_libs" }
|
||||
gradle-license-plugin = { module = "com.jaredsburrows:gradle-license-plugin", version.ref = "gradleLicensePlugin" }
|
||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
||||
@@ -36,14 +41,13 @@ androidx-activity = { group = "androidx.activity", name = "activity", version.re
|
||||
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
|
||||
mmkv-static = { module = "com.tencent:mmkv-static", version.ref = "mmkvStatic" }
|
||||
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
|
||||
rxjava = { module = "io.reactivex.rxjava3:rxjava", version.ref = "rxjava" }
|
||||
rxandroid = { module = "io.reactivex.rxjava3:rxandroid", version.ref = "rxandroid" }
|
||||
rxpermissions = { module = "com.github.tbruyelle:rxpermissions", version.ref = "rxpermissions" }
|
||||
quickie-foss = { module = "com.github.T8RIN.QuickieExtended:quickie-foss", version.ref = "quickieFoss" }
|
||||
kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version = "kotlinx-coroutines-android" }
|
||||
kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version = "kotlinx-coroutines-core" }
|
||||
toastcompat = { module = "me.drakeet.support:toastcompat", version.ref = "toastcompat" }
|
||||
editorkit = { module = "com.blacksquircle.ui:editorkit", version.ref = "editorkit" }
|
||||
language-base = { module = "com.blacksquircle.ui:language-base", version.ref = "editorkit" }
|
||||
language-json = { module = "com.blacksquircle.ui:language-json", version.ref = "editorkit" }
|
||||
quickie-bundled = { module = "io.github.g00fy2.quickie:quickie-bundled", version.ref = "quickieBundled" }
|
||||
core = { module = "com.google.zxing:core", version.ref = "core" }
|
||||
work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "workRuntimeKtx" }
|
||||
work-multiprocess = { module = "androidx.work:work-multiprocess", version.ref = "workRuntimeKtx" }
|
||||
@@ -60,4 +64,3 @@ preference-ktx = { module = "androidx.preference:preference-ktx", version.ref =
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||
android-library = { id = "com.android.library", version.ref = "agp" }
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#Thu Nov 14 12:42:51 BDT 2024
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -16,7 +16,6 @@ dependencyResolutionManagement {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
maven { url = uri("https://jitpack.io") }
|
||||
}
|
||||
}
|
||||
|
||||
1
badvpn
Submodule
1
badvpn
Submodule
Submodule badvpn added at e68480088a
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user