Compare commits

..

1 Commits

Author SHA1 Message Date
dependabot[bot] a848fe0cd6 chore(deps): bump androidGradlePlugin from 8.9.2 to 8.10.0
Bumps `androidGradlePlugin` from 8.9.2 to 8.10.0.

Updates `com.android.application` from 8.9.2 to 8.10.0

Updates `com.android.library` from 8.9.2 to 8.10.0

---
updated-dependencies:
- dependency-name: com.android.application
  dependency-version: 8.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: com.android.library
  dependency-version: 8.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-07 14:02:06 +00:00
139 changed files with 686 additions and 2171 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
- name: Check for new commits
id: check
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
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
+14 -19
View File
@@ -4,9 +4,6 @@ permissions:
packages: write
on:
push:
tags:
- '[0-9]*.[0-9]*.[0-9]*'
workflow_dispatch:
inputs:
track:
@@ -52,19 +49,19 @@ on:
jobs:
build-fdroid:
if: ${{ github.event_name == 'push' || inputs.release_type == 'release' || inputs.flavor == 'fdroid' }}
if: ${{ inputs.release_type == 'release' || inputs.flavor == 'fdroid' }}
uses: ./.github/workflows/build.yml
secrets: inherit
with:
build_type: ${{ github.event_name == 'push' && 'release' || inputs.release_type }}
build_type: ${{ inputs.release_type }}
flavor: fdroid
build-standalone:
if: ${{ github.event_name == 'push' || inputs.release_type == 'release' || inputs.release_type == 'prerelease' || inputs.flavor == 'standalone' }}
if: ${{ inputs.release_type == 'release' || inputs.release_type == 'prerelease' || inputs.flavor == 'standalone' }}
uses: ./.github/workflows/build.yml
secrets: inherit
with:
build_type: ${{ github.event_name == 'push' && 'release' || inputs.release_type }}
build_type: ${{ inputs.release_type }}
flavor: standalone
publish:
@@ -75,7 +72,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event_name == 'push' && github.ref || 'main' }}
ref: main
- name: Install system dependencies
run: |
sudo apt update && sudo apt install -y gh apksigner
@@ -116,7 +113,7 @@ jobs:
merge-multiple: true
- name: Set version release notes
if: ${{ github.event_name == 'push' || inputs.release_type == 'release' }}
if: ${{ 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}")"
@@ -125,7 +122,7 @@ jobs:
echo "EOF" >> $GITHUB_ENV
- name: On prerelease release notes
if: ${{ github.event_name != 'push' && inputs.release_type == 'prerelease' }}
if: ${{ inputs.release_type == 'prerelease' }}
run: |
echo "RELEASE_NOTES=Testing version of app for specific feature." >> $GITHUB_ENV
@@ -159,11 +156,11 @@ jobs:
### Changelog
${{ steps.changelog.outputs.changes }}
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 }}
tag_name: ${{ github.event.inputs.tag_name }}
name: ${{ github.event.inputs.tag_name }}
draft: false
prerelease: ${{ github.event_name != 'push' && inputs.release_type == 'prerelease' }}
make_latest: ${{ github.event_name == 'push' || inputs.release_type == 'release' }}
prerelease: ${{ inputs.release_type == 'prerelease' }}
make_latest: ${{ inputs.release_type == 'release' }}
files: |
${{ github.workspace }}/temp/**/*.apk
env:
@@ -173,7 +170,7 @@ jobs:
runs-on: ubuntu-latest
needs:
- build-fdroid
if: ${{ github.event_name == 'push' || inputs.release_type == 'release' }}
if: inputs.release_type == 'release'
steps:
- name: Dispatch update for fdroid repo
uses: peter-evans/repository-dispatch@v3
@@ -183,7 +180,7 @@ jobs:
event-type: fdroid-update
publish-play:
if: ${{ github.event_name == 'push' || inputs.track != 'none' }}
if: ${{ inputs.track != 'none' }}
name: Publish to Google Play
runs-on: ubuntu-latest
@@ -233,6 +230,4 @@ jobs:
bundler-cache: true
- name: Distribute app to Prod track 🚀
run: |
track=${{ github.event_name == 'push' && 'production' || inputs.track }}
(cd ${{ github.workspace }} && bundle install && bundle exec fastlane $track)
run: (cd ${{ github.workspace }} && bundle install && bundle exec fastlane ${{ inputs.track }})
+1 -12
View File
@@ -123,7 +123,7 @@ android {
licensee {
Constants.allowedLicenses.forEach { allow(it) }
Constants.allowedLicenseUrls.forEach { allowUrl(it) }
allowUrl(Constants.XZING_LICENSE_URL)
}
applicationVariants.all {
@@ -219,10 +219,6 @@ 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") {
@@ -235,10 +231,3 @@ 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,7 +155,9 @@
"columnNames": [
"id"
]
}
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "TunnelConfig",
@@ -225,18 +227,21 @@
"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"
},
{
@@ -270,9 +275,11 @@
"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')"
@@ -1,295 +0,0 @@
{
"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')"
]
}
}
@@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Permissions specific to standalone -->
<!-- Permissions specific to full -->
<!--updater-->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
</manifest>
@@ -49,7 +49,6 @@ 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
@@ -87,8 +86,6 @@ class MainActivity : AppCompatActivity() {
private var lastLocationPermissionState: Boolean? = null
val REQUEST_CODE = 123
@SuppressLint("BatteryLife")
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge(
@@ -287,9 +284,6 @@ 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>()
@@ -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,10 +24,22 @@ import com.zaneschepke.wireguardautotunnel.util.extensions.distinctByKeys
import dagger.hilt.android.AndroidEntryPoint
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
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.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import timber.log.Timber
@AndroidEntryPoint
@@ -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,8 +29,20 @@ import com.zaneschepke.wireguardautotunnel.util.extensions.Tunnels
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import javax.inject.Provider
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
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 timber.log.Timber
@AndroidEntryPoint
@@ -60,8 +72,6 @@ class AutoTunnelService : LifecycleService() {
private val binder = LocalBinder(this)
private var isServiceRunning = false
override fun onCreate() {
super.onCreate()
launchWatcherNotification()
@@ -80,8 +90,6 @@ class AutoTunnelService : LifecycleService() {
}
fun start() {
if (isServiceRunning) return
isServiceRunning = true
kotlin
.runCatching {
launchWatcherNotification()
@@ -94,7 +102,6 @@ class AutoTunnelService : LifecycleService() {
}
fun stop() {
isServiceRunning = false
wakeLock?.let { if (it.isHeld) it.release() }
stopSelf()
}
@@ -254,44 +261,18 @@ class AutoTunnelService : LifecycleService() {
lifecycleScope.launch(ioDispatcher) {
Timber.i("Starting auto-tunnel network event watcher")
val settings = appDataRepository.get().settings.get()
var reevaluationJob: Job? = null
Timber.d("Starting with debounce delay of: ${settings.debounceDelaySeconds} seconds")
autoTunnelStateFlow.debounce(settings.debounceDelayMillis()).collect { watcherState ->
if (watcherState == defaultState) return@collect
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)
}
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")
}
}
}
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
}
}
@@ -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.model.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.entity.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 {
try {
runBlocking {
runBlocking {
try {
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
@@ -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.entity.Settings
import com.zaneschepke.wireguardautotunnel.data.entity.TunnelConfig
import com.zaneschepke.wireguardautotunnel.data.model.Settings
import com.zaneschepke.wireguardautotunnel.data.model.TunnelConfig
@Database(
entities = [Settings::class, TunnelConfig::class],
version = 17,
version = 16,
autoMigrations =
[
AutoMigration(from = 1, to = 2),
@@ -31,11 +31,10 @@ import com.zaneschepke.wireguardautotunnel.data.entity.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(DatabaseConverters::class)
@TypeConverters(DatabaseListConverters::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun settingDao(): SettingsDao
@@ -48,6 +47,3 @@ 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
@@ -1,10 +1,9 @@
package com.zaneschepke.wireguardautotunnel.data
import androidx.room.TypeConverter
import com.zaneschepke.wireguardautotunnel.data.entity.Settings
import kotlinx.serialization.json.Json
class DatabaseConverters {
class DatabaseListConverters {
@TypeConverter
fun listToString(value: MutableList<String>): String {
return Json.encodeToString(value)
@@ -21,10 +20,4 @@ class DatabaseConverters {
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.entity.Settings
import com.zaneschepke.wireguardautotunnel.data.model.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.entity.TunnelConfig
import com.zaneschepke.wireguardautotunnel.data.model.TunnelConfig
import com.zaneschepke.wireguardautotunnel.util.extensions.TunnelConfigs
import kotlinx.coroutines.flow.Flow
@@ -1,24 +0,0 @@
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
}
}
@@ -1,12 +0,0 @@
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>,
)
@@ -1,37 +0,0 @@
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,
)
}
}
}
@@ -1,20 +0,0 @@
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,
)
}
}
}
@@ -1,67 +0,0 @@
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),
)
}
}
@@ -1,48 +0,0 @@
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,4 +1,4 @@
package com.zaneschepke.wireguardautotunnel.data.entity
package com.zaneschepke.wireguardautotunnel.data.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@@ -0,0 +1,54 @@
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
}
}
@@ -0,0 +1,24 @@
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,8 +1,9 @@
package com.zaneschepke.wireguardautotunnel.data.entity
package com.zaneschepke.wireguardautotunnel.data.model
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.zaneschepke.wireguardautotunnel.domain.entity.AppSettings
@Entity
data class Settings(
@@ -31,6 +32,8 @@ 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")
@@ -43,23 +46,61 @@ 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),
) {
enum class WifiDetectionMethod(val value: Int) {
DEFAULT(0),
LEGACY(1),
ROOT(2),
SHIZUKU(3);
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,
)
}
companion object {
fun fromValue(value: Int): WifiDetectionMethod =
entries.find { it.value == value } ?: DEFAULT
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,
)
}
}
}
}
@@ -1,9 +1,10 @@
package com.zaneschepke.wireguardautotunnel.data.entity
package com.zaneschepke.wireguardautotunnel.data.model
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(
@@ -29,7 +30,48 @@ 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,
)
}
}
}
}
@@ -1,6 +1,6 @@
package com.zaneschepke.wireguardautotunnel.data.network
import com.zaneschepke.wireguardautotunnel.data.entity.GitHubRelease
import com.zaneschepke.wireguardautotunnel.data.model.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.entity.GitHubRelease
import com.zaneschepke.wireguardautotunnel.data.model.GitHubRelease
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.plugins.ClientRequestException
@@ -1,6 +1,6 @@
package com.zaneschepke.wireguardautotunnel.data.repository
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
import com.zaneschepke.wireguardautotunnel.domain.repository.AppSettingRepository
import com.zaneschepke.wireguardautotunnel.domain.repository.AppStateRepository
@@ -1,9 +1,8 @@
package com.zaneschepke.wireguardautotunnel.data.repository
import com.zaneschepke.wireguardautotunnel.data.DataStoreManager
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.data.model.GeneralState
import com.zaneschepke.wireguardautotunnel.domain.entity.AppState
import com.zaneschepke.wireguardautotunnel.domain.repository.AppStateRepository
import com.zaneschepke.wireguardautotunnel.ui.theme.Theme
import kotlinx.coroutines.flow.Flow
@@ -154,5 +153,5 @@ class DataStoreAppStateRepository(private val dataStoreManager: DataStoreManager
}
} ?: GeneralState()
}
.map(GeneralStateMapper::toAppState)
.map { it.toAppState() }
}
@@ -2,12 +2,10 @@ 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.model.AppUpdate
import com.zaneschepke.wireguardautotunnel.domain.entity.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
@@ -32,30 +30,24 @@ 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 (isNightly) {
gitHubApi.getNightlyRelease(githubOwner, githubRepo).onFailure(Timber::e)
if (BuildConfig.VERSION_NAME.contains("nightly")) {
gitHubApi.getNightlyRelease(githubOwner, githubRepo)
} else {
gitHubApi.getLatestRelease(githubOwner, githubRepo).onFailure(Timber::e)
gitHubApi.getLatestRelease(githubOwner, githubRepo)
}
release.map { release ->
val apkAsset =
release.assets.find { asset ->
asset.name.startsWith("wgtunnel-${Constants.STANDALONE_FLAVOR}-v") &&
asset.name.endsWith(".apk")
asset.name.startsWith("wgtunnel-full-v") && asset.name.endsWith(".apk")
}
val newVersion =
apkAsset
?.name
?.removePrefix("wgtunnel-${Constants.STANDALONE_FLAVOR}-v")
?.removeSuffix(".apk") ?: return@map null
apkAsset?.name?.removePrefix("wgtunnel-full-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) {
GitHubReleaseMapper.toAppUpdate(release, newVersion)
release.toAppUpdate()
} else {
null
}
@@ -1,10 +1,9 @@
package com.zaneschepke.wireguardautotunnel.data.repository
import com.zaneschepke.wireguardautotunnel.data.dao.SettingsDao
import com.zaneschepke.wireguardautotunnel.data.entity.Settings
import com.zaneschepke.wireguardautotunnel.data.mapper.SettingsMapper
import com.zaneschepke.wireguardautotunnel.data.model.Settings
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
import com.zaneschepke.wireguardautotunnel.domain.model.AppSettings
import com.zaneschepke.wireguardautotunnel.domain.entity.AppSettings
import com.zaneschepke.wireguardautotunnel.domain.repository.AppSettingRepository
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.flowOn
@@ -17,15 +16,15 @@ class RoomSettingsRepository(
) : AppSettingRepository {
override suspend fun save(appSettings: AppSettings) {
withContext(ioDispatcher) { settingsDoa.save(SettingsMapper.toSettings(appSettings)) }
withContext(ioDispatcher) { settingsDoa.save(Settings.from(appSettings)) }
}
override val flow =
settingsDoa.getSettingsFlow().flowOn(ioDispatcher).map(SettingsMapper::toAppSettings)
settingsDoa.getSettingsFlow().flowOn(ioDispatcher).map { it.toAppSettings() }
override suspend fun get(): AppSettings {
return withContext(ioDispatcher) {
SettingsMapper.toAppSettings(settingsDoa.getAll().firstOrNull() ?: Settings())
(settingsDoa.getAll().firstOrNull() ?: Settings()).toAppSettings()
}
}
}
@@ -1,9 +1,9 @@
package com.zaneschepke.wireguardautotunnel.data.repository
import com.zaneschepke.wireguardautotunnel.data.dao.TunnelConfigDao
import com.zaneschepke.wireguardautotunnel.data.mapper.TunnelConfigMapper
import com.zaneschepke.wireguardautotunnel.data.model.TunnelConfig
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.repository.TunnelRepository
import com.zaneschepke.wireguardautotunnel.util.extensions.Tunnels
import kotlinx.coroutines.CoroutineDispatcher
@@ -17,25 +17,19 @@ class RoomTunnelRepository(
) : TunnelRepository {
override val flow =
tunnelConfigDao.getAllFlow().flowOn(ioDispatcher).map {
it.map(TunnelConfigMapper::toTunnelConf)
}
tunnelConfigDao.getAllFlow().flowOn(ioDispatcher).map { it.map { it.toTunnel() } }
override suspend fun getAll(): Tunnels {
return withContext(ioDispatcher) {
tunnelConfigDao.getAll().map(TunnelConfigMapper::toTunnelConf)
}
return withContext(ioDispatcher) { tunnelConfigDao.getAll().map { it.toTunnel() } }
}
override suspend fun save(tunnelConf: TunnelConf) {
withContext(ioDispatcher) {
tunnelConfigDao.save(TunnelConfigMapper.toTunnelConfig(tunnelConf))
}
withContext(ioDispatcher) { tunnelConfigDao.save(TunnelConfig.from(tunnelConf)) }
}
override suspend fun saveAll(tunnelConfList: List<TunnelConf>) {
withContext(ioDispatcher) {
tunnelConfigDao.saveAll(tunnelConfList.map(TunnelConfigMapper::toTunnelConfig))
tunnelConfigDao.saveAll(tunnelConfList.map(TunnelConfig::from))
}
}
@@ -61,21 +55,15 @@ class RoomTunnelRepository(
}
override suspend fun delete(tunnelConf: TunnelConf) {
withContext(ioDispatcher) {
tunnelConfigDao.delete(TunnelConfigMapper.toTunnelConfig(tunnelConf))
}
withContext(ioDispatcher) { tunnelConfigDao.delete(TunnelConfig.from(tunnelConf)) }
}
override suspend fun getById(id: Int): TunnelConf? {
return withContext(ioDispatcher) {
tunnelConfigDao.getById(id.toLong())?.let(TunnelConfigMapper::toTunnelConf)
}
return withContext(ioDispatcher) { tunnelConfigDao.getById(id.toLong())?.toTunnel() }
}
override suspend fun getActive(): Tunnels {
return withContext(ioDispatcher) {
tunnelConfigDao.getActive().map(TunnelConfigMapper::toTunnelConf)
}
return withContext(ioDispatcher) { tunnelConfigDao.getActive().map { it.toTunnel() } }
}
override suspend fun count(): Int {
@@ -83,26 +71,22 @@ class RoomTunnelRepository(
}
override suspend fun findByTunnelName(name: String): TunnelConf? {
return withContext(ioDispatcher) {
tunnelConfigDao.getByName(name)?.let(TunnelConfigMapper::toTunnelConf)
}
return withContext(ioDispatcher) { tunnelConfigDao.getByName(name)?.toTunnel() }
}
override suspend fun findByTunnelNetworksName(name: String): Tunnels {
return withContext(ioDispatcher) {
tunnelConfigDao.findByTunnelNetworkName(name).map(TunnelConfigMapper::toTunnelConf)
tunnelConfigDao.findByTunnelNetworkName(name).map { it.toTunnel() }
}
}
override suspend fun findByMobileDataTunnel(): Tunnels {
return withContext(ioDispatcher) {
tunnelConfigDao.findByMobileDataTunnel().map(TunnelConfigMapper::toTunnelConf)
tunnelConfigDao.findByMobileDataTunnel().map { it.toTunnel() }
}
}
override suspend fun findPrimary(): Tunnels {
return withContext(ioDispatcher) {
tunnelConfigDao.findByPrimary().map(TunnelConfigMapper::toTunnelConf)
}
return withContext(ioDispatcher) { tunnelConfigDao.findByPrimary().map { it.toTunnel() } }
}
}
@@ -21,9 +21,7 @@ import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking
import org.amnezia.awg.backend.Backend
import org.amnezia.awg.backend.GoBackend
import org.amnezia.awg.backend.RootTunnelActionHandler
@@ -114,23 +112,10 @@ class TunnelModule {
fun provideNetworkMonitor(
@ApplicationContext context: Context,
settingsRepository: AppSettingRepository,
@ApplicationScope applicationScope: CoroutineScope,
@AppShell appShell: RootShell,
): NetworkMonitor {
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,
)
return AndroidNetworkMonitor(context) {
runBlocking { settingsRepository.get().isWifiNameByShellEnabled }
}
}
@Singleton
@@ -1,6 +1,4 @@
package com.zaneschepke.wireguardautotunnel.domain.model
import com.zaneschepke.networkmonitor.AndroidNetworkMonitor
package com.zaneschepke.wireguardautotunnel.domain.entity
data class AppSettings(
val id: Int = 0,
@@ -17,30 +15,15 @@ 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,4 +1,4 @@
package com.zaneschepke.wireguardautotunnel.domain.model
package com.zaneschepke.wireguardautotunnel.domain.entity
import com.zaneschepke.wireguardautotunnel.ui.theme.Theme
@@ -1,4 +1,4 @@
package com.zaneschepke.wireguardautotunnel.domain.model
package com.zaneschepke.wireguardautotunnel.domain.entity
data class AppUpdate(
val version: String,
@@ -1,4 +1,4 @@
package com.zaneschepke.wireguardautotunnel.domain.model
package com.zaneschepke.wireguardautotunnel.domain.entity
import com.wireguard.android.backend.Tunnel
import com.wireguard.config.Config
@@ -1,6 +1,6 @@
package com.zaneschepke.wireguardautotunnel.domain.events
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
sealed class AutoTunnelEvent {
data class Start(val tunnelConf: TunnelConf? = null) : AutoTunnelEvent()
@@ -1,6 +1,6 @@
package com.zaneschepke.wireguardautotunnel.domain.repository
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
interface AppDataRepository {
suspend fun getPrimaryOrFirstTunnel(): TunnelConf?
@@ -1,6 +1,6 @@
package com.zaneschepke.wireguardautotunnel.domain.repository
import com.zaneschepke.wireguardautotunnel.domain.model.AppSettings
import com.zaneschepke.wireguardautotunnel.domain.entity.AppSettings
import kotlinx.coroutines.flow.Flow
interface AppSettingRepository {
@@ -1,6 +1,6 @@
package com.zaneschepke.wireguardautotunnel.domain.repository
import com.zaneschepke.wireguardautotunnel.domain.model.AppState
import com.zaneschepke.wireguardautotunnel.domain.entity.AppState
import com.zaneschepke.wireguardautotunnel.ui.theme.Theme
import kotlinx.coroutines.flow.Flow
@@ -1,6 +1,6 @@
package com.zaneschepke.wireguardautotunnel.domain.repository
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
import com.zaneschepke.wireguardautotunnel.util.extensions.Tunnels
import kotlinx.coroutines.flow.Flow
@@ -1,6 +1,6 @@
package com.zaneschepke.wireguardautotunnel.domain.repository
import com.zaneschepke.wireguardautotunnel.domain.model.AppUpdate
import com.zaneschepke.wireguardautotunnel.domain.entity.AppUpdate
import java.io.File
interface UpdateRepository {
@@ -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,7 +16,6 @@ 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 &&
@@ -51,7 +50,6 @@ 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,8 +13,6 @@ sealed class Route {
@Serializable data object AutoTunnelAdvanced : Route()
@Serializable data object WifiDetectionMethod : Route()
@Serializable data object LocationDisclosure : Route()
@Serializable data object Appearance : Route()
@@ -73,7 +73,7 @@ fun IconSurfaceButton(
else MaterialTheme.colorScheme.onSurface,
)
}
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
Column {
Text(title, style = MaterialTheme.typography.titleMedium)
description?.let {
Text(
@@ -188,14 +188,6 @@ 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,
@@ -1,9 +1,18 @@
package com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
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.material.icons.Icons
import androidx.compose.material.icons.outlined.*
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.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
@@ -17,19 +26,14 @@ 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
@@ -43,7 +47,6 @@ fun WifiTunnelingItems(
isWifiNameReadable: () -> Boolean,
): List<SelectionItem> {
val context = LocalContext.current
val navController = LocalNavController.current
val clipboardHelper = rememberClipboardHelper()
val baseItems =
@@ -104,40 +107,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 = {
@@ -202,10 +205,7 @@ fun WifiTunnelingItems(
currentText = currentText,
onSave = { ssid ->
if (
uiState.appSettings.wifiDetectionMethod ==
AndroidNetworkMonitor.WifiDetectionMethod.ROOT ||
uiState.appSettings.wifiDetectionMethod ==
AndroidNetworkMonitor.WifiDetectionMethod.SHIZUKU ||
uiState.appSettings.isWifiNameByShellEnabled ||
isWifiNameReadable()
) {
viewModel.handleEvent(AppEvent.SaveTrustedSSID(ssid))
@@ -1,39 +0,0 @@
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,
)
}
}
}
@@ -51,8 +51,10 @@ fun MainScreen(appUiState: AppUiState, appViewState: AppViewState, viewModel: Ap
rememberLauncherForActivityResult(
contract = ScanContract(),
onResult = { result ->
if (result != null && result.contents.isNotEmpty())
viewModel.handleEvent(AppEvent.ImportTunnelFromQrCode(result.contents))
{
if (result != null && result.contents.isNotEmpty())
viewModel.handleEvent(AppEvent.ImportTunnelFromQrCode(result.contents))
}
},
)
@@ -67,9 +69,7 @@ fun MainScreen(appUiState: AppUiState, appViewState: AppViewState, viewModel: Ap
)
return@rememberLauncherForActivityResult
}
scanLauncher.launch(
ScanOptions().setDesiredBarcodeFormats(ScanOptions.QR_CODE).setBeepEnabled(false)
)
scanLauncher.launch(ScanOptions().setDesiredBarcodeFormats(ScanOptions.QR_CODE))
}
if (appViewState.showModal == AppViewState.ModalType.DELETE) {
@@ -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.model.AppSettings
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.entity.AppSettings
import com.zaneschepke.wireguardautotunnel.domain.entity.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
@@ -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.model.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
@@ -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.model.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
@@ -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.model.AppSettings
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.entity.AppSettings
import com.zaneschepke.wireguardautotunnel.domain.entity.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
@@ -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.model.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
import com.zaneschepke.wireguardautotunnel.ui.Route
import com.zaneschepke.wireguardautotunnel.ui.navigation.LocalIsAndroidTV
@@ -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.model.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
import com.zaneschepke.wireguardautotunnel.ui.common.ExpandingRowListItem
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
@@ -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.model.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelStatistics
import com.zaneschepke.wireguardautotunnel.util.NumberUtils
import com.zaneschepke.wireguardautotunnel.util.extensions.toThreeDecimalPlaceString
@@ -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.model.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.entity.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
@@ -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.model.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.entity.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
@@ -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.model.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.entity.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,6 +1,6 @@
package com.zaneschepke.wireguardautotunnel.ui.screens.main.splittunnel.state
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
data class SplitTunnelUiState(
val loading: Boolean = true,
@@ -10,7 +10,7 @@ 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.model.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
import com.zaneschepke.wireguardautotunnel.ui.common.SectionDivider
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelectionGroupButton
import com.zaneschepke.wireguardautotunnel.ui.navigation.LocalIsAndroidTV
@@ -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.model.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
import com.zaneschepke.wireguardautotunnel.ui.Route
import com.zaneschepke.wireguardautotunnel.ui.common.button.ForwardButton
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
@@ -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.model.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
import com.zaneschepke.wireguardautotunnel.ui.common.config.SubmitConfigurationTextBox
import com.zaneschepke.wireguardautotunnel.util.Constants
@@ -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.model.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
@@ -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.model.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
@@ -35,8 +35,8 @@ 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.entity.TunnelConf
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
@@ -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.model.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
@@ -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.model.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
import com.zaneschepke.wireguardautotunnel.ui.Route
import com.zaneschepke.wireguardautotunnel.ui.common.button.ForwardButton
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
@@ -12,12 +12,6 @@ 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
@@ -78,41 +72,8 @@ fun SupportScreen(viewModel: SupportViewModel = hiltViewModel(), appViewModel: A
verticalArrangement = Arrangement.spacedBy(12.dp, Alignment.Top),
modifier = Modifier.fillMaxWidth(),
) {
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)
Text(uiState.appUpdate?.version ?: "")
Text(uiState.appUpdate?.releaseNotes ?: "")
if (uiState.isLoading) {
LinearProgressIndicator(
progress = { uiState.downloadProgress },
@@ -1,6 +1,6 @@
package com.zaneschepke.wireguardautotunnel.ui.screens.support
import com.zaneschepke.wireguardautotunnel.domain.model.AppUpdate
import com.zaneschepke.wireguardautotunnel.domain.entity.AppUpdate
import com.zaneschepke.wireguardautotunnel.util.StringValue
data class SupportUiState(
@@ -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.model.AppUpdate
import com.zaneschepke.wireguardautotunnel.domain.entity.AppUpdate
import com.zaneschepke.wireguardautotunnel.domain.repository.UpdateRepository
import com.zaneschepke.wireguardautotunnel.util.FileUtils
import com.zaneschepke.wireguardautotunnel.util.StringValue
@@ -1,18 +1,17 @@
package com.zaneschepke.wireguardautotunnel.ui.state
import com.zaneschepke.networkmonitor.NetworkStatus
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.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.domain.state.TunnelState
data class AppUiState(
val appSettings: AppSettings = AppSettings(),
val tunnels: List<TunnelConf> = emptyList(),
val activeTunnels: Map<TunnelConf, TunnelState> = emptyMap(),
val appState: AppState = GeneralStateMapper.toAppState(GeneralState()),
val appState: AppState = GeneralState().toAppState(),
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.model.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
import com.zaneschepke.wireguardautotunnel.util.StringValue
data class AppViewState(
@@ -1,7 +1,7 @@
package com.zaneschepke.wireguardautotunnel.ui.state
import com.wireguard.config.Peer
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
import com.zaneschepke.wireguardautotunnel.util.extensions.joinAndTrim
data class PeerProxy(
@@ -38,6 +38,4 @@ 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.entity.TunnelConfig
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
import com.zaneschepke.wireguardautotunnel.data.model.TunnelConfig
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
import com.zaneschepke.wireguardautotunnel.util.extensions.getInputStreamFromUri
import com.zaneschepke.wireguardautotunnel.util.extensions.installApk
import com.zaneschepke.wireguardautotunnel.util.extensions.launchShareFile
@@ -1,8 +1,8 @@
package com.zaneschepke.wireguardautotunnel.util.extensions
import android.content.pm.PackageInfo
import com.zaneschepke.wireguardautotunnel.data.entity.TunnelConfig
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
import com.zaneschepke.wireguardautotunnel.data.model.TunnelConfig
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
import java.math.BigDecimal
import java.text.DecimalFormat
@@ -1,9 +1,6 @@
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
@@ -14,25 +11,3 @@ 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,6 +1,5 @@
package com.zaneschepke.wireguardautotunnel.viewmodel
import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.net.Uri
import android.os.Build
import androidx.lifecycle.ViewModel
@@ -9,7 +8,6 @@ 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
@@ -20,11 +18,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
@@ -47,7 +45,6 @@ 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
@@ -180,6 +177,7 @@ 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,46 +210,10 @@ 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
@@ -302,7 +264,6 @@ constructor(
}
}
// TODO
private fun handleTunnelErrors() =
viewModelScope.launch { tunnelManager.errorEvents.collect { errorEvent -> } }
@@ -439,6 +400,7 @@ constructor(
private suspend fun handleClipboardImport(config: String, tunnels: List<TunnelConf>) {
runCatching {
Timber.d("Config: $config")
val amConfig = TunnelConf.configFromAmQuick(config)
val tunnelConf = TunnelConf.tunnelConfigFromAmConfig(amConfig)
saveTunnel(
@@ -669,6 +631,14 @@ 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)
@@ -1,9 +1,8 @@
package com.zaneschepke.wireguardautotunnel.viewmodel.event
import android.net.Uri
import com.zaneschepke.networkmonitor.AndroidNetworkMonitor
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
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
@@ -79,9 +78,6 @@ 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()
@@ -94,6 +90,8 @@ 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()
-2
View File
@@ -1,2 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>
-88
View File
@@ -167,94 +167,6 @@
<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>
+5 -71
View File
@@ -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"></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 innecesario</string>
<string name="junk_packet_maximum_size">Tamaño máximo del paquete basura</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 conectado</string>
<string name="tunnel_running">Túnel funcionando</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">Antes de la desactivación</string>
<string name="pre_down">Pre down</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,70 +185,4 @@
<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>
-228
View File
@@ -1,228 +0,0 @@
<?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>
-20
View File
@@ -1,20 +0,0 @@
<?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>
-12
View File
@@ -115,16 +115,4 @@
<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>
-19
View File
@@ -187,23 +187,4 @@
<string name="copy">Copier</string>
<string name="info">Informations</string>
<string name="prefer_ipv4">Préférer une connexion IPv4</string>
<string name="allow">Autoriser</string>
<string name="app_permission_title">Pont de contrôle du tunnel WG</string>
<string name="app_permission_description">Contrôler les tunnels et les fonctions automatiques des tunnels.</string>
<string name="select">Sélectionner</string>
<string name="join_telegram">Rejoindre la communauté Telegram</string>
<string name="join_matrix">Rejoindre la communauté Matrix</string>
<string name="auto_tunnel_channel_description">Un canal pour les notifications de l\'état du tunnel automatique</string>
<string name="tunnel_control">Contrôle du tunnel</string>
<string name="auto_tunnel">Tunnel automatique</string>
<string name="add_tunnel">Ajouter un tunnel</string>
<string name="error_download_failed">Le téléchargement de la configuration a échouée</string>
<string name="multiple">Multiple</string>
<string name="add_from_url">Ajouter depuis un URL</string>
<string name="enter_config_url">Saisissez l\'URL de configuration</string>
<string name="search">Rechercher</string>
<string name="save">Sauvegarder</string>
<string name="copy">Copier</string>
<string name="info">Informations</string>
<string name="prefer_ipv4">Préférer une connexion IPv4</string>
</resources>
-5
View File
@@ -2,8 +2,3 @@
<resources>
<string name="app_name">WG Tunnel</string>
</resources>
<resources>
<string name="app_name">WG Tunnel</string>
<string name="app_permission_description">Alagutak és automatikus alagút funkciók vezérlése.</string>
</resources>
+1 -5
View File
@@ -1,6 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">WG Tunnel</string>
<string name="app_permission_description">Kontroller tunneler og auto-tunnel egenskaper.</string>
<string name="vpn_channel_name">VPN varslingskanal</string>
</resources>
<resources></resources>
+1 -1
View File
@@ -179,7 +179,7 @@
<string name="advanced_settings">Дополнительные настройки</string>
<string name="enable_amnezia_compatibility">Включить совместимость с Amnezia</string>
<string name="include_lan">Включить LAN</string>
<string name="auto_tunnel">Автотуннелирование</string>
<string name="auto_tunnel">Авто-туннелирование</string>
<string name="tunnel_control">Управление туннелями</string>
<string name="kill_switch_off">Без экстренного отключения в доверенных</string>
<string name="prefer_ipv4">Предпочитать соединение IPv4</string>
+137 -141
View File
@@ -2,254 +2,250 @@
<resources>
<string name="peer">پیر</string>
<string name="add_from_qr">کیو آر کوڈ سے شامل کریں</string>
<string name="always_on_vpn_support">ہمیشہ آن وی پی این کی اجازت دیں</string>
<string name="add_peer">پیئر شامل کریں</string>
<string name="turn_off_tunnel">کارروائی کے لیے ٹنل بند کی ضرورت ہے</string>
<string name="rotate_keys">کلید گھمائیں</string>
<string name="copy_public_key">عوامی کلید نقل کریں</string>
<string name="vpn_channel_name">وی پی این نوٹیفکیشن چینل</string>
<string name="always_on_vpn_support">ہمیشہ آن VPN کی اجازت دیں۔</string>
<string name="add_peer">ساتھی شامل کریں۔</string>
<string name="turn_off_tunnel">کارروائی کے لیے سرنگ بند کی ضرورت ہے۔</string>
<string name="rotate_keys">چابیاں گھمائیں۔</string>
<string name="copy_public_key">عوامی کلید کو کاپی کریں۔</string>
<string name="vpn_channel_name">VPN نوٹیفکیشن چینل</string>
<string name="optional">(اختیاری)</string>
<string name="comma_separated_list">کوما سے الگ کردہ فہرست</string>
<string name="location_services_missing_message">ایپ آپ کے آلے پر فعال مقام کی سروسز کا پتہ نہیں لگا رہی۔ ڈیوائس پر منحصر ہے، یہ غیر بھروسہ مند وائی فائی فیچر کے وائی فائی کا نام پڑھنے میں ناکام ہونے کا سبب بن سکتا ہے۔ کیا آپ بہرحال جاری رکھنا چاہیں گے؟</string>
<string name="prominent_background_location_title">پس منظر لوکیشن کا انکشاف</string>
<string name="location_services_missing_message">ایپ آپ کے آلے پر فعال مقام کی خدمات کا پتہ نہیں لگا رہی ہے۔ ڈیوائس پر منحصر ہے، یہ غیر بھروسہ مند وائی فائی فیچر کے وائی فائی کا نام پڑھنے میں ناکام ہونے کا سبب بن سکتا ہے۔ کیا آپ بہرحال جاری رکھنا چاہیں گے؟</string>
<string name="prominent_background_location_title">پس منظر کی جگہ کا انکشاف</string>
<string name="public_key">عوامی کلید</string>
<string name="app_name">ڈبلیو جی ٹنل</string>
<string name="error_file_extension">فائل conf یا zip نہیں ہے۔</string>
<string name="no_tunnels">ابھی تک کوئی ٹنلز شامل نہیں کی گئیں!</string>
<string name="no_tunnels">ابھی تک کوئی سرنگیں شامل نہیں کی گئیں!</string>
<string name="tunnels">ٹنلز</string>
<string name="tunnel_mobile_data">موبائل ڈیٹا پر ٹنل</string>
<string name="privacy_policy">رازداری کی پالیسی دیکھیں</string>
<string name="okay">ٹھیک ہے۔</string>
<string name="tunnel_on_ethernet">ای تھرنیٹ پر ٹنل</string>
<string name="tunnel_on_ethernet">ایتھرنیٹ پر ٹنل</string>
<string name="thank_you">ڈبلیو جی ٹنل استعمال کرنے کا شکریہ!</string>
<string name="trusted_ssid_value_description">ایس ایس آی ڈی جمع کروائیں</string>
<string name="open_file">فائل کھولیں</string>
<string name="trusted_ssid_value_description">SSID جمع کروائیں۔</string>
<string name="open_file">فائل کھولیں۔</string>
<string name="qr_scan">کیو آر اسکین</string>
<string name="tunnel_name">ٹنل کا نام</string>
<string name="exclude">خارج</string>
<string name="include">شامل</string>
<string name="exclude">خارج کرنا</string>
<string name="include">شامل کریں۔</string>
<string name="addresses">پتے</string>
<string name="dns_servers">ڈی این ایس سرورز</string>
<string name="dns_servers">DNS سرورز</string>
<string name="mtu">ایم ٹی یو</string>
<string name="allowed_ips">اجازت یافتہ آئی پی</string>
<string name="allowed_ips">اجازت یافتہ IPs</string>
<string name="endpoint">اختتامی نقطہ</string>
<string name="name">نام</string>
<string name="location_services_not_detected">مقام کی سروسز کا پتہ نہیں چلا</string>
<string name="vpn_on">وی پی این آن</string>
<string name="vpn_off">وی پی این آف</string>
<string name="location_services_not_detected">مقام کی خدمات کا پتہ نہیں چلا</string>
<string name="vpn_on">VPN آن</string>
<string name="vpn_off">VPN آف ہے۔</string>
<string name="turn_on_tunnel">کارروائی کے لیے فعال ٹنل کی ضرورت ہے</string>
<string name="interface_">انٹرفیس</string>
<string name="private_key">نجی کلید</string>
<string name="base64_key">بیس64 کلید</string>
<string name="base64_key">بیس 64 کلید</string>
<string name="listen_port">لسن پورٹ</string>
<string name="random">(بے ترتیب)</string>
<string name="preshared_key">پہلے سے مشترکہ کلید</string>
<string name="cancel">منسوخ</string>
<string name="error_authentication_failed">تصدیق ناکام</string>
<string name="cancel">منسوخ کریں۔</string>
<string name="error_authentication_failed">تصدیق ناکام ہوگئی</string>
<string name="error_authorization_failed">اجازت دینے میں ناکام</string>
<string name="enabled_app_shortcuts">ایپ شارٹ کٹس کو فعال کریں</string>
<string name="enabled_app_shortcuts">ایپ شارٹ کٹس کو فعال کریں۔</string>
<string name="tunnel_on_wifi">ناقابل اعتماد وائی فائی پر ٹنل</string>
<string name="email_subject">ڈبلیو جی ٹنل سپورٹ</string>
<string name="docs_description">دستاویزات پڑھیں</string>
<string name="email_description">مجھے ایک ای میل بھیجیں</string>
<string name="use_kernel">کرنل ماڈیول استعمال کریں</string>
<string name="error_ssid_exists">ایس ایس آی ڈی پہلے سے موجود ہے</string>
<string name="error_root_denied">روٹ شیل سے انکار کر دیا گیا</string>
<string name="error_no_file_explorer">کوئی فائل ایکسپلورر انسٹال نہیں ہے</string>
<string name="email_description">مجھے ایک ای میل بھیجیں۔</string>
<string name="use_kernel">کرنل ماڈیول استعمال کریں۔</string>
<string name="error_ssid_exists">SSID پہلے سے موجود ہے۔</string>
<string name="error_root_denied">روٹ شیل سے انکار کر دیا گیا۔</string>
<string name="error_no_file_explorer">کوئی فائل ایکسپلورر انسٹال نہیں ہے۔</string>
<string name="auto_tunnel_title">خودکار ٹنل سروس</string>
<string name="delete_tunnel">ٹنل حذف کریں</string>
<string name="delete_tunnel">ٹنل کو حذف کریں</string>
<string name="delete_tunnel_message">کیا آپ واقعی اس ٹنل کو حذف کرنا چاہیں گے؟</string>
<string name="yes">ہاں</string>
<string name="yes">جی ہاں</string>
<string name="all">تمام</string>
<string name="no_email_detected">کوئی ای میل ایپ نہیں ملی</string>
<string name="no_browser_detected">کوئی براؤزر نہیں ملا</string>
<string name="open_issue">ایک مسئلہ کھولیں</string>
<string name="read_logs">لاگز پڑھیں</string>
<string name="open_issue">ایک مسئلہ کھولیں۔</string>
<string name="read_logs">نوشتہ جات پڑھیں</string>
<string name="auto">(خودکار)</string>
<string name="incorrect_pin">پن غلط ہے</string>
<string name="pin_created">پن کامیابی کے ساتھ بن گیا</string>
<string name="incorrect_pin">پن غلط ہے۔</string>
<string name="pin_created">پن کامیابی کے ساتھ بن گیا۔</string>
<string name="create_pin">پن بنائیں</string>
<string name="restart_on_ping">پنگ فیل پر دوبارہ شروع کریں (بی ٹا)</string>
<string name="set_primary_tunnel">بنیادی ٹنل کے طور پر سیٹ کریں</string>
<string name="version">ورژن</string>
<string name="junk_packet_count">جنک پیکٹ کی تعداد</string>
<string name="init_packet_junk_size">اِنِٹ پیکٹ جنک سائز</string>
<string name="init_packet_magic_header">اِنِٹ پیکٹ میجک ہیڈر</string>
<string name="junk_packet_count">ردی کے پیکٹ کی گنتی</string>
<string name="init_packet_junk_size">Init پیکٹ ردی کا سائز</string>
<string name="init_packet_magic_header">Init پیکٹ میجک ہیڈر</string>
<string name="transport_packet_magic_header">ٹرانسپورٹ پیکٹ میجک ہیڈر</string>
<string name="see_the">دیکھیں</string>
<string name="error_file_format">غلط ٹنل کنفیگریشن فارمیٹ</string>
<string name="vpn_denied_dialog_title">اجازت نامنظور</string>
<string name="vpn_settings">وی پی این سسٹم کی ترتیبات</string>
<string name="always_on_message">وی پی این کنکشن کی اجازت مسترد کر دی گئی ہے۔ براہ کرم چیک کریں</string>
<string name="vpn_settings">VPN سسٹم کی ترتیبات</string>
<string name="always_on_message">VPN کنکشن کی اجازت مسترد کر دی گئی ہے۔ براہ کرم چیک کریں۔</string>
<string name="app_settings">ایپ کی ترتیبات</string>
<string name="root_accepted">روٹ شیل قبول کر لیا گیا</string>
<string name="set_custom_ping_ip">اپنی مرضی کے مطابق پنگ آئی پی سیٹ کریں</string>
<string name="default_ping_ip">(اختیاری، پیئرز کے لیے طہ شدہ)</string>
<string name="show_amnezia_properties">ایمنیزیا خصوصیات دکھائیں</string>
<string name="root_accepted">روٹ شیل کو قبول کر لیا گیا۔</string>
<string name="set_custom_ping_ip">اپنی مرضی کے مطابق پنگ آئی پی سیٹ کریں۔</string>
<string name="default_ping_ip">(اختیاری، ساتھیوں کے لیے ڈیفالٹس)</string>
<string name="show_amnezia_properties">Amnezia کی خصوصیات دکھائیں۔</string>
<string name="sec">سیکنڈ</string>
<string name="appearance">ظاہری شکل</string>
<string name="notifications">اطلاعات</string>
<string name="automatic">خودکار</string>
<string name="dark">تاریک</string>
<string name="dark">اندھیرا</string>
<string name="dynamic">متحرک</string>
<string name="language">زبان</string>
<string name="display_theme">ڈسپلے تھیم</string>
<string name="trusted_wifi_names">قابل اعتماد وائی فائی نام</string>
<string name="primary_tunnel">بنیادی ٹنل</string>
<string name="mobile_tunnel">موبائل ڈیٹا ٹنل</string>
<string name="skip">نظر انداز</string>
<string name="skip">چھوڑیں۔</string>
<string name="learn_more">مزید جانیں</string>
<string name="kernel_not_supported">کرنل سپورٹڈ نہیں</string>
<string name="start_auto">خودکار ٹنل چلائیں</string>
<string name="donate">پروجیکٹ کے لیے عطیہ کریں</string>
<string name="kernel_not_supported">کرنل تعاون یافتہ نہیں ہے۔</string>
<string name="start_auto">خودکار ٹنل شروع کریں</string>
<string name="donate">پروجیکٹ کے لیے عطیہ کریں۔</string>
<string name="local_logging">مقامی لاگنگ</string>
<string name="enable_local_logging">مقامی لاگنگ فعال کریں</string>
<string name="stop_on_no_internet">بغیر انٹرنیٹ پر روکیں</string>
<string name="stop_on_internet_loss">انٹرنیٹ لاس پر ٹنل روکیں</string>
<string name="vpn_kill_switch">وی پی این کِل سوئچ</string>
<string name="kill_switch_options">کِل سوئچ اختیارات</string>
<string name="allow_lan_traffic">لین ٹریفک کی اجازت دیں</string>
<string name="enable_local_logging">مقامی لاگنگ کو فعال کریں۔</string>
<string name="stop_on_no_internet">بغیر انٹرنیٹ پر رکیں۔</string>
<string name="stop_on_internet_loss">انٹرنیٹ بند ہونے پر ٹنل روکیں</string>
<string name="vpn_kill_switch">VPN کِل سوئچ</string>
<string name="kill_switch_options">کِل سوئچ کے اختیارات</string>
<string name="allow_lan_traffic">LAN ٹریفک کی اجازت دیں۔</string>
<string name="splt_tunneling">سپلٹ ٹنلنگ</string>
<string name="tunnel_specific_settings">ٹنل کی مخصوص ترتیبات</string>
<string name="show_scripts">اسکرپٹس دکھائیں</string>
<string name="show_scripts">اسکرپٹ دکھائیں۔</string>
<string name="pre_up">پری اپ</string>
<string name="post_up">پوسٹ اپ</string>
<string name="pre_down">پری نیچے</string>
<string name="post_down">پوسٹ نیچے</string>
<string name="quick_actions">فوری اقدامات</string>
<string name="advanced_settings">اعلیٰ ترتیبات</string>
<string name="debounce_delay">ڈی باؤنس تاخیر</string>
<string name="hide_amnezia_properties">ایمنیزیا خصوصیات چھپائیں</string>
<string name="exclude_lan">لین خارج کریں</string>
<string name="include_lan">لین شامل کریں</string>
<string name="advanced_settings">اعلی درجے کی ترتیبات</string>
<string name="debounce_delay">ڈیباؤنس میں تاخیر</string>
<string name="hide_amnezia_properties">Amnezia کی خصوصیات کو چھپائیں۔</string>
<string name="exclude_lan">LAN کو خارج کریں۔</string>
<string name="include_lan">LAN شامل کریں۔</string>
<string name="auto_tunnel">خودکار ٹنل</string>
<string name="kill_switch_off">قابل اعتماد پر کِل سوئچ بند کریں</string>
<string name="server_ipv4">آئی پی وی 4 ہوسٹ نیم ریزولیوشن</string>
<string name="kill_switch_off">قابل اعتماد پر کِل سوئچ کو بند کریں۔</string>
<string name="server_ipv4">IPv4 میزبان نام کی قرارداد</string>
<string name="multiple">متعدد</string>
<string name="prominent_background_location_message">اس فیچر کو ایپلیکیشن بند ہونے کے باوجود بھی وای فائی ایس ایس آی ڈی مانیٹرنگ کو فعال کرنے کے لیے پس منظر لوکیشن کی اجازت درکار ہے۔ مزید تفصیلات کے لیے، براہ کرم سپورٹ اسکرین پر منسلک رازداری کی پالیسی دیکھیں۔</string>
<string name="prominent_background_location_message">اس فیچر کو ایپلیکیشن بند ہونے کے باوجود بھی Wi-Fi SSID مانیٹرنگ کو فعال کرنے کے لیے پس منظر کی جگہ کی اجازت درکار ہے۔ مزید تفصیلات کے لیے، براہ کرم سپورٹ اسکرین پر منسلک رازداری کی پالیسی دیکھیں۔</string>
<string name="email_chooser">ای میل بھیجیں…</string>
<string name="add_tunnels_text">فائل یا زپ سے شامل کریں</string>
<string name="config_changes_saved">کنفیگریشن تبدیلیاں محفوظ ہو گئیں۔</string>
<string name="auto_tunneling">خودکار ٹنلنگ</string>
<string name="create_import">شروع سے تخلیق کریں</string>
<string name="create_import">شروع سے تخلیق کریں۔</string>
<string name="unknown_error">نامعلوم خرابی پیش آگئی</string>
<string name="seconds">سیکنڈ</string>
<string name="persistent_keepalive">مسلسل زندہ</string>
<string name="remove_amnezia_compatibility">ایمنیزیا مطابقت ہٹائیں</string>
<string name="light">روشن</string>
<string name="set_ethernet_tunnel">بطور ایتھرنیٹ ٹنل کے سیٹ کریں</string>
<string name="launch_app_settings">ایپ ترتیبات لانچ کریں</string>
<string name="persistent_keepalive">مسلسل زندہ رہنا</string>
<string name="remove_amnezia_compatibility">Amnezia مطابقت کو ہٹا دیں۔</string>
<string name="light">روشنی</string>
<string name="set_ethernet_tunnel">ایتھرنیٹ ٹنل کے طور پر سیٹ کریں</string>
<string name="launch_app_settings">ایپ کی ترتیبات شروع کریں۔</string>
<string name="monitoring_state_changes">ریاستی تبدیلیوں کی نگرانی</string>
<string name="set_custom_ping_internal">پنگ وقفہ (سیکنڈ)</string>
<string name="wifi_name_via_shell">شیل کے ذریعے وائی فائی کا نام</string>
<string name="enter_pin">اپنا پن درج کریں</string>
<string name="enter_pin">اپنا پن درج کریں۔</string>
<string name="getting_started_guide">شروع کرنے کی گائیڈ</string>
<string name="set_custom_ping_cooldown">پنگ دوبارہ شروع کولڈاؤن (سیکنڈ)</string>
<string name="handshake">ہینڈشیک</string>
<string name="set_custom_ping_cooldown">پنگ دوبارہ شروع کول ڈاؤن (سیکنڈ)</string>
<string name="handshake">مصافحہ</string>
<string name="response_packet_magic_header">رسپانس پیکٹ میجک ہیڈر</string>
<string name="unsure_how">اگر آپ کو یقین نہیں کہ کیسے آگے بڑھیں</string>
<string name="unsure_how">اگر آپ کو یقین نہیں ہے کہ کیسے آگے بڑھیں۔</string>
<string name="use_tunnel_on_wifi_name">وائی فائی نام پر ٹنل استعمال کریں</string>
<string name="optional_default">"اختیاری، طہ شدہ: "</string>
<string name="use_root_shell_for_wifi">وائی فائی کا نام حاصل کرنے کے لیے روٹ شیل کا استعمال کریں</string>
<string name="response_packet_junk_size">رسپانس پیکٹ جنک سائز</string>
<string name="optional_default">"اختیاری، ڈیفالٹ: "</string>
<string name="use_root_shell_for_wifi">وائی فائی کا نام حاصل کرنے کے لیے روٹ شیل کا استعمال کریں۔</string>
<string name="response_packet_junk_size">رسپانس پیکٹ ردی کا سائز</string>
<string name="junk_packet_minimum_size">جنک پیکٹ کا کم از کم سائز</string>
<string name="mobile_data_tunnel">موبائل ڈیٹا ٹنل کے طور پر سیٹ کریں</string>
<string name="edit_tunnel">ٹنل میں ترمیم کریں</string>
<string name="enable_app_lock">ایپ لاک فعال کریں</string>
<string name="logs">لاگز</string>
<string name="enable_app_lock">ایپ لاک کو فعال کریں۔</string>
<string name="logs">نوشتہ جات</string>
<string name="kernel">کرنل</string>
<string name="restart_at_boot">بوٹ پر دوبارہ شروع کریں</string>
<string name="support">سپورٹ</string>
<string name="background_location_message2">یہ یقینی بنانے کے لیے کہ یہ اجازتیں فعال ہیں</string>
<string name="restart_at_boot">بوٹ پر دوبارہ شروع کریں۔</string>
<string name="support">حمایت</string>
<string name="background_location_message2">یہ یقینی بنانے کے لیے کہ یہ اجازتیں فعال ہیں۔</string>
<string name="settings">ترتیبات</string>
<string name="junk_packet_maximum_size">جنک پیکٹ کا زیادہ سے زیادہ سائز</string>
<string name="enable_amnezia_compatibility">ایمنیزیا مطابقت فعال کریں</string>
<string name="junk_packet_maximum_size">جنک پیکٹ زیادہ سے زیادہ سائز</string>
<string name="enable_amnezia_compatibility">Amnezia مطابقت کو فعال کریں۔</string>
<string name="kill_switch">کِل سوئچ</string>
<string name="prefer_ipv4">آئی پی وی 4 کنکشن کو ترجیح دیں</string>
<string name="native_kill_switch">مقامی کِل سوئچ</string>
<string name="prefer_ipv4">IPv4 کنکشن کو ترجیح دیں۔</string>
<string name="native_kill_switch">مقامی قتل سوئچ</string>
<string name="auto_tunnel_channel_description">خودکار ٹنل اسٹیٹ کی اطلاعات کے لیے ایک چینل</string>
<string name="use_wildcards">نام وائلڈ کارڈ استعمال کریں</string>
<string name="use_wildcards">نام وائلڈ کارڈ استعمال کریں۔</string>
<string name="stop_auto">خودکار ٹنل روکیں</string>
<string name="add_wifi_name">وائی فائی کا نام شامل کریں</string>
<string name="add_wifi_name">وائی فائی کا نام شامل کریں۔</string>
<string name="wildcards_active">وائلڈ کارڈز فعال</string>
<string name="bypass_lan_for_kill_switch">کِل سوئچ کے لیے لین کو بائی پاس کریں</string>
<string name="stop">روکیں</string>
<string name="bypass_lan_for_kill_switch">کِل سوئچ کے لیے LAN کو بائی پاس کریں۔</string>
<string name="stop">روکو</string>
<string name="never">کبھی نہیں</string>
<string name="underload_packet_magic_header">پیکٹ میجک ہیڈر کو انڈر لوڈ کریں</string>
<string name="underload_packet_magic_header">پیکٹ میجک ہیڈر کو انڈر لوڈ کریں۔</string>
<string name="background_location_message">اس خصوصیت کے لیے ہر وقت مقام کی اجازت اور/یا درست مقام کی ضرورت ہے۔ ملاحظہ فرمائیں</string>
<string name="always_on_message2">یہ یقینی بنانے کے لیے کہ ہمیشہ آن وی پی این دیگر تمام ایپس کے لیے بند ہے اور دوبارہ کوشش کریں</string>
<string name="always_on_message2">یہ یقینی بنانے کے لیے کہ ہمیشہ آن VPN دیگر تمام ایپس کے لیے بند ہے اور دوبارہ کوشش کریں۔</string>
<string name="ethernet_tunnel">ایتھرنیٹ ٹنل</string>
<string name="auto_tunnel_channel_name">خودکار ٹنل نوٹیفکیشن چینل</string>
<string name="tunnel_running">ٹنل چل رہا ہے</string>
<string name="tunnel_control">ٹنل کنٹرول</string>
<string name="vpn_channel_description">وی پی این ریاستی اطلاعات کے لیے ایک چینل</string>
<string name="hide_scripts">اسکرپٹس چھپائیں</string>
<string name="add_from_clipboard">کلپ بورڈ سے شامل کریں</string>
<string name="vpn_channel_description">VPN ریاستی اطلاعات کے لیے ایک چینل</string>
<string name="hide_scripts">اسکرپٹ چھپائیں۔</string>
<string name="add_from_clipboard">کلپ بورڈ سے شامل کریں۔</string>
<string name="remote_key_template">کلید: %1$s</string>
<string name="enable_remote_app_control">ریموٹ ایپ کنٹرول فعال کریں</string>
<string name="kernel_name_error">کرنل ماڈیول نام کی خرابی</string>
<string name="enable_remote_app_control">ریموٹ ایپ کنٹرول کو فعال کریں۔</string>
<string name="kernel_name_error">کرنل ماڈیول نام کی خرابی۔</string>
<string name="app_permission_title">ڈبلیو جی ٹنل کنٹرول برج</string>
<string name="app_permission_description">ٹنل اور خودکار ٹنل کی خصوصیات کو کنٹرول کریں۔</string>
<string name="add_from_url">یو آر ایل سے شامل کریں</string>
<string name="enter_config_url">کنفِگ یو آر ایل درج کریں</string>
<string name="error_download_failed">کنفیگریشن ڈاؤن لوڈ ناکام</string>
<string name="save">محفوظ</string>
<string name="search">تلاش</string>
<string name="select">منتخب</string>
<string name="join_telegram">ٹیلیگرام کمیونٹی میں شامل ہوں</string>
<string name="add_from_url">یو آر ایل سے شامل کریں۔</string>
<string name="enter_config_url">ترتیب یو آر ایل درج کریں۔</string>
<string name="error_download_failed">کنفیگریشن ڈاؤن لوڈ کرنے میں ناکام</string>
<string name="save">محفوظ کریں۔</string>
<string name="search">تلاش کریں۔</string>
<string name="select">منتخب کریں۔</string>
<string name="join_telegram">ٹیلیگرام کمیونٹی میں شامل ہوں۔</string>
<string name="matrix_url">https://matrix.to/#/#wg-tunnel-space:matrix.org</string>
<string name="dropdown">ڈراپ ڈاؤن</string>
<string name="add_tunnel">ٹنل شامل کریں</string>
<string name="export_logs">ذخیرہ شدہ لاگز برآمد کریں</string>
<string name="export_logs">ذخیرہ شدہ نوشتہ جات برآمد کریں۔</string>
<string name="copy">نقل</string>
<string name="info">معلومات</string>
<string name="export_tunnels_wireguard">ٹنلز بطور وائر گارڈ برآمد کریں</string>
<string name="delete">حذف</string>
<string name="camera_permission_required">کیمرے کی اجازت درکار ہے</string>
<string name="export_failed">برآمد ناکام</string>
<string name="export_tunnels_wireguard">وائر گارڈ کے بطور ٹنلز برآمد کریں</string>
<string name="delete">حذف کریں۔</string>
<string name="camera_permission_required">کیمرے کی اجازت درکار ہے۔</string>
<string name="export_failed">ایکسپورٹ ناکام ہو گیا۔</string>
<string name="tunnel_error_template">ٹنل اس کے ساتھ ناکام ہوگئی: %1$s</string>
<string name="wifi_name_template">فعال: %1$s</string>
<string name="config_error">کنفگریشن خرابی</string>
<string name="dns_resolve_error">ڈی این ایس ریزولوشن خرابی</string>
<string name="invalid_config_error">غلط_کنفگریشن_خرابی۔</string>
<string name="config_error">کنفگریشن کی خرابی</string>
<string name="dns_resolve_error">ڈی این ایس ریزولوشن کی خرابی۔</string>
<string name="invalid_config_error">غلط_تشکیل_کی_خرابی۔</string>
<string name="auth_error">غیر مجاز غلطی</string>
<string name="service_running_error">سروس نہیں چل رہی کی خرابی</string>
<string name="service_running_error">سروس نہیں چل رہی خرابی۔</string>
<string name="inactive">غیر فعال</string>
<string name="active">فعال</string>
<string name="status">سٹیٹس</string>
<string name="bio_not_supported">بایومیٹرکس تعاون یافتہ نہیں</string>
<string name="bio_not_created">بائیومیٹرکس نہیں بنائے گئے</string>
<string name="bio_update_required">بائیومیٹرک سیکیورٹی تازہ کاری درکار ہے</string>
<string name="tunnel_starting">ٹنل شروع ہو رہی ہے</string>
<string name="status">حیثیت</string>
<string name="bio_not_supported">بایومیٹرکس تعاون یافتہ نہیں ہیں۔</string>
<string name="bio_not_created">بائیو میٹرکس نہیں بنائے گئے۔</string>
<string name="bio_update_required">بائیو میٹرک سیکیورٹی اپ ڈیٹ درکار ہے۔</string>
<string name="tunnel_starting">ٹنل چل رہی ہے</string>
<string name="nothing_here_yet">ابھی تک یہاں کچھ نہیں!</string>
<string name="join_matrix">میٹرکس کمیونٹی میں شامل ہوں</string>
<string name="delete_logs">لاگز حذف اور صاف کریں</string>
<string name="export_tunnels_amnezia">ٹنلز بطور ایمنیزیا برآمد کریں</string>
<string name="bio_subtitle">اپنی بائیومیٹرک اسناد کا استعمال کرتے ہوئے لاگ ان کریں</string>
<string name="bio_auth_title">بائیومیٹرک تصدیق</string>
<string name="share">اشتراک</string>
<string name="select_all">سبھی کو منتخب کریں</string>
<string name="check_for_update">تازہ کاری کے لیے جانچ کریں</string>
<string name="update_check_failed">تازہ کاری کی جانچ ناکام ہو گئی۔</string>
<string name="checking_for_update">تازہ کاری کی جانچ ہو رہی ہے</string>
<string name="update_download_failed">تازہ کاری ڈاؤن لوڈ ناکام ہو گیا۔</string>
<string name="update_available">تازہ کاری دستیاب ہے!</string>
<string name="download_and_install">ڈاؤن لوڈ اور انسٹال کریں</string>
<string name="permission_required">اجازت درکار</string>
<string name="install_updated_permission">اس ایپ کو تازہ کاریاں انسٹال کرنے کے لیے اجازت درکار ہے۔</string>
<string name="allow">اجازت دیں</string>
<string name="join_matrix">میٹرکس کمیونٹی میں شامل ہوں۔</string>
<string name="delete_logs">نوشتہ جات کو حذف اور صاف کریں۔</string>
<string name="export_tunnels_amnezia">ایمنیزیا کے طور پر ٹنلز برآمد کریں</string>
<string name="bio_subtitle">اپنی بائیو میٹرک اسناد کا استعمال کرتے ہوئے لاگ ان کریں۔</string>
<string name="bio_auth_title">بائیو میٹرک تصدیق</string>
<string name="share">شیئر کریں۔</string>
<string name="select_all">سبھی کو منتخب کریں۔</string>
<string name="check_for_update">اپ ڈیٹ کے لیے چیک کریں۔</string>
<string name="update_check_failed">اپ ڈیٹ چیک ناکام ہو گیا۔</string>
<string name="checking_for_update">اپ ڈیٹس کی جانچ ہو رہی ہے۔</string>
<string name="update_download_failed">اپ ڈیٹ ڈاؤن لوڈ ناکام ہو گیا۔</string>
<string name="update_available">اپ ڈیٹ دستیاب ہے!</string>
<string name="download_and_install">ڈاؤن لوڈ اور انسٹال کریں۔</string>
<string name="permission_required">اجازت درکار ہے۔</string>
<string name="install_updated_permission">اس ایپ کو اپ ڈیٹس انسٹال کرنے کے لیے اجازت درکار ہے۔</string>
<string name="allow">اجازت دیں۔</string>
<string name="licenses">لائسنس</string>
<string name="download">ڈاؤن لوڈ</string>
<string name="download">ڈاؤن لوڈ کریں۔</string>
<string name="version_template">ورژن: %1$s</string>
<string name="export_success">برآمد کامیاب</string>
<string name="export_success">برآمد کی کامیابی</string>
<string name="latest_installed">آپ پہلے ہی تازہ ترین ورژن چلا رہے ہیں۔</string>
<string name="darker">بہت گہرا</string>
<string name="darker">گہرا</string>
<string name="amoled">ایمولیڈ</string>
<string name="security_template">سیکیورٹی: %1$s</string>
<string name="update_check_unsupported">تازہ کاری معائنہ اس قسم کی تعمیر کی حمایت نہیں کرتا ہے۔</string>
<string name="update_check_unsupported">اپ ڈیٹ چیک اس قسم کی تعمیر کی حمایت نہیں کرتا ہے۔</string>
<string name="flavor_template">ذائقہ: %1$s</string>
<string name="show_qr">کیو آر دکھائیں</string>
<string name="amnezia">ایمنیزیا</string>
<string name="wireguard">وائرگارڈ</string>
<string name="done">ہوگیا</string>
</resources>
+6 -50
View File
@@ -38,8 +38,8 @@
<string name="delete_tunnel">刪除隧道</string>
<string name="delete_tunnel_message">您確定要刪除此隧道?</string>
<string name="yes"></string>
<string name="no_browser_detected">找不到瀏覽器</string>
<string name="no_email_detected">找不到電子郵件應用程式</string>
<string name="no_browser_detected">未安裝瀏覽器</string>
<string name="no_email_detected">未安裝電子郵件應用程式</string>
<string name="open_issue">建立新的問題</string>
<string name="auto">(自動)</string>
<string name="create_pin">建立 PIN</string>
@@ -55,7 +55,7 @@
<string name="getting_started_guide">取得入門指南</string>
<string name="settings">設定</string>
<string name="restart_at_boot">開機時重新啟動</string>
<string name="junk_packet_count">垃圾封包數</string>
<string name="junk_packet_count">無效封包數</string>
<string name="set_custom_ping_internal">Ping 間隔 (秒)</string>
<string name="app_settings">應用程式設定</string>
<string name="logs">日誌</string>
@@ -82,12 +82,12 @@
<string name="include_lan">包含 LAN</string>
<string name="unknown_error">發生未知錯誤</string>
<string name="error_file_format">無效的隧道組態檔案格式</string>
<string name="endpoint">端點</string>
<string name="endpoint">端點</string>
<string name="location_services_not_detected">未啟用定位服務</string>
<string name="junk_packet_maximum_size">垃圾封包最大</string>
<string name="junk_packet_maximum_size">無效封包最大大小</string>
<string name="no_tunnels">還沒有新增任何隧道!</string>
<string name="allowed_ips">允許的 IP</string>
<string name="junk_packet_minimum_size">垃圾封包最小</string>
<string name="junk_packet_minimum_size">無效封包最小大小</string>
<string name="error_no_file_explorer">未安裝任何檔案管理器</string>
<string name="unsure_how">如果您不確定如何進行</string>
<string name="see_the">請參閱</string>
@@ -176,48 +176,4 @@
<string name="amoled">AMOLED</string>
<string name="update_check_unsupported">更新檢查不支援此建置類型。</string>
<string name="darker">黑色</string>
<string name="preshared_key">預分享金鑰</string>
<string name="error_authentication_failed">驗證失敗</string>
<string name="transport_packet_magic_header">傳輸封包魔法標頭</string>
<string name="default_ping_ip">(可選,預設選擇端點)</string>
<string name="tunnel_running">隧道運作中</string>
<string name="hide_scripts">隱藏指令碼</string>
<string name="underload_packet_magic_header">負載封包魔法標頭</string>
<string name="email_chooser">發送電子郵件…</string>
<string name="handshake">上次交握時間</string>
<string name="vpn_channel_description">VPN 狀態通知頻道</string>
<string name="auto_tunnel_channel_name">自動隧道管理 通知頻道</string>
<string name="show_scripts">顯示指令碼</string>
<string name="init_packet_junk_size">初始封包垃圾大小</string>
<string name="response_packet_junk_size">回應封包垃圾大小</string>
<string name="auto_tunnel_channel_description">自動隧道管理服務狀態 通知頻道</string>
<string name="prominent_background_location_message">此功能需要背景位置權限以啟用即使應用程式關閉也會持續執行的 Wi-Fi SSID 自動掃描。如果想知道更多資訊,請參閱支援頁面的隱私政策。</string>
<string name="tunnel_starting">隧道正在啟動</string>
<string name="error_authorization_failed">授權失敗</string>
<string name="vpn_channel_name">VPN 通知頻道</string>
<string name="peer">端點</string>
<string name="turn_off_tunnel">此操作需要關閉隧道</string>
<string name="turn_on_tunnel">此操作需要一個已建立的隧道</string>
<string name="rotate_keys">輪換金鑰</string>
<string name="add_peer">新增端點</string>
<string name="init_packet_magic_header">初始封包魔法標頭</string>
<string name="response_packet_magic_header">回應封包魔法標頭</string>
<string name="background_location_message">此功能需要任何時間都被允許使用位置權限或者精確位置。請參閱</string>
<string name="always_on_message2">確保 永久連線的 VPN 已經關閉並再試一次</string>
<string name="enter_config_url">輸入組態 URL</string>
<string name="error_download_failed">下載組態失敗</string>
<string name="config_error">組態錯誤</string>
<string name="invalid_config_error">組態無效</string>
<string name="remote_key_template">金鑰: %1$s</string>
<string name="flavor_template">風格: %1$s</string>
<string name="security_template">安全性: %1$s</string>
<string name="amnezia">Amnezia</string>
<string name="wireguard">WireGuard</string>
<string name="done">完成</string>
<string name="show_qr">顯示 QR code</string>
<string name="optional_default">"可選,預設: "</string>
<string name="pre_up">啟動前</string>
<string name="post_up">啟動後</string>
<string name="pre_down">關閉前</string>
<string name="post_down">關閉後</string>
</resources>
+2 -21
View File
@@ -152,7 +152,7 @@
<string name="learn_more">Learn more</string>
<string name="wildcards_active">Wildcards active</string>
<string name="wifi_name_via_shell">Wifi name via shell</string>
<string name="use_root_shell_for_wifi">Use a root shell to get Wi-Fi information, preventing the need for location permissions</string>
<string name="use_root_shell_for_wifi">Use root shell to get wifi name</string>
<string name="kernel_not_supported">Kernel not supported</string>
<string name="start_auto">Start auto-tunnel</string>
<string name="stop_auto">Stop auto-tunnel</string>
@@ -222,7 +222,6 @@
<string name="remote_key_template">Key: %1$s</string>
<string name="version_template">Version: %1$s</string>
<string name="security_template">Security: %1$s</string>
<string name="current_template">Current: %1$s</string>
<string name="flavor_template">Flavor: %1$s</string>
<string name="config_error">config error</string>
<string name="dns_resolve_error">dns resolution error</string>
@@ -256,29 +255,11 @@
<string name="install_updated_permission">This app needs permission to install updates.</string>
<string name="allow">Allow</string>
<string name="licenses">Licenses</string>
<string name="update_check_unsupported">Update check is not supported for this build type.</string>
<string name="update_check_unsupported">Update check not supported this build type.</string>
<string name="darker">Darker</string>
<string name="amoled">AMOLED</string>
<string name="show_qr">Show QR</string>
<string name="amnezia">Amnezia</string>
<string name="wireguard">WireGuard</string>
<string name="done">Done</string>
<string name="use_deprecated_wifi_api">Use deprecated Wi-Fi API</string>
<string name="app_restart_required">App restart required</string>
<string name="wifi_detection_method">Wi-Fi detection method</string>
<string name="_default">Default</string>
<string name="legacy">Legacy</string>
<string name="root">Root</string>
<string name="shizuku">Shizuku</string>
<string name="legacy_api_description">Use an Android 12 deprecated method for getting Wi-Fi information that limits
location queries, but may not work reliably on newer devices
</string>
<string name="use_shell_via_shizuku">Use shell via Shizuku to get Wi-Fi information, preventing the need for
location permission on non-rooted devices
</string>
<string name="use_android_recommended">Use Android\'s recommended method for getting Wi-Fi information, based on
Android version
</string>
<string name="release_notes">Release notes</string>
<string name="shizuku_not_detected">Shizuku not detected</string>
</resources>

Some files were not shown because too many files have changed in this diff Show More