mirror of
https://github.com/wgtunnel/android.git
synced 2026-06-02 08:33:40 +02:00
Compare commits
139 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1952ff1b02 | |||
| 99f6c0bda5 | |||
| 8b828cca55 | |||
| 28b77d33ca | |||
| b0b070f6aa | |||
| 89b1f314b1 | |||
| a223289949 | |||
| 020263eba6 | |||
| d359215b20 | |||
| a6a0bba569 | |||
| f729e924cd | |||
| 3c1d0f893a | |||
| b184331258 | |||
| 36603ab542 | |||
| d0e21247b5 | |||
| 786a8b54ce | |||
| d762fe7283 | |||
| ed9cab3251 | |||
| 8e1e754a87 | |||
| c6eaaa1f37 | |||
| 5077ec9a46 | |||
| e4a808c6ce | |||
| c8b65fb7fa | |||
| 52efdfa288 | |||
| feec7f0ffc | |||
| b63c6a9b73 | |||
| 46975607c4 | |||
| 0c7bcb5453 | |||
| 599bf9c9e0 | |||
| 03345bdf86 | |||
| b07e604003 | |||
| a4f413e79e | |||
| 71ae3864fc | |||
| 008bc476e0 | |||
| 3947b04e21 | |||
| acef2637c8 | |||
| ba8b09cdc9 | |||
| 79e5ba0cb0 | |||
| c8b3af4857 | |||
| 0a3447c63d | |||
| 7f3297db79 | |||
| 165fda0352 | |||
| c4dfb4d591 | |||
| e80e0b7f94 | |||
| aa33aebd2f | |||
| 53b1d03ca8 | |||
| 53f72850e2 | |||
| b8deb7b644 | |||
| d5a3090782 | |||
| 063cbf3ea6 | |||
| b856fc2230 | |||
| 202ac52e25 | |||
| 2b1a8af998 | |||
| 4a45387efd | |||
| fa064ef3a6 | |||
| 3f8894a566 | |||
| b924845835 | |||
| 0f8d7fed97 | |||
| 20ff172055 | |||
| 42671f616f | |||
| ecca99828f | |||
| 6d77ef878d | |||
| 7f40df9d36 | |||
| 9a497f7892 | |||
| dc5a10ebc9 | |||
| d1a4c7f133 | |||
| 93ebf299e6 | |||
| 1d83da9d44 | |||
| d2abaad5b4 | |||
| 8bf88a89bd | |||
| 96cfd04450 | |||
| c5f39ec906 | |||
| 5cd8f9c2f2 | |||
| 2d11712fdc | |||
| 9528850873 | |||
| a0d5647f23 | |||
| 5ff810a6ef | |||
| 3dc314d79a | |||
| 466a5cecbe | |||
| a13d70227f | |||
| 519981d681 | |||
| 0d17bf28e0 | |||
| fc1d4b22a6 | |||
| d2f5be6e19 | |||
| 27a5b6b9f2 | |||
| 2ae048b1de | |||
| a890c83088 | |||
| 3eedbdccba | |||
| 73b3d03a25 | |||
| b74f84abc2 | |||
| bbb056e9d1 | |||
| 0d953a32be | |||
| 3383bb6b45 | |||
| afcb801806 | |||
| 494835a5a4 | |||
| 2f784f563f | |||
| 11d6fd2ed9 | |||
| 0e8138ba6b | |||
| 0eb3719972 | |||
| 196cd34ef7 | |||
| ed42a2f7df | |||
| 9b944bf51e | |||
| b8c792418d | |||
| 6d8a38c65b | |||
| 09c1c68aad | |||
| 36b8353b07 | |||
| f10ed1269e | |||
| e6e35f01d7 | |||
| 35737bdd84 | |||
| 5a6960bffd | |||
| 10cefe2ab0 | |||
| add53834e7 | |||
| f740ec57ea | |||
| 7eadfe2af2 | |||
| 001a4358e1 | |||
| 31abe64513 | |||
| 900aa86c29 | |||
| 264e17f30b | |||
| fbc3ff3276 | |||
| 151cdd4e03 | |||
| f0deb1294a | |||
| 19efb08cb9 | |||
| 9f4b9ca771 | |||
| a60e480be0 | |||
| f28e21ef79 | |||
| b4ca1e63c4 | |||
| e35f4bbb52 | |||
| f0e575da69 | |||
| a6b7d2091a | |||
| 324a1a66c5 | |||
| a05f11739d | |||
| fe519be5cc | |||
| ab0f67c897 | |||
| a5639cd129 | |||
| babbab051f | |||
| 2e984e8b6f | |||
| 044e6da7f5 | |||
| 77aa2c30d7 | |||
| e773238e6b |
@@ -1,4 +1,6 @@
|
||||
name: Build
|
||||
name: build
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -94,10 +96,7 @@ jobs:
|
||||
run: |
|
||||
store_path=${{ env.KEY_STORE_LOCATION }}${{ env.KEY_STORE_FILE }}
|
||||
echo "KEY_STORE_PATH=$store_path" >> $GITHUB_ENV
|
||||
- name: Create service_account.json
|
||||
if: ${{ inputs.build_type != 'debug' }}
|
||||
id: createServiceAccount
|
||||
run: echo '${{ secrets.SERVICE_ACCOUNT_JSON }}' > service_account.json
|
||||
|
||||
- name: Build APK
|
||||
run: |
|
||||
flavor=${{ inputs.flavor }}
|
||||
@@ -123,6 +122,6 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: android_artifacts_${{ inputs.flavor }}
|
||||
path: app/build/outputs/apk/${{ inputs.flavor }}/release/wgtunnel-${{ inputs.flavor }}-release-*.apk
|
||||
path: app/build/outputs/apk/${{ inputs.flavor }}/${{ inputs.build_type }}/wgtunnel-${{ inputs.flavor }}${{ inputs.flavor == 'fdroid' && '-release' || '' }}-*.apk
|
||||
retention-days: 1
|
||||
if-no-files-found: warn
|
||||
@@ -0,0 +1,127 @@
|
||||
name: nightly
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "4 3 * * *"
|
||||
|
||||
jobs:
|
||||
check_commits:
|
||||
name: Check for New Commits
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
has_new_commits: ${{ steps.check.outputs.new_commits }}
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Check for new commits
|
||||
id: check
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
NEW_COMMITS=$(git rev-list --count --after="$(date -Iseconds -d '23 hours ago')" ${{ github.sha }})
|
||||
echo "new_commits=$NEW_COMMITS" >> $GITHUB_OUTPUT
|
||||
|
||||
build-standalone-nightly:
|
||||
uses: ./.github/workflows/build.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
build_type: "nightly"
|
||||
flavor: standalone
|
||||
|
||||
publish:
|
||||
needs:
|
||||
- check_commits
|
||||
- build-standalone-nightly
|
||||
if: ${{ needs.check_commits.outputs.has_new_commits > 0 && inputs.release_type != 'none' }}
|
||||
name: publish-nightly
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
sudo apt update && sudo apt install -y gh apksigner
|
||||
- name: Set latest tag
|
||||
uses: rickstaa/action-create-tag@v1
|
||||
id: tag_creation
|
||||
with:
|
||||
tag: "latest"
|
||||
message: "Automated tag for HEAD commit"
|
||||
force_push_tag: true
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag_exists_error: false
|
||||
|
||||
- name: Generate Changelog
|
||||
id: changelog
|
||||
uses: requarks/changelog-action@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
toTag: "nightly"
|
||||
fromTag: "latest"
|
||||
writeToFile: false
|
||||
|
||||
- name: Make download dir
|
||||
run: mkdir ${{ github.workspace }}/temp
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: android_artifacts_*
|
||||
path: ${{ github.workspace }}/temp
|
||||
|
||||
- name: Set release notes
|
||||
run: |
|
||||
echo "RELEASE_NOTES=Nightly build for the latest development version of the app." >> $GITHUB_ENV
|
||||
|
||||
- name: Delete previous nightly version
|
||||
uses: ClementTsang/delete-tag-and-release@v0.4.0
|
||||
with:
|
||||
tag_name: "nightly"
|
||||
delete_release: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Get checksum
|
||||
id: checksum
|
||||
run: |
|
||||
file_path=$(find ${{ github.workspace }}/temp -type f -iname "*.apk" | head -n 1)
|
||||
if [ -z "$file_path" ]; then
|
||||
echo "No APK file found"
|
||||
exit 1
|
||||
fi
|
||||
checksum=$(apksigner verify --print-certs "$file_path" | grep -Po "(?<=SHA-256 digest:) .*" | tr -d "[:blank:]")
|
||||
echo "checksum=$checksum" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create nightly release
|
||||
id: create_release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
body: |
|
||||
${{ env.RELEASE_NOTES }}
|
||||
|
||||
SHA-256 fingerprints for the 4096-bit signing certificate:
|
||||
```sh
|
||||
${{ steps.checksum.outputs.checksum }}
|
||||
```
|
||||
|
||||
To verify fingerprint:
|
||||
```sh
|
||||
apksigner verify --print-certs [path to APK file] | grep SHA-256
|
||||
```
|
||||
|
||||
### Changelog
|
||||
${{ steps.changelog.outputs.changes }}
|
||||
tag_name: nightly
|
||||
name: nightly
|
||||
draft: false
|
||||
prerelease: true
|
||||
make_latest: false
|
||||
files: |
|
||||
${{ github.workspace }}/temp/**/*.apk
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -1,4 +1,6 @@
|
||||
name: on-pr
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
name: publish
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "4 3 * * *"
|
||||
push:
|
||||
tags:
|
||||
- '[0-9]*.[0-9]*.[0-9]*'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
track:
|
||||
@@ -22,14 +26,13 @@ on:
|
||||
options:
|
||||
- none
|
||||
- prerelease
|
||||
- nightly
|
||||
- release
|
||||
default: release
|
||||
required: true
|
||||
tag_name:
|
||||
description: "Tag name for release"
|
||||
required: false
|
||||
default: nightly
|
||||
default: 1.1.1
|
||||
flavor:
|
||||
type: choice
|
||||
description: "Product flavor"
|
||||
@@ -46,71 +49,37 @@ on:
|
||||
required: false
|
||||
default: standalone
|
||||
|
||||
env:
|
||||
UPLOAD_DIR_ANDROID: android_artifacts
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
check_commits:
|
||||
name: Check for New Commits
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
has_new_commits: ${{ steps.check.outputs.new_commits }}
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Check for new commits
|
||||
id: check
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.PAT }}
|
||||
run: |
|
||||
NEW_COMMITS=$(git rev-list --count --after="$(date -Iseconds -d '23 hours ago')" ${{ github.sha }})
|
||||
echo "new_commits=$NEW_COMMITS" >> $GITHUB_OUTPUT
|
||||
|
||||
build-fdroid:
|
||||
if: ${{ inputs.release_type == 'release' || inputs.flavor == 'fdroid' }}
|
||||
if: ${{ github.event_name == 'push' || inputs.release_type == 'release' || inputs.flavor == 'fdroid' }}
|
||||
uses: ./.github/workflows/build.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
build_type: ${{ inputs.release_type == '' && 'nightly' || inputs.release_type }}
|
||||
build_type: ${{ github.event_name == 'push' && 'release' || inputs.release_type }}
|
||||
flavor: fdroid
|
||||
|
||||
build-standalone:
|
||||
if: ${{ inputs.release_type == 'release' || inputs.release_type == 'nightly' || inputs.release_type == 'prerelease' || inputs.flavor == 'standalone' }}
|
||||
if: ${{ github.event_name == 'push' || inputs.release_type == 'release' || inputs.release_type == 'prerelease' || inputs.flavor == 'standalone' }}
|
||||
uses: ./.github/workflows/build.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
build_type: ${{ inputs.release_type == '' && 'nightly' || inputs.release_type }}
|
||||
build_type: ${{ github.event_name == 'push' && 'release' || inputs.release_type }}
|
||||
flavor: standalone
|
||||
|
||||
publish:
|
||||
needs:
|
||||
- check_commits
|
||||
- build-standalone
|
||||
if: ${{ needs.check_commits.outputs.has_new_commits > 0 && inputs.release_type != 'none' }}
|
||||
name: publish-github
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: main
|
||||
ref: ${{ github.event_name == 'push' && github.ref || 'main' }}
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
sudo apt update && sudo apt install -y gh apksigner
|
||||
- name: Set TAG_NAME
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
echo "TAG_NAME=${{ github.event.inputs.tag_name }}" >> $GITHUB_ENV
|
||||
elif [ "${{ github.event_name }}" = "schedule" ]; then
|
||||
echo "TAG_NAME=nightly" >> $GITHUB_ENV
|
||||
echo "RELEASE_TYPE=nightly" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Set latest tag
|
||||
uses: rickstaa/action-create-tag@v1
|
||||
id: tag_creation
|
||||
@@ -126,24 +95,28 @@ jobs:
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
latest: true
|
||||
|
||||
- name: Generate Changelog
|
||||
id: changelog
|
||||
uses: requarks/changelog-action@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
toTag: ${{ github.event_name == 'schedule' && 'nightly' || steps.latest_release.outputs.tag_name }}
|
||||
toTag: ${{ steps.latest_release.outputs.tag_name }}
|
||||
fromTag: "latest"
|
||||
writeToFile: false
|
||||
|
||||
- name: Make download dir
|
||||
run: mkdir ${{ github.workspace }}/temp
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: android_artifacts_*
|
||||
path: ${{ github.workspace }}/temp
|
||||
merge-multiple: true
|
||||
|
||||
- name: Set version release notes
|
||||
if: ${{ inputs.release_type == 'release' }}
|
||||
if: ${{ github.event_name == 'push' || inputs.release_type == 'release' }}
|
||||
run: |
|
||||
VERSION_NAME=$(grep "const val VERSION_NAME" buildSrc/src/main/kotlin/Constants.kt | awk -F'"' '{print $2}')
|
||||
RELEASE_NOTES="$(cat ${{ github.workspace }}/fastlane/metadata/android/en-US/changelogs/${VERSION_NAME}.txt || echo "No changelog found for ${VERSION_NAME}")"
|
||||
@@ -151,36 +124,22 @@ jobs:
|
||||
echo "$RELEASE_NOTES" >> $GITHUB_ENV
|
||||
echo "EOF" >> $GITHUB_ENV
|
||||
|
||||
- name: On nightly release notes
|
||||
if: ${{ contains(env.TAG_NAME, 'nightly') }}
|
||||
run: |
|
||||
echo "RELEASE_NOTES=Nightly build for the latest development version of the app." >> $GITHUB_ENV
|
||||
|
||||
- name: On prerelease release notes
|
||||
if: ${{ inputs.release_type == 'prerelease' }}
|
||||
if: ${{ github.event_name != 'push' && inputs.release_type == 'prerelease' }}
|
||||
run: |
|
||||
echo "RELEASE_NOTES=Testing version of app for specific feature." >> $GITHUB_ENV
|
||||
|
||||
- name: Delete previous release
|
||||
if: ${{ contains(env.TAG_NAME, 'nightly') || inputs.release_type == 'prerelease' }}
|
||||
uses: ClementTsang/delete-tag-and-release@v0.4.0
|
||||
with:
|
||||
tag_name: ${{ env.TAG_NAME }}
|
||||
delete_release: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Get checksums
|
||||
- name: Get checksum
|
||||
id: checksum
|
||||
run: |
|
||||
checksums=""
|
||||
for file_path in $(find ${{ github.workspace }}/temp -type f -iname "*.apk"); do
|
||||
checksum=$(apksigner verify -print-certs $file_path | grep -Po "(?<=SHA-256 digest:) .*" | tr -d "[:blank:]")
|
||||
checksums="$checksums\n$file_path: $checksum"
|
||||
done
|
||||
echo "checksum<<EOF" >> $GITHUB_OUTPUT
|
||||
echo -e "$checksums" >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
file_path=$(find ${{ github.workspace }}/temp -type f -iname "*.apk" | head -n 1)
|
||||
if [ -z "$file_path" ]; then
|
||||
echo "No APK file found"
|
||||
exit 1
|
||||
fi
|
||||
checksum=$(apksigner verify --print-certs "$file_path" | grep -Po "(?<=SHA-256 digest:) .*" | tr -d "[:blank:]")
|
||||
echo "checksum=$checksum" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: softprops/action-gh-release@v2
|
||||
@@ -200,13 +159,13 @@ jobs:
|
||||
|
||||
### Changelog
|
||||
${{ steps.changelog.outputs.changes }}
|
||||
tag_name: ${{ env.TAG_NAME }}
|
||||
name: ${{ env.TAG_NAME }}
|
||||
tag_name: ${{ github.event_name == 'push' && github.ref_name || github.event.inputs.tag_name }}
|
||||
name: ${{ github.event_name == 'push' && github.ref_name || github.event.inputs.tag_name }}
|
||||
draft: false
|
||||
prerelease: ${{ inputs.release_type == 'prerelease' || inputs.release_type == '' || inputs.release_type == 'nightly' }}
|
||||
make_latest: ${{ inputs.release_type == 'release' }}
|
||||
prerelease: ${{ github.event_name != 'push' && inputs.release_type == 'prerelease' }}
|
||||
make_latest: ${{ github.event_name == 'push' || inputs.release_type == 'release' }}
|
||||
files: |
|
||||
${{ github.workspace }}/temp/*
|
||||
${{ github.workspace }}/temp/**/*.apk
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -214,7 +173,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build-fdroid
|
||||
if: inputs.release_type == 'release'
|
||||
if: ${{ github.event_name == 'push' || inputs.release_type == 'release' }}
|
||||
steps:
|
||||
- name: Dispatch update for fdroid repo
|
||||
uses: peter-evans/repository-dispatch@v3
|
||||
@@ -224,7 +183,7 @@ jobs:
|
||||
event-type: fdroid-update
|
||||
|
||||
publish-play:
|
||||
if: ${{ inputs.track != 'none' && inputs.track != '' }}
|
||||
if: ${{ github.event_name == 'push' || inputs.track != 'none' }}
|
||||
name: Publish to Google Play
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -274,4 +233,6 @@ jobs:
|
||||
bundler-cache: true
|
||||
|
||||
- name: Distribute app to Prod track 🚀
|
||||
run: (cd ${{ github.workspace }} && bundle install && bundle exec fastlane ${{ inputs.track }})
|
||||
run: |
|
||||
track=${{ github.event_name == 'push' && 'production' || inputs.track }}
|
||||
(cd ${{ github.workspace }} && bundle install && bundle exec fastlane $track)
|
||||
+15
-4
@@ -110,9 +110,9 @@ android {
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
isCoreLibraryDesugaringEnabled = true
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
isCoreLibraryDesugaringEnabled = true
|
||||
}
|
||||
kotlinOptions { jvmTarget = Constants.JVM_TARGET }
|
||||
buildFeatures {
|
||||
@@ -123,8 +123,7 @@ android {
|
||||
|
||||
licensee {
|
||||
Constants.allowedLicenses.forEach { allow(it) }
|
||||
allowUrl(Constants.XZING_LICENSE_URL)
|
||||
allowUrl("https://rafaellins.mit-license.org/2021/")
|
||||
Constants.allowedLicenseUrls.forEach { allowUrl(it) }
|
||||
}
|
||||
|
||||
applicationVariants.all {
|
||||
@@ -198,6 +197,7 @@ dependencies {
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
|
||||
implementation(libs.zxing.android.embedded)
|
||||
|
||||
implementation(libs.material.icons.extended)
|
||||
|
||||
implementation(libs.androidx.biometric.ktx)
|
||||
@@ -210,7 +210,7 @@ dependencies {
|
||||
implementation(libs.androidx.work.runtime)
|
||||
implementation(libs.androidx.hilt.work)
|
||||
|
||||
implementation(libs.qrcode.kotlin)
|
||||
implementation(libs.qrose)
|
||||
implementation(libs.semver4j)
|
||||
|
||||
implementation(libs.ktor.client.core)
|
||||
@@ -219,6 +219,10 @@ dependencies {
|
||||
implementation(libs.ktor.client.content.negotiation)
|
||||
implementation(libs.ktor.serialization.kotlinx.json)
|
||||
implementation(libs.slf4j.android)
|
||||
|
||||
// shizuku
|
||||
implementation(libs.shizuku.api)
|
||||
implementation(libs.shizuku.provider)
|
||||
}
|
||||
|
||||
tasks.register<Copy>("copyLicenseeJsonToAssets") {
|
||||
@@ -231,3 +235,10 @@ tasks.register<Copy>("copyLicenseeJsonToAssets") {
|
||||
}
|
||||
|
||||
tasks.named("preBuild") { dependsOn("copyLicenseeJsonToAssets") }
|
||||
|
||||
// https://gist.github.com/obfusk/61046e09cee352ae6dd109911534b12e#fix-proposed-by-linsui-disable-baseline-profiles
|
||||
tasks.whenTaskAdded {
|
||||
if (name.contains("ArtProfile")) {
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,9 +155,7 @@
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "TunnelConfig",
|
||||
@@ -227,21 +225,18 @@
|
||||
"fieldPath": "pingInterval",
|
||||
"columnName": "ping_interval",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false,
|
||||
"defaultValue": "null"
|
||||
},
|
||||
{
|
||||
"fieldPath": "pingCooldown",
|
||||
"columnName": "ping_cooldown",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false,
|
||||
"defaultValue": "null"
|
||||
},
|
||||
{
|
||||
"fieldPath": "pingIp",
|
||||
"columnName": "ping_ip",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false,
|
||||
"defaultValue": "null"
|
||||
},
|
||||
{
|
||||
@@ -275,11 +270,9 @@
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_TunnelConfig_name` ON `${TABLE_NAME}` (`name`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
]
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ae51793c4d09ea3194ecd26f0606f35c')"
|
||||
|
||||
@@ -0,0 +1,295 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 17,
|
||||
"identityHash": "380d82359c99933cc9ce783347c4ec31",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "Settings",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_tunnel_enabled` INTEGER NOT NULL, `is_tunnel_on_mobile_data_enabled` INTEGER NOT NULL, `trusted_network_ssids` TEXT NOT NULL, `is_always_on_vpn_enabled` INTEGER NOT NULL, `is_tunnel_on_ethernet_enabled` INTEGER NOT NULL, `is_shortcuts_enabled` INTEGER NOT NULL DEFAULT false, `is_tunnel_on_wifi_enabled` INTEGER NOT NULL DEFAULT false, `is_kernel_enabled` INTEGER NOT NULL DEFAULT false, `is_restore_on_boot_enabled` INTEGER NOT NULL DEFAULT false, `is_multi_tunnel_enabled` INTEGER NOT NULL DEFAULT false, `is_ping_enabled` INTEGER NOT NULL DEFAULT false, `is_amnezia_enabled` INTEGER NOT NULL DEFAULT false, `is_wildcards_enabled` INTEGER NOT NULL DEFAULT false, `is_stop_on_no_internet_enabled` INTEGER NOT NULL DEFAULT false, `is_vpn_kill_switch_enabled` INTEGER NOT NULL DEFAULT false, `is_kernel_kill_switch_enabled` INTEGER NOT NULL DEFAULT false, `is_lan_on_kill_switch_enabled` INTEGER NOT NULL DEFAULT false, `debounce_delay_seconds` INTEGER NOT NULL DEFAULT 3, `is_disable_kill_switch_on_trusted_enabled` INTEGER NOT NULL DEFAULT false, `is_tunnel_on_unsecure_enabled` INTEGER NOT NULL DEFAULT false, `split_tunnel_apps` TEXT NOT NULL DEFAULT '', `wifi_detection_method` INTEGER NOT NULL DEFAULT 0)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isAutoTunnelEnabled",
|
||||
"columnName": "is_tunnel_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isTunnelOnMobileDataEnabled",
|
||||
"columnName": "is_tunnel_on_mobile_data_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "trustedNetworkSSIDs",
|
||||
"columnName": "trusted_network_ssids",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isAlwaysOnVpnEnabled",
|
||||
"columnName": "is_always_on_vpn_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isTunnelOnEthernetEnabled",
|
||||
"columnName": "is_tunnel_on_ethernet_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isShortcutsEnabled",
|
||||
"columnName": "is_shortcuts_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isTunnelOnWifiEnabled",
|
||||
"columnName": "is_tunnel_on_wifi_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isKernelEnabled",
|
||||
"columnName": "is_kernel_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isRestoreOnBootEnabled",
|
||||
"columnName": "is_restore_on_boot_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isMultiTunnelEnabled",
|
||||
"columnName": "is_multi_tunnel_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isPingEnabled",
|
||||
"columnName": "is_ping_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isAmneziaEnabled",
|
||||
"columnName": "is_amnezia_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isWildcardsEnabled",
|
||||
"columnName": "is_wildcards_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isStopOnNoInternetEnabled",
|
||||
"columnName": "is_stop_on_no_internet_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isVpnKillSwitchEnabled",
|
||||
"columnName": "is_vpn_kill_switch_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isKernelKillSwitchEnabled",
|
||||
"columnName": "is_kernel_kill_switch_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isLanOnKillSwitchEnabled",
|
||||
"columnName": "is_lan_on_kill_switch_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "debounceDelaySeconds",
|
||||
"columnName": "debounce_delay_seconds",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "3"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isDisableKillSwitchOnTrustedEnabled",
|
||||
"columnName": "is_disable_kill_switch_on_trusted_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isTunnelOnUnsecureEnabled",
|
||||
"columnName": "is_tunnel_on_unsecure_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "splitTunnelApps",
|
||||
"columnName": "split_tunnel_apps",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true,
|
||||
"defaultValue": "''"
|
||||
},
|
||||
{
|
||||
"fieldPath": "wifiDetectionMethod",
|
||||
"columnName": "wifi_detection_method",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "TunnelConfig",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `wg_quick` TEXT NOT NULL, `tunnel_networks` TEXT NOT NULL DEFAULT '', `is_mobile_data_tunnel` INTEGER NOT NULL DEFAULT false, `is_primary_tunnel` INTEGER NOT NULL DEFAULT false, `am_quick` TEXT NOT NULL DEFAULT '', `is_Active` INTEGER NOT NULL DEFAULT false, `is_ping_enabled` INTEGER NOT NULL DEFAULT false, `ping_interval` INTEGER DEFAULT null, `ping_cooldown` INTEGER DEFAULT null, `ping_ip` TEXT DEFAULT null, `is_ethernet_tunnel` INTEGER NOT NULL DEFAULT false, `is_ipv4_preferred` INTEGER NOT NULL DEFAULT true)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "wgQuick",
|
||||
"columnName": "wg_quick",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tunnelNetworks",
|
||||
"columnName": "tunnel_networks",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true,
|
||||
"defaultValue": "''"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isMobileDataTunnel",
|
||||
"columnName": "is_mobile_data_tunnel",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isPrimaryTunnel",
|
||||
"columnName": "is_primary_tunnel",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "amQuick",
|
||||
"columnName": "am_quick",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true,
|
||||
"defaultValue": "''"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isActive",
|
||||
"columnName": "is_Active",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isPingEnabled",
|
||||
"columnName": "is_ping_enabled",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "pingInterval",
|
||||
"columnName": "ping_interval",
|
||||
"affinity": "INTEGER",
|
||||
"defaultValue": "null"
|
||||
},
|
||||
{
|
||||
"fieldPath": "pingCooldown",
|
||||
"columnName": "ping_cooldown",
|
||||
"affinity": "INTEGER",
|
||||
"defaultValue": "null"
|
||||
},
|
||||
{
|
||||
"fieldPath": "pingIp",
|
||||
"columnName": "ping_ip",
|
||||
"affinity": "TEXT",
|
||||
"defaultValue": "null"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isEthernetTunnel",
|
||||
"columnName": "is_ethernet_tunnel",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"fieldPath": "isIpv4Preferred",
|
||||
"columnName": "is_ipv4_preferred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "true"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_TunnelConfig_name",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"name"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_TunnelConfig_name` ON `${TABLE_NAME}` (`name`)"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '380d82359c99933cc9ce783347c4ec31')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,10 @@
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.App.Start"
|
||||
tools:targetApi="tiramisu">
|
||||
<activity
|
||||
android:name="com.journeyapps.barcodescanner.CaptureActivity"
|
||||
android:screenOrientation="portrait"
|
||||
tools:replace="screenOrientation" />
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
@@ -78,10 +82,6 @@
|
||||
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="com.journeyapps.barcodescanner.CaptureActivity"
|
||||
android:screenOrientation="portrait"
|
||||
tools:replace="screenOrientation" />
|
||||
|
||||
<activity
|
||||
android:name=".core.shortcut.ShortcutsActivity"
|
||||
|
||||
@@ -22,7 +22,6 @@ import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.icons.rounded.Settings
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -50,11 +49,11 @@ import com.zaneschepke.wireguardautotunnel.ui.navigation.components.DynamicTopAp
|
||||
import com.zaneschepke.wireguardautotunnel.ui.navigation.components.currentNavBackStackEntryAsNavBarState
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.AutoTunnelScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.advanced.AutoTunnelAdvancedScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.detection.WifiDetectionMethodScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.disclosure.LocationDisclosureScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.main.MainScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.main.autotunnel.TunnelAutoTunnelScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.main.config.ConfigScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.main.scanner.ScannerScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.main.splittunnel.SplitTunnelScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.main.tunneloptions.TunnelOptionsScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.pin.PinLockScreen
|
||||
@@ -88,6 +87,8 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
private var lastLocationPermissionState: Boolean? = null
|
||||
|
||||
val REQUEST_CODE = 123
|
||||
|
||||
@SuppressLint("BatteryLife")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
enableEdgeToEdge(
|
||||
@@ -286,6 +287,9 @@ class MainActivity : AppCompatActivity() {
|
||||
composable<Route.AutoTunnelAdvanced> {
|
||||
AutoTunnelAdvancedScreen(appUiState, viewModel)
|
||||
}
|
||||
composable<Route.WifiDetectionMethod> {
|
||||
WifiDetectionMethodScreen(appUiState, viewModel)
|
||||
}
|
||||
composable<Route.Logs> { LogsScreen(appViewState, viewModel) }
|
||||
composable<Route.Config> { backStack ->
|
||||
val args = backStack.toRoute<Route.Config>()
|
||||
@@ -298,11 +302,10 @@ class MainActivity : AppCompatActivity() {
|
||||
appUiState.tunnels
|
||||
.firstOrNull { it.id == args.id }
|
||||
?.let { config ->
|
||||
TunnelOptionsScreen(config, viewModel)
|
||||
TunnelOptionsScreen(config, viewModel, appViewState)
|
||||
}
|
||||
}
|
||||
composable<Route.Lock> { PinLockScreen(viewModel) }
|
||||
composable<Route.Scanner> { ScannerScreen(viewModel) }
|
||||
composable<Route.KillSwitch> {
|
||||
KillSwitchScreen(appUiState, viewModel)
|
||||
}
|
||||
|
||||
+3
-15
@@ -14,9 +14,9 @@ import com.zaneschepke.wireguardautotunnel.core.notification.NotificationManager
|
||||
import com.zaneschepke.wireguardautotunnel.core.notification.WireGuardNotification
|
||||
import com.zaneschepke.wireguardautotunnel.core.tunnel.TunnelManager
|
||||
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.NotificationAction
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelStatus
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.TunnelRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
||||
import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||
@@ -24,22 +24,10 @@ import com.zaneschepke.wireguardautotunnel.util.extensions.distinctByKeys
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
|
||||
@AndroidEntryPoint
|
||||
|
||||
+44
-25
@@ -15,12 +15,12 @@ import com.zaneschepke.wireguardautotunnel.core.notification.WireGuardNotificati
|
||||
import com.zaneschepke.wireguardautotunnel.core.service.ServiceManager
|
||||
import com.zaneschepke.wireguardautotunnel.core.tunnel.TunnelManager
|
||||
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppSettings
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendState
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.NotificationAction
|
||||
import com.zaneschepke.wireguardautotunnel.domain.events.AutoTunnelEvent
|
||||
import com.zaneschepke.wireguardautotunnel.domain.events.KillSwitchEvent
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.AppSettings
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.AutoTunnelState
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.NetworkState
|
||||
@@ -29,20 +29,8 @@ import com.zaneschepke.wireguardautotunnel.util.extensions.Tunnels
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import timber.log.Timber
|
||||
|
||||
@AndroidEntryPoint
|
||||
@@ -72,6 +60,8 @@ class AutoTunnelService : LifecycleService() {
|
||||
|
||||
private val binder = LocalBinder(this)
|
||||
|
||||
private var isServiceRunning = false
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
launchWatcherNotification()
|
||||
@@ -90,6 +80,8 @@ class AutoTunnelService : LifecycleService() {
|
||||
}
|
||||
|
||||
fun start() {
|
||||
if (isServiceRunning) return
|
||||
isServiceRunning = true
|
||||
kotlin
|
||||
.runCatching {
|
||||
launchWatcherNotification()
|
||||
@@ -102,6 +94,7 @@ class AutoTunnelService : LifecycleService() {
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
isServiceRunning = false
|
||||
wakeLock?.let { if (it.isHeld) it.release() }
|
||||
stopSelf()
|
||||
}
|
||||
@@ -261,18 +254,44 @@ class AutoTunnelService : LifecycleService() {
|
||||
lifecycleScope.launch(ioDispatcher) {
|
||||
Timber.i("Starting auto-tunnel network event watcher")
|
||||
val settings = appDataRepository.get().settings.get()
|
||||
Timber.d("Starting with debounce delay of: ${settings.debounceDelaySeconds} seconds")
|
||||
|
||||
var reevaluationJob: Job? = null
|
||||
|
||||
autoTunnelStateFlow.debounce(settings.debounceDelayMillis()).collect { watcherState ->
|
||||
if (watcherState == defaultState) return@collect
|
||||
Timber.d("New auto tunnel state emitted ${watcherState.networkState}")
|
||||
when (val event = watcherState.asAutoTunnelEvent()) {
|
||||
is AutoTunnelEvent.Start ->
|
||||
(event.tunnelConf ?: appDataRepository.get().getPrimaryOrFirstTunnel())
|
||||
?.let { tunnelManager.startTunnel(it) }
|
||||
// TODO improve this to target specific tunnels to better support multi-tunnel
|
||||
is AutoTunnelEvent.Stop -> tunnelManager.stopTunnel()
|
||||
AutoTunnelEvent.DoNothing -> Timber.i("Auto-tunneling: no condition met")
|
||||
reevaluationJob?.cancel()
|
||||
handleAutoTunnelEvent(watcherState)
|
||||
|
||||
// schedule one-time re-evaluation
|
||||
reevaluationJob = launch {
|
||||
delay(REEVALUATE_CHECK_DELAY)
|
||||
if (watcherState != defaultState) {
|
||||
Timber.d("Re-evaluating auto-tunnel state..")
|
||||
handleAutoTunnelEvent(watcherState)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun handleAutoTunnelEvent(watcherState: AutoTunnelState) {
|
||||
Timber.i("Auto-tunnel settings: ${watcherState.settings.toAutoTunnelStateString()}")
|
||||
Timber.i("Auto-tunnel network state: ${watcherState.networkState}")
|
||||
when (
|
||||
val event =
|
||||
watcherState.asAutoTunnelEvent().also {
|
||||
Timber.i("Auto-tunnel event: ${it.javaClass.simpleName}")
|
||||
}
|
||||
) {
|
||||
is AutoTunnelEvent.Start ->
|
||||
(event.tunnelConf ?: appDataRepository.get().getPrimaryOrFirstTunnel())?.let {
|
||||
tunnelManager.startTunnel(it)
|
||||
}
|
||||
is AutoTunnelEvent.Stop -> tunnelManager.stopTunnel()
|
||||
AutoTunnelEvent.DoNothing -> Timber.i("Auto-tunneling: nothing to do")
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val REEVALUATE_CHECK_DELAY = 5_000L
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
|
||||
import com.zaneschepke.wireguardautotunnel.core.service.ServiceManager
|
||||
import com.zaneschepke.wireguardautotunnel.core.tunnel.TunnelManager
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@@ -3,9 +3,9 @@ package com.zaneschepke.wireguardautotunnel.core.tunnel
|
||||
import com.wireguard.android.backend.Tunnel
|
||||
import com.zaneschepke.wireguardautotunnel.core.service.ServiceManager
|
||||
import com.zaneschepke.wireguardautotunnel.di.ApplicationScope
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendError
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelStatus
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelStatistics
|
||||
@@ -114,16 +114,16 @@ abstract class BaseTunnel(
|
||||
if (this@BaseTunnel is UserspaceTunnel) stopActiveTunnels()
|
||||
tunMutex.withLock {
|
||||
tunThreads[tunnelConf.id] = thread {
|
||||
runBlocking {
|
||||
try {
|
||||
try {
|
||||
runBlocking {
|
||||
Timber.d("Starting tunnel ${tunnelConf.id}...")
|
||||
startTunnelInner(tunnelConf)
|
||||
Timber.d("Started complete for tunnel ${tunnelConf.name}...")
|
||||
} catch (e: InterruptedException) {
|
||||
Timber.w(
|
||||
"Tunnel start has been interrupted as ${tunnelConf.name} failed to start"
|
||||
)
|
||||
}
|
||||
} catch (e: InterruptedException) {
|
||||
Timber.w(
|
||||
"Tunnel start has been interrupted as ${tunnelConf.name} failed to start"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.zaneschepke.wireguardautotunnel.core.tunnel
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelStatus
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ import com.wireguard.android.backend.BackendException
|
||||
import com.wireguard.android.backend.Tunnel
|
||||
import com.zaneschepke.wireguardautotunnel.core.service.ServiceManager
|
||||
import com.zaneschepke.wireguardautotunnel.di.ApplicationScope
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendState
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelStatus
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelStatistics
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.WireGuardStatistics
|
||||
|
||||
@@ -4,10 +4,10 @@ import com.zaneschepke.wireguardautotunnel.di.ApplicationScope
|
||||
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
||||
import com.zaneschepke.wireguardautotunnel.di.Kernel
|
||||
import com.zaneschepke.wireguardautotunnel.di.Userspace
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendError
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendState
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelStatus
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelStatistics
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.zaneschepke.wireguardautotunnel.core.tunnel
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendError
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendState
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelStatus
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelStatistics
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
+1
-1
@@ -2,9 +2,9 @@ package com.zaneschepke.wireguardautotunnel.core.tunnel
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.core.service.ServiceManager
|
||||
import com.zaneschepke.wireguardautotunnel.di.ApplicationScope
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendState
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelStatus
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.AmneziaStatistics
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelStatistics
|
||||
|
||||
@@ -8,12 +8,12 @@ import androidx.room.TypeConverters
|
||||
import androidx.room.migration.AutoMigrationSpec
|
||||
import com.zaneschepke.wireguardautotunnel.data.dao.SettingsDao
|
||||
import com.zaneschepke.wireguardautotunnel.data.dao.TunnelConfigDao
|
||||
import com.zaneschepke.wireguardautotunnel.data.model.Settings
|
||||
import com.zaneschepke.wireguardautotunnel.data.model.TunnelConfig
|
||||
import com.zaneschepke.wireguardautotunnel.data.entity.Settings
|
||||
import com.zaneschepke.wireguardautotunnel.data.entity.TunnelConfig
|
||||
|
||||
@Database(
|
||||
entities = [Settings::class, TunnelConfig::class],
|
||||
version = 16,
|
||||
version = 17,
|
||||
autoMigrations =
|
||||
[
|
||||
AutoMigration(from = 1, to = 2),
|
||||
@@ -31,10 +31,11 @@ import com.zaneschepke.wireguardautotunnel.data.model.TunnelConfig
|
||||
AutoMigration(from = 13, to = 14),
|
||||
AutoMigration(from = 14, to = 15),
|
||||
AutoMigration(from = 15, to = 16),
|
||||
AutoMigration(from = 16, to = 17, spec = WifiDetectionMigration::class),
|
||||
],
|
||||
exportSchema = true,
|
||||
)
|
||||
@TypeConverters(DatabaseListConverters::class)
|
||||
@TypeConverters(DatabaseConverters::class)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
abstract fun settingDao(): SettingsDao
|
||||
|
||||
@@ -47,3 +48,6 @@ class RemoveLegacySettingColumnsMigration : AutoMigrationSpec
|
||||
|
||||
@DeleteColumn(tableName = "Settings", columnName = "is_auto_tunnel_paused")
|
||||
class RemoveTunnelPauseMigration : AutoMigrationSpec
|
||||
|
||||
@DeleteColumn(tableName = "Settings", columnName = "is_wifi_by_shell_enabled")
|
||||
class WifiDetectionMigration : AutoMigrationSpec
|
||||
|
||||
+8
-1
@@ -1,9 +1,10 @@
|
||||
package com.zaneschepke.wireguardautotunnel.data
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import com.zaneschepke.wireguardautotunnel.data.entity.Settings
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
class DatabaseListConverters {
|
||||
class DatabaseConverters {
|
||||
@TypeConverter
|
||||
fun listToString(value: MutableList<String>): String {
|
||||
return Json.encodeToString(value)
|
||||
@@ -20,4 +21,10 @@ class DatabaseListConverters {
|
||||
Json.decodeFromString<MutableList<String>>(json)
|
||||
}
|
||||
}
|
||||
|
||||
@TypeConverter fun fromStatus(status: Settings.WifiDetectionMethod): Int = status.value
|
||||
|
||||
@TypeConverter
|
||||
fun toStatus(value: Int): Settings.WifiDetectionMethod =
|
||||
Settings.WifiDetectionMethod.fromValue(value)
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import com.zaneschepke.wireguardautotunnel.data.model.Settings
|
||||
import com.zaneschepke.wireguardautotunnel.data.entity.Settings
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
|
||||
@@ -5,7 +5,7 @@ import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import com.zaneschepke.wireguardautotunnel.data.model.TunnelConfig
|
||||
import com.zaneschepke.wireguardautotunnel.data.entity.TunnelConfig
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.TunnelConfigs
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.zaneschepke.wireguardautotunnel.data.model
|
||||
package com.zaneschepke.wireguardautotunnel.data.entity
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.zaneschepke.wireguardautotunnel.data.entity
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.ui.theme.Theme
|
||||
|
||||
data class GeneralState(
|
||||
val isLocationDisclosureShown: Boolean = LOCATION_DISCLOSURE_SHOWN_DEFAULT,
|
||||
val isBatteryOptimizationDisableShown: Boolean = BATTERY_OPTIMIZATION_DISABLE_SHOWN_DEFAULT,
|
||||
val isPinLockEnabled: Boolean = PIN_LOCK_ENABLED_DEFAULT,
|
||||
val expandedTunnelIds: List<Int> = emptyList(),
|
||||
val isLocalLogsEnabled: Boolean = IS_LOGS_ENABLED_DEFAULT,
|
||||
val isRemoteControlEnabled: Boolean = IS_REMOTE_CONTROL_ENABLED,
|
||||
val remoteKey: String? = null,
|
||||
val locale: String? = null,
|
||||
val theme: Theme = Theme.AUTOMATIC,
|
||||
) {
|
||||
|
||||
companion object {
|
||||
const val LOCATION_DISCLOSURE_SHOWN_DEFAULT = false
|
||||
const val BATTERY_OPTIMIZATION_DISABLE_SHOWN_DEFAULT = false
|
||||
const val PIN_LOCK_ENABLED_DEFAULT = false
|
||||
const val IS_LOGS_ENABLED_DEFAULT = false
|
||||
const val IS_REMOTE_CONTROL_ENABLED = false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.zaneschepke.wireguardautotunnel.data.entity
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class GitHubRelease(
|
||||
@SerialName("tag_name") val tagName: String,
|
||||
val name: String?,
|
||||
val body: String?,
|
||||
val assets: List<Asset>,
|
||||
)
|
||||
+15
-56
@@ -1,9 +1,8 @@
|
||||
package com.zaneschepke.wireguardautotunnel.data.model
|
||||
package com.zaneschepke.wireguardautotunnel.data.entity
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppSettings
|
||||
|
||||
@Entity
|
||||
data class Settings(
|
||||
@@ -32,8 +31,6 @@ data class Settings(
|
||||
val isAmneziaEnabled: Boolean = false,
|
||||
@ColumnInfo(name = "is_wildcards_enabled", defaultValue = "false")
|
||||
val isWildcardsEnabled: Boolean = false,
|
||||
@ColumnInfo(name = "is_wifi_by_shell_enabled", defaultValue = "false")
|
||||
val isWifiNameByShellEnabled: Boolean = false,
|
||||
@ColumnInfo(name = "is_stop_on_no_internet_enabled", defaultValue = "false")
|
||||
val isStopOnNoInternetEnabled: Boolean = false,
|
||||
@ColumnInfo(name = "is_vpn_kill_switch_enabled", defaultValue = "false")
|
||||
@@ -46,61 +43,23 @@ data class Settings(
|
||||
val debounceDelaySeconds: Int = 3,
|
||||
@ColumnInfo(name = "is_disable_kill_switch_on_trusted_enabled", defaultValue = "false")
|
||||
val isDisableKillSwitchOnTrustedEnabled: Boolean = false,
|
||||
@ColumnInfo(name = "is_tunnel_on_unsecure_enabled", defaultValue = "false")
|
||||
val isTunnelOnUnsecureEnabled: Boolean = false,
|
||||
@ColumnInfo(name = "split_tunnel_apps", defaultValue = "")
|
||||
val splitTunnelApps: MutableList<String> = mutableListOf(),
|
||||
@ColumnInfo(name = "wifi_detection_method", defaultValue = "0")
|
||||
val wifiDetectionMethod: WifiDetectionMethod = WifiDetectionMethod.fromValue(0),
|
||||
) {
|
||||
|
||||
fun toAppSettings(): AppSettings {
|
||||
return AppSettings(
|
||||
id,
|
||||
isAutoTunnelEnabled,
|
||||
isTunnelOnMobileDataEnabled,
|
||||
trustedNetworkSSIDs,
|
||||
isAlwaysOnVpnEnabled,
|
||||
isTunnelOnEthernetEnabled,
|
||||
isShortcutsEnabled,
|
||||
isTunnelOnWifiEnabled,
|
||||
isKernelEnabled,
|
||||
isRestoreOnBootEnabled,
|
||||
isMultiTunnelEnabled,
|
||||
isPingEnabled,
|
||||
isAmneziaEnabled,
|
||||
isWildcardsEnabled,
|
||||
isWifiNameByShellEnabled,
|
||||
isStopOnNoInternetEnabled,
|
||||
isVpnKillSwitchEnabled,
|
||||
isKernelKillSwitchEnabled,
|
||||
isLanOnKillSwitchEnabled,
|
||||
debounceDelaySeconds,
|
||||
isDisableKillSwitchOnTrustedEnabled,
|
||||
)
|
||||
}
|
||||
enum class WifiDetectionMethod(val value: Int) {
|
||||
DEFAULT(0),
|
||||
LEGACY(1),
|
||||
ROOT(2),
|
||||
SHIZUKU(3);
|
||||
|
||||
companion object {
|
||||
fun from(appSettings: AppSettings): Settings {
|
||||
return with(appSettings) {
|
||||
Settings(
|
||||
id,
|
||||
isAutoTunnelEnabled,
|
||||
isTunnelOnMobileDataEnabled,
|
||||
trustedNetworkSSIDs.toMutableList(),
|
||||
isAlwaysOnVpnEnabled,
|
||||
isTunnelOnEthernetEnabled,
|
||||
isShortcutsEnabled,
|
||||
isTunnelOnWifiEnabled,
|
||||
isKernelEnabled,
|
||||
isRestoreOnBootEnabled,
|
||||
isMultiTunnelEnabled,
|
||||
isPingEnabled,
|
||||
isAmneziaEnabled,
|
||||
isWildcardsEnabled,
|
||||
isWifiNameByShellEnabled,
|
||||
isStopOnNoInternetEnabled,
|
||||
isVpnKillSwitchEnabled,
|
||||
isKernelKillSwitchEnabled,
|
||||
isLanOnKillSwitchEnabled,
|
||||
debounceDelaySeconds,
|
||||
isDisableKillSwitchOnTrustedEnabled,
|
||||
)
|
||||
}
|
||||
companion object {
|
||||
fun fromValue(value: Int): WifiDetectionMethod =
|
||||
entries.find { it.value == value } ?: DEFAULT
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-43
@@ -1,10 +1,9 @@
|
||||
package com.zaneschepke.wireguardautotunnel.data.model
|
||||
package com.zaneschepke.wireguardautotunnel.data.entity
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
|
||||
@Entity(indices = [Index(value = ["name"], unique = true)])
|
||||
data class TunnelConfig(
|
||||
@@ -30,48 +29,7 @@ data class TunnelConfig(
|
||||
var isIpv4Preferred: Boolean = true,
|
||||
) {
|
||||
|
||||
fun toTunnel(): TunnelConf {
|
||||
return TunnelConf(
|
||||
id,
|
||||
name,
|
||||
wgQuick,
|
||||
tunnelNetworks,
|
||||
isMobileDataTunnel,
|
||||
isPrimaryTunnel,
|
||||
amQuick,
|
||||
isActive,
|
||||
isPingEnabled,
|
||||
pingInterval,
|
||||
pingCooldown,
|
||||
pingIp,
|
||||
isEthernetTunnel,
|
||||
isIpv4Preferred,
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val AM_QUICK_DEFAULT = ""
|
||||
|
||||
fun from(tunnelConf: TunnelConf): TunnelConfig {
|
||||
return with(tunnelConf) {
|
||||
return TunnelConfig(
|
||||
id,
|
||||
tunName,
|
||||
wgQuick,
|
||||
tunnelNetworks.toMutableList(),
|
||||
isMobileDataTunnel,
|
||||
isPrimaryTunnel,
|
||||
amQuick,
|
||||
isActive,
|
||||
isPingEnabled,
|
||||
pingInterval,
|
||||
pingCooldown,
|
||||
pingIp,
|
||||
isEthernetTunnel,
|
||||
isIpv4Preferred,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package com.zaneschepke.wireguardautotunnel.data.mapper
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.data.entity.GeneralState
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.AppState
|
||||
|
||||
object GeneralStateMapper {
|
||||
fun toAppState(generalState: GeneralState): AppState =
|
||||
with(generalState) {
|
||||
AppState(
|
||||
isLocationDisclosureShown,
|
||||
isBatteryOptimizationDisableShown,
|
||||
isPinLockEnabled,
|
||||
expandedTunnelIds,
|
||||
isLocalLogsEnabled,
|
||||
isRemoteControlEnabled,
|
||||
remoteKey,
|
||||
locale,
|
||||
theme,
|
||||
)
|
||||
}
|
||||
|
||||
fun toGeneralState(appState: AppState): GeneralState {
|
||||
return with(appState) {
|
||||
GeneralState(
|
||||
isLocationDisclosureShown,
|
||||
isBatteryOptimizationDisableShown,
|
||||
isPinLockEnabled,
|
||||
expandedTunnelIds,
|
||||
isLocalLogsEnabled,
|
||||
isRemoteControlEnabled,
|
||||
remoteKey,
|
||||
locale,
|
||||
theme,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.zaneschepke.wireguardautotunnel.data.mapper
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.data.entity.GitHubRelease
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.AppUpdate
|
||||
import kotlin.collections.firstOrNull
|
||||
|
||||
object GitHubReleaseMapper {
|
||||
fun toAppUpdate(gitHubRelease: GitHubRelease, newVersion: String): AppUpdate {
|
||||
with(gitHubRelease) {
|
||||
val apkAsset = assets.firstOrNull { it.name.endsWith(".apk") }
|
||||
return AppUpdate(
|
||||
version = newVersion,
|
||||
title = name ?: "Update $tagName",
|
||||
releaseNotes = body ?: "No release notes provided",
|
||||
apkUrl = apkAsset?.browserDownloadUrl,
|
||||
apkFileName = apkAsset?.name,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.zaneschepke.wireguardautotunnel.data.mapper
|
||||
|
||||
import com.zaneschepke.networkmonitor.AndroidNetworkMonitor
|
||||
import com.zaneschepke.wireguardautotunnel.data.entity.Settings
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.AppSettings
|
||||
|
||||
object SettingsMapper {
|
||||
fun toAppSettings(settings: Settings): AppSettings {
|
||||
return AppSettings(
|
||||
id = settings.id,
|
||||
isAutoTunnelEnabled = settings.isAutoTunnelEnabled,
|
||||
isTunnelOnMobileDataEnabled = settings.isTunnelOnMobileDataEnabled,
|
||||
trustedNetworkSSIDs = settings.trustedNetworkSSIDs,
|
||||
isAlwaysOnVpnEnabled = settings.isAlwaysOnVpnEnabled,
|
||||
isTunnelOnEthernetEnabled = settings.isTunnelOnEthernetEnabled,
|
||||
isShortcutsEnabled = settings.isShortcutsEnabled,
|
||||
isTunnelOnWifiEnabled = settings.isTunnelOnWifiEnabled,
|
||||
isKernelEnabled = settings.isKernelEnabled,
|
||||
isRestoreOnBootEnabled = settings.isRestoreOnBootEnabled,
|
||||
isMultiTunnelEnabled = settings.isMultiTunnelEnabled,
|
||||
isPingEnabled = settings.isPingEnabled,
|
||||
isAmneziaEnabled = settings.isAmneziaEnabled,
|
||||
isWildcardsEnabled = settings.isWildcardsEnabled,
|
||||
isStopOnNoInternetEnabled = settings.isStopOnNoInternetEnabled,
|
||||
isVpnKillSwitchEnabled = settings.isVpnKillSwitchEnabled,
|
||||
isKernelKillSwitchEnabled = settings.isKernelKillSwitchEnabled,
|
||||
isLanOnKillSwitchEnabled = settings.isLanOnKillSwitchEnabled,
|
||||
debounceDelaySeconds = settings.debounceDelaySeconds,
|
||||
isDisableKillSwitchOnTrustedEnabled = settings.isDisableKillSwitchOnTrustedEnabled,
|
||||
isTunnelOnUnsecureEnabled = settings.isTunnelOnUnsecureEnabled,
|
||||
splitTunnelApps = settings.splitTunnelApps,
|
||||
wifiDetectionMethod =
|
||||
AndroidNetworkMonitor.WifiDetectionMethod.fromValue(
|
||||
settings.wifiDetectionMethod.value
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun toSettings(appSettings: AppSettings): Settings {
|
||||
return Settings(
|
||||
id = appSettings.id,
|
||||
isAutoTunnelEnabled = appSettings.isAutoTunnelEnabled,
|
||||
isTunnelOnMobileDataEnabled = appSettings.isTunnelOnMobileDataEnabled,
|
||||
trustedNetworkSSIDs = appSettings.trustedNetworkSSIDs.toMutableList(),
|
||||
isAlwaysOnVpnEnabled = appSettings.isAlwaysOnVpnEnabled,
|
||||
isTunnelOnEthernetEnabled = appSettings.isTunnelOnEthernetEnabled,
|
||||
isShortcutsEnabled = appSettings.isShortcutsEnabled,
|
||||
isTunnelOnWifiEnabled = appSettings.isTunnelOnWifiEnabled,
|
||||
isKernelEnabled = appSettings.isKernelEnabled,
|
||||
isRestoreOnBootEnabled = appSettings.isRestoreOnBootEnabled,
|
||||
isMultiTunnelEnabled = appSettings.isMultiTunnelEnabled,
|
||||
isPingEnabled = appSettings.isPingEnabled,
|
||||
isAmneziaEnabled = appSettings.isAmneziaEnabled,
|
||||
isWildcardsEnabled = appSettings.isWildcardsEnabled,
|
||||
isStopOnNoInternetEnabled = appSettings.isStopOnNoInternetEnabled,
|
||||
isVpnKillSwitchEnabled = appSettings.isVpnKillSwitchEnabled,
|
||||
isKernelKillSwitchEnabled = appSettings.isKernelKillSwitchEnabled,
|
||||
isLanOnKillSwitchEnabled = appSettings.isLanOnKillSwitchEnabled,
|
||||
debounceDelaySeconds = appSettings.debounceDelaySeconds,
|
||||
isDisableKillSwitchOnTrustedEnabled = appSettings.isDisableKillSwitchOnTrustedEnabled,
|
||||
isTunnelOnUnsecureEnabled = appSettings.isTunnelOnUnsecureEnabled,
|
||||
splitTunnelApps = appSettings.splitTunnelApps.toMutableList(),
|
||||
wifiDetectionMethod =
|
||||
Settings.WifiDetectionMethod.fromValue(appSettings.wifiDetectionMethod.value),
|
||||
)
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package com.zaneschepke.wireguardautotunnel.data.mapper
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.data.entity.TunnelConfig
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
|
||||
object TunnelConfigMapper {
|
||||
fun toTunnelConf(tunnelConfig: TunnelConfig): TunnelConf {
|
||||
return with(tunnelConfig) {
|
||||
TunnelConf(
|
||||
id,
|
||||
name,
|
||||
wgQuick,
|
||||
tunnelNetworks,
|
||||
isMobileDataTunnel,
|
||||
isPrimaryTunnel,
|
||||
amQuick,
|
||||
isActive,
|
||||
isPingEnabled,
|
||||
pingInterval,
|
||||
pingCooldown,
|
||||
pingIp,
|
||||
isEthernetTunnel,
|
||||
isIpv4Preferred,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun toTunnelConfig(tunnelConf: TunnelConf): TunnelConfig {
|
||||
return with(tunnelConf) {
|
||||
TunnelConfig(
|
||||
id,
|
||||
tunName,
|
||||
wgQuick,
|
||||
tunnelNetworks.toMutableList(),
|
||||
isMobileDataTunnel,
|
||||
isPrimaryTunnel,
|
||||
amQuick,
|
||||
isActive,
|
||||
isPingEnabled,
|
||||
pingInterval,
|
||||
pingCooldown,
|
||||
pingIp,
|
||||
isEthernetTunnel,
|
||||
isIpv4Preferred,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package com.zaneschepke.wireguardautotunnel.data.model
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppState
|
||||
import com.zaneschepke.wireguardautotunnel.ui.theme.Theme
|
||||
|
||||
data class GeneralState(
|
||||
val isLocationDisclosureShown: Boolean = LOCATION_DISCLOSURE_SHOWN_DEFAULT,
|
||||
val isBatteryOptimizationDisableShown: Boolean = BATTERY_OPTIMIZATION_DISABLE_SHOWN_DEFAULT,
|
||||
val isPinLockEnabled: Boolean = PIN_LOCK_ENABLED_DEFAULT,
|
||||
val expandedTunnelIds: List<Int> = emptyList(),
|
||||
val isLocalLogsEnabled: Boolean = IS_LOGS_ENABLED_DEFAULT,
|
||||
val isRemoteControlEnabled: Boolean = IS_REMOTE_CONTROL_ENABLED,
|
||||
val remoteKey: String? = null,
|
||||
val locale: String? = null,
|
||||
val theme: Theme = Theme.AUTOMATIC,
|
||||
) {
|
||||
|
||||
fun toAppState(): AppState =
|
||||
AppState(
|
||||
isLocationDisclosureShown,
|
||||
isBatteryOptimizationDisableShown,
|
||||
isPinLockEnabled,
|
||||
expandedTunnelIds,
|
||||
isLocalLogsEnabled,
|
||||
isRemoteControlEnabled,
|
||||
remoteKey,
|
||||
locale,
|
||||
theme,
|
||||
)
|
||||
|
||||
companion object {
|
||||
fun from(appState: AppState): GeneralState {
|
||||
return with(appState) {
|
||||
GeneralState(
|
||||
isLocationDisclosureShown,
|
||||
isBatteryOptimizationDisableShown,
|
||||
isPinLockEnabled,
|
||||
expandedTunnelIds,
|
||||
isLocalLogsEnabled,
|
||||
isRemoteControlEnabled,
|
||||
remoteKey,
|
||||
locale,
|
||||
theme,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const val LOCATION_DISCLOSURE_SHOWN_DEFAULT = false
|
||||
const val BATTERY_OPTIMIZATION_DISABLE_SHOWN_DEFAULT = false
|
||||
const val PIN_LOCK_ENABLED_DEFAULT = false
|
||||
const val IS_LOGS_ENABLED_DEFAULT = false
|
||||
const val IS_REMOTE_CONTROL_ENABLED = false
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package com.zaneschepke.wireguardautotunnel.data.model
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppUpdate
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class GitHubRelease(
|
||||
@SerialName("tag_name") val tagName: String,
|
||||
val name: String?,
|
||||
val body: String?,
|
||||
val assets: List<Asset>,
|
||||
) {
|
||||
fun toAppUpdate(): AppUpdate {
|
||||
val apkAsset = assets.firstOrNull { it.name.endsWith(".apk") }
|
||||
return AppUpdate(
|
||||
version = tagName.removePrefix("v"),
|
||||
title = name ?: "Update $tagName",
|
||||
releaseNotes = body ?: "No release notes provided",
|
||||
apkUrl = apkAsset?.browserDownloadUrl,
|
||||
apkFileName = apkAsset?.name,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.zaneschepke.wireguardautotunnel.data.network
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.data.model.GitHubRelease
|
||||
import com.zaneschepke.wireguardautotunnel.data.entity.GitHubRelease
|
||||
|
||||
interface GitHubApi {
|
||||
suspend fun getLatestRelease(owner: String, repo: String): Result<GitHubRelease>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.zaneschepke.wireguardautotunnel.data.network
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.data.model.GitHubRelease
|
||||
import com.zaneschepke.wireguardautotunnel.data.entity.GitHubRelease
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.call.body
|
||||
import io.ktor.client.plugins.ClientRequestException
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
package com.zaneschepke.wireguardautotunnel.data.repository
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppSettingRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppStateRepository
|
||||
|
||||
+4
-3
@@ -1,8 +1,9 @@
|
||||
package com.zaneschepke.wireguardautotunnel.data.repository
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.data.DataStoreManager
|
||||
import com.zaneschepke.wireguardautotunnel.data.model.GeneralState
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppState
|
||||
import com.zaneschepke.wireguardautotunnel.data.entity.GeneralState
|
||||
import com.zaneschepke.wireguardautotunnel.data.mapper.GeneralStateMapper
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.AppState
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppStateRepository
|
||||
import com.zaneschepke.wireguardautotunnel.ui.theme.Theme
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
@@ -153,5 +154,5 @@ class DataStoreAppStateRepository(private val dataStoreManager: DataStoreManager
|
||||
}
|
||||
} ?: GeneralState()
|
||||
}
|
||||
.map { it.toAppState() }
|
||||
.map(GeneralStateMapper::toAppState)
|
||||
}
|
||||
|
||||
+16
-8
@@ -2,10 +2,12 @@ package com.zaneschepke.wireguardautotunnel.data.repository
|
||||
|
||||
import android.content.Context
|
||||
import com.zaneschepke.wireguardautotunnel.BuildConfig
|
||||
import com.zaneschepke.wireguardautotunnel.data.mapper.GitHubReleaseMapper
|
||||
import com.zaneschepke.wireguardautotunnel.data.network.GitHubApi
|
||||
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppUpdate
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.AppUpdate
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.UpdateRepository
|
||||
import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||
import com.zaneschepke.wireguardautotunnel.util.NumberUtils
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.request.get
|
||||
@@ -30,24 +32,30 @@ class GitHubUpdateRepository(
|
||||
override suspend fun checkForUpdate(currentVersion: String): Result<AppUpdate?> =
|
||||
withContext(ioDispatcher) {
|
||||
Timber.i("Checking for update")
|
||||
val isNightly = BuildConfig.VERSION_NAME.contains("nightly")
|
||||
val release =
|
||||
if (BuildConfig.VERSION_NAME.contains("nightly")) {
|
||||
gitHubApi.getNightlyRelease(githubOwner, githubRepo)
|
||||
if (isNightly) {
|
||||
gitHubApi.getNightlyRelease(githubOwner, githubRepo).onFailure(Timber::e)
|
||||
} else {
|
||||
gitHubApi.getLatestRelease(githubOwner, githubRepo)
|
||||
gitHubApi.getLatestRelease(githubOwner, githubRepo).onFailure(Timber::e)
|
||||
}
|
||||
release.map { release ->
|
||||
val apkAsset =
|
||||
release.assets.find { asset ->
|
||||
asset.name.startsWith("wgtunnel-full-v") && asset.name.endsWith(".apk")
|
||||
asset.name.startsWith("wgtunnel-${Constants.STANDALONE_FLAVOR}-v") &&
|
||||
asset.name.endsWith(".apk")
|
||||
}
|
||||
val newVersion =
|
||||
apkAsset?.name?.removePrefix("wgtunnel-full-v")?.removeSuffix(".apk")
|
||||
?: return@map null
|
||||
apkAsset
|
||||
?.name
|
||||
?.removePrefix("wgtunnel-${Constants.STANDALONE_FLAVOR}-v")
|
||||
?.removeSuffix(".apk") ?: return@map null
|
||||
|
||||
Timber.i("Latest version: $newVersion, current version: $currentVersion")
|
||||
if (isNightly && newVersion != currentVersion)
|
||||
return@map GitHubReleaseMapper.toAppUpdate(release, newVersion)
|
||||
if (NumberUtils.compareVersions(newVersion, currentVersion) > 0) {
|
||||
release.toAppUpdate()
|
||||
GitHubReleaseMapper.toAppUpdate(release, newVersion)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
+6
-5
@@ -1,9 +1,10 @@
|
||||
package com.zaneschepke.wireguardautotunnel.data.repository
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.data.dao.SettingsDao
|
||||
import com.zaneschepke.wireguardautotunnel.data.model.Settings
|
||||
import com.zaneschepke.wireguardautotunnel.data.entity.Settings
|
||||
import com.zaneschepke.wireguardautotunnel.data.mapper.SettingsMapper
|
||||
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppSettings
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.AppSettings
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppSettingRepository
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
@@ -16,15 +17,15 @@ class RoomSettingsRepository(
|
||||
) : AppSettingRepository {
|
||||
|
||||
override suspend fun save(appSettings: AppSettings) {
|
||||
withContext(ioDispatcher) { settingsDoa.save(Settings.from(appSettings)) }
|
||||
withContext(ioDispatcher) { settingsDoa.save(SettingsMapper.toSettings(appSettings)) }
|
||||
}
|
||||
|
||||
override val flow =
|
||||
settingsDoa.getSettingsFlow().flowOn(ioDispatcher).map { it.toAppSettings() }
|
||||
settingsDoa.getSettingsFlow().flowOn(ioDispatcher).map(SettingsMapper::toAppSettings)
|
||||
|
||||
override suspend fun get(): AppSettings {
|
||||
return withContext(ioDispatcher) {
|
||||
(settingsDoa.getAll().firstOrNull() ?: Settings()).toAppSettings()
|
||||
SettingsMapper.toAppSettings(settingsDoa.getAll().firstOrNull() ?: Settings())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+29
-13
@@ -1,9 +1,9 @@
|
||||
package com.zaneschepke.wireguardautotunnel.data.repository
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.data.dao.TunnelConfigDao
|
||||
import com.zaneschepke.wireguardautotunnel.data.model.TunnelConfig
|
||||
import com.zaneschepke.wireguardautotunnel.data.mapper.TunnelConfigMapper
|
||||
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.TunnelRepository
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.Tunnels
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
@@ -17,19 +17,25 @@ class RoomTunnelRepository(
|
||||
) : TunnelRepository {
|
||||
|
||||
override val flow =
|
||||
tunnelConfigDao.getAllFlow().flowOn(ioDispatcher).map { it.map { it.toTunnel() } }
|
||||
tunnelConfigDao.getAllFlow().flowOn(ioDispatcher).map {
|
||||
it.map(TunnelConfigMapper::toTunnelConf)
|
||||
}
|
||||
|
||||
override suspend fun getAll(): Tunnels {
|
||||
return withContext(ioDispatcher) { tunnelConfigDao.getAll().map { it.toTunnel() } }
|
||||
return withContext(ioDispatcher) {
|
||||
tunnelConfigDao.getAll().map(TunnelConfigMapper::toTunnelConf)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun save(tunnelConf: TunnelConf) {
|
||||
withContext(ioDispatcher) { tunnelConfigDao.save(TunnelConfig.from(tunnelConf)) }
|
||||
withContext(ioDispatcher) {
|
||||
tunnelConfigDao.save(TunnelConfigMapper.toTunnelConfig(tunnelConf))
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun saveAll(tunnelConfList: List<TunnelConf>) {
|
||||
withContext(ioDispatcher) {
|
||||
tunnelConfigDao.saveAll(tunnelConfList.map(TunnelConfig::from))
|
||||
tunnelConfigDao.saveAll(tunnelConfList.map(TunnelConfigMapper::toTunnelConfig))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,15 +61,21 @@ class RoomTunnelRepository(
|
||||
}
|
||||
|
||||
override suspend fun delete(tunnelConf: TunnelConf) {
|
||||
withContext(ioDispatcher) { tunnelConfigDao.delete(TunnelConfig.from(tunnelConf)) }
|
||||
withContext(ioDispatcher) {
|
||||
tunnelConfigDao.delete(TunnelConfigMapper.toTunnelConfig(tunnelConf))
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getById(id: Int): TunnelConf? {
|
||||
return withContext(ioDispatcher) { tunnelConfigDao.getById(id.toLong())?.toTunnel() }
|
||||
return withContext(ioDispatcher) {
|
||||
tunnelConfigDao.getById(id.toLong())?.let(TunnelConfigMapper::toTunnelConf)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getActive(): Tunnels {
|
||||
return withContext(ioDispatcher) { tunnelConfigDao.getActive().map { it.toTunnel() } }
|
||||
return withContext(ioDispatcher) {
|
||||
tunnelConfigDao.getActive().map(TunnelConfigMapper::toTunnelConf)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun count(): Int {
|
||||
@@ -71,22 +83,26 @@ class RoomTunnelRepository(
|
||||
}
|
||||
|
||||
override suspend fun findByTunnelName(name: String): TunnelConf? {
|
||||
return withContext(ioDispatcher) { tunnelConfigDao.getByName(name)?.toTunnel() }
|
||||
return withContext(ioDispatcher) {
|
||||
tunnelConfigDao.getByName(name)?.let(TunnelConfigMapper::toTunnelConf)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findByTunnelNetworksName(name: String): Tunnels {
|
||||
return withContext(ioDispatcher) {
|
||||
tunnelConfigDao.findByTunnelNetworkName(name).map { it.toTunnel() }
|
||||
tunnelConfigDao.findByTunnelNetworkName(name).map(TunnelConfigMapper::toTunnelConf)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findByMobileDataTunnel(): Tunnels {
|
||||
return withContext(ioDispatcher) {
|
||||
tunnelConfigDao.findByMobileDataTunnel().map { it.toTunnel() }
|
||||
tunnelConfigDao.findByMobileDataTunnel().map(TunnelConfigMapper::toTunnelConf)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findPrimary(): Tunnels {
|
||||
return withContext(ioDispatcher) { tunnelConfigDao.findByPrimary().map { it.toTunnel() } }
|
||||
return withContext(ioDispatcher) {
|
||||
tunnelConfigDao.findByPrimary().map(TunnelConfigMapper::toTunnelConf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,9 @@ import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChangedBy
|
||||
import kotlinx.coroutines.flow.map
|
||||
import org.amnezia.awg.backend.Backend
|
||||
import org.amnezia.awg.backend.GoBackend
|
||||
import org.amnezia.awg.backend.RootTunnelActionHandler
|
||||
@@ -112,10 +114,23 @@ class TunnelModule {
|
||||
fun provideNetworkMonitor(
|
||||
@ApplicationContext context: Context,
|
||||
settingsRepository: AppSettingRepository,
|
||||
@ApplicationScope applicationScope: CoroutineScope,
|
||||
@AppShell appShell: RootShell,
|
||||
): NetworkMonitor {
|
||||
return AndroidNetworkMonitor(context) {
|
||||
runBlocking { settingsRepository.get().isWifiNameByShellEnabled }
|
||||
}
|
||||
return AndroidNetworkMonitor(
|
||||
context,
|
||||
object : AndroidNetworkMonitor.ConfigurationListener {
|
||||
override val detectionMethod: Flow<AndroidNetworkMonitor.WifiDetectionMethod>
|
||||
get() =
|
||||
settingsRepository.flow
|
||||
.distinctUntilChangedBy { it.wifiDetectionMethod }
|
||||
.map { it.wifiDetectionMethod }
|
||||
|
||||
override val rootShell: RootShell
|
||||
get() = appShell
|
||||
},
|
||||
applicationScope,
|
||||
)
|
||||
}
|
||||
|
||||
@Singleton
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.zaneschepke.wireguardautotunnel.domain.enums
|
||||
|
||||
enum class ConfigType {
|
||||
AMNEZIA,
|
||||
AM,
|
||||
WG,
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
package com.zaneschepke.wireguardautotunnel.domain.events
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
|
||||
sealed class AutoTunnelEvent {
|
||||
data class Start(val tunnelConf: TunnelConf? = null) : AutoTunnelEvent()
|
||||
|
||||
+19
-2
@@ -1,4 +1,6 @@
|
||||
package com.zaneschepke.wireguardautotunnel.domain.entity
|
||||
package com.zaneschepke.wireguardautotunnel.domain.model
|
||||
|
||||
import com.zaneschepke.networkmonitor.AndroidNetworkMonitor
|
||||
|
||||
data class AppSettings(
|
||||
val id: Int = 0,
|
||||
@@ -15,15 +17,30 @@ data class AppSettings(
|
||||
val isPingEnabled: Boolean = false,
|
||||
val isAmneziaEnabled: Boolean = false,
|
||||
val isWildcardsEnabled: Boolean = false,
|
||||
val isWifiNameByShellEnabled: Boolean = false,
|
||||
val isStopOnNoInternetEnabled: Boolean = false,
|
||||
val isVpnKillSwitchEnabled: Boolean = false,
|
||||
val isKernelKillSwitchEnabled: Boolean = false,
|
||||
val isLanOnKillSwitchEnabled: Boolean = false,
|
||||
val debounceDelaySeconds: Int = 3,
|
||||
val isDisableKillSwitchOnTrustedEnabled: Boolean = false,
|
||||
val isTunnelOnUnsecureEnabled: Boolean = false,
|
||||
val splitTunnelApps: List<String> = emptyList(),
|
||||
val wifiDetectionMethod: AndroidNetworkMonitor.WifiDetectionMethod =
|
||||
AndroidNetworkMonitor.WifiDetectionMethod.DEFAULT,
|
||||
) {
|
||||
fun debounceDelayMillis(): Long {
|
||||
return debounceDelaySeconds * 1000L
|
||||
}
|
||||
|
||||
fun toAutoTunnelStateString(): String {
|
||||
return """
|
||||
TunnelOnWifi: $isTunnelOnWifiEnabled
|
||||
TunnelOnMobileData: $isTunnelOnMobileDataEnabled
|
||||
TunnelOnEthernet: $isTunnelOnEthernetEnabled
|
||||
Wildcards: $isWildcardsEnabled
|
||||
StopOnNoInternet: $isStopOnNoInternetEnabled
|
||||
Trusted Networks: $trustedNetworkSSIDs
|
||||
"""
|
||||
.trimIndent()
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.zaneschepke.wireguardautotunnel.domain.entity
|
||||
package com.zaneschepke.wireguardautotunnel.domain.model
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.ui.theme.Theme
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.zaneschepke.wireguardautotunnel.domain.entity
|
||||
package com.zaneschepke.wireguardautotunnel.domain.model
|
||||
|
||||
data class AppUpdate(
|
||||
val version: String,
|
||||
+1
-4
@@ -1,4 +1,4 @@
|
||||
package com.zaneschepke.wireguardautotunnel.domain.entity
|
||||
package com.zaneschepke.wireguardautotunnel.domain.model
|
||||
|
||||
import com.wireguard.android.backend.Tunnel
|
||||
import com.wireguard.config.Config
|
||||
@@ -26,7 +26,6 @@ data class TunnelConf(
|
||||
val pingIp: String? = null,
|
||||
val isEthernetTunnel: Boolean = false,
|
||||
val isIpv4Preferred: Boolean = true,
|
||||
val useCache: Boolean = false,
|
||||
@Transient private var stateChangeCallback: ((Any) -> Unit)? = null,
|
||||
) : Tunnel, org.amnezia.awg.backend.Tunnel {
|
||||
|
||||
@@ -111,8 +110,6 @@ data class TunnelConf(
|
||||
|
||||
override fun isIpv4ResolutionPreferred(): Boolean = isIpv4Preferred
|
||||
|
||||
override fun useCache(): Boolean = useCache
|
||||
|
||||
override fun onStateChange(newState: org.amnezia.awg.backend.Tunnel.State) {
|
||||
stateChangeCallback?.invoke(newState)
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
package com.zaneschepke.wireguardautotunnel.domain.repository
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
|
||||
interface AppDataRepository {
|
||||
suspend fun getPrimaryOrFirstTunnel(): TunnelConf?
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
package com.zaneschepke.wireguardautotunnel.domain.repository
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppSettings
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.AppSettings
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface AppSettingRepository {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
package com.zaneschepke.wireguardautotunnel.domain.repository
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppState
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.AppState
|
||||
import com.zaneschepke.wireguardautotunnel.ui.theme.Theme
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
package com.zaneschepke.wireguardautotunnel.domain.repository
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.Tunnels
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
package com.zaneschepke.wireguardautotunnel.domain.repository
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppUpdate
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.AppUpdate
|
||||
import java.io.File
|
||||
|
||||
interface UpdateRepository {
|
||||
|
||||
+4
-2
@@ -3,10 +3,10 @@ package com.zaneschepke.wireguardautotunnel.domain.state
|
||||
import com.zaneschepke.wireguardautotunnel.core.tunnel.allDown
|
||||
import com.zaneschepke.wireguardautotunnel.core.tunnel.hasActive
|
||||
import com.zaneschepke.wireguardautotunnel.core.tunnel.isUp
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppSettings
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.events.AutoTunnelEvent
|
||||
import com.zaneschepke.wireguardautotunnel.domain.events.KillSwitchEvent
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.AppSettings
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.isMatchingToWildcardList
|
||||
|
||||
data class AutoTunnelState(
|
||||
@@ -16,6 +16,7 @@ data class AutoTunnelState(
|
||||
val tunnels: List<TunnelConf> = emptyList(),
|
||||
) {
|
||||
|
||||
// also need to check for Wi-Fi state as there is some overlap when they are both connected
|
||||
private fun isMobileDataActive(): Boolean {
|
||||
return !networkState.isEthernetConnected &&
|
||||
!networkState.isWifiConnected &&
|
||||
@@ -50,6 +51,7 @@ data class AutoTunnelState(
|
||||
return getTunnelWithMatchingTunnelNetwork() ?: tunnels.firstOrNull { it.isPrimaryTunnel }
|
||||
}
|
||||
|
||||
// ignore cellular state as there is overlap where it may still be active, but not prioritized
|
||||
private fun isWifiActive(): Boolean {
|
||||
return !networkState.isEthernetConnected && networkState.isWifiConnected
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ sealed class Route {
|
||||
|
||||
@Serializable data object AutoTunnelAdvanced : Route()
|
||||
|
||||
@Serializable data object WifiDetectionMethod : Route()
|
||||
|
||||
@Serializable data object LocationDisclosure : Route()
|
||||
|
||||
@Serializable data object Appearance : Route()
|
||||
@@ -29,8 +31,6 @@ sealed class Route {
|
||||
|
||||
@Serializable data object Lock : Route()
|
||||
|
||||
@Serializable data object Scanner : Route()
|
||||
|
||||
@Serializable data object License : Route()
|
||||
|
||||
@Serializable data class Config(val id: Int) : Route()
|
||||
|
||||
+1
-1
@@ -73,7 +73,7 @@ fun IconSurfaceButton(
|
||||
else MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
}
|
||||
Column {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
Text(title, style = MaterialTheme.typography.titleMedium)
|
||||
description?.let {
|
||||
Text(
|
||||
|
||||
+20
-12
@@ -4,16 +4,7 @@ import android.os.Build
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Add
|
||||
import androidx.compose.material.icons.rounded.CopyAll
|
||||
import androidx.compose.material.icons.rounded.Delete
|
||||
import androidx.compose.material.icons.rounded.Download
|
||||
import androidx.compose.material.icons.rounded.Edit
|
||||
import androidx.compose.material.icons.rounded.Menu
|
||||
import androidx.compose.material.icons.rounded.PlayArrow
|
||||
import androidx.compose.material.icons.rounded.Save
|
||||
import androidx.compose.material.icons.rounded.SelectAll
|
||||
import androidx.compose.material.icons.rounded.Stop
|
||||
import androidx.compose.material.icons.rounded.*
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
@@ -197,6 +188,14 @@ fun currentNavBackStackEntryAsNavBarState(
|
||||
route = Route.Display,
|
||||
)
|
||||
|
||||
backStackEntry.isCurrentRoute(Route.WifiDetectionMethod::class) ->
|
||||
NavBarState(
|
||||
showTop = true,
|
||||
showBottom = true,
|
||||
topTitle = { Text(stringResource(R.string.wifi_detection_method)) },
|
||||
route = Route.WifiDetectionMethod,
|
||||
)
|
||||
|
||||
backStackEntry.isCurrentRoute(Route.KillSwitch::class) ->
|
||||
NavBarState(
|
||||
showTop = true,
|
||||
@@ -240,8 +239,17 @@ fun currentNavBackStackEntryAsNavBarState(
|
||||
showBottom = true,
|
||||
topTitle = { tunnel?.name?.let { Text(it) } },
|
||||
topTrailing = {
|
||||
ActionIconButton(Icons.Rounded.Edit, R.string.edit_tunnel) {
|
||||
tunnel?.id?.let { navController.navigate(Route.Config(it)) }
|
||||
Row {
|
||||
ActionIconButton(Icons.Rounded.QrCode2, R.string.show_qr) {
|
||||
tunnel?.id?.let {
|
||||
viewModel.handleEvent(
|
||||
AppEvent.SetShowModal(AppViewState.ModalType.QR)
|
||||
)
|
||||
}
|
||||
}
|
||||
ActionIconButton(Icons.Rounded.Edit, R.string.edit_tunnel) {
|
||||
tunnel?.id?.let { navController.navigate(Route.Config(it)) }
|
||||
}
|
||||
}
|
||||
},
|
||||
route = args?.let { Route.TunnelOptions(it.id) },
|
||||
|
||||
+41
-41
@@ -1,18 +1,9 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.components
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Code
|
||||
import androidx.compose.material.icons.outlined.Filter1
|
||||
import androidx.compose.material.icons.outlined.Security
|
||||
import androidx.compose.material.icons.outlined.VpnKeyOff
|
||||
import androidx.compose.material.icons.outlined.Wifi
|
||||
import androidx.compose.material.icons.outlined.*
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
@@ -26,14 +17,19 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.zaneschepke.networkmonitor.AndroidNetworkMonitor
|
||||
import com.zaneschepke.networkmonitor.NetworkStatus
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.ui.Route
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ForwardButton
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.functions.rememberClipboardHelper
|
||||
import com.zaneschepke.wireguardautotunnel.ui.navigation.LocalNavController
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.LearnMoreLinkLabel
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.AppUiState
|
||||
import com.zaneschepke.wireguardautotunnel.ui.theme.iconSize
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.asString
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.openWebUrl
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.event.AppEvent
|
||||
@@ -47,6 +43,7 @@ fun WifiTunnelingItems(
|
||||
isWifiNameReadable: () -> Boolean,
|
||||
): List<SelectionItem> {
|
||||
val context = LocalContext.current
|
||||
val navController = LocalNavController.current
|
||||
val clipboardHelper = rememberClipboardHelper()
|
||||
|
||||
val baseItems =
|
||||
@@ -107,40 +104,40 @@ fun WifiTunnelingItems(
|
||||
}
|
||||
},
|
||||
onClick = { viewModel.handleEvent(AppEvent.ToggleAutoTunnelOnWifi) },
|
||||
),
|
||||
SelectionItem(
|
||||
leadingIcon = Icons.Outlined.Code,
|
||||
title = {
|
||||
Text(
|
||||
stringResource(R.string.wifi_name_via_shell),
|
||||
style =
|
||||
MaterialTheme.typography.bodyMedium.copy(
|
||||
MaterialTheme.colorScheme.onSurface
|
||||
),
|
||||
)
|
||||
},
|
||||
description = {
|
||||
Text(
|
||||
stringResource(R.string.use_root_shell_for_wifi),
|
||||
style =
|
||||
MaterialTheme.typography.bodySmall.copy(
|
||||
MaterialTheme.colorScheme.outline
|
||||
),
|
||||
)
|
||||
},
|
||||
trailing = {
|
||||
ScaledSwitch(
|
||||
checked = uiState.appSettings.isWifiNameByShellEnabled,
|
||||
onClick = { viewModel.handleEvent(AppEvent.ToggleRootShellWifi) },
|
||||
)
|
||||
},
|
||||
onClick = { viewModel.handleEvent(AppEvent.ToggleRootShellWifi) },
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
return if (uiState.appSettings.isTunnelOnWifiEnabled) {
|
||||
baseItems +
|
||||
listOf(
|
||||
SelectionItem(
|
||||
leadingIcon = Icons.Outlined.WifiFind,
|
||||
title = {
|
||||
Text(
|
||||
stringResource(R.string.wifi_detection_method),
|
||||
style =
|
||||
MaterialTheme.typography.bodyMedium.copy(
|
||||
MaterialTheme.colorScheme.onSurface
|
||||
),
|
||||
)
|
||||
},
|
||||
description = {
|
||||
Text(
|
||||
stringResource(
|
||||
R.string.current_template,
|
||||
uiState.appSettings.wifiDetectionMethod.asString(context),
|
||||
),
|
||||
style =
|
||||
MaterialTheme.typography.bodySmall.copy(
|
||||
MaterialTheme.colorScheme.outline
|
||||
),
|
||||
)
|
||||
},
|
||||
trailing = {
|
||||
ForwardButton { navController.navigate(Route.WifiDetectionMethod) }
|
||||
},
|
||||
onClick = { navController.navigate(Route.WifiDetectionMethod) },
|
||||
),
|
||||
SelectionItem(
|
||||
leadingIcon = Icons.Outlined.Filter1,
|
||||
title = {
|
||||
@@ -205,7 +202,10 @@ fun WifiTunnelingItems(
|
||||
currentText = currentText,
|
||||
onSave = { ssid ->
|
||||
if (
|
||||
uiState.appSettings.isWifiNameByShellEnabled ||
|
||||
uiState.appSettings.wifiDetectionMethod ==
|
||||
AndroidNetworkMonitor.WifiDetectionMethod.ROOT ||
|
||||
uiState.appSettings.wifiDetectionMethod ==
|
||||
AndroidNetworkMonitor.WifiDetectionMethod.SHIZUKU ||
|
||||
isWifiNameReadable()
|
||||
) {
|
||||
viewModel.handleEvent(AppEvent.SaveTrustedSSID(ssid))
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.detection
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.zaneschepke.networkmonitor.AndroidNetworkMonitor
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.IconSurfaceButton
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.AppUiState
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.asDescriptionString
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.asString
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.event.AppEvent
|
||||
|
||||
@Composable
|
||||
fun WifiDetectionMethodScreen(uiState: AppUiState, viewModel: AppViewModel) {
|
||||
val context = LocalContext.current
|
||||
Column(
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.spacedBy(24.dp, Alignment.Top),
|
||||
modifier = Modifier.fillMaxSize().padding(top = 24.dp).padding(horizontal = 24.dp),
|
||||
) {
|
||||
enumValues<AndroidNetworkMonitor.WifiDetectionMethod>().forEach {
|
||||
val title = it.asString(context)
|
||||
val description = it.asDescriptionString(context)
|
||||
IconSurfaceButton(
|
||||
title = title,
|
||||
onClick = { viewModel.handleEvent(AppEvent.SetDetectionMethod(it)) },
|
||||
selected = uiState.appSettings.wifiDetectionMethod == it,
|
||||
description = description,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
-1
@@ -9,6 +9,8 @@ import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.journeyapps.barcodescanner.ScanContract
|
||||
import com.journeyapps.barcodescanner.ScanOptions
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.ui.Route
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.dialog.InfoDialog
|
||||
@@ -45,6 +47,15 @@ fun MainScreen(appUiState: AppUiState, appViewState: AppViewState, viewModel: Ap
|
||||
onData = { data -> viewModel.handleEvent(AppEvent.ImportTunnelFromFile(data)) },
|
||||
)
|
||||
|
||||
val scanLauncher =
|
||||
rememberLauncherForActivityResult(
|
||||
contract = ScanContract(),
|
||||
onResult = { result ->
|
||||
if (result != null && result.contents.isNotEmpty())
|
||||
viewModel.handleEvent(AppEvent.ImportTunnelFromQrCode(result.contents))
|
||||
},
|
||||
)
|
||||
|
||||
val requestPermissionLauncher =
|
||||
rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted
|
||||
->
|
||||
@@ -56,7 +67,9 @@ fun MainScreen(appUiState: AppUiState, appViewState: AppViewState, viewModel: Ap
|
||||
)
|
||||
return@rememberLauncherForActivityResult
|
||||
}
|
||||
navController.navigate(Route.Scanner)
|
||||
scanLauncher.launch(
|
||||
ScanOptions().setDesiredBarcodeFormats(ScanOptions.QR_CODE).setBeepEnabled(false)
|
||||
)
|
||||
}
|
||||
|
||||
if (appViewState.showModal == AppViewState.ModalType.DELETE) {
|
||||
|
||||
+2
-2
@@ -15,8 +15,8 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppSettings
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.AppSettings
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelectionGroupButton
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.main.autotunnel.components.EthernetTunnelItem
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.main.autotunnel.components.MobileDataTunnelItem
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
|
||||
|
||||
+2
-2
@@ -16,8 +16,8 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppSettings
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.AppSettings
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.components.TrustedNetworkTextBox
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.components.WildcardsLabel
|
||||
|
||||
+1
-1
@@ -93,7 +93,7 @@ fun ExportTunnelsBottomSheet(viewModel: AppViewModel) {
|
||||
ExportOptionRow(
|
||||
label = stringResource(R.string.export_tunnels_amnezia),
|
||||
onClick = {
|
||||
exportConfigType = ConfigType.AMNEZIA
|
||||
exportConfigType = ConfigType.AM
|
||||
if (!isAuthorized && !isTv) {
|
||||
showAuthPrompt = true
|
||||
} else {
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.zaneschepke.wireguardautotunnel.core.tunnel.getValueById
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
||||
import com.zaneschepke.wireguardautotunnel.ui.Route
|
||||
import com.zaneschepke.wireguardautotunnel.ui.navigation.LocalIsAndroidTV
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.ExpandingRowListItem
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelStatistics
|
||||
import com.zaneschepke.wireguardautotunnel.util.NumberUtils
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.toThreeDecimalPlaceString
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.zaneschepke.wireguardautotunnel.MainActivity
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.prompt.AuthorizationPrompt
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.main.config.components.AddPeerButton
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.main.config.components.InterfaceSection
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.TunnelRepository
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.main.config.state.ConfigUiState
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.ConfigProxy
|
||||
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.screens.main.scanner
|
||||
|
||||
import android.app.Activity
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import com.journeyapps.barcodescanner.CompoundBarcodeView
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.event.AppEvent
|
||||
|
||||
@Composable
|
||||
fun ScannerScreen(viewModel: AppViewModel) {
|
||||
val context = LocalContext.current
|
||||
|
||||
val barcodeView = remember {
|
||||
CompoundBarcodeView(context).apply {
|
||||
this.initializeFromIntent((context as Activity).intent)
|
||||
this.setStatusText("")
|
||||
this.decodeSingle { result ->
|
||||
result.text?.let { barCodeOrQr ->
|
||||
viewModel.handleEvent(AppEvent.ImportTunnelFromQrCode(barCodeOrQr))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
AndroidView(factory = { barcodeView })
|
||||
DisposableEffect(Unit) {
|
||||
barcodeView.resume()
|
||||
onDispose { barcodeView.pause() }
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -4,7 +4,7 @@ import android.content.Context
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.TunnelRepository
|
||||
import com.zaneschepke.wireguardautotunnel.ui.Route
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.main.splittunnel.state.SplitOption
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.screens.main.splittunnel.state
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
|
||||
data class SplitTunnelUiState(
|
||||
val loading: Boolean = true,
|
||||
|
||||
+38
-3
@@ -6,18 +6,53 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.SectionDivider
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelectionGroupButton
|
||||
import com.zaneschepke.wireguardautotunnel.ui.navigation.LocalIsAndroidTV
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.main.tunneloptions.components.*
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.AuthorizationPromptWrapper
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.AppViewState
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.event.AppEvent
|
||||
|
||||
@Composable
|
||||
fun TunnelOptionsScreen(tunnelConf: TunnelConf, viewModel: AppViewModel) {
|
||||
fun TunnelOptionsScreen(
|
||||
tunnelConf: TunnelConf,
|
||||
viewModel: AppViewModel,
|
||||
appViewState: AppViewState,
|
||||
) {
|
||||
val isTv = LocalIsAndroidTV.current
|
||||
|
||||
var showAuthPrompt by remember { mutableStateOf(!isTv) }
|
||||
var isAuthorized by remember { mutableStateOf(isTv) }
|
||||
|
||||
if (appViewState.showModal == AppViewState.ModalType.QR) {
|
||||
|
||||
// Show authorization prompt if needed
|
||||
if (showAuthPrompt) {
|
||||
AuthorizationPromptWrapper(
|
||||
onDismiss = { showAuthPrompt = false },
|
||||
onSuccess = {
|
||||
showAuthPrompt = false
|
||||
isAuthorized = true
|
||||
},
|
||||
viewModel = viewModel,
|
||||
)
|
||||
}
|
||||
if (isAuthorized) {
|
||||
QrCodeDialog(
|
||||
tunnelConf = tunnelConf,
|
||||
onDismiss = {
|
||||
viewModel.handleEvent(AppEvent.SetShowModal(AppViewState.ModalType.NONE))
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.Start,
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.ui.Route
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ForwardButton
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.config.SubmitConfigurationTextBox
|
||||
import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
|
||||
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.screens.main.tunneloptions.components
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Check
|
||||
import androidx.compose.material.icons.outlined.VpnKey
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.MultiChoiceSegmentedButtonRow
|
||||
import androidx.compose.material3.SegmentedButton
|
||||
import androidx.compose.material3.SegmentedButtonDefaults
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import com.zaneschepke.wireguardautotunnel.MainActivity
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.ConfigType
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.setScreenBrightness
|
||||
import io.github.alexzhirkevich.qrose.options.QrBallShape
|
||||
import io.github.alexzhirkevich.qrose.options.QrBrush
|
||||
import io.github.alexzhirkevich.qrose.options.QrErrorCorrectionLevel
|
||||
import io.github.alexzhirkevich.qrose.options.QrFrameShape
|
||||
import io.github.alexzhirkevich.qrose.options.QrOptions
|
||||
import io.github.alexzhirkevich.qrose.options.QrPixelShape
|
||||
import io.github.alexzhirkevich.qrose.options.circle
|
||||
import io.github.alexzhirkevich.qrose.options.roundCorners
|
||||
import io.github.alexzhirkevich.qrose.options.solid
|
||||
import io.github.alexzhirkevich.qrose.rememberQrCodePainter
|
||||
|
||||
@Composable
|
||||
fun QrCodeDialog(tunnelConf: TunnelConf, onDismiss: () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val activity = context as? MainActivity
|
||||
|
||||
// Handle screen brightness
|
||||
DisposableEffect(Unit) {
|
||||
activity?.setScreenBrightness(1.0f)
|
||||
onDispose { activity?.setScreenBrightness(-1f) }
|
||||
}
|
||||
|
||||
QrCodeAlertDialog(tunnelConf = tunnelConf, onDismiss = onDismiss)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun QrCodeAlertDialog(tunnelConf: TunnelConf, onDismiss: () -> Unit) {
|
||||
Surface(color = Color.White, tonalElevation = 0.dp) {
|
||||
AlertDialog(
|
||||
containerColor = Color.White,
|
||||
onDismissRequest = onDismiss,
|
||||
confirmButton = {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text(stringResource(R.string.done), color = MaterialTheme.colorScheme.surface)
|
||||
}
|
||||
},
|
||||
title = {
|
||||
Text(
|
||||
text = tunnelConf.name,
|
||||
color = Color.Black,
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
)
|
||||
},
|
||||
text = { QrCodeContent(tunnelConf = tunnelConf) },
|
||||
properties = DialogProperties(usePlatformDefaultWidth = true),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun QrCodeContent(tunnelConf: TunnelConf) {
|
||||
var selectedOption by remember { mutableStateOf(ConfigType.WG) }
|
||||
val qrCodeText =
|
||||
when (selectedOption) {
|
||||
ConfigType.AM -> tunnelConf.toAmConfig().toAwgQuickString(true)
|
||||
ConfigType.WG -> tunnelConf.toWgConfig().toWgQuickString(true)
|
||||
}
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp, Alignment.Top),
|
||||
) {
|
||||
val qrCodePainter = rememberQrCodePainter(data = qrCodeText, options = createQrOptions())
|
||||
Image(
|
||||
painter = qrCodePainter,
|
||||
contentDescription = stringResource(R.string.show_qr),
|
||||
modifier =
|
||||
Modifier.size(300.dp)
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.padding(16.dp)
|
||||
.background(Color.White),
|
||||
)
|
||||
ConfigTypeSelector(
|
||||
selectedOption = selectedOption,
|
||||
onOptionSelected = { selectedOption = it },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ConfigTypeSelector(selectedOption: ConfigType, onOptionSelected: (ConfigType) -> Unit) {
|
||||
MultiChoiceSegmentedButtonRow(modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)) {
|
||||
ConfigType.entries.sortedDescending().forEachIndexed { index, entry ->
|
||||
val isActive = selectedOption == entry
|
||||
val typeName =
|
||||
stringResource(
|
||||
when (entry) {
|
||||
ConfigType.AM -> R.string.amnezia
|
||||
ConfigType.WG -> R.string.wireguard
|
||||
}
|
||||
)
|
||||
SegmentedButton(
|
||||
shape =
|
||||
SegmentedButtonDefaults.itemShape(
|
||||
index = index,
|
||||
count = ConfigType.entries.size,
|
||||
baseShape = RoundedCornerShape(8.dp),
|
||||
),
|
||||
icon = {
|
||||
SegmentedButtonDefaults.Icon(
|
||||
active = isActive,
|
||||
activeContent = {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Check,
|
||||
contentDescription = stringResource(R.string.select),
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.size(SegmentedButtonDefaults.IconSize),
|
||||
)
|
||||
},
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.VpnKey,
|
||||
contentDescription = typeName,
|
||||
tint = Color.Black,
|
||||
modifier = Modifier.size(SegmentedButtonDefaults.IconSize),
|
||||
)
|
||||
}
|
||||
},
|
||||
colors =
|
||||
SegmentedButtonDefaults.colors()
|
||||
.copy(
|
||||
activeContainerColor = Color.White,
|
||||
inactiveContainerColor = Color.White,
|
||||
),
|
||||
onCheckedChange = { onOptionSelected(entry) },
|
||||
checked = isActive,
|
||||
) {
|
||||
Text(
|
||||
text = typeName,
|
||||
color = Color.Black,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createQrOptions(): QrOptions = QrOptions {
|
||||
shapes {
|
||||
darkPixel = QrPixelShape.circle()
|
||||
ball = QrBallShape.circle()
|
||||
frame = QrFrameShape.roundCorners(0.2f)
|
||||
}
|
||||
colors {
|
||||
dark = QrBrush.solid(Color.Black)
|
||||
frame = QrBrush.solid(Color.Black)
|
||||
ball = QrBrush.solid(Color.Black)
|
||||
}
|
||||
errorCorrectionLevel = QrErrorCorrectionLevel.Medium
|
||||
}
|
||||
+1
-1
@@ -7,7 +7,7 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.ui.Route
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ForwardButton
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
||||
|
||||
+43
-3
@@ -12,6 +12,12 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.StrokeCap
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.LinkAnnotation
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.TextLinkStyles
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.text.withLink
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
@@ -72,8 +78,41 @@ fun SupportScreen(viewModel: SupportViewModel = hiltViewModel(), appViewModel: A
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp, Alignment.Top),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Text(uiState.appUpdate?.version ?: "")
|
||||
Text(uiState.appUpdate?.releaseNotes ?: "")
|
||||
val annotatedString = buildAnnotatedString {
|
||||
append("${uiState.appUpdate?.version ?: ""}\n")
|
||||
// Add clickable text for second line
|
||||
withLink(
|
||||
link =
|
||||
LinkAnnotation.Clickable(
|
||||
tag = stringResource(id = R.string.release_notes),
|
||||
linkInteractionListener = {
|
||||
val version =
|
||||
if (BuildConfig.VERSION_NAME.contains("nightly")) {
|
||||
"nightly"
|
||||
} else {
|
||||
uiState.appUpdate
|
||||
?.version
|
||||
?.removePrefix("v")
|
||||
?.trim() ?: ""
|
||||
}
|
||||
val url = "${Constants.BASE_RELEASE_URL}$version".trim()
|
||||
context.openWebUrl(url)
|
||||
},
|
||||
styles =
|
||||
TextLinkStyles(
|
||||
style =
|
||||
SpanStyle(
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
textDecoration = TextDecoration.Underline,
|
||||
)
|
||||
),
|
||||
)
|
||||
) {
|
||||
append(stringResource(R.string.release_notes))
|
||||
}
|
||||
}
|
||||
|
||||
Text(text = annotatedString)
|
||||
if (uiState.isLoading) {
|
||||
LinearProgressIndicator(
|
||||
progress = { uiState.downloadProgress },
|
||||
@@ -87,7 +126,8 @@ fun SupportScreen(viewModel: SupportViewModel = hiltViewModel(), appViewModel: A
|
||||
},
|
||||
confirmText = {
|
||||
Text(
|
||||
if (BuildConfig.FLAVOR != Constants.STANDALONE_FLAVOR) stringResource(R.string.download)
|
||||
if (BuildConfig.FLAVOR != Constants.STANDALONE_FLAVOR)
|
||||
stringResource(R.string.download)
|
||||
else stringResource(R.string.download_and_install)
|
||||
)
|
||||
},
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.screens.support
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppUpdate
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.AppUpdate
|
||||
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
||||
|
||||
data class SupportUiState(
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.zaneschepke.wireguardautotunnel.BuildConfig
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppUpdate
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.AppUpdate
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.UpdateRepository
|
||||
import com.zaneschepke.wireguardautotunnel.util.FileUtils
|
||||
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.state
|
||||
|
||||
import com.zaneschepke.networkmonitor.NetworkStatus
|
||||
import com.zaneschepke.wireguardautotunnel.data.model.GeneralState
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppSettings
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppState
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.data.entity.GeneralState
|
||||
import com.zaneschepke.wireguardautotunnel.data.mapper.GeneralStateMapper
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.AppSettings
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.AppState
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
||||
|
||||
data class AppUiState(
|
||||
val appSettings: AppSettings = AppSettings(),
|
||||
val tunnels: List<TunnelConf> = emptyList(),
|
||||
val activeTunnels: Map<TunnelConf, TunnelState> = emptyMap(),
|
||||
val appState: AppState = GeneralState().toAppState(),
|
||||
val appState: AppState = GeneralStateMapper.toAppState(GeneralState()),
|
||||
val isAutoTunnelActive: Boolean = false,
|
||||
val appConfigurationChange: Boolean = false,
|
||||
val isAppLoaded: Boolean = false,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.state
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
||||
|
||||
data class AppViewState(
|
||||
@@ -18,6 +18,7 @@ data class AppViewState(
|
||||
NONE,
|
||||
DELETE,
|
||||
INFO,
|
||||
QR,
|
||||
}
|
||||
|
||||
enum class BottomSheet {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.state
|
||||
|
||||
import com.wireguard.config.Peer
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.joinAndTrim
|
||||
|
||||
data class PeerProxy(
|
||||
|
||||
@@ -38,4 +38,6 @@ object Constants {
|
||||
const val GOOGLE_PLAY_FLAVOR = "google"
|
||||
const val STANDALONE_FLAVOR = "standalone"
|
||||
const val RELEASE = "release"
|
||||
|
||||
const val BASE_RELEASE_URL = "https://github.com/wgtunnel/wgtunnel/releases/tag/"
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ import android.provider.OpenableColumns
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.content.FileProvider
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.data.model.TunnelConfig
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.data.entity.TunnelConfig
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.getInputStreamFromUri
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.installApk
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.launchShareFile
|
||||
|
||||
+23
@@ -1,6 +1,7 @@
|
||||
package com.zaneschepke.wireguardautotunnel.util.extensions
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Context.POWER_SERVICE
|
||||
@@ -17,6 +18,9 @@ import android.widget.Toast
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.core.location.LocationManagerCompat
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.core.service.tile.AutoTunnelControlTile
|
||||
import com.zaneschepke.wireguardautotunnel.core.service.tile.TunnelControlTile
|
||||
@@ -225,3 +229,22 @@ fun Context.installApk(apkFile: File) {
|
||||
}
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
fun Activity.setScreenBrightness(brightness: Float) {
|
||||
window.attributes = window.attributes.apply { screenBrightness = brightness }
|
||||
}
|
||||
|
||||
fun Activity.enableImmersiveMode() {
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
val controller = WindowCompat.getInsetsController(window, window.decorView)
|
||||
controller.systemBarsBehavior =
|
||||
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
||||
controller.hide(WindowInsetsCompat.Type.systemBars())
|
||||
}
|
||||
|
||||
fun Activity.disableImmersiveMode() {
|
||||
WindowCompat.setDecorFitsSystemWindows(window, true)
|
||||
val controller = WindowCompat.getInsetsController(window, window.decorView)
|
||||
controller.show(WindowInsetsCompat.Type.systemBars())
|
||||
window.statusBarColor = android.graphics.Color.TRANSPARENT
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.zaneschepke.wireguardautotunnel.util.extensions
|
||||
|
||||
import android.content.pm.PackageInfo
|
||||
import com.zaneschepke.wireguardautotunnel.data.model.TunnelConfig
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.data.entity.TunnelConfig
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import java.math.BigDecimal
|
||||
import java.text.DecimalFormat
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package com.zaneschepke.wireguardautotunnel.util.extensions
|
||||
|
||||
import android.content.Context
|
||||
import androidx.navigation.NavController
|
||||
import com.zaneschepke.networkmonitor.AndroidNetworkMonitor
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.ui.Route
|
||||
import com.zaneschepke.wireguardautotunnel.ui.navigation.isCurrentRoute
|
||||
|
||||
@@ -11,3 +14,25 @@ fun NavController.goFromRoot(route: Route) {
|
||||
launchSingleTop = true
|
||||
}
|
||||
}
|
||||
|
||||
fun AndroidNetworkMonitor.WifiDetectionMethod.asString(context: Context): String {
|
||||
return when (this) {
|
||||
AndroidNetworkMonitor.WifiDetectionMethod.DEFAULT -> context.getString(R.string._default)
|
||||
AndroidNetworkMonitor.WifiDetectionMethod.LEGACY -> context.getString(R.string.legacy)
|
||||
AndroidNetworkMonitor.WifiDetectionMethod.ROOT -> context.getString(R.string.root)
|
||||
AndroidNetworkMonitor.WifiDetectionMethod.SHIZUKU -> context.getString(R.string.shizuku)
|
||||
}
|
||||
}
|
||||
|
||||
fun AndroidNetworkMonitor.WifiDetectionMethod.asDescriptionString(context: Context): String? {
|
||||
return when (this) {
|
||||
AndroidNetworkMonitor.WifiDetectionMethod.LEGACY ->
|
||||
context.getString(R.string.legacy_api_description)
|
||||
AndroidNetworkMonitor.WifiDetectionMethod.ROOT ->
|
||||
context.getString(R.string.use_root_shell_for_wifi)
|
||||
AndroidNetworkMonitor.WifiDetectionMethod.SHIZUKU ->
|
||||
context.getString(R.string.use_shell_via_shizuku)
|
||||
AndroidNetworkMonitor.WifiDetectionMethod.DEFAULT ->
|
||||
context.getString(R.string.use_android_recommended)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.zaneschepke.wireguardautotunnel.viewmodel
|
||||
|
||||
import android.content.pm.PackageManager.PERMISSION_GRANTED
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import androidx.lifecycle.ViewModel
|
||||
@@ -8,6 +9,7 @@ import com.wireguard.android.backend.WgQuickBackend
|
||||
import com.wireguard.android.util.RootShell
|
||||
import com.zaneschepke.logcatter.LogReader
|
||||
import com.zaneschepke.logcatter.model.LogMessage
|
||||
import com.zaneschepke.networkmonitor.AndroidNetworkMonitor
|
||||
import com.zaneschepke.networkmonitor.NetworkMonitor
|
||||
import com.zaneschepke.networkmonitor.NetworkStatus
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
@@ -18,11 +20,11 @@ import com.zaneschepke.wireguardautotunnel.core.tunnel.TunnelManager
|
||||
import com.zaneschepke.wireguardautotunnel.di.AppShell
|
||||
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
||||
import com.zaneschepke.wireguardautotunnel.di.MainDispatcher
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppSettings
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.AppState
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendState
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.ConfigType
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.AppSettings
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.AppState
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.AppUiState
|
||||
@@ -39,14 +41,13 @@ import java.time.Instant
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
import kotlin.collections.component1
|
||||
import kotlin.collections.component2
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import org.amnezia.awg.config.BadConfigException
|
||||
import org.amnezia.awg.config.Config
|
||||
import rikka.shizuku.Shizuku
|
||||
import timber.log.Timber
|
||||
import xyz.teamgravity.pin_lock_compose.PinManager
|
||||
|
||||
@@ -179,7 +180,6 @@ constructor(
|
||||
handleDeleteTrustedSSID(event.ssid, state.appSettings)
|
||||
AppEvent.ToggleAutoTunnelWildcards ->
|
||||
handleToggleAutoTunnelWildcards(state.appSettings)
|
||||
AppEvent.ToggleRootShellWifi -> handleToggleRootShellWifi(state.appSettings)
|
||||
is AppEvent.SaveTrustedSSID ->
|
||||
handleSaveTrustedSSID(event.ssid, state.appSettings)
|
||||
AppEvent.ToggleAutoTunnelOnEthernet ->
|
||||
@@ -212,10 +212,46 @@ constructor(
|
||||
AppEvent.ClearSelectedTunnels -> clearSelectedTunnels()
|
||||
is AppEvent.SetShowModal ->
|
||||
_appViewState.update { it.copy(showModal = event.modalType) }
|
||||
|
||||
is AppEvent.SetDetectionMethod ->
|
||||
handleSetDetectionMethod(event.detectionMethod, state.appSettings)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun handleSetDetectionMethod(
|
||||
detectionMethod: AndroidNetworkMonitor.WifiDetectionMethod,
|
||||
appSettings: AppSettings,
|
||||
) {
|
||||
if (detectionMethod == appSettings.wifiDetectionMethod) return
|
||||
when (detectionMethod) {
|
||||
AndroidNetworkMonitor.WifiDetectionMethod.ROOT -> if (!requestRoot()) return
|
||||
AndroidNetworkMonitor.WifiDetectionMethod.SHIZUKU -> {
|
||||
Shizuku.addRequestPermissionResultListener(
|
||||
Shizuku.OnRequestPermissionResultListener { requestCode: Int, grantResult: Int
|
||||
->
|
||||
if (grantResult != PERMISSION_GRANTED)
|
||||
return@OnRequestPermissionResultListener
|
||||
viewModelScope.launch {
|
||||
saveSettings(appSettings.copy(wifiDetectionMethod = detectionMethod))
|
||||
}
|
||||
}
|
||||
)
|
||||
try {
|
||||
if (Shizuku.checkSelfPermission() != PERMISSION_GRANTED)
|
||||
return Shizuku.requestPermission(123)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
return handleShowMessage(
|
||||
StringValue.StringResource(R.string.shizuku_not_detected)
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
saveSettings(appSettings.copy(wifiDetectionMethod = detectionMethod))
|
||||
}
|
||||
|
||||
private fun handleToggleSelectAllTunnels(tunnels: List<TunnelConf>) =
|
||||
_appViewState.update { it ->
|
||||
val remove = tunnels.size == it.selectedTunnels.size
|
||||
@@ -266,6 +302,7 @@ constructor(
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
private fun handleTunnelErrors() =
|
||||
viewModelScope.launch { tunnelManager.errorEvents.collect { errorEvent -> } }
|
||||
|
||||
@@ -632,14 +669,6 @@ constructor(
|
||||
)
|
||||
)
|
||||
|
||||
private suspend fun handleToggleRootShellWifi(appSettings: AppSettings) {
|
||||
if (requestRoot()) {
|
||||
saveSettings(
|
||||
appSettings.copy(isWifiNameByShellEnabled = !appSettings.isWifiNameByShellEnabled)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun handleToggleTunnelOnEthernet(appSettings: AppSettings) =
|
||||
saveSettings(
|
||||
appSettings.copy(isTunnelOnEthernetEnabled = !appSettings.isTunnelOnEthernetEnabled)
|
||||
@@ -690,7 +719,7 @@ constructor(
|
||||
if (tunnels.isEmpty()) return
|
||||
val (files, shareFileName) =
|
||||
when (configType) {
|
||||
ConfigType.AMNEZIA -> {
|
||||
ConfigType.AM -> {
|
||||
val amFiles = fileUtils.createAmFiles(tunnels)
|
||||
if (amFiles.isEmpty()) {
|
||||
throw IOException("No valid Amnezia config files created")
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package com.zaneschepke.wireguardautotunnel.viewmodel.event
|
||||
|
||||
import android.net.Uri
|
||||
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
|
||||
import com.zaneschepke.networkmonitor.AndroidNetworkMonitor
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.ConfigType
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.AppViewState
|
||||
import com.zaneschepke.wireguardautotunnel.ui.theme.Theme
|
||||
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
||||
@@ -78,6 +79,9 @@ sealed class AppEvent {
|
||||
|
||||
data class SetTheme(val theme: Theme) : AppEvent()
|
||||
|
||||
data class SetDetectionMethod(val detectionMethod: AndroidNetworkMonitor.WifiDetectionMethod) :
|
||||
AppEvent()
|
||||
|
||||
data object ToggleAutoTunnelOnWifi : AppEvent()
|
||||
|
||||
data object ToggleAutoTunnelOnCellular : AppEvent()
|
||||
@@ -90,8 +94,6 @@ sealed class AppEvent {
|
||||
|
||||
data object ToggleAutoTunnelWildcards : AppEvent()
|
||||
|
||||
data object ToggleRootShellWifi : AppEvent()
|
||||
|
||||
data class DeleteTrustedSSID(val ssid: String) : AppEvent()
|
||||
|
||||
data class SaveTrustedSSID(val ssid: String) : AppEvent()
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
@@ -82,7 +82,7 @@
|
||||
<string name="no_browser_detected">Žádný prohlížeč nebyl nalezen</string>
|
||||
<string name="read_logs">Přečíst si logy</string>
|
||||
<string name="pin_created">PIN úspěšně vytvořen</string>
|
||||
<string name="enter_pin">Vložte Váš PIN</string>
|
||||
<string name="enter_pin">Zadejte Váš PIN</string>
|
||||
<string name="enable_app_lock">Zapnout zámek aplikace</string>
|
||||
<string name="restart_on_ping">Restartovat při selhání pingu</string>
|
||||
<string name="mobile_data_tunnel">Nastavit jako tunel pro mobilní data</string>
|
||||
@@ -163,4 +163,162 @@
|
||||
<string name="learn_more">Zjistit více</string>
|
||||
<string name="stop">zastavit</string>
|
||||
<string name="server_ipv4">Překlad názvu hostitele IPv4</string>
|
||||
<string name="select_all">Vybrat vše</string>
|
||||
<string name="share">Sdílet</string>
|
||||
<string name="trusted_ssid_value_description">Odeslat SSID</string>
|
||||
<string name="app_settings">nastavení aplikace</string>
|
||||
<string name="debounce_delay">Zpoždění odezvy</string>
|
||||
<string name="always_on_message">Autorizace připojení VPN byla zamítnuta. Zkontrolujte prosím</string>
|
||||
<string name="bio_not_created">Biometrické údaje nebyly vytvořeny</string>
|
||||
<string name="bio_not_supported">Biometrie není podporována</string>
|
||||
<string name="bio_subtitle">Přihlášení pomocí biometrických údajů</string>
|
||||
<string name="config_error">Chybná konfigurace</string>
|
||||
<string name="prominent_background_location_title">Zpřístupnění stávající polohy na pozadí</string>
|
||||
<string name="vpn_denied_dialog_title">Povolení zamítnuto</string>
|
||||
<string name="app_permission_title">Řídicí most pro WG tunely</string>
|
||||
<string name="app_permission_description">Ovládání funkcí tunelu a automatického tunelu.</string>
|
||||
<string name="enable_remote_app_control">Povolit vzdálené ovládání aplikace</string>
|
||||
<string name="tunnel_starting">Spuštění tunelu</string>
|
||||
<string name="bio_auth_title">Biometrické ověření</string>
|
||||
<string name="nothing_here_yet">Zatím zde nic není!</string>
|
||||
<string name="export_success">Export byl úspěšně dokončen</string>
|
||||
<string name="download">Stáhnout</string>
|
||||
<string name="check_for_update">Zkontrolovat aktualizaci</string>
|
||||
<string name="update_check_failed">Kontrola aktualizace se nezdařila.</string>
|
||||
<string name="version_template">Verze: %1$s</string>
|
||||
<string name="update_download_failed">Stažení aktualizace se nezdařilo.</string>
|
||||
<string name="update_available">Dostupná aktualizace!</string>
|
||||
<string name="download_and_install">Stáhnout a nainstalovat</string>
|
||||
<string name="allow">Povolit</string>
|
||||
<string name="permission_required">Je vyžadováno oprávnění</string>
|
||||
<string name="licenses">Licence</string>
|
||||
<string name="latest_installed">Již používáte nejnovější verzi.</string>
|
||||
<string name="install_updated_permission">Tato aplikace potřebuje oprávnění k instalaci aktualizací.</string>
|
||||
<string name="checking_for_update">Kontrola aktualizací</string>
|
||||
<string name="add_from_url">Přidat z adresy URL</string>
|
||||
<string name="inactive">Neaktivní</string>
|
||||
<string name="auth_error">Chyba: neautorizováno</string>
|
||||
<string name="kernel_name_error">Chyba názvu modulu jádra</string>
|
||||
<string name="export_failed">Export se nezdařil</string>
|
||||
<string name="delete">Smazat</string>
|
||||
<string name="export_tunnels_wireguard">Exportovat tunely jako WireGuard</string>
|
||||
<string name="export_tunnels_amnezia">Exportovat tunely jako Amnezia</string>
|
||||
<string name="remote_key_template">Klíč: %1$s</string>
|
||||
<string name="active">Aktivní</string>
|
||||
<string name="service_running_error">Chyba: Služba není spuštěna</string>
|
||||
<string name="wifi_name_template">Aktivní: %1$s</string>
|
||||
<string name="tunnel_error_template">Tunel selhal s: %1$s</string>
|
||||
<string name="camera_permission_required">Vyžadováno oprávnění k použití fotoaparátu</string>
|
||||
<string name="info">Informace</string>
|
||||
<string name="copy">Kopírovat</string>
|
||||
<string name="status">Stav</string>
|
||||
<string name="launch_app_settings">Spustit nastavení aplikace</string>
|
||||
<string name="tunnel_running">Tunel je v provozu</string>
|
||||
<string name="wildcards_active">Zástupné znaky(wildcards) aktivní</string>
|
||||
<string name="root_accepted">Root shell přijata</string>
|
||||
<string name="background_location_message">Autorizace povolit vždy polohu a/nebo přesná poloha je vyžadováno pro tuto funkci. Viz</string>
|
||||
<string name="update_check_unsupported">Kontrola aktualizací není u tohoto typu sestavení podporována.</string>
|
||||
<string name="background_location_message2">abyste se ujistili, že jsou tato oprávnění povolena</string>
|
||||
<string name="darker">Tmavší</string>
|
||||
<string name="amoled">AMOLED</string>
|
||||
<string name="default_ping_ip">(nepovinné, výchozí hodnota je peers)</string>
|
||||
<string name="monitoring_state_changes">Monitorování změn stavu</string>
|
||||
<string name="pre_up">Před aktivací</string>
|
||||
<string name="pre_down">Před deaktivací</string>
|
||||
<string name="post_up">Po aktivaci</string>
|
||||
<string name="optional_default">"nepovinné, výchozí: "</string>
|
||||
<string name="flavor_template">Varianta: %1$s</string>
|
||||
<string name="security_template">Zabezpečení: %1$s</string>
|
||||
<string name="done">Hotovo</string>
|
||||
<string name="wireguard">WireGuard</string>
|
||||
<string name="amnezia">Amnezia</string>
|
||||
<string name="show_qr">Zobrazit QR kód</string>
|
||||
<string name="always_on_message2">ujistěte se, že je pro všechny ostatní aplikace vypnutá funkce trvalé připojení VPN, a zkuste to znovu</string>
|
||||
<string name="use_wildcards">Použít zástupné znaky(wildcards) pro názvy</string>
|
||||
<string name="multiple">Několik</string>
|
||||
<string name="enter_config_url">Zadejte adresu URL konfigurace</string>
|
||||
<string name="search">Hledat</string>
|
||||
<string name="join_matrix">Připojte se k Matrix komunitě</string>
|
||||
<string name="join_telegram">Připojte se k Telegram komunitě</string>
|
||||
<string name="post_down">Po deaktivaci</string>
|
||||
<string name="invalid_config_error">chyba_neplatné_konfigurace</string>
|
||||
<string name="error_download_failed">Nepodařilo se stáhnout konfiguraci</string>
|
||||
<string name="select">Vybrat</string>
|
||||
<string name="save">Uložit</string>
|
||||
<string name="dns_resolve_error">Chyba překladu dns</string>
|
||||
<string name="bio_update_required">Vyžadována aktualizace biometrického zabezpečení</string>
|
||||
<string name="export_logs">Exportovat uložené protokoly</string>
|
||||
<string name="add_tunnel">Přidat tunel</string>
|
||||
<string name="delete_logs">Smazat a vyčistit protokoly</string>
|
||||
<string name="dropdown">Rozbalovací nabídka</string>
|
||||
<string name="select_all">Vybrat vše</string>
|
||||
<string name="share">Sdílet</string>
|
||||
<string name="trusted_ssid_value_description">Odeslat SSID</string>
|
||||
<string name="app_settings">nastavení aplikace</string>
|
||||
<string name="debounce_delay">Debounce zpoždění</string>
|
||||
<string name="always_on_message">Autorizace připojení VPN byla zamítnuta. Zkontrolujte prosím</string>
|
||||
<string name="bio_not_created">Biometrické údaje nebyly vytvořeny</string>
|
||||
<string name="bio_not_supported">Biometrie není podporována</string>
|
||||
<string name="bio_subtitle">Přihlášení pomocí biometrických údajů</string>
|
||||
<string name="config_error">Chybná konfigurace</string>
|
||||
<string name="prominent_background_location_title">Zpřístupnění stávající polohy na pozadí</string>
|
||||
<string name="vpn_denied_dialog_title">Povolení zamítnuto</string>
|
||||
<string name="app_permission_title">Řídicí most pro WG tunely</string>
|
||||
<string name="app_permission_description">Ovládání funkcí tunelu a automatického tunelu.</string>
|
||||
<string name="enable_remote_app_control">Povolit vzdálené ovládání aplikace</string>
|
||||
<string name="tunnel_starting">Spuštění tunelu</string>
|
||||
<string name="bio_auth_title">Biometrické ověření</string>
|
||||
<string name="nothing_here_yet">Zatím zde nic není!</string>
|
||||
<string name="export_success">Export byl úspěšně dokončen</string>
|
||||
<string name="download">Stáhnout</string>
|
||||
<string name="check_for_update">Zkontrolovat aktualizaci</string>
|
||||
<string name="update_check_failed">Kontrola aktualizace se nezdařila.</string>
|
||||
<string name="version_template">Verze: %1$s</string>
|
||||
<string name="update_download_failed">Stažení aktualizace se nezdařilo.</string>
|
||||
<string name="update_available">Dostupná aktualizace!</string>
|
||||
<string name="download_and_install">Stáhnout a nainstalovat</string>
|
||||
<string name="allow">Povolit</string>
|
||||
<string name="permission_required">Je vyžadováno oprávnění</string>
|
||||
<string name="licenses">Licence</string>
|
||||
<string name="latest_installed">Již používáte nejnovější verzi.</string>
|
||||
<string name="install_updated_permission">Tato aplikace potřebuje oprávnění k instalaci aktualizací.</string>
|
||||
<string name="checking_for_update">Kontrola aktualizací</string>
|
||||
<string name="add_from_url">Přidat z adresy URL</string>
|
||||
<string name="inactive">Neaktivní</string>
|
||||
<string name="auth_error">Chyba: neautorizováno</string>
|
||||
<string name="kernel_name_error">Chyba názvu modulu jádra</string>
|
||||
<string name="export_failed">Export se nezdařil</string>
|
||||
<string name="delete">Smazat</string>
|
||||
<string name="export_tunnels_wireguard">Exportovat tunely jako WireGuard</string>
|
||||
<string name="export_tunnels_amnezia">Exportovat tunely jako Amnezia</string>
|
||||
<string name="remote_key_template">Klíč: %1$s</string>
|
||||
<string name="active">Aktivní</string>
|
||||
<string name="service_running_error">Chyba: Služba není spuštěna</string>
|
||||
<string name="wifi_name_template">Aktivní: %1$s</string>
|
||||
<string name="tunnel_error_template">Tunel selhal s: %1$s</string>
|
||||
<string name="camera_permission_required">Vyžadováno oprávnění k použití fotoaparátu</string>
|
||||
<string name="info">Informace</string>
|
||||
<string name="copy">Kopírovat</string>
|
||||
<string name="status">Stav</string>
|
||||
<string name="launch_app_settings">Spustit nastavení aplikace</string>
|
||||
<string name="tunnel_running">Tunel je v provozu</string>
|
||||
<string name="wildcards_active">Wildcards aktivní</string>
|
||||
<string name="root_accepted">Root shell přijata</string>
|
||||
<string name="background_location_message">Autorizace povolit vždy polohu a/nebo přesná poloha je vyžadováno pro tuto funkci. Viz</string>
|
||||
<string name="update_check_unsupported">Kontrola aktualizací není u tohoto typu sestavení podporována.</string>
|
||||
<string name="background_location_message2">abyste se ujistili, že jsou tato oprávnění povolena</string>
|
||||
<string name="darker">Tmavší</string>
|
||||
<string name="amoled">AMOLED</string>
|
||||
<string name="default_ping_ip">(nepovinné, výchozí hodnota je peers)</string>
|
||||
<string name="monitoring_state_changes">Monitorování změn stavu</string>
|
||||
<string name="pre_up">Před aktivací</string>
|
||||
<string name="pre_down">Před deaktivací</string>
|
||||
<string name="post_up">Po aktivaci</string>
|
||||
<string name="optional_default">"nepovinné, výchozí: "</string>
|
||||
<string name="flavor_template">Varianta: %1$s</string>
|
||||
<string name="security_template">Zabezpečení: %1$s</string>
|
||||
<string name="done">Hotovo</string>
|
||||
<string name="wireguard">WireGuard</string>
|
||||
<string name="amnezia">Amnezia</string>
|
||||
<string name="show_qr">Zobrazit QR kód</string>
|
||||
</resources>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<string name="turn_on_tunnel">Für diese Aktion muss ein aktiver Tunnel bestehen</string>
|
||||
<string name="prominent_background_location_message">Diese Funktion erfordert die Erlaubnis zur Standortbestimmung im Hintergrund, um die Überwachung der WLAN SSID zu ermöglichen, auch wenn die Anwendung geschlossen ist. Weitere Einzelheiten in den Datenschutzbestimmungen, die auf dem Support-Bildschirm verlinkt sind.</string>
|
||||
<string name="prominent_background_location_title">Vereinbarung der Standortberechtigung im Hintergrund</string>
|
||||
<string name="thank_you">Danke fürs Benutzen von WG Tunnel!</string>
|
||||
<string name="thank_you">Danke für die Nutzung von WG Tunnel!</string>
|
||||
<string name="trusted_ssid_value_description">SSID übermitteln</string>
|
||||
<string name="add_tunnels_text">Von Datei oder ZIP hinzufügen</string>
|
||||
<string name="open_file">Datei öffnen</string>
|
||||
@@ -140,7 +140,7 @@
|
||||
<string name="local_logging">Lokales Logging</string>
|
||||
<string name="enable_local_logging">Lokales Logging aktivieren</string>
|
||||
<string name="add_from_clipboard">Aus Zwischenablage einfügen</string>
|
||||
<string name="kill_switch">Kill Switch</string>
|
||||
<string name="kill_switch">Notschalter</string>
|
||||
<string name="automatic">Automatisch</string>
|
||||
<string name="language">Sprache</string>
|
||||
<string name="display_theme">Anzeigetheme</string>
|
||||
@@ -227,4 +227,29 @@
|
||||
<string name="wifi_name_template">Aktiv: %1$s</string>
|
||||
<string name="delete">Löschen</string>
|
||||
<string name="nothing_here_yet">Noch nix hier!</string>
|
||||
<string name="share">Teilen</string>
|
||||
<string name="select_all">Alles auswählen</string>
|
||||
<string name="version_template">Version: %1$s</string>
|
||||
<string name="export_success">Export Erfolg</string>
|
||||
<string name="download">Download</string>
|
||||
<string name="check_for_update">Auf Update prüfen</string>
|
||||
<string name="update_check_failed">Updateprüfung fehlgeschlagen.</string>
|
||||
<string name="checking_for_update">Überpüfe auf Updates</string>
|
||||
<string name="update_download_failed">Updatedownload fehlgeschlagen.</string>
|
||||
<string name="update_available">Update verfügbar!</string>
|
||||
<string name="download_and_install">Herunterladen und installieren</string>
|
||||
<string name="permission_required">Berechtigung erforderlich</string>
|
||||
<string name="licenses">Lizenzen</string>
|
||||
<string name="allow">Erlauben</string>
|
||||
<string name="install_updated_permission">Diese App benötigt die Berechtigung, um Updates zu installieren.</string>
|
||||
<string name="latest_installed">Du verwendesz bereits die neueste Version.</string>
|
||||
<string name="security_template">Sicherheit: %1$s</string>
|
||||
<string name="amoled">AMOLED</string>
|
||||
<string name="flavor_template">Variante: %1$s</string>
|
||||
<string name="darker">Dunkler</string>
|
||||
<string name="update_check_unsupported">Updateprüfung wird bei diesem Build-Typ nicht unterstützt.</string>
|
||||
<string name="wireguard">WireGuard</string>
|
||||
<string name="done">Erledigt</string>
|
||||
<string name="show_qr">QR anzeigen</string>
|
||||
<string name="amnezia">Amnezia</string>
|
||||
</resources>
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
<string name="name">Nombre</string>
|
||||
<string name="always_on_vpn_support">Permitir VPN siempre-activada</string>
|
||||
<string name="location_services_not_detected">Servicios de Ubicación No Detectados</string>
|
||||
<string name="auto_tunneling">Túnel-automático</string>
|
||||
<string name="auto_tunneling">Túnel automático</string>
|
||||
<string name="vpn_on">VPN on</string>
|
||||
<string name="vpn_off">VPN off</string>
|
||||
<string name="create_import">Crear desde cero</string>
|
||||
@@ -68,7 +68,7 @@
|
||||
<string name="error_ssid_exists">SSID existente</string>
|
||||
<string name="error_root_denied">Shell root denegado</string>
|
||||
<string name="error_no_file_explorer">Explorador de archivos no instalado</string>
|
||||
<string name="auto_tunnel_title">Servicio de túnel-automático</string>
|
||||
<string name="auto_tunnel_title">Servicio de túnel automático</string>
|
||||
<string name="delete_tunnel">Eliminar túnel</string>
|
||||
<string name="delete_tunnel_message">¿Estás seguro de que quieres eliminar este túnel?</string>
|
||||
<string name="yes">Sí</string>
|
||||
@@ -103,7 +103,7 @@
|
||||
<string name="always_on_message">Se ha denegado el permiso de conexión VPN. Por favor, compruebe el</string>
|
||||
<string name="always_on_message2">para asegurarse de que el VPN Siempre encendido esté desactivada para todas las demás aplicaciones e inténtelo de nuevo</string>
|
||||
<string name="response_packet_magic_header">Encabezado del paquete de respuesta</string>
|
||||
<string name="junk_packet_maximum_size">Tamaño máximo del paquete basura</string>
|
||||
<string name="junk_packet_maximum_size">Tamaño máximo del paquete innecesario</string>
|
||||
<string name="init_packet_junk_size">Tamaño basura del paquete de inicialización</string>
|
||||
<string name="unsure_how">Si no estás seguro de cómo proceder</string>
|
||||
<string name="see_the">Ver la</string>
|
||||
@@ -152,7 +152,7 @@
|
||||
<string name="light">Claro</string>
|
||||
<string name="dark">Oscuro</string>
|
||||
<string name="use_root_shell_for_wifi">Utilizar el shell root para obtener el nombre del wifi</string>
|
||||
<string name="tunnel_running">Túnel funcionando</string>
|
||||
<string name="tunnel_running">Túnel conectado</string>
|
||||
<string name="monitoring_state_changes">Monitorizando cambios de estado</string>
|
||||
<string name="dynamic">Dinámico</string>
|
||||
<string name="language">Idioma</string>
|
||||
@@ -175,7 +175,7 @@
|
||||
<string name="splt_tunneling">Túnel dividido</string>
|
||||
<string name="pre_up">Pre up</string>
|
||||
<string name="post_up">Post up</string>
|
||||
<string name="pre_down">Pre down</string>
|
||||
<string name="pre_down">Antes de la desactivación</string>
|
||||
<string name="post_down">Post down</string>
|
||||
<string name="quick_actions">Acciones rápidas</string>
|
||||
<string name="remove_amnezia_compatibility">Eliminar compatibilidad con Amnezia</string>
|
||||
@@ -185,4 +185,70 @@
|
||||
<string name="server_ipv4">Resolución de host IPv4</string>
|
||||
<string name="prefer_ipv4">Preferir conexión IPv4</string>
|
||||
<string name="multiple">Múltiple</string>
|
||||
<string name="add_from_url">Añadir desde URL</string>
|
||||
<string name="save">Guardar</string>
|
||||
<string name="select">Seleccionar</string>
|
||||
<string name="join_telegram">Únete a la comunidad de Telegram</string>
|
||||
<string name="join_matrix">Únete a la comunidad de Matrix</string>
|
||||
<string name="share">Compartir</string>
|
||||
<string name="update_download_failed">Fallo al descargar actualización.</string>
|
||||
<string name="licenses">Licencias</string>
|
||||
<string name="checking_for_update">Comprobando actualización</string>
|
||||
<string name="select_all">Seleccionar todos</string>
|
||||
<string name="permission_required">Permiso requerido</string>
|
||||
<string name="active">Activo</string>
|
||||
<string name="copy">Copiar</string>
|
||||
<string name="bio_not_supported">Datos biométricos no admitidos</string>
|
||||
<string name="export_tunnels_wireguard">Exportar túneles como WireGuard</string>
|
||||
<string name="export_tunnels_amnezia">Exportar túneles como Amnezia</string>
|
||||
<string name="wifi_name_template">Activo: %1$s</string>
|
||||
<string name="add_tunnel">Añadir túnel</string>
|
||||
<string name="export_logs">Exportar registros almacenados</string>
|
||||
<string name="inactive">Inactivo</string>
|
||||
<string name="search">Buscar</string>
|
||||
<string name="bio_subtitle">Inicia sesión con tu credencial biométrica</string>
|
||||
<string name="kernel_name_error">error en el nombre del módulo del kernel</string>
|
||||
<string name="camera_permission_required">Permiso de cámara requerido</string>
|
||||
<string name="dns_resolve_error">error de resolución DNS</string>
|
||||
<string name="error_download_failed">Fallo al descargar configuración</string>
|
||||
<string name="enter_config_url">Introducir URL de configuración</string>
|
||||
<string name="bio_update_required">Actualización de seguridad biométrica requerida</string>
|
||||
<string name="delete_logs">Eliminar y limpiar registros</string>
|
||||
<string name="app_permission_title">Puente de Control WG Tunnel</string>
|
||||
<string name="dropdown">Menú desplegable</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="delete">Eliminar</string>
|
||||
<string name="export_failed">Exportación fallida</string>
|
||||
<string name="app_permission_description">Controlar túneles y funcionalidades de auto-túnel</string>
|
||||
<string name="tunnel_error_template">Túnel fallido con: %1$s</string>
|
||||
<string name="invalid_config_error">invalid_config_error</string>
|
||||
<string name="remote_key_template">Clave: %1$s</string>
|
||||
<string name="config_error">Error de configuración</string>
|
||||
<string name="auth_error">error no autorizado</string>
|
||||
<string name="service_running_error">Error de servicio no en ejecución</string>
|
||||
<string name="status">Estado</string>
|
||||
<string name="bio_auth_title">Autenticación biométrica</string>
|
||||
<string name="version_template">Versión: %1$s</string>
|
||||
<string name="check_for_update">Comprobar actualización</string>
|
||||
<string name="update_check_failed">Fallo al comprobar la actualización.</string>
|
||||
<string name="security_template">Seguridad: %1$s</string>
|
||||
<string name="flavor_template">Sabor: %1$s</string>
|
||||
<string name="bio_not_created">Datos biométricos no creados</string>
|
||||
<string name="tunnel_starting">Iniciando túnel</string>
|
||||
<string name="enable_remote_app_control">Activar control remoto de la app</string>
|
||||
<string name="export_success">Éxito al exportar</string>
|
||||
<string name="download">Descargar</string>
|
||||
<string name="latest_installed">Ya se está ejecutando la última versión.</string>
|
||||
<string name="update_available">¡Actualización disponible!</string>
|
||||
<string name="download_and_install">Descargar e instalar</string>
|
||||
<string name="install_updated_permission">Esta app necesita permiso para instalar actualizaciónes.</string>
|
||||
<string name="allow">Permitir</string>
|
||||
<string name="update_check_unsupported">Comprobación de actualización no permitoda en esta compilación.</string>
|
||||
<string name="darker">Más oscuro</string>
|
||||
<string name="amoled">AMOLED</string>
|
||||
<string name="show_qr">Mostrar QR</string>
|
||||
<string name="amnezia">Amnezia</string>
|
||||
<string name="wireguard">WireGuard</string>
|
||||
<string name="done">Hecho</string>
|
||||
<string name="nothing_here_yet">¡No hay nada aquí de momento!</string>
|
||||
</resources>
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">WG Tunnel</string>
|
||||
<string name="error_file_extension">See pole .conf või .zip fail</string>
|
||||
<string name="vpn_channel_name">VPN-i teavituskanal</string>
|
||||
<string name="turn_off_tunnel">See toiming eeldab, et tunnel pole töös</string>
|
||||
<string name="prominent_background_location_message">See funktsionaalsus eeldab, et rakendusel on õigus asukoha ja WiFi SSID tuvastamiseks taustal, seda ka siis, kui rakendus on suletud. Lisateavet leiad Privaatsusreeglite lehelt, mille leiad Kasutajatoe vaatest.</string>
|
||||
<string name="no_tunnels">Ühtegi tunnelit pole veel lisatud!</string>
|
||||
<string name="tunnels">Tunnelid</string>
|
||||
<string name="tunnel_mobile_data">Loo tunnel mobiilse andmesidega</string>
|
||||
<string name="privacy_policy">Vaata privaatsusreegleid</string>
|
||||
<string name="okay">Sobib</string>
|
||||
<string name="tunnel_on_ethernet">Loo tunnel kohtvõrgus</string>
|
||||
<string name="add_tunnels_text">Lisa conf- või zip-failist</string>
|
||||
<string name="open_file">Ava fail</string>
|
||||
<string name="add_from_qr">Lisa QR-koodist</string>
|
||||
<string name="qr_scan">Skaneeri QR-koodi</string>
|
||||
<string name="tunnel_name">Tunneli nimi</string>
|
||||
<string name="exclude">Välista</string>
|
||||
<string name="include">Kaasa</string>
|
||||
<string name="config_changes_saved">Seadistuse muudatused on salvestatud.</string>
|
||||
<string name="public_key">Avalik võti</string>
|
||||
<string name="addresses">Aadressid</string>
|
||||
<string name="dns_servers">Nimeserverid</string>
|
||||
<string name="mtu">MTU</string>
|
||||
<string name="app_permission_description">Halda tunneleid ja tunnelite automaatse käivitamise seadistusi.</string>
|
||||
<string name="thank_you">Tänud, et kasutad rakendust WG Tunnel!</string>
|
||||
<string name="allowed_ips">Lubatud IP-aadressid</string>
|
||||
<string name="endpoint">Otspunkt</string>
|
||||
<string name="name">Nimi</string>
|
||||
<string name="always_on_vpn_support">Luba, et VPN on alati sisse lülitatud</string>
|
||||
<string name="location_services_not_detected">Asukohateenused pole tuvastatavad</string>
|
||||
<string name="auto_tunneling">Automaatne tunneldus</string>
|
||||
<string name="vpn_on">VPN on kasutusel</string>
|
||||
<string name="vpn_off">VPN pole kasutusel</string>
|
||||
<string name="create_import">Loo nullist</string>
|
||||
<string name="turn_on_tunnel">Toiming eeldab aktiivse tunneli olemasolu</string>
|
||||
<string name="licenses">Litsentsid</string>
|
||||
<string name="update_check_unsupported">Selle rakenduse versiooni puhul pole uuenduste kontrollimine toetatud.</string>
|
||||
<string name="darker">Tumedam kujundus</string>
|
||||
<string name="amoled">AMOLED</string>
|
||||
<string name="show_qr">Näita QR-koodi</string>
|
||||
<string name="amnezia">Amnezia</string>
|
||||
<string name="wireguard">WireGuard</string>
|
||||
<string name="done">Valmis</string>
|
||||
<string name="download">Laadi alla</string>
|
||||
<string name="check_for_update">Kontrolli rakenduse uuendusi</string>
|
||||
<string name="update_check_failed">Uuenduse kontrollimine ei õnnestunud.</string>
|
||||
<string name="checking_for_update">Kontrollin uuendusi</string>
|
||||
<string name="latest_installed">Sa juba kasutad viimast versiooni.</string>
|
||||
<string name="update_download_failed">Uuenduse allalaadimine ei õnnestunud.</string>
|
||||
<string name="update_available">Uus versioon on saadaval!</string>
|
||||
<string name="download_and_install">Laadi alla ja paigalda</string>
|
||||
<string name="permission_required">Õigused on vajalikud</string>
|
||||
<string name="install_updated_permission">See rakendus vajab uuenduse paigaldamiseks õigusi.</string>
|
||||
<string name="allow">Luba</string>
|
||||
<string name="nothing_here_yet">Siin pole veel midagi!</string>
|
||||
<string name="share">Jaga</string>
|
||||
<string name="select_all">Vali kõik</string>
|
||||
<string name="export_success">Eksportimine õnnestus</string>
|
||||
<string name="inactive">Pole aktiivne</string>
|
||||
<string name="active">Aktiivne</string>
|
||||
<string name="status">Olek</string>
|
||||
<string name="bio_auth_title">Biomeetriline autentimine</string>
|
||||
<string name="bio_subtitle">Logi sisse kasutades biomeetrilist autentimist</string>
|
||||
<string name="bio_not_supported">Biomeetriline tuvastamine pole toetatud</string>
|
||||
<string name="bio_not_created">Biomeetriline tuvastamine pole seadistatud</string>
|
||||
<string name="bio_update_required">Vajalik on biomeetrilise tuvastamise turvauuendus</string>
|
||||
<string name="tunnel_starting">Tunnel käivitub</string>
|
||||
<string name="enable_remote_app_control">Luba rakenduse kaugjuhtimine</string>
|
||||
<string name="add_from_url">Lisa võrguaadressilt</string>
|
||||
<string name="enter_config_url">Sisesta seadistuse võrguaadress</string>
|
||||
<string name="error_download_failed">Seadistuse allalaadimine ei õnnestunud</string>
|
||||
<string name="save">Salvesta</string>
|
||||
<string name="search">Otsi</string>
|
||||
<string name="select">Vali</string>
|
||||
<string name="join_telegram">Liitu kogukonnaga Telegramis</string>
|
||||
<string name="join_matrix">Liitu kogukonnaga Matrixis</string>
|
||||
<string name="add_tunnel">Lisa tunnel</string>
|
||||
<string name="export_logs">Ekspordi salvestatud logid</string>
|
||||
<string name="delete_logs">Kustuta ja eemalda logid</string>
|
||||
<string name="copy">Kopeeri</string>
|
||||
<string name="info">Teave</string>
|
||||
<string name="export_tunnels_amnezia">Ekspordi tunnelid Amnezia jaoks</string>
|
||||
<string name="export_tunnels_wireguard">Ekspordi tunnelid WireGuardi jaoks</string>
|
||||
<string name="advanced_settings">Täiendavad seadistused</string>
|
||||
<string name="show_scripts">Näita skripte</string>
|
||||
<string name="pre_up">Käivituseelne</string>
|
||||
<string name="post_up">Käivitusjärgne</string>
|
||||
<string name="pre_down">Sulgemiseelne</string>
|
||||
<string name="post_down">Sulgemijärgne</string>
|
||||
<string name="hide_scripts">Peida skriptid</string>
|
||||
<string name="enable_amnezia_compatibility">Lisa ühilduvus Amnezia teenustega</string>
|
||||
<string name="remove_amnezia_compatibility">Eemalda ühilduvus Amnezia teenustega</string>
|
||||
<string name="show_amnezia_properties">Näita Amnezia seadistusi</string>
|
||||
<string name="hide_amnezia_properties">Peida Amnezia seadistused</string>
|
||||
<string name="donate">Toeta projekti rahaliselt</string>
|
||||
<string name="local_logging">Kohalik logimine</string>
|
||||
<string name="enable_local_logging">Logi andmed nutiseadmes</string>
|
||||
<string name="add_from_clipboard">Lisa lõikelaualt</string>
|
||||
<string name="appearance">Välimus</string>
|
||||
<string name="notifications">Teavitused</string>
|
||||
<string name="automatic">Automaatne</string>
|
||||
<string name="light">Hele kujundus</string>
|
||||
<string name="dark">Tume kujundus</string>
|
||||
<string name="dynamic">Dünaamiline kujundus</string>
|
||||
<string name="language">Keel</string>
|
||||
<string name="display_theme">Kujundus</string>
|
||||
<string name="interface_">Võrguliides</string>
|
||||
<string name="private_key">Privaatvõti</string>
|
||||
<string name="copy_public_key">Kopeeri avalik võti</string>
|
||||
<string name="base64_key">base64-kodeeringus võti</string>
|
||||
<string name="comma_separated_list">komadega eraldatud loend</string>
|
||||
<string name="listen_port">Kuulatav port</string>
|
||||
<string name="random">(juhuslik)</string>
|
||||
<string name="optional">(valikuline)</string>
|
||||
<string name="preshared_key">Eeljagatud võti</string>
|
||||
<string name="seconds">sekundit</string>
|
||||
<string name="persistent_keepalive">Pidev elumärksõnum</string>
|
||||
<string name="cancel">Katkesta</string>
|
||||
<string name="error_authentication_failed">Autentimine ei õnnestunud</string>
|
||||
<string name="exclude_lan">Välista kohtvõrgud</string>
|
||||
<string name="include_lan">Kaasa kohtvõrgud</string>
|
||||
<string name="dns_resolve_error">nimelahenduse viga</string>
|
||||
<string name="peer">Partner</string>
|
||||
<string name="add_peer">Lisa partner</string>
|
||||
<string name="default_ping_ip">(valikuline, vaikimisi partneri otspunkt)</string>
|
||||
<string name="rotate_keys">Vaheta võtmeid</string>
|
||||
<string name="delete_tunnel">Kustuta tunnel</string>
|
||||
<string name="delete_tunnel_message">Kas sa oled kindel, et soovid selle tunneli kustutada?</string>
|
||||
<string name="yes">Jah</string>
|
||||
<string name="all">kõik</string>
|
||||
<string name="no_email_detected">E-posti rakendust ei õnnestu tuvastada</string>
|
||||
<string name="no_browser_detected">Veebibrauserit ei õnnestu tuvastada</string>
|
||||
<string name="open_issue">Alusta veateate koostamist</string>
|
||||
<string name="read_logs">Loe logisid</string>
|
||||
<string name="auto">(automaatne)</string>
|
||||
<string name="incorrect_pin">PIN-kood pole õige</string>
|
||||
<string name="pin_created">PIN-koodi loomine õnnestus</string>
|
||||
<string name="enter_pin">Sisesta oma PIN-kood</string>
|
||||
<string name="create_pin">Loo PIN-kood</string>
|
||||
<string name="edit_tunnel">Muuda tunnelit</string>
|
||||
<string name="version">Versioon</string>
|
||||
<string name="settings">Seadistused</string>
|
||||
<string name="support">Kasutajatugi</string>
|
||||
<string name="unknown_error">Tekkis tundmatu viga</string>
|
||||
<string name="tunnel_on_wifi">Loo tunnel ebausaldusväärses WiFi võrgus</string>
|
||||
<string name="email_subject">WG Tunneli kasutajatugi</string>
|
||||
<string name="email_chooser">Saada e-kiri…</string>
|
||||
<string name="docs_description">Loe dokumentatsiooni</string>
|
||||
<string name="email_description">Saada mulle e-kiri</string>
|
||||
<string name="use_kernel">Kasuta kernelimoodulit</string>
|
||||
<string name="error_ssid_exists">SSID on juba olemas</string>
|
||||
<string name="error_root_denied">Juurkasutaja õigustes kest on keelatud</string>
|
||||
<string name="error_no_file_explorer">Failihaldurit pole paigaldatud</string>
|
||||
<string name="set_primary_tunnel">Määra põhiliseks tunneliks</string>
|
||||
<string name="skip">Jäta vahele</string>
|
||||
<string name="export_failed">Eksportimine ei õnnestunud</string>
|
||||
<string name="tunnel_error_template">Viga tunneli töös: %1$s</string>
|
||||
<string name="wifi_name_template">Aktiivne: %1$s</string>
|
||||
<string name="remote_key_template">Võti: %1$s</string>
|
||||
<string name="version_template">Versioon: %1$s</string>
|
||||
<string name="security_template">Turvalisus: %1$s</string>
|
||||
<string name="flavor_template">Levitusviis: %1$s</string>
|
||||
<string name="config_error">seadistusviga</string>
|
||||
<string name="invalid_config_error">vigane_seadistus_viga</string>
|
||||
<string name="kernel_name_error">viga kerneli mooduli nimes</string>
|
||||
<string name="auth_error">viga autentimisel</string>
|
||||
<string name="service_running_error">viga, kus teenus ei toimi</string>
|
||||
<string name="unsure_how">kui sa ei tea, mida järgmiseks teha</string>
|
||||
<string name="see_the">Vaata</string>
|
||||
<string name="getting_started_guide">esimeste toimingute juhendit</string>
|
||||
<string name="restart_at_boot">Käivita alglaadimisel uuesti</string>
|
||||
<string name="vpn_denied_dialog_title">Õigused on puudu</string>
|
||||
<string name="set_custom_ping_internal">Pingi välp (sekundites)</string>
|
||||
<string name="optional_default">"valikuline, vaikimisi: "</string>
|
||||
<string name="never">mitte kunagi</string>
|
||||
<string name="sec">sek</string>
|
||||
<string name="handshake">kätlus</string>
|
||||
<string name="logs">Logid</string>
|
||||
<string name="kill_switch">Kiirpeatamine</string>
|
||||
<string name="trusted_wifi_names">Usaldusväärsete WiFi-võrkude nimed</string>
|
||||
<string name="add_wifi_name">Lisa WiFi võrgunimi</string>
|
||||
<string name="primary_tunnel">Põhiline tunnel</string>
|
||||
<string name="app_settings">rakenduse seadistused</string>
|
||||
<string name="background_location_message2">tagamaks, et need õigused on lubatud</string>
|
||||
<string name="root_accepted">Juurkasutaja kest on lubatud</string>
|
||||
<string name="set_custom_ping_ip">Sisesta muu ip-aadress</string>
|
||||
<string name="learn_more">Lisateave</string>
|
||||
<string name="monitoring_state_changes">Jälgin oleku muudatusi</string>
|
||||
<string name="tunnel_running">Tunnel töötab</string>
|
||||
<string name="tunnel_specific_settings">Tunnelikohased seadistused</string>
|
||||
<string name="kernel_not_supported">Kernel pole toetatud</string>
|
||||
<string name="start_auto">Käivita automaatne tunneldus</string>
|
||||
<string name="stop_auto">Peata automaatne tunneldus</string>
|
||||
<string name="quick_actions">Kiirtoimingud</string>
|
||||
<string name="tunnel_control">Tunneli juhtimine</string>
|
||||
<string name="auto_tunnel">Automaatne tunneldus</string>
|
||||
<string name="delete">Kustuta</string>
|
||||
<string name="camera_permission_required">Vajalik on õigus kasutada kaamerat</string>
|
||||
<string name="dropdown">Rippmenüü</string>
|
||||
<string name="splt_tunneling">Jagatud tunneldus</string>
|
||||
<string name="stop">peata</string>
|
||||
<string name="stop_on_no_internet">Peata internetiühenduse puudumisel</string>
|
||||
<string name="stop_on_internet_loss">Peata tunnel internetiühenduse kadumisel</string>
|
||||
<string name="enable_app_lock">Kasuta rakenduse lukustust</string>
|
||||
<string name="restart_on_ping">Pingimise mittetoimimisel käivita uuesti (beeta)</string>
|
||||
<string name="mobile_data_tunnel">Määra mobiilse andmeside tunneliks</string>
|
||||
<string name="use_tunnel_on_wifi_name">Kasuta tunnelit WiFi nime puhul</string>
|
||||
<string name="kernel">Tuum/Kernel</string>
|
||||
<string name="error_file_format">Vigane tunneli seadistuste vorming</string>
|
||||
<string name="mobile_tunnel">Mobiilside andmetunnel</string>
|
||||
<string name="launch_app_settings">Käivita rakenduse seadistused</string>
|
||||
<string name="use_wildcards">Kasuta nimedes metamärke</string>
|
||||
<string name="wildcards_active">Metamärgid on kasutusel</string>
|
||||
<string name="prefer_ipv4">Eelista IPv4 ühendust</string>
|
||||
<string name="server_ipv4">IPv4 hostinime nimelahendus</string>
|
||||
<string name="junk_packet_count">Rämpspakettide arv</string>
|
||||
<string name="junk_packet_minimum_size">Rämpspaketi miinimumsuurus</string>
|
||||
<string name="junk_packet_maximum_size">Rämpspaketi maksimumsuurus</string>
|
||||
<string name="init_packet_junk_size">Alustava paketi rämpsuosa suurus</string>
|
||||
<string name="response_packet_junk_size">Vastuspaketi rämpsuosa suurus</string>
|
||||
<string name="init_packet_magic_header">Alustava paketi päise kohandatud osa</string>
|
||||
<string name="response_packet_magic_header">Vastuspaketi päise kohandatud osa</string>
|
||||
<string name="transport_packet_magic_header">Transpordipaketi päise kohandatud osa</string>
|
||||
<string name="underload_packet_magic_header">Väikese koormusega paketi päise kohandatud osa</string>
|
||||
<string name="vpn_settings">VPN-i süsteemsed seadistused</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_permission_title">پل کنترل تونل وایرگارد</string>
|
||||
<string name="privacy_policy">مشاهده سیاست حفظ حریم خصوصی</string>
|
||||
<string name="vpn_channel_name">کانال اطلاع رسانی VPN</string>
|
||||
<string name="error_file_extension">فایل .conf یا .zip نیست.</string>
|
||||
<string name="turn_off_tunnel">اقدام نیاز به تونل زنی دارد</string>
|
||||
<string name="no_tunnels">هنوز تونلی اضافه نشده!</string>
|
||||
<string name="tunnels">تونلها</string>
|
||||
<string name="tunnel_mobile_data">تونل روی داده تلفن همراه</string>
|
||||
<string name="okay">باشه</string>
|
||||
<string name="prominent_background_location_title">افشای موقعیت مکانی پسزمینه</string>
|
||||
<string name="thank_you">از استفاده شما از تونل WG متشکریم!</string>
|
||||
<string name="trusted_ssid_value_description">ارسال SSID</string>
|
||||
<string name="add_tunnels_text">از فایل یا زیپ اضافه کنید</string>
|
||||
<string name="app_permission_description">تونلهای کنترل و ویژگیهای تونل خودکار.</string>
|
||||
<string name="tunnel_on_ethernet">تونل روی اترنت</string>
|
||||
<string name="prominent_background_location_message">این ویژگی برای فعال کردن نظارت بر SSID وایفای، حتی در زمان بسته بودن برنامه، به مجوز موقعیت مکانی در پسزمینه نیاز دارد. برای جزئیات بیشتر، لطفاً به سیاست حفظ حریم خصوصی که در صفحه پشتیبانی لینک شده است، مراجعه کنید.</string>
|
||||
<string name="app_name">WG Tunnel</string>
|
||||
</resources>
|
||||
@@ -115,4 +115,16 @@
|
||||
<string name="sec">sek</string>
|
||||
<string name="read_logs">Lue lokitiedot</string>
|
||||
<string name="mobile_tunnel">Mobiilidatatunneli</string>
|
||||
<string name="restart_at_boot">Käynnistä laitteen käynnistyksen yhteydessä</string>
|
||||
<string name="always_on_message2">varmistaaksesi, että Aina päällä oleva VPN on kytketty pois päältä kaikkien muiden sovellusten osalta ja yritä uudelleen</string>
|
||||
<string name="background_location_message">Tätä toimintoa varten tulee sijainnin käyttölupa olla aina sallittuna ja/tai tarkka sijainti käytössä. Katso</string>
|
||||
<string name="app_settings">sovelluksen asetukset</string>
|
||||
<string name="root_accepted">Root hyväksytty</string>
|
||||
<string name="set_custom_ping_ip">Määrittele pingin ip-osoite</string>
|
||||
<string name="set_custom_ping_internal">Pingin aikaväli (sek)</string>
|
||||
<string name="show_amnezia_properties">Näytä Amnezia-asetukset</string>
|
||||
<string name="launch_app_settings">Käynnistä sovelluksen asetukset</string>
|
||||
<string name="use_wildcards">Käytä jokerimerkkiä nimissä</string>
|
||||
<string name="wildcards_active">Jokerimerkit aktivoitu</string>
|
||||
<string name="tunnel_running">Tunneli käytössä</string>
|
||||
</resources>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user