Compare commits

..

10 Commits

Author SHA1 Message Date
zaneschepke d78443e7fa chore: release version 5.0.0 2026-06-07 02:05:19 -04:00
zaneschepke 40d0466c14 fix: init notification race 2026-06-07 01:33:35 -04:00
zaneschepke 5220c1a10c fix: proxy settings save bug, improve feature descriptions 2026-06-07 00:35:08 -04:00
zaneschepke 0e4e421628 fix: backend races, auto tunnel override bug, tunnel state display bug 2026-06-06 05:18:24 -04:00
zaneschepke abdbf74755 fix: tunnel start/stop race on fast toggles 2026-06-05 04:24:34 -04:00
zaneschepke 5bc49eec50 fix: auto tunnel should be neutral on no connectivity state 2026-06-05 02:32:33 -04:00
zaneschepke c7040b8081 fix: make stop on not internet deferred to prevent unwanted stops on flaky network states 2026-06-05 02:16:21 -04:00
zaneschepke 26ecfec3fc fix: root shell prompt for scripts 2026-06-05 01:45:11 -04:00
zaneschepke 5408cf3954 feat: move active tunnels to top
closes #915
2026-06-05 01:30:48 -04:00
Weblate (bot) 22c4a303fc feat(lang): updated localizations (#1256)
Co-authored-by: Prefill add-on <noreply-addon-prefill@weblate.org>
Co-authored-by: Matthaiks <kitynska@gmail.com>
Co-authored-by: Fill read-only add-on <noreply-addon-fill@weblate.org>
2026-06-05 00:49:40 -04:00
86 changed files with 1364 additions and 1972 deletions
@@ -10,13 +10,18 @@ import com.zaneschepke.wireguardautotunnel.core.notification.TunnelNotificationS
import com.zaneschepke.wireguardautotunnel.domain.repository.TunnelRepository import com.zaneschepke.wireguardautotunnel.domain.repository.TunnelRepository
import com.zaneschepke.wireguardautotunnel.ui.state.DisplayTunnelState import com.zaneschepke.wireguardautotunnel.ui.state.DisplayTunnelState
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlin.time.Duration.Companion.milliseconds
class TunnelEventDispatcher( class TunnelEventDispatcher(
private val notificationManager: TunnelNotificationService, private val notificationManager: TunnelNotificationService,
@@ -24,6 +29,7 @@ class TunnelEventDispatcher(
private val context: Context, private val context: Context,
) { ) {
@OptIn(FlowPreview::class)
fun bind( fun bind(
scope: CoroutineScope, scope: CoroutineScope,
providerEvents: Flow<TunnelEvent>, providerEvents: Flow<TunnelEvent>,
@@ -102,7 +108,8 @@ class TunnelEventDispatcher(
val tunnel = allTunnels.find { it.id == id } ?: return@mapNotNull null val tunnel = allTunnels.find { it.id == id } ?: return@mapNotNull null
val displayState = val displayState =
displayStates[id] ?: DisplayTunnelState.from(activeTunnel) displayStates[id]
?: DisplayTunnelState.from(activeTunnel, System.currentTimeMillis())
TunnelNotificationLine( TunnelNotificationLine(
id = id, id = id,
@@ -113,6 +120,7 @@ class TunnelEventDispatcher(
.associateBy { it.id } .associateBy { it.id }
} }
.distinctUntilChanged() .distinctUntilChanged()
.debounce(500.milliseconds) // give the service notification time to display
.onEach { vpnLines -> notificationManager.updateVpnPersistentNotification(vpnLines) } .onEach { vpnLines -> notificationManager.updateVpnPersistentNotification(vpnLines) }
.launchIn(scope) .launchIn(scope)
@@ -129,7 +137,8 @@ class TunnelEventDispatcher(
val tunnel = allTunnels.find { it.id == id } ?: return@mapNotNull null val tunnel = allTunnels.find { it.id == id } ?: return@mapNotNull null
val displayState = val displayState =
displayStates[id] ?: DisplayTunnelState.from(activeTunnel) displayStates[id]
?: DisplayTunnelState.from(activeTunnel, System.currentTimeMillis())
TunnelNotificationLine( TunnelNotificationLine(
id = id, id = id,
@@ -140,6 +149,7 @@ class TunnelEventDispatcher(
.associateBy { it.id } .associateBy { it.id }
} }
.distinctUntilChanged() .distinctUntilChanged()
.debounce(500.milliseconds) // give the service notification time to display
.onEach { proxyLines -> .onEach { proxyLines ->
notificationManager.updateProxyPersistentNotification(proxyLines) notificationManager.updateProxyPersistentNotification(proxyLines)
} }
@@ -47,12 +47,15 @@ class TunnelCoordinator(
scope: CoroutineScope, scope: CoroutineScope,
) { ) {
private val _userOverrideFlow = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
val userOverrideFlow = _userOverrideFlow.asSharedFlow()
@OptIn(FlowPreview::class) @OptIn(FlowPreview::class)
val tunnelDisplayStates: StateFlow<Map<Int, DisplayTunnelState>> = val tunnelDisplayStates: StateFlow<Map<Int, DisplayTunnelState>> =
tunnelProvider.backendStatus tunnelProvider.backendStatus
.map { status -> .map { status ->
status.activeTunnels.mapValues { (_, activeTunnel) -> status.activeTunnels.mapValues { (_, activeTunnel) ->
DisplayTunnelState.from(activeTunnel) DisplayTunnelState.from(activeTunnel, System.currentTimeMillis())
} }
} }
.debounce(400L.milliseconds) .debounce(400L.milliseconds)
@@ -107,11 +110,24 @@ class TunnelCoordinator(
) = tunnelMutex.withLock { ) = tunnelMutex.withLock {
// wait for app to be bootstrapped // wait for app to be bootstrapped
bootstrapCoordinator.isReady.first { it } bootstrapCoordinator.isReady.first { it }
if (source == TunnelActionSource.USER) {
_userOverrideFlow.tryEmit(Unit)
}
// enforce single tunnel, for now
if (backendStatus.value.activeTunnels.isNotEmpty()) {
stopActiveTunnelsInternal()
}
startTunnelInternal(config, source) startTunnelInternal(config, source)
} }
suspend fun stopTunnel(id: Int, source: TunnelActionSource = TunnelActionSource.USER) = suspend fun stopTunnel(id: Int, source: TunnelActionSource = TunnelActionSource.USER) =
tunnelMutex.withLock { tunnelMutex.withLock {
if (source == TunnelActionSource.USER) {
_userOverrideFlow.tryEmit(Unit)
}
stopTunnelInternal(id, source) stopTunnelInternal(id, source)
} }
@@ -167,9 +183,6 @@ class TunnelCoordinator(
} }
} }
// TODO for now, enforce single tunnel until multi-tunneling is implement
stopActiveTunnelsInternal()
tunnelProvider tunnelProvider
.startTunnel( .startTunnel(
tunnel = tunnel =
@@ -17,6 +17,7 @@ class AutoTunnelEngine {
} }
} }
Decision.None -> AutoTunnelEvent.DoNothing Decision.None -> AutoTunnelEvent.DoNothing
is Decision.StopDueToNoInternet -> AutoTunnelEvent.StopAllDueToNoInternet
} }
} }
@@ -27,13 +28,17 @@ class AutoTunnelEngine {
val activeTunnelIds = backend.activeTunnels.keys.toSet() val activeTunnelIds = backend.activeTunnels.keys.toSet()
val desiredTunnels = resolveDesiredTunnels(state).map { it.id }.toSet() if (!network.hasInternet()) {
return if (settings.isStopOnNoInternetEnabled) {
// stop condition overrides everything Decision.StopDueToNoInternet
if (!network.hasInternet() && settings.isStopOnNoInternetEnabled) { } else {
return Decision.Sync(start = emptySet(), stop = activeTunnelIds) // keep tunnel state neutral on no internet otherwise
Decision.None
}
} }
val desiredTunnels = resolveDesiredTunnels(state).map { it.id }.toSet()
val toStart = desiredTunnels - activeTunnelIds val toStart = desiredTunnels - activeTunnelIds
val toStop = activeTunnelIds - desiredTunnels val toStop = activeTunnelIds - desiredTunnels
@@ -96,5 +101,7 @@ class AutoTunnelEngine {
data class Sync(val start: Set<TunnelConfig>, val stop: Set<Int>) : Decision data class Sync(val start: Set<TunnelConfig>, val stop: Set<Int>) : Decision
data object None : Decision data object None : Decision
data object StopDueToNoInternet : Decision
} }
} }
@@ -16,7 +16,6 @@ import com.zaneschepke.wireguardautotunnel.domain.enums.NotificationAction
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelActionSource import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelActionSource
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelMode import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelMode
import com.zaneschepke.wireguardautotunnel.domain.events.AutoTunnelEvent import com.zaneschepke.wireguardautotunnel.domain.events.AutoTunnelEvent
import com.zaneschepke.wireguardautotunnel.domain.events.TunnelActionEvent
import com.zaneschepke.wireguardautotunnel.domain.model.AutoTunnelSettings import com.zaneschepke.wireguardautotunnel.domain.model.AutoTunnelSettings
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConfig import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConfig
import com.zaneschepke.wireguardautotunnel.domain.repository.AutoTunnelSettingsRepository import com.zaneschepke.wireguardautotunnel.domain.repository.AutoTunnelSettingsRepository
@@ -26,13 +25,16 @@ import com.zaneschepke.wireguardautotunnel.domain.state.AutoTunnelState
import com.zaneschepke.wireguardautotunnel.domain.state.toDomain import com.zaneschepke.wireguardautotunnel.domain.state.toDomain
import com.zaneschepke.wireguardautotunnel.util.Constants import com.zaneschepke.wireguardautotunnel.util.Constants
import com.zaneschepke.wireguardautotunnel.util.extensions.to import com.zaneschepke.wireguardautotunnel.util.extensions.to
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.distinctUntilChangedBy import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -63,8 +65,7 @@ class AutoTunnelService : LifecycleService() {
private var autoTunnelJob: Job? = null private var autoTunnelJob: Job? = null
private var permissionsJob: Job? = null private var permissionsJob: Job? = null
private var overridesJob: Job? = null private var overridesJob: Job? = null
private var noInternetStopJob: Job? = null
@Volatile private var manualOverrideState = ManualOverrideState()
private data class PermissionWarningState( private data class PermissionWarningState(
val detectionMethod: AndroidNetworkMonitor.WifiDetectionMethod, val detectionMethod: AndroidNetworkMonitor.WifiDetectionMethod,
@@ -73,11 +74,8 @@ class AutoTunnelService : LifecycleService() {
val ssidReadRequired: Boolean, val ssidReadRequired: Boolean,
) )
private data class ManualOverrideState( @Volatile private var hasUserOverride = false
val fingerprint: AutoTunnelState.NetworkFingerprint? = null, private var lastNetworkFingerprint: AutoTunnelState.NetworkFingerprint? = null
val stoppedTunnelIds: Set<Int> = emptySet(),
val startedTunnelIds: Set<Int> = emptySet(),
)
private val autoTunnelStateFlow: Flow<AutoTunnelState> by lazy { private val autoTunnelStateFlow: Flow<AutoTunnelState> by lazy {
val networkFlow = networkEngine.stableState.mapNotNull { it?.state?.toDomain() } val networkFlow = networkEngine.stableState.mapNotNull { it?.state?.toDomain() }
@@ -121,7 +119,7 @@ class AutoTunnelService : LifecycleService() {
permissionsJob?.cancel() permissionsJob?.cancel()
permissionsJob = startLocationPermissionsNotificationJob() permissionsJob = startLocationPermissionsNotificationJob()
overridesJob?.cancel() overridesJob?.cancel()
overridesJob = startOverridesJob() overridesJob = startUserOverrideJob()
} }
fun stop() { fun stop() {
@@ -130,48 +128,23 @@ class AutoTunnelService : LifecycleService() {
} }
override fun onDestroy() { override fun onDestroy() {
cancelNoInternetStopJob()
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE) ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
stateHolder.setActive(false) stateHolder.setActive(false)
AutoTunnelTileRefresher.refresh(this) AutoTunnelTileRefresher.refresh(this)
super.onDestroy() super.onDestroy()
} }
private fun startOverridesJob(): Job = private fun startUserOverrideJob(): Job =
lifecycleScope.launch(ioDispatcher) { lifecycleScope.launch(ioDispatcher) {
tunnelCoordinator.actions.collect { action -> tunnelCoordinator.userOverrideFlow.collect {
reconciliationMutex.withLock { reconciliationMutex.withLock {
manualOverrideState = if (!hasUserOverride) {
when (action) { Timber.d(
is TunnelActionEvent.Started -> { "User manually overrode Auto Tunnel on current network. Pausing auto decisions."
)
if (action.source != TunnelActionSource.USER) { }
return@withLock hasUserOverride = true
}
manualOverrideState.copy(
startedTunnelIds =
manualOverrideState.startedTunnelIds + action.tunnelId,
stoppedTunnelIds =
manualOverrideState.stoppedTunnelIds - action.tunnelId,
)
}
is TunnelActionEvent.Stopped -> {
if (action.source != TunnelActionSource.USER) {
return@withLock
}
manualOverrideState.copy(
stoppedTunnelIds =
manualOverrideState.stoppedTunnelIds + action.tunnelId,
startedTunnelIds =
manualOverrideState.startedTunnelIds - action.tunnelId,
)
}
}
Timber.d("Updated manual overrides: $manualOverrideState")
} }
} }
} }
@@ -202,50 +175,83 @@ class AutoTunnelService : LifecycleService() {
) )
} }
// Instead of stopping tunnel right away on no internet, we kick off this job to add short delay
// and re-evaluation to prevent unwanted stops
// on flaky networks and network transitions
private fun scheduleNoInternetStop() {
noInternetStopJob?.cancel()
noInternetStopJob =
lifecycleScope.launch(ioDispatcher) {
delay(NO_INTERNET_GRACE_PERIOD_MS.milliseconds)
reconciliationMutex.withLock {
val currentNetworkState = networkEngine.stableState.value?.state?.toDomain()
val stillNoInternet = currentNetworkState?.hasInternet() == false
val stopOnNoInternetEnabled =
autoTunnelRepository.flow.firstOrNull()?.isStopOnNoInternetEnabled == true
if (stillNoInternet && stopOnNoInternetEnabled) {
val currentActiveIds =
tunnelCoordinator.backendStatus.value.activeTunnels.keys
if (currentActiveIds.isNotEmpty()) {
Timber.w(
"No internet grace period expired and still no internet. Stopping tunnels: $currentActiveIds"
)
currentActiveIds.forEach { tunnelId ->
tunnelCoordinator.stopTunnel(
tunnelId,
TunnelActionSource.AUTO_TUNNEL,
)
}
}
} else {
Timber.d(
"No internet grace period expired, but internet is back or setting disabled. Doing nothing."
)
}
}
}
}
private fun cancelNoInternetStopJob() {
noInternetStopJob?.cancel()
noInternetStopJob = null
}
private fun startAutoTunnelStateJob(): Job = private fun startAutoTunnelStateJob(): Job =
lifecycleScope.launch(ioDispatcher) { lifecycleScope.launch(ioDispatcher) {
autoTunnelStateFlow.collectLatest { state -> autoTunnelStateFlow.collectLatest { state ->
reconciliationMutex.withLock { reconciliationMutex.withLock {
updateFingerprintIfNeeded(state) updateFingerprintIfNeeded(state)
val rawEvent = engine.evaluate(state) val rawEvent = engine.evaluate(state)
val event = applyOverrides(rawEvent) val event = applyOverrides(rawEvent)
Timber.d("AutoTunnel reconciliation event: $event") Timber.d("AutoTunnel reconciliation event: $event")
handleAutoTunnelEvent(event) handleAutoTunnelEvent(event)
} }
} }
} }
private fun updateFingerprintIfNeeded(state: AutoTunnelState) { private fun updateFingerprintIfNeeded(state: AutoTunnelState) {
val fingerprint = state.networkFingerPrint val currentFingerprint = state.networkFingerPrint
if (manualOverrideState.fingerprint != fingerprint) { if (lastNetworkFingerprint != currentFingerprint) {
Timber.d("Network changed, clearing overrides") if (hasUserOverride) {
Timber.d("Network fingerprint changed, clearing user override")
manualOverrideState = ManualOverrideState(fingerprint = fingerprint) }
hasUserOverride = false
lastNetworkFingerprint = currentFingerprint
} }
} }
private fun applyOverrides(event: AutoTunnelEvent): AutoTunnelEvent { private fun applyOverrides(event: AutoTunnelEvent): AutoTunnelEvent {
return if (hasUserOverride) {
if (event !is AutoTunnelEvent.Sync) { AutoTunnelEvent.DoNothing
return event } else {
event
} }
val filteredStart =
event.start.filterNot { it.id in manualOverrideState.stoppedTunnelIds }.toSet()
val filteredStop =
event.stop.filterNot { it in manualOverrideState.startedTunnelIds }.toSet()
if (filteredStart.isEmpty() && filteredStop.isEmpty()) {
return AutoTunnelEvent.DoNothing
}
return event.copy(start = filteredStart, stop = filteredStop)
} }
private fun combineSettings(): private fun combineSettings():
@@ -334,7 +340,7 @@ class AutoTunnelService : LifecycleService() {
private suspend fun handleAutoTunnelEvent(event: AutoTunnelEvent) { private suspend fun handleAutoTunnelEvent(event: AutoTunnelEvent) {
when (event) { when (event) {
is AutoTunnelEvent.Sync -> { is AutoTunnelEvent.Sync -> {
cancelNoInternetStopJob()
event.stop.forEach { tunnelId -> event.stop.forEach { tunnelId ->
Timber.d("Stopping tunnel: $tunnelId") Timber.d("Stopping tunnel: $tunnelId")
tunnelCoordinator.stopTunnel(tunnelId, TunnelActionSource.AUTO_TUNNEL) tunnelCoordinator.stopTunnel(tunnelId, TunnelActionSource.AUTO_TUNNEL)
@@ -345,8 +351,12 @@ class AutoTunnelService : LifecycleService() {
tunnelCoordinator.startTunnel(config, TunnelActionSource.AUTO_TUNNEL) tunnelCoordinator.startTunnel(config, TunnelActionSource.AUTO_TUNNEL)
} }
} }
AutoTunnelEvent.StopAllDueToNoInternet -> scheduleNoInternetStop()
AutoTunnelEvent.DoNothing -> Unit AutoTunnelEvent.DoNothing -> Unit
} }
} }
companion object {
private const val NO_INTERNET_GRACE_PERIOD_MS = 10_000L
}
} }
@@ -7,4 +7,6 @@ sealed interface AutoTunnelEvent {
data class Sync(val start: Set<TunnelConfig>, val stop: Set<Int>) : AutoTunnelEvent data class Sync(val start: Set<TunnelConfig>, val stop: Set<Int>) : AutoTunnelEvent
data object DoNothing : AutoTunnelEvent data object DoNothing : AutoTunnelEvent
data object StopAllDueToNoInternet : AutoTunnelEvent
} }
@@ -4,7 +4,12 @@ import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelActionSource
sealed interface TunnelActionEvent { sealed interface TunnelActionEvent {
data class Started(val tunnelId: Int, val source: TunnelActionSource) : TunnelActionEvent val source: TunnelActionSource
val tunnelId: Int
data class Stopped(val tunnelId: Int, val source: TunnelActionSource) : TunnelActionEvent data class Started(override val tunnelId: Int, override val source: TunnelActionSource) :
TunnelActionEvent
data class Stopped(override val tunnelId: Int, override val source: TunnelActionSource) :
TunnelActionEvent
} }
@@ -250,7 +250,7 @@ fun currentRouteAsNavbarState(
val global = route !is ConfigEdit val global = route !is ConfigEdit
val tunnelName = val tunnelName =
if (!global) globalState.tunnelNames[route.id] if (!global) globalState.tunnelNames[route.id]
else context.getString(R.string.configuration_globals) else context.getString(R.string.tunnel_configuration)
NavbarState( NavbarState(
topLeading = { TvBackButton { navController.pop() } }, topLeading = { TvBackButton { navController.pop() } },
showBottomItems = true, showBottomItems = true,
@@ -297,7 +297,7 @@ fun currentRouteAsNavbarState(
is SplitTunnelGlobal -> { is SplitTunnelGlobal -> {
val tunnelName = val tunnelName =
if (route is SplitTunnel) globalState.tunnelNames[route.id] if (route is SplitTunnel) globalState.tunnelNames[route.id]
else context.getString(R.string.global_split_tunneling) else context.getString(R.string.splt_tunneling)
NavbarState( NavbarState(
topLeading = { TvBackButton { navController.pop() } }, topLeading = { TvBackButton { navController.pop() } },
topTitle = tunnelName ?: "", topTitle = tunnelName ?: "",
@@ -302,7 +302,9 @@ fun AutoTunnelScreen(
SurfaceRow( SurfaceRow(
leading = { Icon(Icons.Outlined.PublicOff, contentDescription = null) }, leading = { Icon(Icons.Outlined.PublicOff, contentDescription = null) },
title = stringResource(R.string.stop_on_no_internet), title = stringResource(R.string.stop_on_no_internet),
description = { DescriptionText(stringResource(R.string.stop_on_internet_loss)) }, description = {
DescriptionText(stringResource(R.string.stop_on_no_internet_desc))
},
trailing = { trailing = {
ThemedSwitch( ThemedSwitch(
checked = uiState.autoTunnelSettings.isStopOnNoInternetEnabled, checked = uiState.autoTunnelSettings.isStopOnNoInternetEnabled,
@@ -318,7 +320,7 @@ fun AutoTunnelScreen(
} }
Column { Column {
GroupLabel( GroupLabel(
stringResource(R.string.other), stringResource(R.string.automation),
modifier = Modifier.padding(horizontal = 16.dp), modifier = Modifier.padding(horizontal = 16.dp),
) )
SurfaceRow( SurfaceRow(
@@ -330,6 +332,7 @@ fun AutoTunnelScreen(
onClick = { viewModel.setStartAtBoot(it) }, onClick = { viewModel.setStartAtBoot(it) },
) )
}, },
description = { DescriptionText(stringResource(R.string.start_on_boot_desc)) },
onClick = { viewModel.setStartAtBoot(!uiState.autoTunnelSettings.startOnBoot) }, onClick = { viewModel.setStartAtBoot(!uiState.autoTunnelSettings.startOnBoot) },
) )
} }
@@ -176,6 +176,7 @@ fun SettingsScreen(
leading = { Icon(Icons.Outlined.Public, contentDescription = null) }, leading = { Icon(Icons.Outlined.Public, contentDescription = null) },
title = stringResource(R.string.tunnel_globals), title = stringResource(R.string.tunnel_globals),
onClick = { navController.push(Route.TunnelGlobals) }, onClick = { navController.push(Route.TunnelGlobals) },
description = { DescriptionText(stringResource(R.string.tunnel_globals_desc)) },
) )
SurfaceRow( SurfaceRow(
leading = { Icon(Icons.Outlined.Terminal, contentDescription = null) }, leading = { Icon(Icons.Outlined.Terminal, contentDescription = null) },
@@ -232,6 +233,7 @@ fun SettingsScreen(
modifier = modifier, modifier = modifier,
) )
}, },
description = { DescriptionText(stringResource(R.string.local_logging_desc)) },
onClick = { navController.push(Route.Logs) }, onClick = { navController.push(Route.Logs) },
) )
SurfaceRow( SurfaceRow(
@@ -56,7 +56,7 @@ fun TunnelGlobalsScreen(
) )
}, },
enabled = sharedUiState.tunnelMode != TunnelMode.PROXY, enabled = sharedUiState.tunnelMode != TunnelMode.PROXY,
title = stringResource(R.string.global_split_tunneling), title = stringResource(R.string.splt_tunneling),
trailing = { modifier -> trailing = { modifier ->
SwitchWithDivider( SwitchWithDivider(
checked = uiState.settings.isGlobalSplitTunnelEnabled, checked = uiState.settings.isGlobalSplitTunnelEnabled,
@@ -82,7 +82,7 @@ fun TunnelGlobalsScreen(
) )
SurfaceRow( SurfaceRow(
leading = { Icon(Icons.Outlined.Description, contentDescription = null) }, leading = { Icon(Icons.Outlined.Description, contentDescription = null) },
title = stringResource(R.string.configuration_globals), title = stringResource(R.string.tunnel_configuration),
onClick = { onClick = {
uiState.globalTunnelConfig?.let { uiState.globalTunnelConfig?.let {
navController.push(Route.ConfigGlobal(id = it.id)) navController.push(Route.ConfigGlobal(id = it.id))
@@ -11,7 +11,7 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.Launch import androidx.compose.material.icons.automirrored.outlined.Launch
import androidx.compose.material.icons.filled.AppShortcut import androidx.compose.material.icons.filled.AppShortcut
import androidx.compose.material.icons.filled.SmartToy import androidx.compose.material.icons.filled.SettingsRemote
import androidx.compose.material.icons.outlined.AdminPanelSettings import androidx.compose.material.icons.outlined.AdminPanelSettings
import androidx.compose.material.icons.outlined.ContentCopy import androidx.compose.material.icons.outlined.ContentCopy
import androidx.compose.material.icons.outlined.Key import androidx.compose.material.icons.outlined.Key
@@ -133,20 +133,22 @@ fun AndroidIntegrationsScreen(viewModel: SettingsViewModel = koinViewModel()) {
onClick = { viewModel.setShortcutsEnabled(it) }, onClick = { viewModel.setShortcutsEnabled(it) },
) )
}, },
title = stringResource(R.string.enabled_app_shortcuts), title = stringResource(R.string.app_shortcuts),
description = { DescriptionText(stringResource(R.string.app_shortcuts_desc)) },
onClick = { onClick = {
viewModel.setShortcutsEnabled(!settingsState.settings.isShortcutsEnabled) viewModel.setShortcutsEnabled(!settingsState.settings.isShortcutsEnabled)
}, },
) )
SurfaceRow( SurfaceRow(
leading = { Icon(Icons.Filled.SmartToy, contentDescription = null) }, leading = { Icon(Icons.Filled.SettingsRemote, contentDescription = null) },
trailing = { trailing = {
ThemedSwitch( ThemedSwitch(
checked = settingsState.isRemoteEnabled, checked = settingsState.isRemoteEnabled,
onClick = { viewModel.setRemoteEnabled(it) }, onClick = { viewModel.setRemoteEnabled(it) },
) )
}, },
title = stringResource(R.string.enable_remote_app_control), title = stringResource(R.string.remote_control),
description = { DescriptionText(stringResource(R.string.remote_control_desc)) },
onClick = { viewModel.setRemoteEnabled(!settingsState.isRemoteEnabled) }, onClick = { viewModel.setRemoteEnabled(!settingsState.isRemoteEnabled) },
) )
AnimatedVisibility(settingsState.isRemoteEnabled) { AnimatedVisibility(settingsState.isRemoteEnabled) {
@@ -2,6 +2,8 @@ package com.zaneschepke.wireguardautotunnel.ui.screens.settings.proxy.compoents
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelMode import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelMode
import com.zaneschepke.wireguardautotunnel.ui.common.sheet.CustomBottomSheet import com.zaneschepke.wireguardautotunnel.ui.common.sheet.CustomBottomSheet
import com.zaneschepke.wireguardautotunnel.ui.common.sheet.SheetOption import com.zaneschepke.wireguardautotunnel.ui.common.sheet.SheetOption
@@ -27,6 +29,12 @@ fun AppModeBottomSheet(
onDismiss() onDismiss()
onAppModeChange(it) onAppModeChange(it)
}, },
description =
when (it) {
TunnelMode.VPN -> stringResource(R.string.vpn_desc)
TunnelMode.PROXY -> stringResource(R.string.local_proxy_desc)
TunnelMode.LOCK_DOWN -> stringResource(R.string.lockdown_desc)
},
selected = tunnelMode == it, selected = tunnelMode == it,
) )
} }
@@ -19,6 +19,7 @@ import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.ui.LocalNavController import com.zaneschepke.wireguardautotunnel.ui.LocalNavController
import com.zaneschepke.wireguardautotunnel.ui.common.button.SurfaceRow import com.zaneschepke.wireguardautotunnel.ui.common.button.SurfaceRow
import com.zaneschepke.wireguardautotunnel.ui.common.button.ThemedSwitch import com.zaneschepke.wireguardautotunnel.ui.common.button.ThemedSwitch
import com.zaneschepke.wireguardautotunnel.ui.common.text.DescriptionText
import com.zaneschepke.wireguardautotunnel.ui.navigation.Route import com.zaneschepke.wireguardautotunnel.ui.navigation.Route
import com.zaneschepke.wireguardautotunnel.viewmodel.SharedAppViewModel import com.zaneschepke.wireguardautotunnel.viewmodel.SharedAppViewModel
import org.koin.compose.viewmodel.koinActivityViewModel import org.koin.compose.viewmodel.koinActivityViewModel
@@ -45,13 +46,16 @@ fun SecurityScreen(viewModel: SharedAppViewModel = koinActivityViewModel()) {
onClick = { viewModel.setScreenRecordingSecurity(it) }, onClick = { viewModel.setScreenRecordingSecurity(it) },
) )
}, },
description = {
DescriptionText(stringResource(R.string.screen_recording_protection_desc))
},
onClick = { onClick = {
viewModel.setScreenRecordingSecurity(!uiState.isScreenRecordingProtectionEnabled) viewModel.setScreenRecordingSecurity(!uiState.isScreenRecordingProtectionEnabled)
}, },
) )
SurfaceRow( SurfaceRow(
leading = { Icon(Icons.Outlined.Pin, contentDescription = null) }, leading = { Icon(Icons.Outlined.Pin, contentDescription = null) },
title = stringResource(R.string.enable_app_lock), title = stringResource(R.string.app_lock),
trailing = { trailing = {
ThemedSwitch( ThemedSwitch(
checked = uiState.pinLockEnabled, checked = uiState.pinLockEnabled,
@@ -64,6 +68,7 @@ fun SecurityScreen(viewModel: SharedAppViewModel = koinActivityViewModel()) {
}, },
) )
}, },
description = { DescriptionText(stringResource(R.string.app_lock_desc)) },
onClick = { onClick = {
if (!uiState.pinLockEnabled) { if (!uiState.pinLockEnabled) {
navController.push(Route.Lock) navController.push(Route.Lock)
@@ -14,6 +14,8 @@ import androidx.compose.material.icons.rounded.Circle
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@@ -34,6 +36,8 @@ import com.zaneschepke.wireguardautotunnel.ui.state.DisplayTunnelState
import com.zaneschepke.wireguardautotunnel.ui.state.TunnelsUiState import com.zaneschepke.wireguardautotunnel.ui.state.TunnelsUiState
import com.zaneschepke.wireguardautotunnel.util.extensions.openWebUrl import com.zaneschepke.wireguardautotunnel.util.extensions.openWebUrl
import com.zaneschepke.wireguardautotunnel.viewmodel.SharedAppViewModel import com.zaneschepke.wireguardautotunnel.viewmodel.SharedAppViewModel
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.delay
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
@@ -46,6 +50,14 @@ fun TunnelList(
val context = LocalContext.current val context = LocalContext.current
val isTv = LocalIsAndroidTV.current val isTv = LocalIsAndroidTV.current
val now by
produceState(System.currentTimeMillis()) {
while (true) {
delay(1_000L.milliseconds)
value = System.currentTimeMillis()
}
}
val focusRequester = remember { FocusRequester() } val focusRequester = remember { FocusRequester() }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
@@ -88,7 +100,9 @@ fun TunnelList(
} }
val displayState = val displayState =
uiState.displayStates[tunnel.id] ?: DisplayTunnelState.from(activeTunnel) remember(activeTunnel, now, uiState.displayStates[tunnel.id]) {
uiState.displayStates[tunnel.id] ?: DisplayTunnelState.from(activeTunnel, now)
}
val isRunning = uiState.backendStatus.activeTunnels.containsKey(tunnel.id) val isRunning = uiState.backendStatus.activeTunnels.containsKey(tunnel.id)
@@ -107,7 +121,7 @@ fun TunnelList(
Icon( Icon(
Icons.Rounded.Circle, Icons.Rounded.Circle,
contentDescription = stringResource(R.string.tunnel_monitoring), contentDescription = stringResource(R.string.tunnel_monitoring),
tint = displayState.asColor(), tint = remember(displayState) { displayState.asColor() },
modifier = Modifier.size(14.dp), modifier = Modifier.size(14.dp),
) )
}, },
@@ -141,6 +141,10 @@ fun TunnelSettingsScreen(
onClick = { navController.push(Route.IPv6(tunnel.id)) }, onClick = { navController.push(Route.IPv6(tunnel.id)) },
) )
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val meteredDisabled = sharedUiState.tunnelMode == TunnelMode.PROXY
val meteredTunnelDesc =
if (meteredDisabled) stringResource(R.string.unavailable_in_mode)
else stringResource(R.string.metered_tunnel_desc)
SurfaceRow( SurfaceRow(
leading = { leading = {
Icon( Icon(
@@ -153,15 +157,9 @@ fun TunnelSettingsScreen(
}, },
title = stringResource(R.string.metered_tunnel), title = stringResource(R.string.metered_tunnel),
enabled = sharedUiState.tunnelMode != TunnelMode.PROXY, enabled = sharedUiState.tunnelMode != TunnelMode.PROXY,
description = description = {
if (sharedUiState.tunnelMode == TunnelMode.PROXY) { DescriptionText(meteredTunnelDesc, disabled = meteredDisabled)
{ },
DescriptionText(
stringResource(R.string.unavailable_in_mode),
disabled = true,
)
}
} else null,
trailing = { trailing = {
ThemedSwitch( ThemedSwitch(
checked = tunnel.isMetered, checked = tunnel.isMetered,
@@ -172,26 +170,26 @@ fun TunnelSettingsScreen(
onClick = { viewModel.onMetered(!tunnel.isMetered) }, onClick = { viewModel.onMetered(!tunnel.isMetered) },
) )
} }
Column { }
GroupLabel( Column {
stringResource(R.string.automation), GroupLabel(
modifier = Modifier.padding(horizontal = 16.dp), stringResource(R.string.automation),
) modifier = Modifier.padding(horizontal = 16.dp),
SurfaceRow( )
leading = { Icon(Icons.Outlined.Dns, contentDescription = null) }, SurfaceRow(
title = stringResource(R.string.ddns_auto_update), leading = { Icon(Icons.Outlined.Dns, contentDescription = null) },
description = { title = stringResource(R.string.ddns_auto_update),
DescriptionText(stringResource(R.string.ddns_auto_update_description)) description = {
}, DescriptionText(stringResource(R.string.ddns_auto_update_description))
trailing = { },
ThemedSwitch( trailing = {
checked = tunnel.dynamicDnsEnabled, ThemedSwitch(
onClick = { viewModel.onDynamicDns(it) }, checked = tunnel.dynamicDnsEnabled,
) onClick = { viewModel.onDynamicDns(it) },
}, )
onClick = { viewModel.onDynamicDns(!tunnel.dynamicDnsEnabled) }, },
) onClick = { viewModel.onDynamicDns(!tunnel.dynamicDnsEnabled) },
} )
} }
} }
} }
@@ -112,7 +112,7 @@ fun ConfigEditScreen(
leading = { leading = {
Icon(ImageVector.vectorResource(R.drawable.host), contentDescription = null) Icon(ImageVector.vectorResource(R.drawable.host), contentDescription = null)
}, },
title = stringResource(R.string.global_dns_servers), title = stringResource(R.string.dns_servers),
trailing = { modifier -> trailing = { modifier ->
ThemedSwitch( ThemedSwitch(
checked = uiState.globalSettings.dnsEnabled, checked = uiState.globalSettings.dnsEnabled,
@@ -126,7 +126,7 @@ fun ConfigEditScreen(
) )
SurfaceRow( SurfaceRow(
leading = { Icon(Icons.Outlined.HdrAuto, contentDescription = null) }, leading = { Icon(Icons.Outlined.HdrAuto, contentDescription = null) },
title = stringResource(R.string.global_amnezia_configuration), title = stringResource(R.string.amnezia_configuration),
trailing = { modifier -> trailing = { modifier ->
ThemedSwitch( ThemedSwitch(
checked = uiState.globalSettings.amneziaEnabled, checked = uiState.globalSettings.amneziaEnabled,
@@ -61,36 +61,47 @@ sealed class DisplayTunnelState {
} }
companion object { companion object {
fun from(activeTunnel: ActiveTunnel): DisplayTunnelState { private const val HANDSHAKE_FAILURE_DEGRADED_THRESHOLD_MS = 6_000L
// During this window we avoid showing Degraded even if we see HandshakeFailure
private const val POST_RESOLUTION_GRACE_PERIOD_MS = 3_500L
fun from(activeTunnel: ActiveTunnel, now: Long): DisplayTunnelState {
val transport = activeTunnel.transportState val transport = activeTunnel.transportState
val bootstrap = activeTunnel.bootstrapState val bootstrap = activeTunnel.bootstrapState
val mode = activeTunnel.mode val mode = activeTunnel.mode
val isVpnStyle = mode is BackendMode.Vpn || mode is BackendMode.Proxy.KillSwitchPrimary val isVpnStyle = mode is BackendMode.Vpn || mode is BackendMode.Proxy.KillSwitchPrimary
// Static peers bootstrap never goes to complete, treat none the same
val bootstrapPhaseDone = val bootstrapPhaseDone =
bootstrap is BootstrapState.Complete || bootstrap is BootstrapState.None bootstrap is BootstrapState.Complete || bootstrap is BootstrapState.None
// Check if we recently completed peer resolution
val recentlyResolvedPeers =
activeTunnel.lastPeerUpdateMs > 0 &&
(now - activeTunnel.lastPeerUpdateMs) < POST_RESOLUTION_GRACE_PERIOD_MS
return when { return when {
transport is Tunnel.State.Down -> Disconnected transport is Tunnel.State.Down -> Disconnected
bootstrap is BootstrapState.Failed -> Degraded bootstrap is BootstrapState.Failed -> Degraded
// DNS resolution still in progress
bootstrap is BootstrapState.ResolvingDns || bootstrap is BootstrapState.ResolvingDns ||
bootstrap is BootstrapState.UpdatingPeers -> ResolvingDns bootstrap is BootstrapState.UpdatingPeers -> ResolvingDns
transport is Tunnel.State.Up.Healthy -> Connected transport is Tunnel.State.Up.Healthy -> Connected
transport is Tunnel.State.Up.HandshakeFailure -> { transport is Tunnel.State.Up.HandshakeFailure -> {
val age = System.currentTimeMillis() - activeTunnel.lastStateChangeMs val age = now - activeTunnel.lastStateChangeMs
if (age > 15_000L && bootstrapPhaseDone) { if (recentlyResolvedPeers && bootstrapPhaseDone) {
if (isVpnStyle) EstablishingConnection else Ready
} else if (
age > HANDSHAKE_FAILURE_DEGRADED_THRESHOLD_MS && bootstrapPhaseDone
) {
Degraded Degraded
} else if (isVpnStyle && bootstrapPhaseDone) { } else if (isVpnStyle && bootstrapPhaseDone) {
EstablishingConnection EstablishingConnection
} else if (bootstrapPhaseDone) { } else if (bootstrapPhaseDone) {
// For regular proxy mode, we go to ready once past bootstrap phase
Ready Ready
} else { } else {
Connecting Connecting
@@ -99,16 +110,12 @@ sealed class DisplayTunnelState {
transport is Tunnel.State.Starting -> { transport is Tunnel.State.Starting -> {
when { when {
bootstrapPhaseDone -> { bootstrapPhaseDone -> if (isVpnStyle) EstablishingConnection else Ready
if (isVpnStyle) EstablishingConnection else Ready
}
else -> Connecting else -> Connecting
} }
} }
// Final fallback after bootstrap phase is done
bootstrapPhaseDone -> if (isVpnStyle) EstablishingConnection else Ready bootstrapPhaseDone -> if (isVpnStyle) EstablishingConnection else Ready
else -> Connecting else -> Connecting
} }
} }
@@ -61,7 +61,7 @@ fun TunnelMode.asTitleString(context: Context): String {
fun TunnelMode.asString(context: Context): String { fun TunnelMode.asString(context: Context): String {
return when (this) { return when (this) {
TunnelMode.VPN -> context.getString(R.string.vpn) TunnelMode.VPN -> context.getString(R.string.vpn)
TunnelMode.PROXY -> context.getString(R.string.proxy) TunnelMode.PROXY -> context.getString(R.string.local_proxy)
TunnelMode.LOCK_DOWN -> context.getString(R.string.lockdown) TunnelMode.LOCK_DOWN -> context.getString(R.string.lockdown)
} }
} }
@@ -124,7 +124,7 @@ fun DnsError.labelRes(): Int {
fun ActiveTunnel.statusText(context: Context): String { fun ActiveTunnel.statusText(context: Context): String {
return context.getString( return context.getString(
R.string.status_template, R.string.status_template,
DisplayTunnelState.from(this).asLocalizedString(context), DisplayTunnelState.from(this, System.currentTimeMillis()).asLocalizedString(context),
) )
} }
@@ -42,6 +42,7 @@ class ProxySettingsViewModel(
.collect { reduce { it } } .collect { reduce { it } }
} }
// TODO add a dialog requesting restart if any tunnels active
fun save() = intent { fun save() = intent {
reduce { state.copy(showSaveModal = false) } reduce { state.copy(showSaveModal = false) }
@@ -98,9 +99,6 @@ class ProxySettingsViewModel(
proxySettingsRepository.upsert(updated) proxySettingsRepository.upsert(updated)
tunnelCoordinator.toggleTunnels()
tunnelCoordinator.toggleTunnels()
postSideEffect( postSideEffect(
GlobalSideEffect.Snackbar(StringValue.StringResource(R.string.config_changes_saved)) GlobalSideEffect.Snackbar(StringValue.StringResource(R.string.config_changes_saved))
) )
@@ -18,6 +18,7 @@ import com.zaneschepke.wireguardautotunnel.domain.repository.TunnelRepository
import com.zaneschepke.wireguardautotunnel.domain.sideeffect.GlobalSideEffect import com.zaneschepke.wireguardautotunnel.domain.sideeffect.GlobalSideEffect
import com.zaneschepke.wireguardautotunnel.parser.ConfigParseException import com.zaneschepke.wireguardautotunnel.parser.ConfigParseException
import com.zaneschepke.wireguardautotunnel.ui.sideeffect.LocalSideEffect import com.zaneschepke.wireguardautotunnel.ui.sideeffect.LocalSideEffect
import com.zaneschepke.wireguardautotunnel.ui.state.DisplayTunnelState
import com.zaneschepke.wireguardautotunnel.ui.state.GlobalAppUiState import com.zaneschepke.wireguardautotunnel.ui.state.GlobalAppUiState
import com.zaneschepke.wireguardautotunnel.ui.state.TunnelsUiState import com.zaneschepke.wireguardautotunnel.ui.state.TunnelsUiState
import com.zaneschepke.wireguardautotunnel.ui.theme.Theme import com.zaneschepke.wireguardautotunnel.ui.theme.Theme
@@ -71,10 +72,22 @@ class SharedAppViewModel(
tunnelRepository.userTunnelsFlow, tunnelRepository.userTunnelsFlow,
tunnelCoordinator.backendStatus, tunnelCoordinator.backendStatus,
selectedTunnelsRepository.flow, selectedTunnelsRepository.flow,
tunnelCoordinator.tunnelDisplayStates, ) { tunnels, backendStatus, selectedTuns ->
) { tunnels, backendStatus, selectedTuns, displayStates -> val activeTunnelIds = backendStatus.activeTunnels.keys
val sortedTunnels =
tunnels.sortedWith(
compareByDescending<TunnelConfig> { it.id in activeTunnelIds }
.thenBy { it.position }
)
val displayStates =
backendStatus.activeTunnels.mapValues { (_, activeTunnel) ->
DisplayTunnelState.from(activeTunnel, System.currentTimeMillis())
}
TunnelsUiState( TunnelsUiState(
tunnels = tunnels, tunnels = sortedTunnels,
backendStatus = backendStatus, backendStatus = backendStatus,
displayStates = displayStates, displayStates = displayStates,
selectedTunnels = selectedTuns, selectedTunnels = selectedTuns,
-11
View File
@@ -15,7 +15,6 @@
<string name="delete_tunnel">Delete tunnel</string> <string name="delete_tunnel">Delete tunnel</string>
<string name="tunnel_mobile_data">Tunnel on mobile data</string> <string name="tunnel_mobile_data">Tunnel on mobile data</string>
<string name="logs">Logs</string> <string name="logs">Logs</string>
<string name="enable_app_lock">Enable app lock</string>
<string name="config_changes_saved">Configuration changes saved.</string> <string name="config_changes_saved">Configuration changes saved.</string>
<string name="join_telegram">Join Telegram community</string> <string name="join_telegram">Join Telegram community</string>
<string name="pin_created">Pin successfully created</string> <string name="pin_created">Pin successfully created</string>
@@ -27,7 +26,6 @@
<string name="remote_key">Remote key</string> <string name="remote_key">Remote key</string>
<string name="mobile_data">Mobile data</string> <string name="mobile_data">Mobile data</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_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="stop_on_internet_loss">Stop tunnel on internet loss</string>
<string name="vpn">VPN</string> <string name="vpn">VPN</string>
<string name="tunnel_boot_description">Start the default tunnel on boot</string> <string name="tunnel_boot_description">Start the default tunnel on boot</string>
<string name="allow_lan_traffic">Allow LAN traffic</string> <string name="allow_lan_traffic">Allow LAN traffic</string>
@@ -133,7 +131,6 @@
<string name="kofi">Ko-fi</string> <string name="kofi">Ko-fi</string>
<string name="donation_signoff">Gratefully,</string> <string name="donation_signoff">Gratefully,</string>
<string name="selected">Selected</string> <string name="selected">Selected</string>
<string name="global_split_tunneling">Global split tunneling</string>
<string name="active_network">Active Network:</string> <string name="active_network">Active Network:</string>
<string name="range_hint">(%1$d%2$d)</string> <string name="range_hint">(%1$d%2$d)</string>
<string name="delete_active_message">Cannot delete active tunnel.</string> <string name="delete_active_message">Cannot delete active tunnel.</string>
@@ -156,7 +153,6 @@
<string name="mtu">MTU</string> <string name="mtu">MTU</string>
<string name="configuration">Configuration</string> <string name="configuration">Configuration</string>
<string name="drag_handle">Drag Handle</string> <string name="drag_handle">Drag Handle</string>
<string name="global_dns_servers">Global DNS servers</string>
<string name="display_theme">Display theme</string> <string name="display_theme">Display theme</string>
<string name="contact">Contact</string> <string name="contact">Contact</string>
<string name="ports_must_differ">Failed. Proxies must have different ports.</string> <string name="ports_must_differ">Failed. Proxies must have different ports.</string>
@@ -257,7 +253,6 @@
<string name="unavailable_in_mode">Unavailable in current mode</string> <string name="unavailable_in_mode">Unavailable in current mode</string>
<string name="server_port">Server:Port</string> <string name="server_port">Server:Port</string>
<string name="camera_permission_required">Camera permission required</string> <string name="camera_permission_required">Camera permission required</string>
<string name="enabled_app_shortcuts">Enable app shortcuts</string>
<string name="preferred_tunnel">Preferred tunnel</string> <string name="preferred_tunnel">Preferred tunnel</string>
<string name="allow">Allow</string> <string name="allow">Allow</string>
<string name="underload_packet_magic_header">Underload packet magic header</string> <string name="underload_packet_magic_header">Underload packet magic header</string>
@@ -272,7 +267,6 @@
<string name="settings">Settings</string> <string name="settings">Settings</string>
<string name="incorrect_pin">Pin is incorrect</string> <string name="incorrect_pin">Pin is incorrect</string>
<string name="export_failed">Export failed</string> <string name="export_failed">Export failed</string>
<string name="enable_remote_app_control">Enable remote app control</string>
<string name="donation_closing">It\'s my dream to work for you on this project full-time.</string> <string name="donation_closing">It\'s my dream to work for you on this project full-time.</string>
<string name="update_download_failed">Update download failed.</string> <string name="update_download_failed">Update download failed.</string>
<string name="network_name">Network:</string> <string name="network_name">Network:</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
-11
View File
@@ -21,7 +21,6 @@
<string name="preshared_key">Předsdílený klíč</string> <string name="preshared_key">Předsdílený klíč</string>
<string name="seconds">s</string> <string name="seconds">s</string>
<string name="cancel">Zrušit</string> <string name="cancel">Zrušit</string>
<string name="enabled_app_shortcuts">Zapnout zkratky aplikací</string>
<string name="unknown_error">Došlo k neznámé chybě</string> <string name="unknown_error">Došlo k neznámé chybě</string>
<string name="tunnel_on_wifi">Tunelovat na Wi-Fi</string> <string name="tunnel_on_wifi">Tunelovat na Wi-Fi</string>
<string name="email_subject">WG Tunnel podpora</string> <string name="email_subject">WG Tunnel podpora</string>
@@ -65,7 +64,6 @@
<string name="no_browser_detected">Žádný prohlížeč nebyl nalezen</string> <string name="no_browser_detected">Žádný prohlížeč nebyl nalezen</string>
<string name="pin_created">PIN úspěšně vytvořen</string> <string name="pin_created">PIN úspěšně vytvořen</string>
<string name="enter_pin">Zadejte PIN</string> <string name="enter_pin">Zadejte PIN</string>
<string name="enable_app_lock">Zapnout zámek aplikace</string>
<string name="settings">Nastavení</string> <string name="settings">Nastavení</string>
<string name="support">Podpora</string> <string name="support">Podpora</string>
<string name="app_name">WG Tunnel</string> <string name="app_name">WG Tunnel</string>
@@ -90,7 +88,6 @@
<string name="primary_tunnel">Výchozí tunel</string> <string name="primary_tunnel">Výchozí tunel</string>
<string name="skip">Přeskočit</string> <string name="skip">Přeskočit</string>
<string name="donate">Přispět na projekt</string> <string name="donate">Přispět na projekt</string>
<string name="stop_on_internet_loss">Zastavit tunel při ztrátě internetu</string>
<string name="stop_on_no_internet">Zastavit při ztrátě internetu</string> <string name="stop_on_no_internet">Zastavit při ztrátě internetu</string>
<string name="native_kill_switch">Nativní kill switch</string> <string name="native_kill_switch">Nativní kill switch</string>
<string name="vpn_channel_description">Kanál pro oznámení o stavu VPN</string> <string name="vpn_channel_description">Kanál pro oznámení o stavu VPN</string>
@@ -144,7 +141,6 @@
<string name="vpn_denied_dialog_title">Oprávnění zamítnuto</string> <string name="vpn_denied_dialog_title">Oprávnění zamítnuto</string>
<string name="app_permission_title">Ovládání tunelů a funkcí automatického tunelování</string> <string name="app_permission_title">Ovládání tunelů a funkcí automatického tunelování</string>
<string name="app_permission_description">https://hosted.weblate.org/translate/wg-tunnel/strings/en/?checksum=e52d7eb2e28a9a12 Ovládání funkcí tunelu a automatického tunelování.</string> <string name="app_permission_description">https://hosted.weblate.org/translate/wg-tunnel/strings/en/?checksum=e52d7eb2e28a9a12 Ovládání funkcí tunelu a automatického tunelování.</string>
<string name="enable_remote_app_control">Povolit vzdálené ovládání aplikace</string>
<string name="export_success">Export byl úspěšně dokončen</string> <string name="export_success">Export byl úspěšně dokončen</string>
<string name="download">Stáhnout</string> <string name="download">Stáhnout</string>
<string name="check_for_update">Zkontrolovat aktualizaci</string> <string name="check_for_update">Zkontrolovat aktualizaci</string>
@@ -276,7 +272,6 @@
<string name="back">Zpět</string> <string name="back">Zpět</string>
<string name="already_donated">Již darováno</string> <string name="already_donated">Již darováno</string>
<string name="selected">Vybrané</string> <string name="selected">Vybrané</string>
<string name="global_split_tunneling">Globální dělené tunelování</string>
<string name="active_network">Aktivní síť:</string> <string name="active_network">Aktivní síť:</string>
<string name="delete_active_message">Aktivní tunel nelze odstranit.</string> <string name="delete_active_message">Aktivní tunel nelze odstranit.</string>
<string name="help_translate">Pomozte s překladem aplikace</string> <string name="help_translate">Pomozte s překladem aplikace</string>
@@ -284,7 +279,6 @@
<string name="other">Ostatní</string> <string name="other">Ostatní</string>
<string name="kill_switch">kill switch</string> <string name="kill_switch">kill switch</string>
<string name="configuration">Konfigurace</string> <string name="configuration">Konfigurace</string>
<string name="global_dns_servers">Globální DNS servery</string>
<string name="contact">Kontakt</string> <string name="contact">Kontakt</string>
<string name="backup_and_restore">Zálohování a obnovení</string> <string name="backup_and_restore">Zálohování a obnovení</string>
<string name="about">O aplikaci</string> <string name="about">O aplikaci</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
-11
View File
@@ -17,7 +17,6 @@
<string name="delete_tunnel">Delete tunnel</string> <string name="delete_tunnel">Delete tunnel</string>
<string name="tunnel_mobile_data">Tunnel on mobile data</string> <string name="tunnel_mobile_data">Tunnel on mobile data</string>
<string name="logs">Logs</string> <string name="logs">Logs</string>
<string name="enable_app_lock">Enable app lock</string>
<string name="config_changes_saved">Configuration changes saved.</string> <string name="config_changes_saved">Configuration changes saved.</string>
<string name="join_telegram">Join Telegram community</string> <string name="join_telegram">Join Telegram community</string>
<string name="pin_created">Pin successfully created</string> <string name="pin_created">Pin successfully created</string>
@@ -29,7 +28,6 @@
<string name="remote_key">Remote key</string> <string name="remote_key">Remote key</string>
<string name="mobile_data">Mobile data</string> <string name="mobile_data">Mobile data</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_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="stop_on_internet_loss">Stop tunnel on internet loss</string>
<string name="vpn">VPN</string> <string name="vpn">VPN</string>
<string name="tunnel_boot_description">Start the default tunnel on boot</string> <string name="tunnel_boot_description">Start the default tunnel on boot</string>
<string name="allow_lan_traffic">Allow LAN traffic</string> <string name="allow_lan_traffic">Allow LAN traffic</string>
@@ -135,7 +133,6 @@
<string name="kofi">Ko-fi</string> <string name="kofi">Ko-fi</string>
<string name="donation_signoff">Gratefully,</string> <string name="donation_signoff">Gratefully,</string>
<string name="selected">Selected</string> <string name="selected">Selected</string>
<string name="global_split_tunneling">Global split tunneling</string>
<string name="active_network">Active Network:</string> <string name="active_network">Active Network:</string>
<string name="range_hint">(%1$d%2$d)</string> <string name="range_hint">(%1$d%2$d)</string>
<string name="delete_active_message">Cannot delete active tunnel.</string> <string name="delete_active_message">Cannot delete active tunnel.</string>
@@ -158,7 +155,6 @@
<string name="mtu">MTU</string> <string name="mtu">MTU</string>
<string name="configuration">Configuration</string> <string name="configuration">Configuration</string>
<string name="drag_handle">Drag Handle</string> <string name="drag_handle">Drag Handle</string>
<string name="global_dns_servers">Global DNS servers</string>
<string name="display_theme">Display theme</string> <string name="display_theme">Display theme</string>
<string name="contact">Contact</string> <string name="contact">Contact</string>
<string name="ports_must_differ">Failed. Proxies must have different ports.</string> <string name="ports_must_differ">Failed. Proxies must have different ports.</string>
@@ -258,7 +254,6 @@
<string name="unavailable_in_mode">Unavailable in current mode</string> <string name="unavailable_in_mode">Unavailable in current mode</string>
<string name="server_port">Server:Port</string> <string name="server_port">Server:Port</string>
<string name="camera_permission_required">Camera permission required</string> <string name="camera_permission_required">Camera permission required</string>
<string name="enabled_app_shortcuts">Enable app shortcuts</string>
<string name="preferred_tunnel">Preferred tunnel</string> <string name="preferred_tunnel">Preferred tunnel</string>
<string name="allow">Allow</string> <string name="allow">Allow</string>
<string name="underload_packet_magic_header">Underload packet magic header</string> <string name="underload_packet_magic_header">Underload packet magic header</string>
@@ -273,7 +268,6 @@
<string name="settings">Settings</string> <string name="settings">Settings</string>
<string name="incorrect_pin">Pin is incorrect</string> <string name="incorrect_pin">Pin is incorrect</string>
<string name="export_failed">Export failed</string> <string name="export_failed">Export failed</string>
<string name="enable_remote_app_control">Enable remote app control</string>
<string name="donation_closing">It\'s my dream to work for you on this project full-time.</string> <string name="donation_closing">It\'s my dream to work for you on this project full-time.</string>
<string name="update_download_failed">Update download failed.</string> <string name="update_download_failed">Update download failed.</string>
<string name="network_name">Network:</string> <string name="network_name">Network:</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
-11
View File
@@ -37,7 +37,6 @@
<string name="base64_key">base64-Schlüssel</string> <string name="base64_key">base64-Schlüssel</string>
<string name="delete_tunnel">Tunnel löschen</string> <string name="delete_tunnel">Tunnel löschen</string>
<string name="persistent_keepalive">Dauerhaftes Keepalive</string> <string name="persistent_keepalive">Dauerhaftes Keepalive</string>
<string name="enable_app_lock">App-Sperre aktivieren</string>
<string name="interface_">Schnittstelle</string> <string name="interface_">Schnittstelle</string>
<string name="listen_port">Eingehender Port</string> <string name="listen_port">Eingehender Port</string>
<string name="random">(zufällig)</string> <string name="random">(zufällig)</string>
@@ -45,7 +44,6 @@
<string name="seconds">Sekunden</string> <string name="seconds">Sekunden</string>
<string name="cancel">Abbrechen</string> <string name="cancel">Abbrechen</string>
<string name="preshared_key">Geteilter Schlüssel</string> <string name="preshared_key">Geteilter Schlüssel</string>
<string name="enabled_app_shortcuts">App-Verknüpfungen aktivieren</string>
<string name="tunnel_on_wifi">Tunnel bei WLAN</string> <string name="tunnel_on_wifi">Tunnel bei WLAN</string>
<string name="email_subject">WG Tunnel Unterstützung</string> <string name="email_subject">WG Tunnel Unterstützung</string>
<string name="docs_description">Dokumentation lesen</string> <string name="docs_description">Dokumentation lesen</string>
@@ -125,7 +123,6 @@
<string name="pre_down">Vor Deaktivierung</string> <string name="pre_down">Vor Deaktivierung</string>
<string name="post_down">Nach Deaktivierung</string> <string name="post_down">Nach Deaktivierung</string>
<string name="quick_actions">Schnellaktionen</string> <string name="quick_actions">Schnellaktionen</string>
<string name="stop_on_internet_loss">Stoppen, wenn die Internetverbindung getrennt wird</string>
<string name="bypass_lan_for_kill_switch">LAN umgehen für Notschalter</string> <string name="bypass_lan_for_kill_switch">LAN umgehen für Notschalter</string>
<string name="hide_scripts">Skripte verbergen</string> <string name="hide_scripts">Skripte verbergen</string>
<string name="enable_amnezia_compatibility">Amnezia Kompatibilität aktivieren</string> <string name="enable_amnezia_compatibility">Amnezia Kompatibilität aktivieren</string>
@@ -153,7 +150,6 @@
<string name="copy">Kopieren</string> <string name="copy">Kopieren</string>
<string name="service_running_error">Dienst läuft nicht</string> <string name="service_running_error">Dienst läuft nicht</string>
<string name="auth_error">Nicht autorisiert</string> <string name="auth_error">Nicht autorisiert</string>
<string name="enable_remote_app_control">App-Fernsteuerung aktivieren</string>
<string name="export_failed">Export fehlgeschlagen</string> <string name="export_failed">Export fehlgeschlagen</string>
<string name="app_permission_description">https://hosted.weblate.org/translate/wg-tunnel/strings/en/?checksum=e52d7eb2e28a9a12Steuere Tunnel und Auto-Tunnel Funktionen.</string> <string name="app_permission_description">https://hosted.weblate.org/translate/wg-tunnel/strings/en/?checksum=e52d7eb2e28a9a12Steuere Tunnel und Auto-Tunnel Funktionen.</string>
<string name="dns_resolve_error">DNS-Auflösung fehlgeschlagen</string> <string name="dns_resolve_error">DNS-Auflösung fehlgeschlagen</string>
@@ -281,14 +277,12 @@
<string name="resources">Resourcen</string> <string name="resources">Resourcen</string>
<string name="back">Zurück</string> <string name="back">Zurück</string>
<string name="already_donated">Bereits gespendet</string> <string name="already_donated">Bereits gespendet</string>
<string name="global_split_tunneling">Globales Split-Tunneling</string>
<string name="active_network">Aktives Netzwerk:</string> <string name="active_network">Aktives Netzwerk:</string>
<string name="help_translate">Hilf mit, die App zu übersetzen</string> <string name="help_translate">Hilf mit, die App zu übersetzen</string>
<string name="ethernet">Ethernet</string> <string name="ethernet">Ethernet</string>
<string name="other">Sonstiges</string> <string name="other">Sonstiges</string>
<string name="kill_switch">Notschalter</string> <string name="kill_switch">Notschalter</string>
<string name="configuration">Konfiguration</string> <string name="configuration">Konfiguration</string>
<string name="global_dns_servers">Globale DNS Server</string>
<string name="contact">Kontakt</string> <string name="contact">Kontakt</string>
<string name="backup_and_restore">Sichern und Wiederherstellen</string> <string name="backup_and_restore">Sichern und Wiederherstellen</string>
<string name="about">Über</string> <string name="about">Über</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
-11
View File
@@ -41,7 +41,6 @@
<string name="optional">(opcional)</string> <string name="optional">(opcional)</string>
<string name="seconds">Segundos</string> <string name="seconds">Segundos</string>
<string name="cancel">Cancelar</string> <string name="cancel">Cancelar</string>
<string name="enabled_app_shortcuts">Habilitar acesos directos de app</string>
<string name="unknown_error">Error desconocido</string> <string name="unknown_error">Error desconocido</string>
<string name="email_subject">Ayuda de WG Tunnel</string> <string name="email_subject">Ayuda de WG Tunnel</string>
<string name="interface_">Interfaz</string> <string name="interface_">Interfaz</string>
@@ -65,7 +64,6 @@
<string name="pin_created">Pin creado con éxito</string> <string name="pin_created">Pin creado con éxito</string>
<string name="enter_pin">Introduce tu pin</string> <string name="enter_pin">Introduce tu pin</string>
<string name="create_pin">Crear pin</string> <string name="create_pin">Crear pin</string>
<string name="enable_app_lock">Activar el bloqueo de aplicaciones</string>
<string name="set_primary_tunnel">Establecer como túnel Principal</string> <string name="set_primary_tunnel">Establecer como túnel Principal</string>
<string name="edit_tunnel">Editar túnel</string> <string name="edit_tunnel">Editar túnel</string>
<string name="settings">Ajustes</string> <string name="settings">Ajustes</string>
@@ -121,7 +119,6 @@
<string name="learn_more">Saber más</string> <string name="learn_more">Saber más</string>
<string name="wildcards_active">Comodines activos</string> <string name="wildcards_active">Comodines activos</string>
<string name="stop_on_no_internet">Detener cuando no hay internet</string> <string name="stop_on_no_internet">Detener cuando no hay internet</string>
<string name="stop_on_internet_loss">Detener túnel cuando se pierda el internet</string>
<string name="native_kill_switch">Interruptor de apagado nativo</string> <string name="native_kill_switch">Interruptor de apagado nativo</string>
<string name="allow_lan_traffic">Permitir tráfico LAN</string> <string name="allow_lan_traffic">Permitir tráfico LAN</string>
<string name="bypass_lan_for_kill_switch">Excluir LAN del interruptor de apagado</string> <string name="bypass_lan_for_kill_switch">Excluir LAN del interruptor de apagado</string>
@@ -166,7 +163,6 @@
<string name="check_for_update">Comprobar actualización</string> <string name="check_for_update">Comprobar actualización</string>
<string name="update_check_failed">Fallo al comprobar la actualización.</string> <string name="update_check_failed">Fallo al comprobar la actualización.</string>
<string name="flavor_template">Variante: %1$s</string> <string name="flavor_template">Variante: %1$s</string>
<string name="enable_remote_app_control">Activar control remoto de la app</string>
<string name="export_success">Exportación completada</string> <string name="export_success">Exportación completada</string>
<string name="download">Descargar</string> <string name="download">Descargar</string>
<string name="latest_installed">Ya se está ejecutando la última versión.</string> <string name="latest_installed">Ya se está ejecutando la última versión.</string>
@@ -280,14 +276,12 @@
<string name="resources">Recursos</string> <string name="resources">Recursos</string>
<string name="back">Atrás</string> <string name="back">Atrás</string>
<string name="already_donated">Ya he donado</string> <string name="already_donated">Ya he donado</string>
<string name="global_split_tunneling">Túnel dividido global</string>
<string name="active_network">Red activa:</string> <string name="active_network">Red activa:</string>
<string name="help_translate">Ayuda a traducir la app</string> <string name="help_translate">Ayuda a traducir la app</string>
<string name="ethernet">Ethernet</string> <string name="ethernet">Ethernet</string>
<string name="other">Otros</string> <string name="other">Otros</string>
<string name="kill_switch">kill switch</string> <string name="kill_switch">kill switch</string>
<string name="configuration">Configuración</string> <string name="configuration">Configuración</string>
<string name="global_dns_servers">Servidores DNS globales</string>
<string name="contact">Contacto</string> <string name="contact">Contacto</string>
<string name="backup_and_restore">Copia de seguridad/Restaurar</string> <string name="backup_and_restore">Copia de seguridad/Restaurar</string>
<string name="about">Acerca de</string> <string name="about">Acerca de</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
-11
View File
@@ -47,7 +47,6 @@
<string name="allow">Luba</string> <string name="allow">Luba</string>
<string name="select_all">Vali kõik</string> <string name="select_all">Vali kõik</string>
<string name="export_success">Eksportimine õnnestus</string> <string name="export_success">Eksportimine õnnestus</string>
<string name="enable_remote_app_control">Luba rakenduse kaugjuhtimine</string>
<string name="add_from_url">Lisa võrguaadressilt</string> <string name="add_from_url">Lisa võrguaadressilt</string>
<string name="enter_config_url">Sisesta seadistuse võrguaadress</string> <string name="enter_config_url">Sisesta seadistuse võrguaadress</string>
<string name="error_download_failed">Seadistuse allalaadimine ei õnnestunud</string> <string name="error_download_failed">Seadistuse allalaadimine ei õnnestunud</string>
@@ -151,8 +150,6 @@
<string name="splt_tunneling">Jagatud tunneldus</string> <string name="splt_tunneling">Jagatud tunneldus</string>
<string name="stop">Peata</string> <string name="stop">Peata</string>
<string name="stop_on_no_internet">Peata internetiühenduse puudumisel</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">Luba rakenduse lukustamine</string>
<string name="launch_app_settings">Käivita rakenduse seadistused</string> <string name="launch_app_settings">Käivita rakenduse seadistused</string>
<string name="use_wildcards">Kasuta nimedes metamärke</string> <string name="use_wildcards">Kasuta nimedes metamärke</string>
<string name="wildcards_active">Metamärgid on kasutusel</string> <string name="wildcards_active">Metamärgid on kasutusel</string>
@@ -181,7 +178,6 @@
<string name="_default">Vaikimisi meetod</string> <string name="_default">Vaikimisi meetod</string>
<string name="wifi_detection_method">WiFi tuvastamise meetod</string> <string name="wifi_detection_method">WiFi tuvastamise meetod</string>
<string name="current_template">Praegune: %1$s</string> <string name="current_template">Praegune: %1$s</string>
<string name="enabled_app_shortcuts">Luba rakenduse otseteed</string>
<string name="auto_tunnel_title">Tunneli automaatne loomine</string> <string name="auto_tunnel_title">Tunneli automaatne loomine</string>
<string name="auto_tunnel_not_running">Tunneli automaatne käivitamine pole kasutusel</string> <string name="auto_tunnel_not_running">Tunneli automaatne käivitamine pole kasutusel</string>
<string name="auto_tunnel_running">Tunneli automaatne käivitamine on kasutusel</string> <string name="auto_tunnel_running">Tunneli automaatne käivitamine on kasutusel</string>
@@ -280,14 +276,12 @@
<string name="resources">Ressursid</string> <string name="resources">Ressursid</string>
<string name="back">Tagasi</string> <string name="back">Tagasi</string>
<string name="already_donated">Juba annetasin</string> <string name="already_donated">Juba annetasin</string>
<string name="global_split_tunneling">Üldine jagatud tunneldus</string>
<string name="active_network">Aktiivne võrk:</string> <string name="active_network">Aktiivne võrk:</string>
<string name="help_translate">Aita seda rakendust tõlkida</string> <string name="help_translate">Aita seda rakendust tõlkida</string>
<string name="ethernet">Ethernet</string> <string name="ethernet">Ethernet</string>
<string name="other">Muu</string> <string name="other">Muu</string>
<string name="kill_switch">kiirpeatamine</string> <string name="kill_switch">kiirpeatamine</string>
<string name="configuration">Seadistused</string> <string name="configuration">Seadistused</string>
<string name="global_dns_servers">Üldised nimeserverid</string>
<string name="contact">Kontakt</string> <string name="contact">Kontakt</string>
<string name="backup_and_restore">Varundus ja taastamine</string> <string name="backup_and_restore">Varundus ja taastamine</string>
<string name="about">Rakenduse teave</string> <string name="about">Rakenduse teave</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
-11
View File
@@ -27,7 +27,6 @@
<string name="save">Save</string> <string name="save">Save</string>
<string name="delete_tunnel">Delete tunnel</string> <string name="delete_tunnel">Delete tunnel</string>
<string name="logs">Logs</string> <string name="logs">Logs</string>
<string name="enable_app_lock">Enable app lock</string>
<string name="config_changes_saved">Configuration changes saved.</string> <string name="config_changes_saved">Configuration changes saved.</string>
<string name="join_telegram">Join Telegram community</string> <string name="join_telegram">Join Telegram community</string>
<string name="pin_created">Pin successfully created</string> <string name="pin_created">Pin successfully created</string>
@@ -39,7 +38,6 @@
<string name="remote_key">Remote key</string> <string name="remote_key">Remote key</string>
<string name="mobile_data">Mobile data</string> <string name="mobile_data">Mobile data</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_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="stop_on_internet_loss">Stop tunnel on internet loss</string>
<string name="vpn">VPN</string> <string name="vpn">VPN</string>
<string name="tunnel_boot_description">Start the default tunnel on boot</string> <string name="tunnel_boot_description">Start the default tunnel on boot</string>
<string name="allow_lan_traffic">Allow LAN traffic</string> <string name="allow_lan_traffic">Allow LAN traffic</string>
@@ -143,7 +141,6 @@
<string name="kofi">Ko-fi</string> <string name="kofi">Ko-fi</string>
<string name="donation_signoff">Gratefully,</string> <string name="donation_signoff">Gratefully,</string>
<string name="selected">Selected</string> <string name="selected">Selected</string>
<string name="global_split_tunneling">Global split tunneling</string>
<string name="active_network">Active Network:</string> <string name="active_network">Active Network:</string>
<string name="range_hint">(%1$d%2$d)</string> <string name="range_hint">(%1$d%2$d)</string>
<string name="delete_active_message">Cannot delete active tunnel.</string> <string name="delete_active_message">Cannot delete active tunnel.</string>
@@ -165,7 +162,6 @@
<string name="mtu">MTU</string> <string name="mtu">MTU</string>
<string name="configuration">Configuration</string> <string name="configuration">Configuration</string>
<string name="drag_handle">Drag Handle</string> <string name="drag_handle">Drag Handle</string>
<string name="global_dns_servers">Global DNS servers</string>
<string name="display_theme">Display theme</string> <string name="display_theme">Display theme</string>
<string name="contact">Contact</string> <string name="contact">Contact</string>
<string name="ports_must_differ">Failed. Proxies must have different ports.</string> <string name="ports_must_differ">Failed. Proxies must have different ports.</string>
@@ -259,7 +255,6 @@
<string name="unavailable_in_mode">Unavailable in current mode</string> <string name="unavailable_in_mode">Unavailable in current mode</string>
<string name="server_port">Server:Port</string> <string name="server_port">Server:Port</string>
<string name="camera_permission_required">Camera permission required</string> <string name="camera_permission_required">Camera permission required</string>
<string name="enabled_app_shortcuts">Enable app shortcuts</string>
<string name="preferred_tunnel">Preferred tunnel</string> <string name="preferred_tunnel">Preferred tunnel</string>
<string name="allow">Allow</string> <string name="allow">Allow</string>
<string name="underload_packet_magic_header">Underload packet magic header</string> <string name="underload_packet_magic_header">Underload packet magic header</string>
@@ -274,7 +269,6 @@
<string name="settings">Settings</string> <string name="settings">Settings</string>
<string name="incorrect_pin">Pin is incorrect</string> <string name="incorrect_pin">Pin is incorrect</string>
<string name="export_failed">Export failed</string> <string name="export_failed">Export failed</string>
<string name="enable_remote_app_control">Enable remote app control</string>
<string name="donation_closing">It\'s my dream to work for you on this project full-time.</string> <string name="donation_closing">It\'s my dream to work for you on this project full-time.</string>
<string name="update_download_failed">Update download failed.</string> <string name="update_download_failed">Update download failed.</string>
<string name="network_name">Network:</string> <string name="network_name">Network:</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
-11
View File
@@ -47,7 +47,6 @@
<string name="pin_created">Pin luotu</string> <string name="pin_created">Pin luotu</string>
<string name="enter_pin">Syötä pin-koodi</string> <string name="enter_pin">Syötä pin-koodi</string>
<string name="create_pin">Luo pin-koodi</string> <string name="create_pin">Luo pin-koodi</string>
<string name="enable_app_lock">Ota käyttöön sovelluksen lukitus</string>
<string name="set_primary_tunnel">Aseta ensisijaiseksi tunneliksi</string> <string name="set_primary_tunnel">Aseta ensisijaiseksi tunneliksi</string>
<string name="edit_tunnel">Muokkaa tunnelia</string> <string name="edit_tunnel">Muokkaa tunnelia</string>
<string name="settings">Asetukset</string> <string name="settings">Asetukset</string>
@@ -87,7 +86,6 @@
<string name="add_wifi_name">Lisää WIFI:n nimi</string> <string name="add_wifi_name">Lisää WIFI:n nimi</string>
<string name="language">Kieli</string> <string name="language">Kieli</string>
<string name="include">Sisällytä</string> <string name="include">Sisällytä</string>
<string name="enabled_app_shortcuts">Salli sovelluksen pikakuvakkeet</string>
<string name="automatic">Automaattinen</string> <string name="automatic">Automaattinen</string>
<string name="tunnel_name">Tunnelin nimi</string> <string name="tunnel_name">Tunnelin nimi</string>
<string name="restart_at_boot">Käynnistä laitteen käynnistyksen yhteydessä</string> <string name="restart_at_boot">Käynnistä laitteen käynnistyksen yhteydessä</string>
@@ -114,7 +112,6 @@
<string name="remote_key">Remote key</string> <string name="remote_key">Remote key</string>
<string name="mobile_data">Mobile data</string> <string name="mobile_data">Mobile data</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_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="stop_on_internet_loss">Stop tunnel on internet loss</string>
<string name="vpn">VPN</string> <string name="vpn">VPN</string>
<string name="tunnel_boot_description">Start the default tunnel on boot</string> <string name="tunnel_boot_description">Start the default tunnel on boot</string>
<string name="google_donation_message">Unfortunately, due to Google\'s policies, donation links are not allowed in the Play Store version of this app. Please browse the project\'s webpages to find where to donate.</string> <string name="google_donation_message">Unfortunately, due to Google\'s policies, donation links are not allowed in the Play Store version of this app. Please browse the project\'s webpages to find where to donate.</string>
@@ -187,7 +184,6 @@
<string name="kofi">Ko-fi</string> <string name="kofi">Ko-fi</string>
<string name="donation_signoff">Gratefully,</string> <string name="donation_signoff">Gratefully,</string>
<string name="selected">Selected</string> <string name="selected">Selected</string>
<string name="global_split_tunneling">Global split tunneling</string>
<string name="active_network">Active Network:</string> <string name="active_network">Active Network:</string>
<string name="range_hint">(%1$d%2$d)</string> <string name="range_hint">(%1$d%2$d)</string>
<string name="delete_active_message">Cannot delete active tunnel.</string> <string name="delete_active_message">Cannot delete active tunnel.</string>
@@ -203,7 +199,6 @@
<string name="kill_switch">kill switch</string> <string name="kill_switch">kill switch</string>
<string name="configuration">Configuration</string> <string name="configuration">Configuration</string>
<string name="drag_handle">Drag Handle</string> <string name="drag_handle">Drag Handle</string>
<string name="global_dns_servers">Global DNS servers</string>
<string name="contact">Contact</string> <string name="contact">Contact</string>
<string name="ports_must_differ">Failed. Proxies must have different ports.</string> <string name="ports_must_differ">Failed. Proxies must have different ports.</string>
<string name="join_matrix">Join Matrix community</string> <string name="join_matrix">Join Matrix community</string>
@@ -282,7 +277,6 @@
<string name="fix">Fix</string> <string name="fix">Fix</string>
<string name="tunnel_running_name_message">Name unchangeable while tunnel is active.</string> <string name="tunnel_running_name_message">Name unchangeable while tunnel is active.</string>
<string name="export_failed">Export failed</string> <string name="export_failed">Export failed</string>
<string name="enable_remote_app_control">Enable remote app control</string>
<string name="donation_closing">It\'s my dream to work for you on this project full-time.</string> <string name="donation_closing">It\'s my dream to work for you on this project full-time.</string>
<string name="update_download_failed">Update download failed.</string> <string name="update_download_failed">Update download failed.</string>
<string name="network_name">Network:</string> <string name="network_name">Network:</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
-11
View File
@@ -71,7 +71,6 @@
<string name="show_amnezia_properties">Voir les propriétés d\'Amnezia</string> <string name="show_amnezia_properties">Voir les propriétés d\'Amnezia</string>
<string name="never">Jamais</string> <string name="never">Jamais</string>
<string name="logs">Journaux</string> <string name="logs">Journaux</string>
<string name="enabled_app_shortcuts">Activer les raccourcis</string>
<string name="unknown_error">Une erreur inconnue s\'est produite</string> <string name="unknown_error">Une erreur inconnue s\'est produite</string>
<string name="email_subject">Assistance WG Tunnel</string> <string name="email_subject">Assistance WG Tunnel</string>
<string name="yes">Oui</string> <string name="yes">Oui</string>
@@ -79,7 +78,6 @@
<string name="set_primary_tunnel">Tunnel utilisé quand aucun tunnel favori n\'est défini</string> <string name="set_primary_tunnel">Tunnel utilisé quand aucun tunnel favori n\'est défini</string>
<string name="auto">(Auto)</string> <string name="auto">(Auto)</string>
<string name="pin_created">Code PIN bien créé</string> <string name="pin_created">Code PIN bien créé</string>
<string name="enable_app_lock">Activer le verrouillage de l\'appli</string>
<string name="edit_tunnel">Éditer le tunnel</string> <string name="edit_tunnel">Éditer le tunnel</string>
<string name="settings">Réglages</string> <string name="settings">Réglages</string>
<string name="junk_packet_count">Nombre de paquets indésirables</string> <string name="junk_packet_count">Nombre de paquets indésirables</string>
@@ -118,7 +116,6 @@
<string name="remove_amnezia_compatibility">Retirer la prise en charge d\'Amnezia</string> <string name="remove_amnezia_compatibility">Retirer la prise en charge d\'Amnezia</string>
<string name="hide_amnezia_properties">Cacher les propriétés d\'Amnezia</string> <string name="hide_amnezia_properties">Cacher les propriétés d\'Amnezia</string>
<string name="stop_on_no_internet">Arrêt en l\'absence d\'internet</string> <string name="stop_on_no_internet">Arrêt en l\'absence d\'internet</string>
<string name="stop_on_internet_loss">Couper les tunnels en l\'absence d\'internet</string>
<string name="native_kill_switch">Arrêt d\'urgence natif</string> <string name="native_kill_switch">Arrêt d\'urgence natif</string>
<string name="allow_lan_traffic">Autoriser le trafic LAN</string> <string name="allow_lan_traffic">Autoriser le trafic LAN</string>
<string name="bypass_lan_for_kill_switch">Contourner le LAN en cas d\'arrêt d\'urgence</string> <string name="bypass_lan_for_kill_switch">Contourner le LAN en cas d\'arrêt d\'urgence</string>
@@ -213,7 +210,6 @@
<string name="kofi">Ko-fi</string> <string name="kofi">Ko-fi</string>
<string name="donation_signoff">Avec toute ma gratitude,</string> <string name="donation_signoff">Avec toute ma gratitude,</string>
<string name="selected">Sélectionné</string> <string name="selected">Sélectionné</string>
<string name="global_split_tunneling">Tunneling sélectif global</string>
<string name="active_network">Réseau actif:</string> <string name="active_network">Réseau actif:</string>
<string name="range_hint">(%1$d%2$d)</string> <string name="range_hint">(%1$d%2$d)</string>
<string name="delete_active_message">Impossible de supprimer un tunnel actif.</string> <string name="delete_active_message">Impossible de supprimer un tunnel actif.</string>
@@ -227,7 +223,6 @@
<string name="kill_switch">kill switch</string> <string name="kill_switch">kill switch</string>
<string name="configuration">Configuration</string> <string name="configuration">Configuration</string>
<string name="drag_handle">Poignée de glissement</string> <string name="drag_handle">Poignée de glissement</string>
<string name="global_dns_servers">Serveurs DNS global</string>
<string name="contact">Contact</string> <string name="contact">Contact</string>
<string name="ports_must_differ">Échec. Les serveurs proxy doivent utiliser des ports différents.</string> <string name="ports_must_differ">Échec. Les serveurs proxy doivent utiliser des ports différents.</string>
<string name="backup_and_restore">Sauvegarde et restauration</string> <string name="backup_and_restore">Sauvegarde et restauration</string>
@@ -287,7 +282,6 @@
<string name="fix">Corriger</string> <string name="fix">Corriger</string>
<string name="tunnel_running_name_message">Le nom ne peut pas être modifié tant que le tunnel est actif.</string> <string name="tunnel_running_name_message">Le nom ne peut pas être modifié tant que le tunnel est actif.</string>
<string name="export_failed">L\'exportation a échoué</string> <string name="export_failed">L\'exportation a échoué</string>
<string name="enable_remote_app_control">Activer le contrôle à distance des applications</string>
<string name="donation_closing">Mon rêve serait de travailler à plein temps pour vous sur ce projet.</string> <string name="donation_closing">Mon rêve serait de travailler à plein temps pour vous sur ce projet.</string>
<string name="update_download_failed">La mise à jour n\'a pas pu être téléchargée.</string> <string name="update_download_failed">La mise à jour n\'a pas pu être téléchargée.</string>
<string name="network_name">Réseau:</string> <string name="network_name">Réseau:</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
-11
View File
@@ -15,7 +15,6 @@
<string name="delete_tunnel">Delete tunnel</string> <string name="delete_tunnel">Delete tunnel</string>
<string name="tunnel_mobile_data">Tunnel on mobile data</string> <string name="tunnel_mobile_data">Tunnel on mobile data</string>
<string name="logs">Logs</string> <string name="logs">Logs</string>
<string name="enable_app_lock">Enable app lock</string>
<string name="config_changes_saved">Configuration changes saved.</string> <string name="config_changes_saved">Configuration changes saved.</string>
<string name="join_telegram">Join Telegram community</string> <string name="join_telegram">Join Telegram community</string>
<string name="pin_created">Pin successfully created</string> <string name="pin_created">Pin successfully created</string>
@@ -27,7 +26,6 @@
<string name="remote_key">Remote key</string> <string name="remote_key">Remote key</string>
<string name="mobile_data">Mobile data</string> <string name="mobile_data">Mobile data</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_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="stop_on_internet_loss">Stop tunnel on internet loss</string>
<string name="vpn">VPN</string> <string name="vpn">VPN</string>
<string name="tunnel_boot_description">Start the default tunnel on boot</string> <string name="tunnel_boot_description">Start the default tunnel on boot</string>
<string name="allow_lan_traffic">Allow LAN traffic</string> <string name="allow_lan_traffic">Allow LAN traffic</string>
@@ -134,7 +132,6 @@
<string name="kofi">Ko-fi</string> <string name="kofi">Ko-fi</string>
<string name="donation_signoff">Gratefully,</string> <string name="donation_signoff">Gratefully,</string>
<string name="selected">Selected</string> <string name="selected">Selected</string>
<string name="global_split_tunneling">Global split tunneling</string>
<string name="active_network">Active Network:</string> <string name="active_network">Active Network:</string>
<string name="range_hint">(%1$d%2$d)</string> <string name="range_hint">(%1$d%2$d)</string>
<string name="delete_active_message">Cannot delete active tunnel.</string> <string name="delete_active_message">Cannot delete active tunnel.</string>
@@ -157,7 +154,6 @@
<string name="mtu">MTU</string> <string name="mtu">MTU</string>
<string name="configuration">Configuration</string> <string name="configuration">Configuration</string>
<string name="drag_handle">Drag Handle</string> <string name="drag_handle">Drag Handle</string>
<string name="global_dns_servers">Global DNS servers</string>
<string name="display_theme">Display theme</string> <string name="display_theme">Display theme</string>
<string name="contact">Contact</string> <string name="contact">Contact</string>
<string name="ports_must_differ">Failed. Proxies must have different ports.</string> <string name="ports_must_differ">Failed. Proxies must have different ports.</string>
@@ -259,7 +255,6 @@
<string name="unavailable_in_mode">Unavailable in current mode</string> <string name="unavailable_in_mode">Unavailable in current mode</string>
<string name="server_port">Server:Port</string> <string name="server_port">Server:Port</string>
<string name="camera_permission_required">Camera permission required</string> <string name="camera_permission_required">Camera permission required</string>
<string name="enabled_app_shortcuts">Enable app shortcuts</string>
<string name="preferred_tunnel">Preferred tunnel</string> <string name="preferred_tunnel">Preferred tunnel</string>
<string name="allow">Allow</string> <string name="allow">Allow</string>
<string name="underload_packet_magic_header">Underload packet magic header</string> <string name="underload_packet_magic_header">Underload packet magic header</string>
@@ -274,7 +269,6 @@
<string name="settings">Settings</string> <string name="settings">Settings</string>
<string name="incorrect_pin">Pin is incorrect</string> <string name="incorrect_pin">Pin is incorrect</string>
<string name="export_failed">Export failed</string> <string name="export_failed">Export failed</string>
<string name="enable_remote_app_control">Enable remote app control</string>
<string name="donation_closing">It\'s my dream to work for you on this project full-time.</string> <string name="donation_closing">It\'s my dream to work for you on this project full-time.</string>
<string name="update_download_failed">Update download failed.</string> <string name="update_download_failed">Update download failed.</string>
<string name="network_name">Network:</string> <string name="network_name">Network:</string>
@@ -390,7 +384,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -432,7 +425,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -466,7 +458,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
-11
View File
@@ -17,7 +17,6 @@
<string name="delete_tunnel">Alagút törlése</string> <string name="delete_tunnel">Alagút törlése</string>
<string name="tunnel_mobile_data">Alagút mobiladat-forgalmon</string> <string name="tunnel_mobile_data">Alagút mobiladat-forgalmon</string>
<string name="logs">Naplók</string> <string name="logs">Naplók</string>
<string name="enable_app_lock">Alkalmazászár engedélyezése</string>
<string name="config_changes_saved">Konfigurációs módosítások mentve.</string> <string name="config_changes_saved">Konfigurációs módosítások mentve.</string>
<string name="join_telegram">Csatlakozás a Telegram közösséghez</string> <string name="join_telegram">Csatlakozás a Telegram közösséghez</string>
<string name="pin_created">PIN-kód sikeresen létrehozva</string> <string name="pin_created">PIN-kód sikeresen létrehozva</string>
@@ -29,7 +28,6 @@
<string name="remote_key">Távoli kulcs</string> <string name="remote_key">Távoli kulcs</string>
<string name="mobile_data">Mobiladat</string> <string name="mobile_data">Mobiladat</string>
<string name="use_shell_via_shizuku">Shell használata Shizuku-n keresztül a Wi-Fi információkhoz, így nincs szükség helymeghatározási engedélyre nem rootolt eszközökön</string> <string name="use_shell_via_shizuku">Shell használata Shizuku-n keresztül a Wi-Fi információkhoz, így nincs szükség helymeghatározási engedélyre nem rootolt eszközökön</string>
<string name="stop_on_internet_loss">Alagút leállítása az internet megszűnésekor</string>
<string name="vpn">VPN</string> <string name="vpn">VPN</string>
<string name="tunnel_boot_description">Alapértelmezett alagút indítása rendszerindításkor</string> <string name="tunnel_boot_description">Alapértelmezett alagút indítása rendszerindításkor</string>
<string name="allow_lan_traffic">LAN forgalom engedélyezése</string> <string name="allow_lan_traffic">LAN forgalom engedélyezése</string>
@@ -135,7 +133,6 @@
<string name="kofi">Ko-fi</string> <string name="kofi">Ko-fi</string>
<string name="donation_signoff">Hálával,</string> <string name="donation_signoff">Hálával,</string>
<string name="selected">Kiválasztva</string> <string name="selected">Kiválasztva</string>
<string name="global_split_tunneling">Globális split tunneling</string>
<string name="active_network">Aktív hálózat:</string> <string name="active_network">Aktív hálózat:</string>
<string name="range_hint">(%1$d%2$d)</string> <string name="range_hint">(%1$d%2$d)</string>
<string name="delete_active_message">Aktív alagút nem törölhető.</string> <string name="delete_active_message">Aktív alagút nem törölhető.</string>
@@ -158,7 +155,6 @@
<string name="mtu">MTU</string> <string name="mtu">MTU</string>
<string name="configuration">Konfiguráció</string> <string name="configuration">Konfiguráció</string>
<string name="drag_handle">Húzóka</string> <string name="drag_handle">Húzóka</string>
<string name="global_dns_servers">Globális DNS szerverek</string>
<string name="display_theme">Megjelenítési téma</string> <string name="display_theme">Megjelenítési téma</string>
<string name="contact">Kapcsolat</string> <string name="contact">Kapcsolat</string>
<string name="ports_must_differ">Sikertelen. A proxyknak különböző portokat kell használniuk.</string> <string name="ports_must_differ">Sikertelen. A proxyknak különböző portokat kell használniuk.</string>
@@ -258,7 +254,6 @@
<string name="unavailable_in_mode">Nem érhető el a jelenlegi módban</string> <string name="unavailable_in_mode">Nem érhető el a jelenlegi módban</string>
<string name="server_port">Szerver:Port</string> <string name="server_port">Szerver:Port</string>
<string name="camera_permission_required">Kamera engedély szükséges</string> <string name="camera_permission_required">Kamera engedély szükséges</string>
<string name="enabled_app_shortcuts">App gyorsindítók engedélyezése</string>
<string name="preferred_tunnel">Preferált alagút</string> <string name="preferred_tunnel">Preferált alagút</string>
<string name="allow">Engedélyezés</string> <string name="allow">Engedélyezés</string>
<string name="underload_packet_magic_header">Underload csomag magic header</string> <string name="underload_packet_magic_header">Underload csomag magic header</string>
@@ -273,7 +268,6 @@
<string name="settings">Beállítások</string> <string name="settings">Beállítások</string>
<string name="incorrect_pin">A PIN-kód helytelen</string> <string name="incorrect_pin">A PIN-kód helytelen</string>
<string name="export_failed">Exportálás sikertelen</string> <string name="export_failed">Exportálás sikertelen</string>
<string name="enable_remote_app_control">Távoli vezérlés engedélyezése</string>
<string name="donation_closing">Minden álmom, hogy teljes munkaidőben ezen a projekten dolgozhassak.</string> <string name="donation_closing">Minden álmom, hogy teljes munkaidőben ezen a projekten dolgozhassak.</string>
<string name="update_download_failed">Frissítés letöltése sikertelen.</string> <string name="update_download_failed">Frissítés letöltése sikertelen.</string>
<string name="network_name">Hálózat:</string> <string name="network_name">Hálózat:</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
-11
View File
@@ -29,7 +29,6 @@
<string name="seconds">Detik</string> <string name="seconds">Detik</string>
<string name="persistent_keepalive">Keepalive persisten</string> <string name="persistent_keepalive">Keepalive persisten</string>
<string name="cancel">Batal</string> <string name="cancel">Batal</string>
<string name="enabled_app_shortcuts">Aktifkan pintasan aplikasi</string>
<string name="unknown_error">Terjadi kesalahan tidak dikenal</string> <string name="unknown_error">Terjadi kesalahan tidak dikenal</string>
<string name="tunnel_on_wifi">Terowongan pada Wi-Fi</string> <string name="tunnel_on_wifi">Terowongan pada Wi-Fi</string>
<string name="email_subject">Dukungan WG Tunnel</string> <string name="email_subject">Dukungan WG Tunnel</string>
@@ -64,7 +63,6 @@
<string name="prominent_background_location_message">Fitur ini memerlukan izin lokasi latar belakang untuk mengaktifkan pemantauan SSID Wi-Fi bahkan saat aplikasi ditutup. Untuk detail selengkapnya, silakan lihat Kebijakan Privasi yang tertaut di layar Dukungan.</string> <string name="prominent_background_location_message">Fitur ini memerlukan izin lokasi latar belakang untuk mengaktifkan pemantauan SSID Wi-Fi bahkan saat aplikasi ditutup. Untuk detail selengkapnya, silakan lihat Kebijakan Privasi yang tertaut di layar Dukungan.</string>
<string name="copy_public_key">Salin kunci publik</string> <string name="copy_public_key">Salin kunci publik</string>
<string name="base64_key">Kunci Base64</string> <string name="base64_key">Kunci Base64</string>
<string name="enable_app_lock">Aktifkan kunci aplikasi</string>
<string name="delete_tunnel">Hapus terowongan</string> <string name="delete_tunnel">Hapus terowongan</string>
<string name="delete_tunnel_message">Apakah Anda yakin ingin menghapus terowongan yang dipilih?</string> <string name="delete_tunnel_message">Apakah Anda yakin ingin menghapus terowongan yang dipilih?</string>
<string name="no_email_detected">Tidak ada aplikasi email yang terdeteksi</string> <string name="no_email_detected">Tidak ada aplikasi email yang terdeteksi</string>
@@ -93,7 +91,6 @@
<string name="remote_key">Kunci jarak jauh</string> <string name="remote_key">Kunci jarak jauh</string>
<string name="mobile_data">Data seluler</string> <string name="mobile_data">Data seluler</string>
<string name="use_shell_via_shizuku">Gunakan shell melalui Shizuku untuk mendapatkan informasi Wi-Fi, sehingga tidak memerlukan izin lokasi pada perangkat yang tidak di-root</string> <string name="use_shell_via_shizuku">Gunakan shell melalui Shizuku untuk mendapatkan informasi Wi-Fi, sehingga tidak memerlukan izin lokasi pada perangkat yang tidak di-root</string>
<string name="stop_on_internet_loss">Hentikan terowongan saat koneksi internet terputus</string>
<string name="vpn">VPN</string> <string name="vpn">VPN</string>
<string name="tunnel_boot_description">Mulai terowongan bawaan saat booting</string> <string name="tunnel_boot_description">Mulai terowongan bawaan saat booting</string>
<string name="allow_lan_traffic">Izinkan lalu lintas LAN</string> <string name="allow_lan_traffic">Izinkan lalu lintas LAN</string>
@@ -174,7 +171,6 @@
<string name="kofi">Ko-fi</string> <string name="kofi">Ko-fi</string>
<string name="donation_signoff">Dengan penuh rasa syukur,</string> <string name="donation_signoff">Dengan penuh rasa syukur,</string>
<string name="selected">Dipilih</string> <string name="selected">Dipilih</string>
<string name="global_split_tunneling">Terowongan terpisah global</string>
<string name="active_network">Jaringan Aktif:</string> <string name="active_network">Jaringan Aktif:</string>
<string name="range_hint">(%1$d%2$d)</string> <string name="range_hint">(%1$d%2$d)</string>
<string name="delete_active_message">Tidak dapat menghapus terowongan yang sedang aktif.</string> <string name="delete_active_message">Tidak dapat menghapus terowongan yang sedang aktif.</string>
@@ -192,7 +188,6 @@
<string name="mtu">MTU</string> <string name="mtu">MTU</string>
<string name="configuration">Konfigurasi</string> <string name="configuration">Konfigurasi</string>
<string name="drag_handle">Pegangan Seret</string> <string name="drag_handle">Pegangan Seret</string>
<string name="global_dns_servers">Server DNS global</string>
<string name="display_theme">Tema tampilan</string> <string name="display_theme">Tema tampilan</string>
<string name="contact">Kontak</string> <string name="contact">Kontak</string>
<string name="ports_must_differ">Gagal. Proksi harus memiliki port yang berbeda.</string> <string name="ports_must_differ">Gagal. Proksi harus memiliki port yang berbeda.</string>
@@ -279,7 +274,6 @@
<string name="fix">Perbaiki</string> <string name="fix">Perbaiki</string>
<string name="tunnel_running_name_message">Nama tidak dapat diubah saat terowongan aktif.</string> <string name="tunnel_running_name_message">Nama tidak dapat diubah saat terowongan aktif.</string>
<string name="export_failed">Ekspor gagal</string> <string name="export_failed">Ekspor gagal</string>
<string name="enable_remote_app_control">Aktifkan kontrol aplikasi jarak jauh</string>
<string name="donation_closing">Impian saya adalah bekerja penuh waktu untuk Anda pada proyek ini.</string> <string name="donation_closing">Impian saya adalah bekerja penuh waktu untuk Anda pada proyek ini.</string>
<string name="update_download_failed">Unduhan pembaruan gagal.</string> <string name="update_download_failed">Unduhan pembaruan gagal.</string>
<string name="network_name">Jaringan:</string> <string name="network_name">Jaringan:</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
-11
View File
@@ -13,7 +13,6 @@
<string name="public_key">Chiave pubblica</string> <string name="public_key">Chiave pubblica</string>
<string name="addresses">Indirizzi</string> <string name="addresses">Indirizzi</string>
<string name="dns_servers">Server DNS</string> <string name="dns_servers">Server DNS</string>
<string name="enabled_app_shortcuts">Abilita le scorciatoie da app</string>
<string name="email_subject">Supporto di Tunnel WG</string> <string name="email_subject">Supporto di Tunnel WG</string>
<string name="email_chooser">Invia una email…</string> <string name="email_chooser">Invia una email…</string>
<string name="docs_description">Leggi la documentazione</string> <string name="docs_description">Leggi la documentazione</string>
@@ -64,7 +63,6 @@
<string name="no_browser_detected">Nessun browser rilevato</string> <string name="no_browser_detected">Nessun browser rilevato</string>
<string name="incorrect_pin">Il PIN non è corretto</string> <string name="incorrect_pin">Il PIN non è corretto</string>
<string name="pin_created">PIN correttamente creato</string> <string name="pin_created">PIN correttamente creato</string>
<string name="enable_app_lock">Abilita blocco app</string>
<string name="set_primary_tunnel">Tunnel utilizzato quando non è configurato alcun tunnel preferito</string> <string name="set_primary_tunnel">Tunnel utilizzato quando non è configurato alcun tunnel preferito</string>
<string name="edit_tunnel">Modifica tunnel</string> <string name="edit_tunnel">Modifica tunnel</string>
<string name="settings">Impostazioni</string> <string name="settings">Impostazioni</string>
@@ -88,7 +86,6 @@
<string name="prominent_background_location_message">Questa caratteristica richiede il permesso di localizzazione in background per abilitare il monitoraggio dell\'SSID delle reti Wi-fi anche quando l\'applicazione è chiusa. Per maggiori dettagli, consultare il link alla Privacy Policy presente nella schermata \"Supporto\".</string> <string name="prominent_background_location_message">Questa caratteristica richiede il permesso di localizzazione in background per abilitare il monitoraggio dell\'SSID delle reti Wi-fi anche quando l\'applicazione è chiusa. Per maggiori dettagli, consultare il link alla Privacy Policy presente nella schermata \"Supporto\".</string>
<string name="auto_tunnel_title">Servizio tunneling automatico</string> <string name="auto_tunnel_title">Servizio tunneling automatico</string>
<string name="use_root_shell_for_wifi">Utilizzare una shell root per ottenere le informazioni Wi-Fi, evitando la necessità dell\'autorizzazioni della posizione</string> <string name="use_root_shell_for_wifi">Utilizzare una shell root per ottenere le informazioni Wi-Fi, evitando la necessità dell\'autorizzazioni della posizione</string>
<string name="stop_on_internet_loss">Arresta il tunnel in caso di assenza di connessione internet</string>
<string name="allow_lan_traffic">Abilita traffico LAN</string> <string name="allow_lan_traffic">Abilita traffico LAN</string>
<string name="tunnel_control">Controllo tunnel</string> <string name="tunnel_control">Controllo tunnel</string>
<string name="include_lan">Includi la LAN</string> <string name="include_lan">Includi la LAN</string>
@@ -158,7 +155,6 @@
<string name="service_running_error">Il servizio non è in esecuzione</string> <string name="service_running_error">Il servizio non è in esecuzione</string>
<string name="config_error">Configurazione non valida</string> <string name="config_error">Configurazione non valida</string>
<string name="dns_resolve_error">Risoluzione DNS fallita</string> <string name="dns_resolve_error">Risoluzione DNS fallita</string>
<string name="enable_remote_app_control">Abilita il controllo dell\'app da remoto</string>
<string name="version_template">Versione: %1$s</string> <string name="version_template">Versione: %1$s</string>
<string name="flavor_template">Caratteristica: %1$s</string> <string name="flavor_template">Caratteristica: %1$s</string>
<string name="update_available">Aggiornamento disponibile!</string> <string name="update_available">Aggiornamento disponibile!</string>
@@ -272,7 +268,6 @@
<string name="back">Indietro</string> <string name="back">Indietro</string>
<string name="active_tunnel_update_failed">Aggiornamento tunnel attivo non riuscito</string> <string name="active_tunnel_update_failed">Aggiornamento tunnel attivo non riuscito</string>
<string name="already_donated">Già donato</string> <string name="already_donated">Già donato</string>
<string name="global_split_tunneling">Tunneling diviso globale</string>
<string name="active_network">Rete attiva:</string> <string name="active_network">Rete attiva:</string>
<string name="help_translate">Aiuta a tradurre l\'app</string> <string name="help_translate">Aiuta a tradurre l\'app</string>
<string name="ethernet">Ethernet</string> <string name="ethernet">Ethernet</string>
@@ -280,7 +275,6 @@
<string name="kill_switch">kill switch</string> <string name="kill_switch">kill switch</string>
<string name="configuration">Configurazione</string> <string name="configuration">Configurazione</string>
<string name="drag_handle">Drag Handle</string> <string name="drag_handle">Drag Handle</string>
<string name="global_dns_servers">Server DNS globali</string>
<string name="contact">Contatti</string> <string name="contact">Contatti</string>
<string name="backup_and_restore">Backup e ripristino</string> <string name="backup_and_restore">Backup e ripristino</string>
<string name="about">Informazioni</string> <string name="about">Informazioni</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
-11
View File
@@ -18,7 +18,6 @@
<string name="cancel">キャンセル</string> <string name="cancel">キャンセル</string>
<string name="unknown_error">不明なエラーが発生しました</string> <string name="unknown_error">不明なエラーが発生しました</string>
<string name="error_no_file_explorer">ファイルエクスプローラーはインストールされていません</string> <string name="error_no_file_explorer">ファイルエクスプローラーはインストールされていません</string>
<string name="enabled_app_shortcuts">アプリのショートカットを有効にする</string>
<string name="no_email_detected">メールアプリは検出されません</string> <string name="no_email_detected">メールアプリは検出されません</string>
<string name="email_description">メールを送る</string> <string name="email_description">メールを送る</string>
<string name="no_browser_detected">ブラウザは検出されません</string> <string name="no_browser_detected">ブラウザは検出されません</string>
@@ -55,7 +54,6 @@
<string name="auto_tunnel_title">自動トンネルサービス</string> <string name="auto_tunnel_title">自動トンネルサービス</string>
<string name="edit_tunnel">トンネルの編集</string> <string name="edit_tunnel">トンネルの編集</string>
<string name="create_pin">新規PINを作成</string> <string name="create_pin">新規PINを作成</string>
<string name="enable_app_lock">アプリロックを有効にする</string>
<string name="always_on_message">VPN接続の許可が拒否されました。</string> <string name="always_on_message">VPN接続の許可が拒否されました。</string>
<string name="always_on_message2">他のすべてのアプリで常時接続VPNがオフになっていることを確認して、再度お試しください</string> <string name="always_on_message2">他のすべてのアプリで常時接続VPNがオフになっていることを確認して、再度お試しください</string>
<string name="notifications">通知</string> <string name="notifications">通知</string>
@@ -83,7 +81,6 @@
<string name="remote_key">Remote key</string> <string name="remote_key">Remote key</string>
<string name="mobile_data">Mobile data</string> <string name="mobile_data">Mobile data</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_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="stop_on_internet_loss">Stop tunnel on internet loss</string>
<string name="vpn">VPN</string> <string name="vpn">VPN</string>
<string name="tunnel_boot_description">Start the default tunnel on boot</string> <string name="tunnel_boot_description">Start the default tunnel on boot</string>
<string name="allow_lan_traffic">Allow LAN traffic</string> <string name="allow_lan_traffic">Allow LAN traffic</string>
@@ -167,7 +164,6 @@
<string name="kofi">Ko-fi</string> <string name="kofi">Ko-fi</string>
<string name="donation_signoff">Gratefully,</string> <string name="donation_signoff">Gratefully,</string>
<string name="selected">Selected</string> <string name="selected">Selected</string>
<string name="global_split_tunneling">Global split tunneling</string>
<string name="active_network">Active Network:</string> <string name="active_network">Active Network:</string>
<string name="range_hint">(%1$d%2$d)</string> <string name="range_hint">(%1$d%2$d)</string>
<string name="delete_active_message">Cannot delete active tunnel.</string> <string name="delete_active_message">Cannot delete active tunnel.</string>
@@ -184,7 +180,6 @@
<string name="kill_switch">kill switch</string> <string name="kill_switch">kill switch</string>
<string name="configuration">Configuration</string> <string name="configuration">Configuration</string>
<string name="drag_handle">Drag Handle</string> <string name="drag_handle">Drag Handle</string>
<string name="global_dns_servers">Global DNS servers</string>
<string name="display_theme">Display theme</string> <string name="display_theme">Display theme</string>
<string name="contact">Contact</string> <string name="contact">Contact</string>
<string name="ports_must_differ">Failed. Proxies must have different ports.</string> <string name="ports_must_differ">Failed. Proxies must have different ports.</string>
@@ -277,7 +272,6 @@
<string name="fix">Fix</string> <string name="fix">Fix</string>
<string name="tunnel_running_name_message">Name unchangeable while tunnel is active.</string> <string name="tunnel_running_name_message">Name unchangeable while tunnel is active.</string>
<string name="export_failed">Export failed</string> <string name="export_failed">Export failed</string>
<string name="enable_remote_app_control">Enable remote app control</string>
<string name="donation_closing">It\'s my dream to work for you on this project full-time.</string> <string name="donation_closing">It\'s my dream to work for you on this project full-time.</string>
<string name="update_download_failed">Update download failed.</string> <string name="update_download_failed">Update download failed.</string>
<string name="network_name">Network:</string> <string name="network_name">Network:</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
-11
View File
@@ -44,7 +44,6 @@
<string name="seconds">წამები</string> <string name="seconds">წამები</string>
<string name="persistent_keepalive">მუდმივი keepalive</string> <string name="persistent_keepalive">მუდმივი keepalive</string>
<string name="cancel">უარყოფა</string> <string name="cancel">უარყოფა</string>
<string name="enabled_app_shortcuts">გააქტიურე აპლიკაციის მალსახმობები</string>
<string name="unknown_error">დაფიქსირდა უცნობი შეცდომა</string> <string name="unknown_error">დაფიქსირდა უცნობი შეცდომა</string>
<string name="tunnel_on_wifi">ტუნელის გამოტენება Wi-Fi-ს მეშვეობით</string> <string name="tunnel_on_wifi">ტუნელის გამოტენება Wi-Fi-ს მეშვეობით</string>
<string name="email_subject">ვგ ტუნელის დახმარება</string> <string name="email_subject">ვგ ტუნელის დახმარება</string>
@@ -61,7 +60,6 @@
<string name="pin_created">პინი წარმატებით შეიქმნა</string> <string name="pin_created">პინი წარმატებით შეიქმნა</string>
<string name="enter_pin">შეიყვანეთ პინი</string> <string name="enter_pin">შეიყვანეთ პინი</string>
<string name="create_pin">შექმენით პინი</string> <string name="create_pin">შექმენით პინი</string>
<string name="enable_app_lock">გააქტიურე აპლიკაციის დაბლოკვა</string>
<string name="restart_at_boot">ჩართვისას დაწყება</string> <string name="restart_at_boot">ჩართვისას დაწყება</string>
<string name="vpn_denied_dialog_title">უფლება უარყოფილია</string> <string name="vpn_denied_dialog_title">უფლება უარყოფილია</string>
<string name="auto_tunnel_channel_id" translatable="false">Auto-tunnel Channel</string> <string name="auto_tunnel_channel_id" translatable="false">Auto-tunnel Channel</string>
@@ -141,7 +139,6 @@
<string name="remote_key">Remote key</string> <string name="remote_key">Remote key</string>
<string name="mobile_data">Mobile data</string> <string name="mobile_data">Mobile data</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_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="stop_on_internet_loss">Stop tunnel on internet loss</string>
<string name="vpn">VPN</string> <string name="vpn">VPN</string>
<string name="tunnel_boot_description">Start the default tunnel on boot</string> <string name="tunnel_boot_description">Start the default tunnel on boot</string>
<string name="allow_lan_traffic">Allow LAN traffic</string> <string name="allow_lan_traffic">Allow LAN traffic</string>
@@ -218,7 +215,6 @@
<string name="kofi">Ko-fi</string> <string name="kofi">Ko-fi</string>
<string name="donation_signoff">Gratefully,</string> <string name="donation_signoff">Gratefully,</string>
<string name="selected">Selected</string> <string name="selected">Selected</string>
<string name="global_split_tunneling">Global split tunneling</string>
<string name="active_network">Active Network:</string> <string name="active_network">Active Network:</string>
<string name="range_hint">(%1$d%2$d)</string> <string name="range_hint">(%1$d%2$d)</string>
<string name="delete_active_message">Cannot delete active tunnel.</string> <string name="delete_active_message">Cannot delete active tunnel.</string>
@@ -236,7 +232,6 @@
<string name="kill_switch">kill switch</string> <string name="kill_switch">kill switch</string>
<string name="configuration">Configuration</string> <string name="configuration">Configuration</string>
<string name="drag_handle">Drag Handle</string> <string name="drag_handle">Drag Handle</string>
<string name="global_dns_servers">Global DNS servers</string>
<string name="display_theme">Display theme</string> <string name="display_theme">Display theme</string>
<string name="contact">Contact</string> <string name="contact">Contact</string>
<string name="ports_must_differ">Failed. Proxies must have different ports.</string> <string name="ports_must_differ">Failed. Proxies must have different ports.</string>
@@ -319,7 +314,6 @@
<string name="tunnel_running_name_message">Name unchangeable while tunnel is active.</string> <string name="tunnel_running_name_message">Name unchangeable while tunnel is active.</string>
<string name="settings">Settings</string> <string name="settings">Settings</string>
<string name="export_failed">Export failed</string> <string name="export_failed">Export failed</string>
<string name="enable_remote_app_control">Enable remote app control</string>
<string name="donation_closing">It\'s my dream to work for you on this project full-time.</string> <string name="donation_closing">It\'s my dream to work for you on this project full-time.</string>
<string name="update_download_failed">Update download failed.</string> <string name="update_download_failed">Update download failed.</string>
<string name="network_name">Network:</string> <string name="network_name">Network:</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
-11
View File
@@ -15,7 +15,6 @@
<string name="delete_tunnel">터널 삭제</string> <string name="delete_tunnel">터널 삭제</string>
<string name="tunnel_mobile_data">모바일 데이터에서 터널 사용</string> <string name="tunnel_mobile_data">모바일 데이터에서 터널 사용</string>
<string name="logs">로그</string> <string name="logs">로그</string>
<string name="enable_app_lock">앱 잠금 켜기</string>
<string name="config_changes_saved">변경한 설정이 저장되었습니다.</string> <string name="config_changes_saved">변경한 설정이 저장되었습니다.</string>
<string name="join_telegram">Telegram 커뮤니티 참여</string> <string name="join_telegram">Telegram 커뮤니티 참여</string>
<string name="pin_created">Pin 생성 성공</string> <string name="pin_created">Pin 생성 성공</string>
@@ -27,7 +26,6 @@
<string name="remote_key">원격 키</string> <string name="remote_key">원격 키</string>
<string name="mobile_data">모바일 데이터</string> <string name="mobile_data">모바일 데이터</string>
<string name="use_shell_via_shizuku">Shizuku 셸을 이용하여, 루팅하지 않은 기기에서 위치 권한을 요구하지 않고 Wi-Fi 정보를 얻습니다</string> <string name="use_shell_via_shizuku">Shizuku 셸을 이용하여, 루팅하지 않은 기기에서 위치 권한을 요구하지 않고 Wi-Fi 정보를 얻습니다</string>
<string name="stop_on_internet_loss">인터넷이 끊겼을 때 터널 중지</string>
<string name="vpn">VPN</string> <string name="vpn">VPN</string>
<string name="tunnel_boot_description">부팅 시 기본 터널 시작</string> <string name="tunnel_boot_description">부팅 시 기본 터널 시작</string>
<string name="allow_lan_traffic">LAN 트래픽 허용</string> <string name="allow_lan_traffic">LAN 트래픽 허용</string>
@@ -134,7 +132,6 @@
<string name="kofi">Ko-fi</string> <string name="kofi">Ko-fi</string>
<string name="donation_signoff">감사를 담아,</string> <string name="donation_signoff">감사를 담아,</string>
<string name="selected">선택함</string> <string name="selected">선택함</string>
<string name="global_split_tunneling">글로벌 분할 터널링</string>
<string name="active_network">활성 네트워크:</string> <string name="active_network">활성 네트워크:</string>
<string name="range_hint">(%1$d%2$d)</string> <string name="range_hint">(%1$d%2$d)</string>
<string name="delete_active_message">활성화된 터널은 삭제할 수 없습니다.</string> <string name="delete_active_message">활성화된 터널은 삭제할 수 없습니다.</string>
@@ -157,7 +154,6 @@
<string name="mtu">MTU</string> <string name="mtu">MTU</string>
<string name="configuration">설정</string> <string name="configuration">설정</string>
<string name="drag_handle">핸들 드래그</string> <string name="drag_handle">핸들 드래그</string>
<string name="global_dns_servers">글로벌 DNS 서버</string>
<string name="display_theme">표시 테마</string> <string name="display_theme">표시 테마</string>
<string name="contact">연락</string> <string name="contact">연락</string>
<string name="ports_must_differ">실패. 프록시는 다른 포트를 이용해야 합니다.</string> <string name="ports_must_differ">실패. 프록시는 다른 포트를 이용해야 합니다.</string>
@@ -259,7 +255,6 @@
<string name="unavailable_in_mode">현재 모드에서는 이용 불가</string> <string name="unavailable_in_mode">현재 모드에서는 이용 불가</string>
<string name="server_port">서버:포트</string> <string name="server_port">서버:포트</string>
<string name="camera_permission_required">카메라 권한 필요</string> <string name="camera_permission_required">카메라 권한 필요</string>
<string name="enabled_app_shortcuts">앱 바로가기 켜기</string>
<string name="preferred_tunnel">선호 터널</string> <string name="preferred_tunnel">선호 터널</string>
<string name="allow">허용</string> <string name="allow">허용</string>
<string name="underload_packet_magic_header">언더로드 패킷 매직 헤더</string> <string name="underload_packet_magic_header">언더로드 패킷 매직 헤더</string>
@@ -274,7 +269,6 @@
<string name="settings">설정</string> <string name="settings">설정</string>
<string name="incorrect_pin">Pin이 잘못되었습니다</string> <string name="incorrect_pin">Pin이 잘못되었습니다</string>
<string name="export_failed">내보내기 실패</string> <string name="export_failed">내보내기 실패</string>
<string name="enable_remote_app_control">원격 앱 제어 켜기</string>
<string name="donation_closing">프로젝트에 모든 시간을 할애하는 것이 제 꿈입니다.</string> <string name="donation_closing">프로젝트에 모든 시간을 할애하는 것이 제 꿈입니다.</string>
<string name="update_download_failed">업데이트 다운로드에 실패했습니다.</string> <string name="update_download_failed">업데이트 다운로드에 실패했습니다.</string>
<string name="network_name">네트워크:</string> <string name="network_name">네트워크:</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
@@ -17,7 +17,6 @@
<string name="delete_tunnel">Delete tunnel</string> <string name="delete_tunnel">Delete tunnel</string>
<string name="tunnel_mobile_data">Tunnel on mobile data</string> <string name="tunnel_mobile_data">Tunnel on mobile data</string>
<string name="logs">Logs</string> <string name="logs">Logs</string>
<string name="enable_app_lock">Enable app lock</string>
<string name="config_changes_saved">Configuration changes saved.</string> <string name="config_changes_saved">Configuration changes saved.</string>
<string name="join_telegram">Join Telegram community</string> <string name="join_telegram">Join Telegram community</string>
<string name="pin_created">Pin successfully created</string> <string name="pin_created">Pin successfully created</string>
@@ -29,7 +28,6 @@
<string name="remote_key">Remote key</string> <string name="remote_key">Remote key</string>
<string name="mobile_data">Mobile data</string> <string name="mobile_data">Mobile data</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_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="stop_on_internet_loss">Stop tunnel on internet loss</string>
<string name="vpn">VPN</string> <string name="vpn">VPN</string>
<string name="tunnel_boot_description">Start the default tunnel on boot</string> <string name="tunnel_boot_description">Start the default tunnel on boot</string>
<string name="allow_lan_traffic">Allow LAN traffic</string> <string name="allow_lan_traffic">Allow LAN traffic</string>
@@ -135,7 +133,6 @@
<string name="kofi">Ko-fi</string> <string name="kofi">Ko-fi</string>
<string name="donation_signoff">Gratefully,</string> <string name="donation_signoff">Gratefully,</string>
<string name="selected">Selected</string> <string name="selected">Selected</string>
<string name="global_split_tunneling">Global split tunneling</string>
<string name="active_network">Active Network:</string> <string name="active_network">Active Network:</string>
<string name="range_hint">(%1$d%2$d)</string> <string name="range_hint">(%1$d%2$d)</string>
<string name="delete_active_message">Cannot delete active tunnel.</string> <string name="delete_active_message">Cannot delete active tunnel.</string>
@@ -158,7 +155,6 @@
<string name="mtu">MTU</string> <string name="mtu">MTU</string>
<string name="configuration">Configuration</string> <string name="configuration">Configuration</string>
<string name="drag_handle">Drag Handle</string> <string name="drag_handle">Drag Handle</string>
<string name="global_dns_servers">Global DNS servers</string>
<string name="display_theme">Display theme</string> <string name="display_theme">Display theme</string>
<string name="contact">Contact</string> <string name="contact">Contact</string>
<string name="ports_must_differ">Failed. Proxies must have different ports.</string> <string name="ports_must_differ">Failed. Proxies must have different ports.</string>
@@ -258,7 +254,6 @@
<string name="unavailable_in_mode">Unavailable in current mode</string> <string name="unavailable_in_mode">Unavailable in current mode</string>
<string name="server_port">Server:Port</string> <string name="server_port">Server:Port</string>
<string name="camera_permission_required">Camera permission required</string> <string name="camera_permission_required">Camera permission required</string>
<string name="enabled_app_shortcuts">Enable app shortcuts</string>
<string name="preferred_tunnel">Preferred tunnel</string> <string name="preferred_tunnel">Preferred tunnel</string>
<string name="allow">Allow</string> <string name="allow">Allow</string>
<string name="underload_packet_magic_header">Underload packet magic header</string> <string name="underload_packet_magic_header">Underload packet magic header</string>
@@ -273,7 +268,6 @@
<string name="settings">Settings</string> <string name="settings">Settings</string>
<string name="incorrect_pin">Pin is incorrect</string> <string name="incorrect_pin">Pin is incorrect</string>
<string name="export_failed">Export failed</string> <string name="export_failed">Export failed</string>
<string name="enable_remote_app_control">Enable remote app control</string>
<string name="donation_closing">It\'s my dream to work for you on this project full-time.</string> <string name="donation_closing">It\'s my dream to work for you on this project full-time.</string>
<string name="update_download_failed">Update download failed.</string> <string name="update_download_failed">Update download failed.</string>
<string name="network_name">Network:</string> <string name="network_name">Network:</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
-11
View File
@@ -17,7 +17,6 @@
<string name="name">Naam</string> <string name="name">Naam</string>
<string name="seconds">Seconden</string> <string name="seconds">Seconden</string>
<string name="persistent_keepalive">Persistent keepalive</string> <string name="persistent_keepalive">Persistent keepalive</string>
<string name="enabled_app_shortcuts">App snelkoppelingen inschakelen</string>
<string name="random">(willekeurig)</string> <string name="random">(willekeurig)</string>
<string name="thank_you">Bedankt voor het gebruiken van WG Tunnel!</string> <string name="thank_you">Bedankt voor het gebruiken van WG Tunnel!</string>
<string name="trusted_ssid_value_description">Verstuur SSID</string> <string name="trusted_ssid_value_description">Verstuur SSID</string>
@@ -44,7 +43,6 @@
<string name="auto_tunnel_title">Auto-tunnel service</string> <string name="auto_tunnel_title">Auto-tunnel service</string>
<string name="open_issue">Open een melding</string> <string name="open_issue">Open een melding</string>
<string name="create_pin">Maak PIN</string> <string name="create_pin">Maak PIN</string>
<string name="enable_app_lock">Schakel app-lock in</string>
<string name="init_packet_junk_size">Initiële junk packet grootte</string> <string name="init_packet_junk_size">Initiële junk packet grootte</string>
<string name="junk_packet_maximum_size">Junk packet maximum grootte</string> <string name="junk_packet_maximum_size">Junk packet maximum grootte</string>
<string name="response_packet_junk_size">Response junk packet grootte</string> <string name="response_packet_junk_size">Response junk packet grootte</string>
@@ -110,7 +108,6 @@
<string name="enable_amnezia_compatibility">Amnezia compatibiliteit inschakelen</string> <string name="enable_amnezia_compatibility">Amnezia compatibiliteit inschakelen</string>
<string name="add_from_url">Toevoegen met link</string> <string name="add_from_url">Toevoegen met link</string>
<string name="stop_on_no_internet">Stoppen wanneer geen internet</string> <string name="stop_on_no_internet">Stoppen wanneer geen internet</string>
<string name="stop_on_internet_loss">Stop tunnel bij verlies van internet</string>
<string name="wildcards_active">Wildcards actief</string> <string name="wildcards_active">Wildcards actief</string>
<string name="use_root_shell_for_wifi">Gebruik root shell om wifi naam te bepalen, zodat locatie toestemmingen niet nodig zijn</string> <string name="use_root_shell_for_wifi">Gebruik root shell om wifi naam te bepalen, zodat locatie toestemmingen niet nodig zijn</string>
<string name="start_auto">Start auto-tunnel</string> <string name="start_auto">Start auto-tunnel</string>
@@ -154,7 +151,6 @@
<string name="auto_tunnel_not_running">Auto-tunnel is niet actief</string> <string name="auto_tunnel_not_running">Auto-tunnel is niet actief</string>
<string name="auth_error">Niet toegelaten</string> <string name="auth_error">Niet toegelaten</string>
<string name="service_running_error">Service niet actief</string> <string name="service_running_error">Service niet actief</string>
<string name="enable_remote_app_control">Activeer applicatie controle vanop afstand</string>
<string name="select_all">Selecteer alles</string> <string name="select_all">Selecteer alles</string>
<string name="export_success">Export gelukt</string> <string name="export_success">Export gelukt</string>
<string name="download">Download</string> <string name="download">Download</string>
@@ -251,7 +247,6 @@
<string name="kofi">Ko-fi</string> <string name="kofi">Ko-fi</string>
<string name="donation_signoff">Hartelijk bedankt,</string> <string name="donation_signoff">Hartelijk bedankt,</string>
<string name="selected">Geselecteerd</string> <string name="selected">Geselecteerd</string>
<string name="global_split_tunneling">Globale split tunneling</string>
<string name="active_network">Actief Netwerk:</string> <string name="active_network">Actief Netwerk:</string>
<string name="range_hint">(%1$d%2$d)</string> <string name="range_hint">(%1$d%2$d)</string>
<string name="delete_active_message">Actieve tunnel kan niet worden verwijderd.</string> <string name="delete_active_message">Actieve tunnel kan niet worden verwijderd.</string>
@@ -262,7 +257,6 @@
<string name="new_tunnel">Nieuwe tunnel</string> <string name="new_tunnel">Nieuwe tunnel</string>
<string name="kill_switch">kill switch</string> <string name="kill_switch">kill switch</string>
<string name="configuration">Configuratie</string> <string name="configuration">Configuratie</string>
<string name="global_dns_servers">Globale DNS servers</string>
<string name="contact">Contact</string> <string name="contact">Contact</string>
<string name="ports_must_differ">Mislukt. Proxyservers moeten verschillende poorten hebben.</string> <string name="ports_must_differ">Mislukt. Proxyservers moeten verschillende poorten hebben.</string>
<string name="backup_and_restore">Backup en herstellen</string> <string name="backup_and_restore">Backup en herstellen</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
-11
View File
@@ -15,7 +15,6 @@
<string name="delete_tunnel">Delete tunnel</string> <string name="delete_tunnel">Delete tunnel</string>
<string name="tunnel_mobile_data">Tunnel on mobile data</string> <string name="tunnel_mobile_data">Tunnel on mobile data</string>
<string name="logs">Logs</string> <string name="logs">Logs</string>
<string name="enable_app_lock">Enable app lock</string>
<string name="config_changes_saved">Configuration changes saved.</string> <string name="config_changes_saved">Configuration changes saved.</string>
<string name="join_telegram">Join Telegram community</string> <string name="join_telegram">Join Telegram community</string>
<string name="pin_created">Pin successfully created</string> <string name="pin_created">Pin successfully created</string>
@@ -27,7 +26,6 @@
<string name="remote_key">Remote key</string> <string name="remote_key">Remote key</string>
<string name="mobile_data">Mobile data</string> <string name="mobile_data">Mobile data</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_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="stop_on_internet_loss">Stop tunnel on internet loss</string>
<string name="vpn">VPN</string> <string name="vpn">VPN</string>
<string name="tunnel_boot_description">Start the default tunnel on boot</string> <string name="tunnel_boot_description">Start the default tunnel on boot</string>
<string name="allow_lan_traffic">Allow LAN traffic</string> <string name="allow_lan_traffic">Allow LAN traffic</string>
@@ -134,7 +132,6 @@
<string name="kofi">Ko-fi</string> <string name="kofi">Ko-fi</string>
<string name="donation_signoff">Gratefully,</string> <string name="donation_signoff">Gratefully,</string>
<string name="selected">Selected</string> <string name="selected">Selected</string>
<string name="global_split_tunneling">Global split tunneling</string>
<string name="active_network">Active Network:</string> <string name="active_network">Active Network:</string>
<string name="range_hint">(%1$d%2$d)</string> <string name="range_hint">(%1$d%2$d)</string>
<string name="delete_active_message">Cannot delete active tunnel.</string> <string name="delete_active_message">Cannot delete active tunnel.</string>
@@ -157,7 +154,6 @@
<string name="mtu">MTU</string> <string name="mtu">MTU</string>
<string name="configuration">Configuration</string> <string name="configuration">Configuration</string>
<string name="drag_handle">Drag Handle</string> <string name="drag_handle">Drag Handle</string>
<string name="global_dns_servers">Global DNS servers</string>
<string name="display_theme">Display theme</string> <string name="display_theme">Display theme</string>
<string name="contact">Contact</string> <string name="contact">Contact</string>
<string name="ports_must_differ">Failed. Proxies must have different ports.</string> <string name="ports_must_differ">Failed. Proxies must have different ports.</string>
@@ -259,7 +255,6 @@
<string name="unavailable_in_mode">Unavailable in current mode</string> <string name="unavailable_in_mode">Unavailable in current mode</string>
<string name="server_port">Server:Port</string> <string name="server_port">Server:Port</string>
<string name="camera_permission_required">Camera permission required</string> <string name="camera_permission_required">Camera permission required</string>
<string name="enabled_app_shortcuts">Enable app shortcuts</string>
<string name="preferred_tunnel">Preferred tunnel</string> <string name="preferred_tunnel">Preferred tunnel</string>
<string name="allow">Allow</string> <string name="allow">Allow</string>
<string name="underload_packet_magic_header">Underload packet magic header</string> <string name="underload_packet_magic_header">Underload packet magic header</string>
@@ -274,7 +269,6 @@
<string name="settings">Settings</string> <string name="settings">Settings</string>
<string name="incorrect_pin">Pin is incorrect</string> <string name="incorrect_pin">Pin is incorrect</string>
<string name="export_failed">Export failed</string> <string name="export_failed">Export failed</string>
<string name="enable_remote_app_control">Enable remote app control</string>
<string name="donation_closing">It\'s my dream to work for you on this project full-time.</string> <string name="donation_closing">It\'s my dream to work for you on this project full-time.</string>
<string name="update_download_failed">Update download failed.</string> <string name="update_download_failed">Update download failed.</string>
<string name="network_name">Network:</string> <string name="network_name">Network:</string>
@@ -390,7 +384,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -432,7 +425,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -466,7 +458,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
+91 -102
View File
@@ -11,13 +11,11 @@
<string name="vpn_on">Włącz VPN</string> <string name="vpn_on">Włącz VPN</string>
<string name="vpn_off">Wyłącz VPN</string> <string name="vpn_off">Wyłącz VPN</string>
<string name="interface_">Interfejs</string> <string name="interface_">Interfejs</string>
<string name="enabled_app_shortcuts">Włącz skróty aplikacji</string>
<string name="privacy_policy">Polityka prywatności</string> <string name="privacy_policy">Polityka prywatności</string>
<string name="tunnel_mobile_data">Tunel przez mobilną transmisję danych</string> <string name="tunnel_mobile_data">Tunel przez mobilną transmisję danych</string>
<string name="random">(losowy)</string> <string name="random">(losowy)</string>
<string name="pin_created">Kod PIN został pomyślnie utworzony</string> <string name="pin_created">Kod PIN został pomyślnie utworzony</string>
<string name="enter_pin">Podaj kod PIN</string> <string name="enter_pin">Podaj kod PIN</string>
<string name="enable_app_lock">Włącz blokadę aplikacji</string>
<string name="response_packet_junk_size">Rozmiar śmieciowego pakietu odpowiedzi</string> <string name="response_packet_junk_size">Rozmiar śmieciowego pakietu odpowiedzi</string>
<string name="response_packet_magic_header">Nagłówek magicznego pakietu odpowiedzi</string> <string name="response_packet_magic_header">Nagłówek magicznego pakietu odpowiedzi</string>
<string name="transport_packet_magic_header">Nagłówek magicznego pakietu transportowego</string> <string name="transport_packet_magic_header">Nagłówek magicznego pakietu transportowego</string>
@@ -57,7 +55,7 @@
<string name="wildcards_active">Symbole wieloznaczne aktywne</string> <string name="wildcards_active">Symbole wieloznaczne aktywne</string>
<string name="create_pin">Utwórz kod PIN</string> <string name="create_pin">Utwórz kod PIN</string>
<string name="junk_packet_maximum_size">Maksymalny rozmiar pakietu śmieciowego</string> <string name="junk_packet_maximum_size">Maksymalny rozmiar pakietu śmieciowego</string>
<string name="local_logging">Lokalne rejestrowanie</string> <string name="local_logging">Monitor lokalnych dzienników</string>
<string name="monitoring_state_changes">Monitorowanie zmian stanu</string> <string name="monitoring_state_changes">Monitorowanie zmian stanu</string>
<string name="add_tunnels_text">Dodaj z pliku lub archiwum ZIP</string> <string name="add_tunnels_text">Dodaj z pliku lub archiwum ZIP</string>
<string name="unknown_error">Wystąpił nieznany błąd</string> <string name="unknown_error">Wystąpił nieznany błąd</string>
@@ -114,11 +112,10 @@
<string name="set_primary_tunnel">Tunel używany, gdy nie skonfigurowano preferowanego tunelu</string> <string name="set_primary_tunnel">Tunel używany, gdy nie skonfigurowano preferowanego tunelu</string>
<string name="vpn_channel_id" translatable="false">VPN Channel</string> <string name="vpn_channel_id" translatable="false">VPN Channel</string>
<string name="stop_on_no_internet">Zatrzymaj, gdy nie ma Internetu</string> <string name="stop_on_no_internet">Zatrzymaj, gdy nie ma Internetu</string>
<string name="stop_on_internet_loss">Zatrzymaj tunel przy utracie Internetu</string>
<string name="allow_lan_traffic">Zezwól na ruch LAN</string> <string name="allow_lan_traffic">Zezwól na ruch LAN</string>
<string name="bypass_lan_for_kill_switch">Omiń LAN dla wyłącznika awaryjnego</string> <string name="bypass_lan_for_kill_switch">Omiń LAN dla wyłącznika awaryjnego</string>
<string name="vpn_channel_description">Powiadomienia dotyczące tuneli VPN.</string> <string name="vpn_channel_description">Kanał powiadomień o stanie VPN</string>
<string name="auto_tunnel_channel_description">Powiadomienia o zdarzeniach autotunelowania.</string> <string name="auto_tunnel_channel_description">Kanał powiadomień o stanie autotunelowania</string>
<string name="stop">Zatrzymaj</string> <string name="stop">Zatrzymaj</string>
<string name="splt_tunneling">Tunelowanie dzielone</string> <string name="splt_tunneling">Tunelowanie dzielone</string>
<string name="pre_up">Przed aktywacją</string> <string name="pre_up">Przed aktywacją</string>
@@ -143,7 +140,7 @@
<string name="join_telegram">Dołącz do społeczności Telegramu</string> <string name="join_telegram">Dołącz do społeczności Telegramu</string>
<string name="error_download_failed">Nie udało się pobrać konfiguracji</string> <string name="error_download_failed">Nie udało się pobrać konfiguracji</string>
<string name="service_running_error">Usługa nie działa</string> <string name="service_running_error">Usługa nie działa</string>
<string name="app_permission_title">Sterowanie tunelem i funkcje autotunelowania.</string> <string name="app_permission_title">Mostek sterujący WG Tunnel</string>
<string name="enter_config_url">Wpisz adres URL konfiguracji</string> <string name="enter_config_url">Wpisz adres URL konfiguracji</string>
<string name="save">Zapisz</string> <string name="save">Zapisz</string>
<string name="search">Szukaj</string> <string name="search">Szukaj</string>
@@ -156,7 +153,6 @@
<string name="config_error">Nieprawidłowa konfiguracja</string> <string name="config_error">Nieprawidłowa konfiguracja</string>
<string name="dropdown">Rozwijane</string> <string name="dropdown">Rozwijane</string>
<string name="auth_error">Brak autoryzacji</string> <string name="auth_error">Brak autoryzacji</string>
<string name="enable_remote_app_control">Włącz zdalne sterowanie aplikacją</string>
<string name="add_tunnel">Dodaj tunel</string> <string name="add_tunnel">Dodaj tunel</string>
<string name="select">Wybierz</string> <string name="select">Wybierz</string>
<string name="dns_resolve_error">Rozwiązywanie DNS się nie powiodło</string> <string name="dns_resolve_error">Rozwiązywanie DNS się nie powiodło</string>
@@ -242,14 +238,14 @@
<string name="read_failed">Nie udało się odczytać danych.</string> <string name="read_failed">Nie udało się odczytać danych.</string>
<string name="config_error_template">Błędna konfiguracja. %1$s w lokalizacji: %2$s.</string> <string name="config_error_template">Błędna konfiguracja. %1$s w lokalizacji: %2$s.</string>
<string name="ports_must_differ">Nie udało się. Serwery proxy muszą mieć różne porty.</string> <string name="ports_must_differ">Nie udało się. Serwery proxy muszą mieć różne porty.</string>
<string name="password_no_spaces">Hasło nie może zawierać spacji</string> <string name="password_no_spaces">Hasło nie może zawierać spacji.</string>
<string name="tunnel_name_taken">Nazwa tunelu jest już używana</string> <string name="tunnel_name_taken">Nazwa tunelu jest już używana.</string>
<string name="range_hint">(%1$d%2$d)</string> <string name="range_hint">(%1$d%2$d)</string>
<string name="mimic_quic">Imituj QUIC</string> <string name="mimic_quic">Imituj QUIC</string>
<string name="mimic_dns">Imituj DNS</string> <string name="mimic_dns">Imituj DNS</string>
<string name="mimic_sip">Imituj SIP</string> <string name="mimic_sip">Imituj SIP</string>
<string name="ddns_auto_update">Dynamiczny DNS (DDNS)</string> <string name="ddns_auto_update">Automatyczna aktualizacja dynamicznego DNS</string>
<string name="ddns_auto_update_description">Ponownie rozwiązuje nazwę hosta serwera i aktualizuje punkty końcowe równorzędne po zmianach DDNS</string> <string name="ddns_auto_update_description">Automatyczna aktualizacja adresu IP po zmianach DDNS</string>
<string name="mode_disabled_template">Funkcja niedostępna w trybie %1$s.</string> <string name="mode_disabled_template">Funkcja niedostępna w trybie %1$s.</string>
<string name="lockdown">Zablokowane</string> <string name="lockdown">Zablokowane</string>
<string name="active_tunnel_update_failed">Aktualizacja aktywnego tunelu się nie powiodła</string> <string name="active_tunnel_update_failed">Aktualizacja aktywnego tunelu się nie powiodła</string>
@@ -302,8 +298,6 @@
<string name="metered_tunnel">Tunel taryfowy</string> <string name="metered_tunnel">Tunel taryfowy</string>
<string name="lockdown_settings">Ustawienia blokady</string> <string name="lockdown_settings">Ustawienia blokady</string>
<string name="unavailable_in_mode">Niedostępne w obecnym trybie</string> <string name="unavailable_in_mode">Niedostępne w obecnym trybie</string>
<string name="global_split_tunneling">Globalne tunelowanie dzielone</string>
<string name="global_dns_servers">Globalne serwery DNS</string>
<string name="dual_stack">Sieć dual-stack</string> <string name="dual_stack">Sieć dual-stack</string>
<string name="dual_stack_description">Tunele muszą obsługiwać protokoły IPv4 i IPv6</string> <string name="dual_stack_description">Tunele muszą obsługiwać protokoły IPv4 i IPv6</string>
<string name="save_changes">Zapisz zmiany</string> <string name="save_changes">Zapisz zmiany</string>
@@ -358,93 +352,91 @@
<string name="github_sponsors_url" translatable="false">https://github.com/sponsors/zaneschepke</string> <string name="github_sponsors_url" translatable="false">https://github.com/sponsors/zaneschepke</string>
<string name="transport_packet_junk_size">Rozmiar śmieciowego pakietu transportu</string> <string name="transport_packet_junk_size">Rozmiar śmieciowego pakietu transportu</string>
<string name="cookie_packet_junk_size">Rozmiar śmieciowego pakietu ciasteczka</string> <string name="cookie_packet_junk_size">Rozmiar śmieciowego pakietu ciasteczka</string>
<string name="fallback_to_ipv4">Awaryjny powrót do IPv4</string> <string name="fallback_to_ipv4">Fallback to IPv4</string>
<string name="excluded_apps">Wykluczone aplikacje: %1$s</string> <string name="excluded_apps">%1$s apps excluded</string>
<string name="resolution_method">Metoda rozwiązania</string> <string name="resolution_method">Resolution method</string>
<string name="notification_ipv6_recovery_message">%1$s odzyskał łączność IPv6</string> <string name="notification_ipv6_recovery_message">%1$s recovered IPv6 connectivity</string>
<string name="tunnel_state_resolving_dns">Rozwiązywanie DNS</string> <string name="tunnel_state_resolving_dns">Resolving DNS</string>
<string name="handshake_template">wymiana potwierdzeń: %1$s</string> <string name="handshake_template">handshake: %1$s</string>
<string name="peer_template">peer: %1$s</string> <string name="peer_template">peer: %1$s</string>
<string name="errors">Błędy</string> <string name="errors">Errors</string>
<string name="no_system_dns_information">Brak informacji o systemie DNS</string> <string name="no_system_dns_information">No system DNS information</string>
<string name="export_unsupported">Eksport nie jest obsługiwany na tym urządzeniu</string> <string name="export_unsupported">Export is not supported on this device</string>
<string name="tunnel_globals">Globalne zmienne tunelu</string> <string name="tunnel_globals">Tunnel globals</string>
<string name="sort_by_latency">Sortuj według opóźnienia</string> <string name="sort_by_latency">Sort by latency</string>
<string name="ready">Gotowy</string> <string name="ready">Ready</string>
<string name="no_system_dns_detected">Nie wykryto systemu DNS</string> <string name="no_system_dns_detected">No system DNS detected</string>
<string name="name_error_empty">Nazwa tunelu nie może być pusta</string> <string name="name_error_empty">Tunnel name cannot be empty</string>
<string name="notification_ipv4_fallback_message">%1$s przełączył się na łączność IPv4</string> <string name="notification_ipv4_fallback_message">%1$s switched to IPv4 connectivity</string>
<string name="notification_tunnel_status_format">%1$s • %2$s</string> <string name="notification_tunnel_status_format">%1$s • %2$s</string>
<string name="dns_error_invalid_ip_or_host">Nieprawidłowy adres IP lub nazwa hosta</string> <string name="dns_error_invalid_ip_or_host">Invalid IP address or hostname</string>
<string name="proxy_channel_description">Powiadomienia dotyczące tuneli proxy.</string> <string name="proxy_channel_description">Notifications for proxy tunnels.</string>
<string name="system_dns_servers">Serwery: %1$s</string> <string name="system_dns_servers">Servers: %1$s</string>
<string name="balanced">Zrównoważona (3 s)</string> <string name="balanced">Balanced (3s)</string>
<string name="import_url_description">Adres URL musi być bezpieczny i obsługiwać plik .conf.</string> <string name="import_url_description">The URL must be secure and serve a .conf file.</string>
<string name="dns_error_invalid_port">Port musi być pomiędzy 1 a 65535</string> <string name="dns_error_invalid_port">Port must be between 1 and 65535</string>
<string name="view_configuration">Wyświetl konfigurację</string> <string name="view_configuration">View configuration</string>
<string name="dot">DNS poprzez TLS (DoT)</string> <string name="dot">DNS over TLS (DoT)</string>
<string name="ipv6_recovery">Odzyskiwanie IPv6</string> <string name="ipv6_recovery">IPv6 recovery</string>
<string name="screen_recording_protection">Ochrona przed nagrywaniem ekranu</string> <string name="screen_recording_protection">Screen recording protection</string>
<string name="dns_error_invalid_host">Host nie może być pusty</string> <string name="dns_error_invalid_host">Host cannot be empty</string>
<string name="toggle_sensitive_data_visibility">Przełącz widoczność poufnych danych</string> <string name="toggle_sensitive_data_visibility">Toggle sensitive data visibility</string>
<string name="ipv4_fallback">Zapasowy IPv4</string> <string name="ipv4_fallback">IPv4 fallback</string>
<string name="dns_error_empty">Punkt końcowy nie może być pusty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Połączono</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH musi używać HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Globalne zmienne konfiguracji</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="private_dns_hostname">Prywatny DNS: nazwa hosta (%1$s)</string> <string name="app">App</string>
<string name="app">Aplikacja</string> <string name="refresh_rate">Statistics refresh rate</string>
<string name="refresh_rate">Częstotliwość odświeżania statystyki</string> <string name="app_selection">App selection</string>
<string name="app_selection">Wybór aplikacji</string> <string name="tunnel_state_handshake_failure">Handshake failure</string>
<string name="tunnel_state_handshake_failure">Niepowodzenie wymiany potwierdzeń</string> <string name="notification_dynamic_dns_message">%1$s updated after Dynamic DNS change</string>
<string name="notification_dynamic_dns_message">%1$s zaktualizowany po zmianie dynamicznego DNS</string> <string name="tunnel_state_establishing_connection">Establishing connection</string>
<string name="tunnel_state_establishing_connection">Nawiązywanie połączenia</string> <string name="prefer_ipv6">Prefer IPv6</string>
<string name="prefer_ipv6">Preferuj IPv6</string> <string name="peer_endpoints">Peer endpoints</string>
<string name="peer_endpoints">Punkty końcowe peerów</string> <string name="dns_endpoint_hint">IP, hostname, or DoH URL</string>
<string name="dns_endpoint_hint">Adres IP, nazwa hosta lub adres URL DoH</string> <string name="restore_ipv6">Restore IPv6</string>
<string name="restore_ipv6">Przywróć IPv6</string> <string name="network">Network</string>
<string name="network">Sieć</string> <string name="fallback_to_ipv4_desc">Switch to IPv4 if IPv6 fails, without restarting the tunnel.</string>
<string name="fallback_to_ipv4_desc">W przypadku awarii protokołu IPv6 przełącz się na protokół IPv4 bez ponownego uruchamiania tunelu.</string> <string name="tunnel_state_disconnected">Disconnected</string>
<string name="tunnel_state_disconnected">Rozłączono</string> <string name="live">Real-time (1s)</string>
<string name="live">Czasu rzeczywistego (1 s)</string> <string name="uptime_template">uptime: %1$s</string>
<string name="uptime_template">czas pracy: %1$s</string> <string name="events">Events</string>
<string name="events">Zdarzenia</string> <string name="peer_resolution">Peer Resolution</string>
<string name="peer_resolution">Rozwiązanie peerów</string> <string name="restore_ipv6_desc">Switch back to IPv6 when an IPv6 network is detected.</string>
<string name="restore_ipv6_desc">Po wykryciu sieci IPv6 przełącz się z powrotem na IPv6.</string> <string name="errors_channel_description">A channel for application and tunnel errors</string>
<string name="errors_channel_description">Kanał dla błędów aplikacji i tuneli</string> <string name="vpn_permission_required">VPN permission required</string>
<string name="vpn_permission_required">Wymagane zezwolenie VPN</string> <string name="app_channel_description">A channel for general application notifications, like version updates</string>
<string name="app_channel_description">Kanał powiadomień ogólnych aplikacji, takich jak aktualizacje wersji</string> <string name="automation">Automation</string>
<string name="automation">Automatyzacja</string> <string name="balance_saver">Battery Saver (10s)</string>
<string name="balance_saver">Oszczędzanie baterii (10 s)</string> <string name="pinging_servers">Pinging servers…</string>
<string name="pinging_servers">Pingowanie serwerów…</string> <string name="dynamic_dns_update">Dynamic DNS update</string>
<string name="dynamic_dns_update">Aktualizacja dynamicznego DNS</string> <string name="statistics">Statistics</string>
<string name="statistics">Statystyka</string> <string name="events_channel_description">A channel for app events, like automation event</string>
<string name="events_channel_description">Kanał dla zdarzeń aplikacji, takich jak zdarzenia automatyzacji</string> <string name="private_dns_automatic">Private DNS: automatic</string>
<string name="private_dns_automatic">Prywatny DNS: automatyczny</string> <string name="tunnel_statistics">Live tunnel statistics</string>
<string name="tunnel_statistics">Statystyka aktywnego tunelu</string> <string name="ipv6_settings">IPv6 settings</string>
<string name="ipv6_settings">Ustawienia IPv6</string> <string name="error">Error</string>
<string name="error">Błąd</string> <string name="dns_error_invalid_url">Invalid URL format</string>
<string name="dns_error_invalid_url">Nieprawidłowy format adresu URL</string> <string name="view_live_tunnel">View live tunnel</string>
<string name="view_live_tunnel">Wyświetl aktywny tunel</string> <string name="mode">Mode</string>
<string name="mode">Tryb</string> <string name="tunnel_state_starting">Starting</string>
<string name="tunnel_state_starting">Uruchamianie</string> <string name="dns_endpoint_label">DNS server endpoint</string>
<string name="dns_endpoint_label">Punkt końcowy serwera DNS</string> <string name="plain_dns">Plain DNS (port 53)</string>
<string name="plain_dns">Zwykły DNS (port 53)</string> <string name="included_apps">%1$s apps included</string>
<string name="included_apps">Uwzględnione aplikacje: %1$s</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="error_http_port_unavailable">Port nasłuchiwania HTTP %1$d jest już używany.\nWybierz inny port.</string> <string name="current_system_dns">Current system DNS</string>
<string name="current_system_dns">Bieżący system DNS</string> <string name="security">Security</string>
<string name="global_amnezia_configuration">Globalna konfiguracja Amnezia</string> <string name="more_options">More options</string>
<string name="security">Bezpieczeństwo</string> <string name="status_template">status: %1$s</string>
<string name="more_options">Więcej opcji</string> <string name="export_canceled">Export canceled</string>
<string name="status_template">stan: %1$s</string> <string name="prefer_ipv6_desc">Use IPv6 endpoints when the network supports it.</string>
<string name="export_canceled">Eksport anulowano</string> <string name="stop_all">Stop all</string>
<string name="prefer_ipv6_desc">Używaj punktów końcowych IPv6, jeśli sieć to obsługuje.</string> <string name="copy_from">Copy from</string>
<string name="stop_all">Zatrzymaj wszystko</string> <string name="special_junk_packet">Special junk packet</string>
<string name="copy_from">Skopiuj z</string> <string name="error_socks5_port_unavailable">SOCKS5 port %1$d is already in use.\nPlease choose a different port.</string>
<string name="special_junk_packet">Specjalny pakiet śmieciowy</string> <string name="tunnel_scripting">Pre/Post script support</string>
<string name="error_socks5_port_unavailable">Port SOCKS5 %1$d jest już zajęty.\nWybierz inny port.</string> <string name="initializing">Initializing…</string>
<string name="tunnel_scripting">Obsługa skryptu przed/po</string>
<string name="initializing">Inicjalizacja…</string>
<string name="errors_channel_id" translatable="false">Errors Channel</string> <string name="errors_channel_id" translatable="false">Errors Channel</string>
<string name="s2" translatable="false">S2</string> <string name="s2" translatable="false">S2</string>
<string name="jc" translatable="false">Jc</string> <string name="jc" translatable="false">Jc</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
@@ -24,7 +24,6 @@
<string name="app_name">WG Tunnel</string> <string name="app_name">WG Tunnel</string>
<string name="error_file_extension">O ficheiro não é .conf ou .zip</string> <string name="error_file_extension">O ficheiro não é .conf ou .zip</string>
<string name="prominent_background_location_message">Este recurso precisa de permissões de localização em segundo plano para ativar o monitoramento do SSID da rede Wi-Fi mesmo quando a aplicação está fechado. Para mais pormenores, por favor veja a Política de Privacidade no ecrã de Suporte.</string> <string name="prominent_background_location_message">Este recurso precisa de permissões de localização em segundo plano para ativar o monitoramento do SSID da rede Wi-Fi mesmo quando a aplicação está fechado. Para mais pormenores, por favor veja a Política de Privacidade no ecrã de Suporte.</string>
<string name="enabled_app_shortcuts">Ativar atalhos de aplicações</string>
<string name="tunnels">Túneis</string> <string name="tunnels">Túneis</string>
<string name="privacy_policy">Política de Privacidade</string> <string name="privacy_policy">Política de Privacidade</string>
<string name="okay">OK</string> <string name="okay">OK</string>
@@ -71,7 +70,6 @@
<string name="auto">(automático)</string> <string name="auto">(automático)</string>
<string name="pin_created">Pin criado com sucesso</string> <string name="pin_created">Pin criado com sucesso</string>
<string name="create_pin">Criar um pin</string> <string name="create_pin">Criar um pin</string>
<string name="enable_app_lock">Ligar bloqueio de aplicação</string>
<string name="edit_tunnel">Editar túnel</string> <string name="edit_tunnel">Editar túnel</string>
<string name="set_primary_tunnel">Selecionar como túnel principal</string> <string name="set_primary_tunnel">Selecionar como túnel principal</string>
<string name="support">Suporte</string> <string name="support">Suporte</string>
@@ -110,7 +108,6 @@
<string name="donate">Contribua com esse projeto</string> <string name="donate">Contribua com esse projeto</string>
<string name="local_logging">Registro local</string> <string name="local_logging">Registro local</string>
<string name="stop_on_no_internet">Interromper quando não há internet</string> <string name="stop_on_no_internet">Interromper quando não há internet</string>
<string name="stop_on_internet_loss">Interrompa o túnel quando a internet não estiver disponível</string>
<string name="native_kill_switch">Interruptor de desligamento padrão</string> <string name="native_kill_switch">Interruptor de desligamento padrão</string>
<string name="allow_lan_traffic">Permitir tráfego LAN</string> <string name="allow_lan_traffic">Permitir tráfego LAN</string>
<string name="bypass_lan_for_kill_switch">Ignorar LAN no interruptor de desligamento</string> <string name="bypass_lan_for_kill_switch">Ignorar LAN no interruptor de desligamento</string>
@@ -201,7 +198,6 @@
<string name="kofi">Ko-fi</string> <string name="kofi">Ko-fi</string>
<string name="donation_signoff">Gratefully,</string> <string name="donation_signoff">Gratefully,</string>
<string name="selected">Selected</string> <string name="selected">Selected</string>
<string name="global_split_tunneling">Global split tunneling</string>
<string name="active_network">Active Network:</string> <string name="active_network">Active Network:</string>
<string name="range_hint">(%1$d%2$d)</string> <string name="range_hint">(%1$d%2$d)</string>
<string name="delete_active_message">Cannot delete active tunnel.</string> <string name="delete_active_message">Cannot delete active tunnel.</string>
@@ -216,7 +212,6 @@
<string name="kill_switch">kill switch</string> <string name="kill_switch">kill switch</string>
<string name="configuration">Configuration</string> <string name="configuration">Configuration</string>
<string name="drag_handle">Drag Handle</string> <string name="drag_handle">Drag Handle</string>
<string name="global_dns_servers">Global DNS servers</string>
<string name="contact">Contact</string> <string name="contact">Contact</string>
<string name="ports_must_differ">Failed. Proxies must have different ports.</string> <string name="ports_must_differ">Failed. Proxies must have different ports.</string>
<string name="join_matrix">Join Matrix community</string> <string name="join_matrix">Join Matrix community</string>
@@ -286,7 +281,6 @@
<string name="fix">Fix</string> <string name="fix">Fix</string>
<string name="tunnel_running_name_message">Name unchangeable while tunnel is active.</string> <string name="tunnel_running_name_message">Name unchangeable while tunnel is active.</string>
<string name="export_failed">Export failed</string> <string name="export_failed">Export failed</string>
<string name="enable_remote_app_control">Enable remote app control</string>
<string name="donation_closing">It\'s my dream to work for you on this project full-time.</string> <string name="donation_closing">It\'s my dream to work for you on this project full-time.</string>
<string name="update_download_failed">Update download failed.</string> <string name="update_download_failed">Update download failed.</string>
<string name="network_name">Network:</string> <string name="network_name">Network:</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
@@ -20,7 +20,6 @@
<string name="okay">OK</string> <string name="okay">OK</string>
<string name="tunnel_on_ethernet">Túnel na ethernet</string> <string name="tunnel_on_ethernet">Túnel na ethernet</string>
<string name="create_import">Criar do zero</string> <string name="create_import">Criar do zero</string>
<string name="enabled_app_shortcuts">Ativar atalhos de aplicações</string>
<string name="tunnel_on_wifi">Túnel em Wi-Fi não confiável</string> <string name="tunnel_on_wifi">Túnel em Wi-Fi não confiável</string>
<string name="email_subject">Apoio para o WG Tunnel</string> <string name="email_subject">Apoio para o WG Tunnel</string>
<string name="email_chooser">Enviar um email…</string> <string name="email_chooser">Enviar um email…</string>
@@ -34,7 +33,6 @@
<string name="auto">(automático)</string> <string name="auto">(automático)</string>
<string name="pin_created">Pin criado com sucesso</string> <string name="pin_created">Pin criado com sucesso</string>
<string name="enter_pin">Digite o seu pin</string> <string name="enter_pin">Digite o seu pin</string>
<string name="enable_app_lock">Ligar bloqueio de aplicação</string>
<string name="edit_tunnel">Editar túnel</string> <string name="edit_tunnel">Editar túnel</string>
<string name="junk_packet_count">Quantidade de pacotes-lixo</string> <string name="junk_packet_count">Quantidade de pacotes-lixo</string>
<string name="junk_packet_minimum_size">Tamanho mínimo de pacote-lixo</string> <string name="junk_packet_minimum_size">Tamanho mínimo de pacote-lixo</string>
@@ -111,7 +109,6 @@
<string name="monitoring_state_changes">Monitorar estado de alterações</string> <string name="monitoring_state_changes">Monitorar estado de alterações</string>
<string name="donate">Contribua com projeto</string> <string name="donate">Contribua com projeto</string>
<string name="add_from_clipboard">Adicionar da área de transferência</string> <string name="add_from_clipboard">Adicionar da área de transferência</string>
<string name="stop_on_internet_loss">Interrompa o túnel quando a internet não estiver disponível</string>
<string name="stop">pausar</string> <string name="stop">pausar</string>
<string name="splt_tunneling">Tunelamento dividido</string> <string name="splt_tunneling">Tunelamento dividido</string>
<string name="show_scripts">Mostrar scripts</string> <string name="show_scripts">Mostrar scripts</string>
@@ -202,7 +199,6 @@
<string name="kofi">Ko-fi</string> <string name="kofi">Ko-fi</string>
<string name="donation_signoff">Gratefully,</string> <string name="donation_signoff">Gratefully,</string>
<string name="selected">Selected</string> <string name="selected">Selected</string>
<string name="global_split_tunneling">Global split tunneling</string>
<string name="active_network">Active Network:</string> <string name="active_network">Active Network:</string>
<string name="range_hint">(%1$d%2$d)</string> <string name="range_hint">(%1$d%2$d)</string>
<string name="delete_active_message">Cannot delete active tunnel.</string> <string name="delete_active_message">Cannot delete active tunnel.</string>
@@ -216,7 +212,6 @@
<string name="kill_switch">kill switch</string> <string name="kill_switch">kill switch</string>
<string name="configuration">Configuration</string> <string name="configuration">Configuration</string>
<string name="drag_handle">Drag Handle</string> <string name="drag_handle">Drag Handle</string>
<string name="global_dns_servers">Global DNS servers</string>
<string name="contact">Contact</string> <string name="contact">Contact</string>
<string name="ports_must_differ">Failed. Proxies must have different ports.</string> <string name="ports_must_differ">Failed. Proxies must have different ports.</string>
<string name="join_matrix">Join Matrix community</string> <string name="join_matrix">Join Matrix community</string>
@@ -286,7 +281,6 @@
<string name="fix">Fix</string> <string name="fix">Fix</string>
<string name="tunnel_running_name_message">Name unchangeable while tunnel is active.</string> <string name="tunnel_running_name_message">Name unchangeable while tunnel is active.</string>
<string name="export_failed">Export failed</string> <string name="export_failed">Export failed</string>
<string name="enable_remote_app_control">Enable remote app control</string>
<string name="donation_closing">It\'s my dream to work for you on this project full-time.</string> <string name="donation_closing">It\'s my dream to work for you on this project full-time.</string>
<string name="update_download_failed">Update download failed.</string> <string name="update_download_failed">Update download failed.</string>
<string name="network_name">Network:</string> <string name="network_name">Network:</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
-11
View File
@@ -22,7 +22,6 @@
<string name="all">Все</string> <string name="all">Все</string>
<string name="no_email_detected">Приложение для отправки почты не найдено</string> <string name="no_email_detected">Приложение для отправки почты не найдено</string>
<string name="enter_pin">Введите PIN-код</string> <string name="enter_pin">Введите PIN-код</string>
<string name="enable_app_lock">Защита приложения</string>
<string name="settings">Настройки</string> <string name="settings">Настройки</string>
<string name="support">Поддержка</string> <string name="support">Поддержка</string>
<string name="init_packet_junk_size">S1</string> <string name="init_packet_junk_size">S1</string>
@@ -63,7 +62,6 @@
<string name="yes">Да</string> <string name="yes">Да</string>
<string name="prominent_background_location_message">Эта функция требует фоновый доступ к местоположению для отслеживания имён сетей Wi-Fi, даже когда приложение закрыто. Для получения дополнительной информации, прочтите политику конфиденциальности на экране поддержки.</string> <string name="prominent_background_location_message">Эта функция требует фоновый доступ к местоположению для отслеживания имён сетей Wi-Fi, даже когда приложение закрыто. Для получения дополнительной информации, прочтите политику конфиденциальности на экране поддержки.</string>
<string name="copy_public_key">Копировать открытый ключ</string> <string name="copy_public_key">Копировать открытый ключ</string>
<string name="enabled_app_shortcuts">Ярлыки приложения</string>
<string name="open_issue">Сообщить о проблеме</string> <string name="open_issue">Сообщить о проблеме</string>
<string name="incorrect_pin">Некорректный PIN-код</string> <string name="incorrect_pin">Некорректный PIN-код</string>
<string name="pin_created">PIN-код создан успешно</string> <string name="pin_created">PIN-код создан успешно</string>
@@ -115,7 +113,6 @@
<string name="show_scripts">Показать сценарии</string> <string name="show_scripts">Показать сценарии</string>
<string name="quick_actions">Быстрые действия</string> <string name="quick_actions">Быстрые действия</string>
<string name="stop_on_no_internet">Остановить без интернета</string> <string name="stop_on_no_internet">Остановить без интернета</string>
<string name="stop_on_internet_loss">Остановить туннель при потере интернета</string>
<string name="native_kill_switch">Штатное экстренное отключение</string> <string name="native_kill_switch">Штатное экстренное отключение</string>
<string name="allow_lan_traffic">Обход LAN</string> <string name="allow_lan_traffic">Обход LAN</string>
<string name="bypass_lan_for_kill_switch">Разрешать трафик LAN при экстренном отключении</string> <string name="bypass_lan_for_kill_switch">Разрешать трафик LAN при экстренном отключении</string>
@@ -157,7 +154,6 @@
<string name="copy">Копировать</string> <string name="copy">Копировать</string>
<string name="delete">Удалить</string> <string name="delete">Удалить</string>
<string name="export_failed">Экспорт не выполнен</string> <string name="export_failed">Экспорт не выполнен</string>
<string name="enable_remote_app_control">Удалённое управление приложением</string>
<string name="error_download_failed">Невозможно скачать конфигурацию</string> <string name="error_download_failed">Невозможно скачать конфигурацию</string>
<string name="select_all">Выбрать все</string> <string name="select_all">Выбрать все</string>
<string name="export_success">Экспорт успешно выполнен</string> <string name="export_success">Экспорт успешно выполнен</string>
@@ -281,14 +277,12 @@
<string name="resources">Ресурсы</string> <string name="resources">Ресурсы</string>
<string name="back">Назад</string> <string name="back">Назад</string>
<string name="already_donated">Пожертвование уже сделано</string> <string name="already_donated">Пожертвование уже сделано</string>
<string name="global_split_tunneling">Общее раздельное туннелирование</string>
<string name="active_network">Активная сеть:</string> <string name="active_network">Активная сеть:</string>
<string name="help_translate">Помочь перевести приложение</string> <string name="help_translate">Помочь перевести приложение</string>
<string name="ethernet">Ethernet</string> <string name="ethernet">Ethernet</string>
<string name="other">Другое</string> <string name="other">Другое</string>
<string name="kill_switch">экстренное отключение</string> <string name="kill_switch">экстренное отключение</string>
<string name="configuration">Конфигурация</string> <string name="configuration">Конфигурация</string>
<string name="global_dns_servers">Общие серверы DNS</string>
<string name="contact">Контакты</string> <string name="contact">Контакты</string>
<string name="backup_and_restore">Резервное копирование</string> <string name="backup_and_restore">Резервное копирование</string>
<string name="about">О приложении</string> <string name="about">О приложении</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
-11
View File
@@ -42,7 +42,6 @@
<string name="seconds">Sekundy</string> <string name="seconds">Sekundy</string>
<string name="persistent_keepalive">Trvalé udržanie spojenia</string> <string name="persistent_keepalive">Trvalé udržanie spojenia</string>
<string name="cancel">Zrušiť</string> <string name="cancel">Zrušiť</string>
<string name="enabled_app_shortcuts">Povoliť skratky aplikácií</string>
<string name="unknown_error">Došlo k neznámej chybe</string> <string name="unknown_error">Došlo k neznámej chybe</string>
<string name="tunnel_on_wifi">Tunel na nedôveryhodnej wifi</string> <string name="tunnel_on_wifi">Tunel na nedôveryhodnej wifi</string>
<string name="email_subject">Podpora tunela WG</string> <string name="email_subject">Podpora tunela WG</string>
@@ -71,7 +70,6 @@
<string name="save">Save</string> <string name="save">Save</string>
<string name="tunnel_mobile_data">Tunnel on mobile data</string> <string name="tunnel_mobile_data">Tunnel on mobile data</string>
<string name="logs">Logs</string> <string name="logs">Logs</string>
<string name="enable_app_lock">Zapnúť App Lock</string>
<string name="join_telegram">Join Telegram community</string> <string name="join_telegram">Join Telegram community</string>
<string name="pin_created">Pin úspešne vytvorený</string> <string name="pin_created">Pin úspešne vytvorený</string>
<string name="post_up">Post up</string> <string name="post_up">Post up</string>
@@ -82,7 +80,6 @@
<string name="remote_key">Remote key</string> <string name="remote_key">Remote key</string>
<string name="mobile_data">Mobile data</string> <string name="mobile_data">Mobile data</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_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="stop_on_internet_loss">Stop tunnel on internet loss</string>
<string name="vpn">VPN</string> <string name="vpn">VPN</string>
<string name="tunnel_boot_description">Start the default tunnel on boot</string> <string name="tunnel_boot_description">Start the default tunnel on boot</string>
<string name="allow_lan_traffic">Allow LAN traffic</string> <string name="allow_lan_traffic">Allow LAN traffic</string>
@@ -166,7 +163,6 @@
<string name="kofi">Ko-fi</string> <string name="kofi">Ko-fi</string>
<string name="donation_signoff">Gratefully,</string> <string name="donation_signoff">Gratefully,</string>
<string name="selected">Selected</string> <string name="selected">Selected</string>
<string name="global_split_tunneling">Global split tunneling</string>
<string name="active_network">Active Network:</string> <string name="active_network">Active Network:</string>
<string name="range_hint">(%1$d%2$d)</string> <string name="range_hint">(%1$d%2$d)</string>
<string name="delete_active_message">Cannot delete active tunnel.</string> <string name="delete_active_message">Cannot delete active tunnel.</string>
@@ -184,7 +180,6 @@
<string name="kill_switch">kill switch</string> <string name="kill_switch">kill switch</string>
<string name="configuration">Configuration</string> <string name="configuration">Configuration</string>
<string name="drag_handle">Drag Handle</string> <string name="drag_handle">Drag Handle</string>
<string name="global_dns_servers">Global DNS servers</string>
<string name="display_theme">Motív zobrazenia</string> <string name="display_theme">Motív zobrazenia</string>
<string name="contact">Contact</string> <string name="contact">Contact</string>
<string name="ports_must_differ">Failed. Proxies must have different ports.</string> <string name="ports_must_differ">Failed. Proxies must have different ports.</string>
@@ -276,7 +271,6 @@
<string name="settings">Nastavenia</string> <string name="settings">Nastavenia</string>
<string name="incorrect_pin">Pin je nesprávny</string> <string name="incorrect_pin">Pin je nesprávny</string>
<string name="export_failed">Export failed</string> <string name="export_failed">Export failed</string>
<string name="enable_remote_app_control">Enable remote app control</string>
<string name="donation_closing">It\'s my dream to work for you on this project full-time.</string> <string name="donation_closing">It\'s my dream to work for you on this project full-time.</string>
<string name="update_download_failed">Update download failed.</string> <string name="update_download_failed">Update download failed.</string>
<string name="network_name">Network:</string> <string name="network_name">Network:</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
-11
View File
@@ -15,7 +15,6 @@
<string name="delete_tunnel">Delete tunnel</string> <string name="delete_tunnel">Delete tunnel</string>
<string name="tunnel_mobile_data">Tunnel on mobile data</string> <string name="tunnel_mobile_data">Tunnel on mobile data</string>
<string name="logs">Logs</string> <string name="logs">Logs</string>
<string name="enable_app_lock">Enable app lock</string>
<string name="config_changes_saved">Configuration changes saved.</string> <string name="config_changes_saved">Configuration changes saved.</string>
<string name="join_telegram">Join Telegram community</string> <string name="join_telegram">Join Telegram community</string>
<string name="pin_created">Pin successfully created</string> <string name="pin_created">Pin successfully created</string>
@@ -27,7 +26,6 @@
<string name="remote_key">Remote key</string> <string name="remote_key">Remote key</string>
<string name="mobile_data">Mobile data</string> <string name="mobile_data">Mobile data</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_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="stop_on_internet_loss">Stop tunnel on internet loss</string>
<string name="vpn">VPN</string> <string name="vpn">VPN</string>
<string name="tunnel_boot_description">Start the default tunnel on boot</string> <string name="tunnel_boot_description">Start the default tunnel on boot</string>
<string name="allow_lan_traffic">Allow LAN traffic</string> <string name="allow_lan_traffic">Allow LAN traffic</string>
@@ -133,7 +131,6 @@
<string name="kofi">Ko-fi</string> <string name="kofi">Ko-fi</string>
<string name="donation_signoff">Gratefully,</string> <string name="donation_signoff">Gratefully,</string>
<string name="selected">Selected</string> <string name="selected">Selected</string>
<string name="global_split_tunneling">Global split tunneling</string>
<string name="active_network">Active Network:</string> <string name="active_network">Active Network:</string>
<string name="range_hint">(%1$d%2$d)</string> <string name="range_hint">(%1$d%2$d)</string>
<string name="delete_active_message">Cannot delete active tunnel.</string> <string name="delete_active_message">Cannot delete active tunnel.</string>
@@ -156,7 +153,6 @@
<string name="mtu">MTU</string> <string name="mtu">MTU</string>
<string name="configuration">Configuration</string> <string name="configuration">Configuration</string>
<string name="drag_handle">Drag Handle</string> <string name="drag_handle">Drag Handle</string>
<string name="global_dns_servers">Global DNS servers</string>
<string name="display_theme">Display theme</string> <string name="display_theme">Display theme</string>
<string name="contact">Contact</string> <string name="contact">Contact</string>
<string name="ports_must_differ">Failed. Proxies must have different ports.</string> <string name="ports_must_differ">Failed. Proxies must have different ports.</string>
@@ -257,7 +253,6 @@
<string name="unavailable_in_mode">Unavailable in current mode</string> <string name="unavailable_in_mode">Unavailable in current mode</string>
<string name="server_port">Server:Port</string> <string name="server_port">Server:Port</string>
<string name="camera_permission_required">Camera permission required</string> <string name="camera_permission_required">Camera permission required</string>
<string name="enabled_app_shortcuts">Enable app shortcuts</string>
<string name="preferred_tunnel">Preferred tunnel</string> <string name="preferred_tunnel">Preferred tunnel</string>
<string name="allow">Allow</string> <string name="allow">Allow</string>
<string name="underload_packet_magic_header">Underload packet magic header</string> <string name="underload_packet_magic_header">Underload packet magic header</string>
@@ -272,7 +267,6 @@
<string name="settings">Settings</string> <string name="settings">Settings</string>
<string name="incorrect_pin">Pin is incorrect</string> <string name="incorrect_pin">Pin is incorrect</string>
<string name="export_failed">Export failed</string> <string name="export_failed">Export failed</string>
<string name="enable_remote_app_control">Enable remote app control</string>
<string name="donation_closing">It\'s my dream to work for you on this project full-time.</string> <string name="donation_closing">It\'s my dream to work for you on this project full-time.</string>
<string name="update_download_failed">Update download failed.</string> <string name="update_download_failed">Update download failed.</string>
<string name="network_name">Network:</string> <string name="network_name">Network:</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
-11
View File
@@ -29,7 +29,6 @@
<string name="listen_port">துறைமுகம் கேளுங்கள்</string> <string name="listen_port">துறைமுகம் கேளுங்கள்</string>
<string name="persistent_keepalive">தொடர்ச்சியான கீப்அலிவ்</string> <string name="persistent_keepalive">தொடர்ச்சியான கீப்அலிவ்</string>
<string name="cancel">ரத்துசெய்</string> <string name="cancel">ரத்துசெய்</string>
<string name="enabled_app_shortcuts">பயன்பாட்டு குறுக்குவழிகளை இயக்கவும்</string>
<string name="unknown_error">தெரியாத பிழை ஏற்பட்டது</string> <string name="unknown_error">தெரியாத பிழை ஏற்பட்டது</string>
<string name="tunnel_on_wifi">நம்பத்தகாத வைஃபை மீது சுரங்கப்பாதை</string> <string name="tunnel_on_wifi">நம்பத்தகாத வைஃபை மீது சுரங்கப்பாதை</string>
<string name="email_subject">WG சுரங்கப்பாதை உதவி</string> <string name="email_subject">WG சுரங்கப்பாதை உதவி</string>
@@ -48,7 +47,6 @@
<string name="pin_created">முள் வெற்றிகரமாக உருவாக்கப்பட்டது</string> <string name="pin_created">முள் வெற்றிகரமாக உருவாக்கப்பட்டது</string>
<string name="enter_pin">உங்கள் முள் உள்ளிடவும்</string> <string name="enter_pin">உங்கள் முள் உள்ளிடவும்</string>
<string name="create_pin">முள் உருவாக்கவும்</string> <string name="create_pin">முள் உருவாக்கவும்</string>
<string name="enable_app_lock">பயன்பாட்டு பூட்டை இயக்கவும்</string>
<string name="settings">அமைப்புகள்</string> <string name="settings">அமைப்புகள்</string>
<string name="support">உதவி</string> <string name="support">உதவி</string>
<string name="junk_packet_count">குப்பை பாக்கெட் எண்ணிக்கை</string> <string name="junk_packet_count">குப்பை பாக்கெட் எண்ணிக்கை</string>
@@ -92,7 +90,6 @@
<string name="local_logging">உள்ளூர்வாசிகள்</string> <string name="local_logging">உள்ளூர்வாசிகள்</string>
<string name="add_from_clipboard">கிளிப்போர்டில் இருந்து சேர்க்கவும்</string> <string name="add_from_clipboard">கிளிப்போர்டில் இருந்து சேர்க்கவும்</string>
<string name="stop_on_no_internet">இணையத்தில் நிறுத்துங்கள்</string> <string name="stop_on_no_internet">இணையத்தில் நிறுத்துங்கள்</string>
<string name="stop_on_internet_loss">இணைய இழப்பில் சுரங்கப்பாதையை நிறுத்துங்கள்</string>
<string name="native_kill_switch">சொந்த கொலை சுவிட்ச்</string> <string name="native_kill_switch">சொந்த கொலை சுவிட்ச்</string>
<string name="allow_lan_traffic">லேன் போக்குவரத்தை அனுமதிக்கவும்</string> <string name="allow_lan_traffic">லேன் போக்குவரத்தை அனுமதிக்கவும்</string>
<string name="bypass_lan_for_kill_switch">கொலை சுவிட்சுக்கு பைபாச் லேன்</string> <string name="bypass_lan_for_kill_switch">கொலை சுவிட்சுக்கு பைபாச் லேன்</string>
@@ -157,7 +154,6 @@
<string name="dns_resolve_error">டிஎன்எச் தீர்மானம் பிழை</string> <string name="dns_resolve_error">டிஎன்எச் தீர்மானம் பிழை</string>
<string name="auth_error">அங்கீகரிக்கப்பட்ட பிழை இல்லை</string> <string name="auth_error">அங்கீகரிக்கப்பட்ட பிழை இல்லை</string>
<string name="service_running_error">பணி இயங்கும் பிழை</string> <string name="service_running_error">பணி இயங்கும் பிழை</string>
<string name="enable_remote_app_control">தொலைநிலை பயன்பாட்டுக் கட்டுப்பாட்டை இயக்கவும்</string>
<string name="flavor_template">சுவை: %1$s</string> <string name="flavor_template">சுவை: %1$s</string>
<string name="add_from_url">முகவரி இலிருந்து சேர்க்கவும்</string> <string name="add_from_url">முகவரி இலிருந்து சேர்க்கவும்</string>
<string name="delete_logs">பதிவுகளை நீக்கவும் அழிக்கவும்</string> <string name="delete_logs">பதிவுகளை நீக்கவும் அழிக்கவும்</string>
@@ -232,7 +228,6 @@
<string name="kofi">Ko-fi</string> <string name="kofi">Ko-fi</string>
<string name="donation_signoff">Gratefully,</string> <string name="donation_signoff">Gratefully,</string>
<string name="selected">Selected</string> <string name="selected">Selected</string>
<string name="global_split_tunneling">Global split tunneling</string>
<string name="active_network">Active Network:</string> <string name="active_network">Active Network:</string>
<string name="range_hint">(%1$d%2$d)</string> <string name="range_hint">(%1$d%2$d)</string>
<string name="delete_active_message">Cannot delete active tunnel.</string> <string name="delete_active_message">Cannot delete active tunnel.</string>
@@ -245,7 +240,6 @@
<string name="kill_switch">kill switch</string> <string name="kill_switch">kill switch</string>
<string name="configuration">Configuration</string> <string name="configuration">Configuration</string>
<string name="drag_handle">Drag Handle</string> <string name="drag_handle">Drag Handle</string>
<string name="global_dns_servers">Global DNS servers</string>
<string name="contact">Contact</string> <string name="contact">Contact</string>
<string name="ports_must_differ">Failed. Proxies must have different ports.</string> <string name="ports_must_differ">Failed. Proxies must have different ports.</string>
<string name="backup_and_restore">Backup and restore</string> <string name="backup_and_restore">Backup and restore</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
-11
View File
@@ -15,7 +15,6 @@
<string name="delete_tunnel">Delete tunnel</string> <string name="delete_tunnel">Delete tunnel</string>
<string name="tunnel_mobile_data">Tunnel on mobile data</string> <string name="tunnel_mobile_data">Tunnel on mobile data</string>
<string name="logs">Logs</string> <string name="logs">Logs</string>
<string name="enable_app_lock">Enable app lock</string>
<string name="config_changes_saved">Configuration changes saved.</string> <string name="config_changes_saved">Configuration changes saved.</string>
<string name="join_telegram">Join Telegram community</string> <string name="join_telegram">Join Telegram community</string>
<string name="pin_created">Pin successfully created</string> <string name="pin_created">Pin successfully created</string>
@@ -27,7 +26,6 @@
<string name="remote_key">Remote key</string> <string name="remote_key">Remote key</string>
<string name="mobile_data">Mobile data</string> <string name="mobile_data">Mobile data</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_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="stop_on_internet_loss">Stop tunnel on internet loss</string>
<string name="vpn">VPN</string> <string name="vpn">VPN</string>
<string name="tunnel_boot_description">Start the default tunnel on boot</string> <string name="tunnel_boot_description">Start the default tunnel on boot</string>
<string name="allow_lan_traffic">Allow LAN traffic</string> <string name="allow_lan_traffic">Allow LAN traffic</string>
@@ -134,7 +132,6 @@
<string name="kofi">Ko-fi</string> <string name="kofi">Ko-fi</string>
<string name="donation_signoff">Gratefully,</string> <string name="donation_signoff">Gratefully,</string>
<string name="selected">Selected</string> <string name="selected">Selected</string>
<string name="global_split_tunneling">Global split tunneling</string>
<string name="active_network">Active Network:</string> <string name="active_network">Active Network:</string>
<string name="range_hint">(%1$d%2$d)</string> <string name="range_hint">(%1$d%2$d)</string>
<string name="delete_active_message">Cannot delete active tunnel.</string> <string name="delete_active_message">Cannot delete active tunnel.</string>
@@ -157,7 +154,6 @@
<string name="mtu">MTU</string> <string name="mtu">MTU</string>
<string name="configuration">Configuration</string> <string name="configuration">Configuration</string>
<string name="drag_handle">Drag Handle</string> <string name="drag_handle">Drag Handle</string>
<string name="global_dns_servers">Global DNS servers</string>
<string name="display_theme">Display theme</string> <string name="display_theme">Display theme</string>
<string name="contact">Contact</string> <string name="contact">Contact</string>
<string name="ports_must_differ">Failed. Proxies must have different ports.</string> <string name="ports_must_differ">Failed. Proxies must have different ports.</string>
@@ -258,7 +254,6 @@
<string name="unavailable_in_mode">Unavailable in current mode</string> <string name="unavailable_in_mode">Unavailable in current mode</string>
<string name="server_port">Server:Port</string> <string name="server_port">Server:Port</string>
<string name="camera_permission_required">Camera permission required</string> <string name="camera_permission_required">Camera permission required</string>
<string name="enabled_app_shortcuts">Enable app shortcuts</string>
<string name="preferred_tunnel">Preferred tunnel</string> <string name="preferred_tunnel">Preferred tunnel</string>
<string name="allow">Allow</string> <string name="allow">Allow</string>
<string name="underload_packet_magic_header">Underload packet magic header</string> <string name="underload_packet_magic_header">Underload packet magic header</string>
@@ -273,7 +268,6 @@
<string name="settings">Settings</string> <string name="settings">Settings</string>
<string name="incorrect_pin">Pin is incorrect</string> <string name="incorrect_pin">Pin is incorrect</string>
<string name="export_failed">Export failed</string> <string name="export_failed">Export failed</string>
<string name="enable_remote_app_control">Enable remote app control</string>
<string name="donation_closing">It\'s my dream to work for you on this project full-time.</string> <string name="donation_closing">It\'s my dream to work for you on this project full-time.</string>
<string name="update_download_failed">Update download failed.</string> <string name="update_download_failed">Update download failed.</string>
<string name="network_name">Network:</string> <string name="network_name">Network:</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
-11
View File
@@ -49,7 +49,6 @@
<string name="seconds">Saniye</string> <string name="seconds">Saniye</string>
<string name="persistent_keepalive">Kalıcı canlı tutma</string> <string name="persistent_keepalive">Kalıcı canlı tutma</string>
<string name="cancel">İptal</string> <string name="cancel">İptal</string>
<string name="enabled_app_shortcuts">Uygulama kısayollarını etkinleştir</string>
<string name="unknown_error">Bilinmeyen bir hata oluştu</string> <string name="unknown_error">Bilinmeyen bir hata oluştu</string>
<string name="tunnel_on_wifi">Güvenilmeyen wifida tünel</string> <string name="tunnel_on_wifi">Güvenilmeyen wifida tünel</string>
<string name="my_email" translatable="false">support@zaneschepke.com</string> <string name="my_email" translatable="false">support@zaneschepke.com</string>
@@ -73,7 +72,6 @@
<string name="pin_created">Pin başarıyla oluşturuldu</string> <string name="pin_created">Pin başarıyla oluşturuldu</string>
<string name="enter_pin">Pininizi girin</string> <string name="enter_pin">Pininizi girin</string>
<string name="create_pin">Pin oluştur</string> <string name="create_pin">Pin oluştur</string>
<string name="enable_app_lock">Uygulama kilidini etkinleştir</string>
<string name="set_primary_tunnel">Birincil tünel olarak ayarla</string> <string name="set_primary_tunnel">Birincil tünel olarak ayarla</string>
<string name="edit_tunnel">Tüneli düzenle</string> <string name="edit_tunnel">Tüneli düzenle</string>
<string name="settings">Ayarlar</string> <string name="settings">Ayarlar</string>
@@ -121,7 +119,6 @@
<string name="local_logging">Yerel günlüğe kaydetme</string> <string name="local_logging">Yerel günlüğe kaydetme</string>
<string name="add_from_clipboard">Panodan ekle</string> <string name="add_from_clipboard">Panodan ekle</string>
<string name="stop_on_no_internet">İnternet olmadığında durdur</string> <string name="stop_on_no_internet">İnternet olmadığında durdur</string>
<string name="stop_on_internet_loss">İnternet kaybında tüneli durdur</string>
<string name="native_kill_switch">Yerel kill switch</string> <string name="native_kill_switch">Yerel kill switch</string>
<string name="allow_lan_traffic">LAN trafiğine izin ver</string> <string name="allow_lan_traffic">LAN trafiğine izin ver</string>
<string name="bypass_lan_for_kill_switch">Kill switch için LAN’ı atla</string> <string name="bypass_lan_for_kill_switch">Kill switch için LAN’ı atla</string>
@@ -219,7 +216,6 @@
<string name="kofi">Ko-fi</string> <string name="kofi">Ko-fi</string>
<string name="donation_signoff">Gratefully,</string> <string name="donation_signoff">Gratefully,</string>
<string name="selected">Selected</string> <string name="selected">Selected</string>
<string name="global_split_tunneling">Global split tunneling</string>
<string name="active_network">Active Network:</string> <string name="active_network">Active Network:</string>
<string name="range_hint">(%1$d%2$d)</string> <string name="range_hint">(%1$d%2$d)</string>
<string name="delete_active_message">Cannot delete active tunnel.</string> <string name="delete_active_message">Cannot delete active tunnel.</string>
@@ -233,7 +229,6 @@
<string name="kill_switch">kill switch</string> <string name="kill_switch">kill switch</string>
<string name="configuration">Configuration</string> <string name="configuration">Configuration</string>
<string name="drag_handle">Drag Handle</string> <string name="drag_handle">Drag Handle</string>
<string name="global_dns_servers">Global DNS servers</string>
<string name="contact">Contact</string> <string name="contact">Contact</string>
<string name="ports_must_differ">Failed. Proxies must have different ports.</string> <string name="ports_must_differ">Failed. Proxies must have different ports.</string>
<string name="join_matrix">Join Matrix community</string> <string name="join_matrix">Join Matrix community</string>
@@ -297,7 +292,6 @@
<string name="fix">Fix</string> <string name="fix">Fix</string>
<string name="tunnel_running_name_message">Name unchangeable while tunnel is active.</string> <string name="tunnel_running_name_message">Name unchangeable while tunnel is active.</string>
<string name="export_failed">Export failed</string> <string name="export_failed">Export failed</string>
<string name="enable_remote_app_control">Enable remote app control</string>
<string name="donation_closing">It\'s my dream to work for you on this project full-time.</string> <string name="donation_closing">It\'s my dream to work for you on this project full-time.</string>
<string name="update_download_failed">Update download failed.</string> <string name="update_download_failed">Update download failed.</string>
<string name="network_name">Network:</string> <string name="network_name">Network:</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
-11
View File
@@ -47,7 +47,6 @@
<string name="preshared_key">Загальний ключ</string> <string name="preshared_key">Загальний ключ</string>
<string name="seconds">Секунд</string> <string name="seconds">Секунд</string>
<string name="persistent_keepalive">Підтримка роботи тунелю (keepalive)</string> <string name="persistent_keepalive">Підтримка роботи тунелю (keepalive)</string>
<string name="enabled_app_shortcuts">Дозволити ярлики</string>
<string name="unknown_error">Невідома помилка</string> <string name="unknown_error">Невідома помилка</string>
<string name="tunnel_on_wifi">Тунелювати недовірені мережі Wi-Fi</string> <string name="tunnel_on_wifi">Тунелювати недовірені мережі Wi-Fi</string>
<string name="email_subject">Підтримка WG-Tunnel</string> <string name="email_subject">Підтримка WG-Tunnel</string>
@@ -67,7 +66,6 @@
<string name="pin_created">PIN-код створено успішно</string> <string name="pin_created">PIN-код створено успішно</string>
<string name="enter_pin">Введіть PIN-код</string> <string name="enter_pin">Введіть PIN-код</string>
<string name="create_pin">Створити PIN-код</string> <string name="create_pin">Створити PIN-код</string>
<string name="enable_app_lock">Увімкнути блокування додатку</string>
<string name="edit_tunnel">Редагувати тунель</string> <string name="edit_tunnel">Редагувати тунель</string>
<string name="set_primary_tunnel">Встановити як основний тунель</string> <string name="set_primary_tunnel">Встановити як основний тунель</string>
<string name="support">Підтримка</string> <string name="support">Підтримка</string>
@@ -132,7 +130,6 @@
<string name="local_logging">Локальне ведення журналу</string> <string name="local_logging">Локальне ведення журналу</string>
<string name="start_auto">Запустити автотунель</string> <string name="start_auto">Запустити автотунель</string>
<string name="stop_auto">Зупинити автотунель</string> <string name="stop_auto">Зупинити автотунель</string>
<string name="stop_on_internet_loss">Зупинити тунель під час втрати інтернету</string>
<string name="vpn_channel_description">Канал сповіщень про стан VPN</string> <string name="vpn_channel_description">Канал сповіщень про стан VPN</string>
<string name="enable_amnezia_compatibility">Включити сумісність із Amnezia</string> <string name="enable_amnezia_compatibility">Включити сумісність із Amnezia</string>
<string name="enter_config_url">Введіть URL-адресу конфігурації</string> <string name="enter_config_url">Введіть URL-адресу конфігурації</string>
@@ -208,7 +205,6 @@
<string name="kofi">Ko-fi</string> <string name="kofi">Ko-fi</string>
<string name="donation_signoff">З вдячністю,</string> <string name="donation_signoff">З вдячністю,</string>
<string name="selected">Вибрані</string> <string name="selected">Вибрані</string>
<string name="global_split_tunneling">Глобальне роздільне тунелювання</string>
<string name="active_network">Активна мережа:</string> <string name="active_network">Активна мережа:</string>
<string name="range_hint">(%1$d%2$d)</string> <string name="range_hint">(%1$d%2$d)</string>
<string name="delete_active_message">Неможливо видалити активний тунель.</string> <string name="delete_active_message">Неможливо видалити активний тунель.</string>
@@ -222,7 +218,6 @@
<string name="kill_switch">kill switch</string> <string name="kill_switch">kill switch</string>
<string name="configuration">Конфігурація</string> <string name="configuration">Конфігурація</string>
<string name="drag_handle">Елемент перетягування</string> <string name="drag_handle">Елемент перетягування</string>
<string name="global_dns_servers">Глобальні DNS сервери</string>
<string name="contact">Зв\'язок</string> <string name="contact">Зв\'язок</string>
<string name="ports_must_differ">Помилка. Проксі мають мати різні порти.</string> <string name="ports_must_differ">Помилка. Проксі мають мати різні порти.</string>
<string name="join_matrix">Приєднуйтесь до спільноти Matrix</string> <string name="join_matrix">Приєднуйтесь до спільноти Matrix</string>
@@ -288,7 +283,6 @@
<string name="fix">Виправлення</string> <string name="fix">Виправлення</string>
<string name="tunnel_running_name_message">Неможливо змінити назву, поки тунель активний.</string> <string name="tunnel_running_name_message">Неможливо змінити назву, поки тунель активний.</string>
<string name="export_failed">Експорт не вдався</string> <string name="export_failed">Експорт не вдався</string>
<string name="enable_remote_app_control">Увімкнути дистанційне керування застосунком</string>
<string name="donation_closing">Присвятити весь свій робочий час цьому проєкту — моя мрія.</string> <string name="donation_closing">Присвятити весь свій робочий час цьому проєкту — моя мрія.</string>
<string name="update_download_failed">Не вдалося завантажити оновлення.</string> <string name="update_download_failed">Не вдалося завантажити оновлення.</string>
<string name="network_name">Мережа:</string> <string name="network_name">Мережа:</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
-11
View File
@@ -37,7 +37,6 @@
<string name="random">(بے ترتیب)</string> <string name="random">(بے ترتیب)</string>
<string name="preshared_key">پہلے سے مشترکہ کلید</string> <string name="preshared_key">پہلے سے مشترکہ کلید</string>
<string name="cancel">منسوخ</string> <string name="cancel">منسوخ</string>
<string name="enabled_app_shortcuts">ایپ شارٹ کٹس کو فعال کریں</string>
<string name="tunnel_on_wifi">ناقابل اعتماد وائی فائی پر ٹنل</string> <string name="tunnel_on_wifi">ناقابل اعتماد وائی فائی پر ٹنل</string>
<string name="email_subject">ڈبلیو جی ٹنل سپورٹ</string> <string name="email_subject">ڈبلیو جی ٹنل سپورٹ</string>
<string name="docs_description">دستاویزات پڑھیں</string> <string name="docs_description">دستاویزات پڑھیں</string>
@@ -82,7 +81,6 @@
<string name="donate">پروجیکٹ کے لیے عطیہ کریں</string> <string name="donate">پروجیکٹ کے لیے عطیہ کریں</string>
<string name="local_logging">مقامی لاگنگ</string> <string name="local_logging">مقامی لاگنگ</string>
<string name="stop_on_no_internet">بغیر انٹرنیٹ پر روکیں</string> <string name="stop_on_no_internet">بغیر انٹرنیٹ پر روکیں</string>
<string name="stop_on_internet_loss">انٹرنیٹ لاس پر ٹنل روکیں</string>
<string name="allow_lan_traffic">لین ٹریفک کی اجازت دیں</string> <string name="allow_lan_traffic">لین ٹریفک کی اجازت دیں</string>
<string name="splt_tunneling">سپلٹ ٹنلنگ</string> <string name="splt_tunneling">سپلٹ ٹنلنگ</string>
<string name="show_scripts">اسکرپٹس دکھائیں</string> <string name="show_scripts">اسکرپٹس دکھائیں</string>
@@ -113,7 +111,6 @@
<string name="response_packet_junk_size">رسپانس پیکٹ جنک سائز</string> <string name="response_packet_junk_size">رسپانس پیکٹ جنک سائز</string>
<string name="junk_packet_minimum_size">جنک پیکٹ کا کم از کم سائز</string> <string name="junk_packet_minimum_size">جنک پیکٹ کا کم از کم سائز</string>
<string name="edit_tunnel">ٹنل میں ترمیم کریں</string> <string name="edit_tunnel">ٹنل میں ترمیم کریں</string>
<string name="enable_app_lock">ایپ لاک فعال کریں</string>
<string name="logs">لاگز</string> <string name="logs">لاگز</string>
<string name="restart_at_boot">بوٹ پر دوبارہ شروع کریں</string> <string name="restart_at_boot">بوٹ پر دوبارہ شروع کریں</string>
<string name="support">سپورٹ</string> <string name="support">سپورٹ</string>
@@ -135,7 +132,6 @@
<string name="vpn_channel_description">وی پی این ریاستی اطلاعات کے لیے ایک چینل</string> <string name="vpn_channel_description">وی پی این ریاستی اطلاعات کے لیے ایک چینل</string>
<string name="hide_scripts">اسکرپٹس چھپائیں</string> <string name="hide_scripts">اسکرپٹس چھپائیں</string>
<string name="add_from_clipboard">کلپ بورڈ سے شامل کریں</string> <string name="add_from_clipboard">کلپ بورڈ سے شامل کریں</string>
<string name="enable_remote_app_control">ریموٹ ایپ کنٹرول فعال کریں</string>
<string name="app_permission_title">ڈبلیو جی ٹنل کنٹرول برج</string> <string name="app_permission_title">ڈبلیو جی ٹنل کنٹرول برج</string>
<string name="app_permission_description">ٹنل اور خودکار ٹنل کی خصوصیات کو کنٹرول کریں۔</string> <string name="app_permission_description">ٹنل اور خودکار ٹنل کی خصوصیات کو کنٹرول کریں۔</string>
<string name="add_from_url">یو آر ایل سے شامل کریں</string> <string name="add_from_url">یو آر ایل سے شامل کریں</string>
@@ -281,14 +277,12 @@
<string name="resources">Resources</string> <string name="resources">Resources</string>
<string name="back">Back</string> <string name="back">Back</string>
<string name="already_donated">Already donated</string> <string name="already_donated">Already donated</string>
<string name="global_split_tunneling">Global split tunneling</string>
<string name="active_network">Active Network:</string> <string name="active_network">Active Network:</string>
<string name="help_translate">Help translate the app</string> <string name="help_translate">Help translate the app</string>
<string name="ethernet">Ethernet</string> <string name="ethernet">Ethernet</string>
<string name="other">Other</string> <string name="other">Other</string>
<string name="kill_switch">kill switch</string> <string name="kill_switch">kill switch</string>
<string name="configuration">Configuration</string> <string name="configuration">Configuration</string>
<string name="global_dns_servers">Global DNS servers</string>
<string name="contact">Contact</string> <string name="contact">Contact</string>
<string name="backup_and_restore">Backup and restore</string> <string name="backup_and_restore">Backup and restore</string>
<string name="about">About</string> <string name="about">About</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
-11
View File
@@ -17,7 +17,6 @@
<string name="delete_tunnel">Delete tunnel</string> <string name="delete_tunnel">Delete tunnel</string>
<string name="tunnel_mobile_data">Tunnel trên dữ liệu di động</string> <string name="tunnel_mobile_data">Tunnel trên dữ liệu di động</string>
<string name="logs">Logs</string> <string name="logs">Logs</string>
<string name="enable_app_lock">Enable app lock</string>
<string name="config_changes_saved">Các thay đổi cấu hình đã được lưu.</string> <string name="config_changes_saved">Các thay đổi cấu hình đã được lưu.</string>
<string name="join_telegram">Join Telegram community</string> <string name="join_telegram">Join Telegram community</string>
<string name="pin_created">Pin successfully created</string> <string name="pin_created">Pin successfully created</string>
@@ -29,7 +28,6 @@
<string name="remote_key">Remote key</string> <string name="remote_key">Remote key</string>
<string name="mobile_data">Mobile data</string> <string name="mobile_data">Mobile data</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_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="stop_on_internet_loss">Stop tunnel on internet loss</string>
<string name="vpn">VPN</string> <string name="vpn">VPN</string>
<string name="tunnel_boot_description">Start the default tunnel on boot</string> <string name="tunnel_boot_description">Start the default tunnel on boot</string>
<string name="allow_lan_traffic">Allow LAN traffic</string> <string name="allow_lan_traffic">Allow LAN traffic</string>
@@ -134,7 +132,6 @@
<string name="kofi">Ko-fi</string> <string name="kofi">Ko-fi</string>
<string name="donation_signoff">Gratefully,</string> <string name="donation_signoff">Gratefully,</string>
<string name="selected">Selected</string> <string name="selected">Selected</string>
<string name="global_split_tunneling">Global split tunneling</string>
<string name="active_network">Active Network:</string> <string name="active_network">Active Network:</string>
<string name="range_hint">(%1$d%2$d)</string> <string name="range_hint">(%1$d%2$d)</string>
<string name="delete_active_message">Cannot delete active tunnel.</string> <string name="delete_active_message">Cannot delete active tunnel.</string>
@@ -157,7 +154,6 @@
<string name="mtu">MTU</string> <string name="mtu">MTU</string>
<string name="configuration">Configuration</string> <string name="configuration">Configuration</string>
<string name="drag_handle">Drag Handle</string> <string name="drag_handle">Drag Handle</string>
<string name="global_dns_servers">Global DNS servers</string>
<string name="display_theme">Display theme</string> <string name="display_theme">Display theme</string>
<string name="contact">Contact</string> <string name="contact">Contact</string>
<string name="ports_must_differ">Failed. Proxies must have different ports.</string> <string name="ports_must_differ">Failed. Proxies must have different ports.</string>
@@ -257,7 +253,6 @@
<string name="unavailable_in_mode">Unavailable in current mode</string> <string name="unavailable_in_mode">Unavailable in current mode</string>
<string name="server_port">Server:Port</string> <string name="server_port">Server:Port</string>
<string name="camera_permission_required">Camera permission required</string> <string name="camera_permission_required">Camera permission required</string>
<string name="enabled_app_shortcuts">Enable app shortcuts</string>
<string name="preferred_tunnel">Preferred tunnel</string> <string name="preferred_tunnel">Preferred tunnel</string>
<string name="allow">Allow</string> <string name="allow">Allow</string>
<string name="underload_packet_magic_header">Underload packet magic header</string> <string name="underload_packet_magic_header">Underload packet magic header</string>
@@ -272,7 +267,6 @@
<string name="settings">Settings</string> <string name="settings">Settings</string>
<string name="incorrect_pin">Pin is incorrect</string> <string name="incorrect_pin">Pin is incorrect</string>
<string name="export_failed">Export failed</string> <string name="export_failed">Export failed</string>
<string name="enable_remote_app_control">Enable remote app control</string>
<string name="donation_closing">It\'s my dream to work for you on this project full-time.</string> <string name="donation_closing">It\'s my dream to work for you on this project full-time.</string>
<string name="update_download_failed">Update download failed.</string> <string name="update_download_failed">Update download failed.</string>
<string name="network_name">Network:</string> <string name="network_name">Network:</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
@@ -25,7 +25,6 @@
<string name="private_key">私钥</string> <string name="private_key">私钥</string>
<string name="listen_port">监听端口</string> <string name="listen_port">监听端口</string>
<string name="optional">(可选)</string> <string name="optional">(可选)</string>
<string name="enabled_app_shortcuts">创建桌面快捷方式</string>
<string name="docs_description">阅读文档</string> <string name="docs_description">阅读文档</string>
<string name="email_description">给作者发邮件</string> <string name="email_description">给作者发邮件</string>
<string name="error_root_denied">root 权限未开启</string> <string name="error_root_denied">root 权限未开启</string>
@@ -57,7 +56,6 @@
<string name="yes"></string> <string name="yes"></string>
<string name="all">全部</string> <string name="all">全部</string>
<string name="no_email_detected">未安装邮件应用</string> <string name="no_email_detected">未安装邮件应用</string>
<string name="enable_app_lock">锁定应用</string>
<string name="settings">设置</string> <string name="settings">设置</string>
<string name="support">支持</string> <string name="support">支持</string>
<string name="junk_packet_minimum_size">无效包最小值</string> <string name="junk_packet_minimum_size">无效包最小值</string>
@@ -113,7 +111,6 @@
<string name="donate">捐赠</string> <string name="donate">捐赠</string>
<string name="local_logging">本地日志监控器</string> <string name="local_logging">本地日志监控器</string>
<string name="stop_on_no_internet">无网络时停用</string> <string name="stop_on_no_internet">无网络时停用</string>
<string name="stop_on_internet_loss">网络丢失时停止隧道</string>
<string name="bypass_lan_for_kill_switch">绕过局域网流量</string> <string name="bypass_lan_for_kill_switch">绕过局域网流量</string>
<string name="auto_tunnel_channel_description">自动隧道状态通知频道</string> <string name="auto_tunnel_channel_description">自动隧道状态通知频道</string>
<string name="stop">停止</string> <string name="stop">停止</string>
@@ -154,7 +151,6 @@
<string name="copy">复制</string> <string name="copy">复制</string>
<string name="config_error">无效配置</string> <string name="config_error">无效配置</string>
<string name="service_running_error">服务未运行</string> <string name="service_running_error">服务未运行</string>
<string name="enable_remote_app_control">开启远程应用控制</string>
<string name="join_matrix">加入 Matrix 社区</string> <string name="join_matrix">加入 Matrix 社区</string>
<string name="app_permission_description">控制隧道和自动隧道功能.</string> <string name="app_permission_description">控制隧道和自动隧道功能.</string>
<string name="app_permission_title">控制隧道和自动隧道功能。</string> <string name="app_permission_title">控制隧道和自动隧道功能。</string>
@@ -300,8 +296,6 @@
<string name="metered_tunnel">流量计费的隧道</string> <string name="metered_tunnel">流量计费的隧道</string>
<string name="lockdown_settings">锁定设置</string> <string name="lockdown_settings">锁定设置</string>
<string name="unavailable_in_mode">当前模式下不可用</string> <string name="unavailable_in_mode">当前模式下不可用</string>
<string name="global_split_tunneling">全局分流隧道</string>
<string name="global_dns_servers">全局 DNS 服务器</string>
<string name="dual_stack">双栈</string> <string name="dual_stack">双栈</string>
<string name="dual_stack_description">隧道必须支持 IPv4 和 IPv6</string> <string name="dual_stack_description">隧道必须支持 IPv4 和 IPv6</string>
<string name="save_changes">保存更改</string> <string name="save_changes">保存更改</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
@@ -41,7 +41,6 @@
<string name="enter_pin">輸入 PIN</string> <string name="enter_pin">輸入 PIN</string>
<string name="pin_created">成功建立 PIN</string> <string name="pin_created">成功建立 PIN</string>
<string name="incorrect_pin">PIN 不正確</string> <string name="incorrect_pin">PIN 不正確</string>
<string name="enable_app_lock">啟用應用程式鎖定</string>
<string name="set_primary_tunnel">沒有配置首選隧道時使用的隧道</string> <string name="set_primary_tunnel">沒有配置首選隧道時使用的隧道</string>
<string name="edit_tunnel">編輯隧道</string> <string name="edit_tunnel">編輯隧道</string>
<string name="vpn_settings">系統 VPN 設定</string> <string name="vpn_settings">系統 VPN 設定</string>
@@ -64,7 +63,6 @@
<string name="add_wifi_name">新增 WiFi 名稱</string> <string name="add_wifi_name">新增 WiFi 名稱</string>
<string name="allow_lan_traffic">允許 LAN 流量</string> <string name="allow_lan_traffic">允許 LAN 流量</string>
<string name="stop_on_no_internet">沒有連上網路時停止</string> <string name="stop_on_no_internet">沒有連上網路時停止</string>
<string name="stop_on_internet_loss">網路斷線時停止隧道</string>
<string name="add_from_clipboard">從剪貼簿新增</string> <string name="add_from_clipboard">從剪貼簿新增</string>
<string name="stop">停止</string> <string name="stop">停止</string>
<string name="exclude_lan">排除 LAN</string> <string name="exclude_lan">排除 LAN</string>
@@ -83,7 +81,6 @@
<string name="vpn_off">VPN 已關閉</string> <string name="vpn_off">VPN 已關閉</string>
<string name="error_root_denied">無法取得 root 權限</string> <string name="error_root_denied">無法取得 root 權限</string>
<string name="root_accepted">已取得 root 權限</string> <string name="root_accepted">已取得 root 權限</string>
<string name="enable_remote_app_control">啟用遠端應用程式控制</string>
<string name="join_telegram">加入 Telegram 社群</string> <string name="join_telegram">加入 Telegram 社群</string>
<string name="add_from_url">從 URL 新增</string> <string name="add_from_url">從 URL 新增</string>
<string name="join_matrix">加入 Matrix 社群</string> <string name="join_matrix">加入 Matrix 社群</string>
@@ -248,7 +245,6 @@
<string name="kofi">Ko-fi</string> <string name="kofi">Ko-fi</string>
<string name="donation_signoff">感激地,</string> <string name="donation_signoff">感激地,</string>
<string name="selected">已選擇的</string> <string name="selected">已選擇的</string>
<string name="global_split_tunneling">全域隧道拆分</string>
<string name="active_network">正在使用的網路:</string> <string name="active_network">正在使用的網路:</string>
<string name="range_hint">(%1$d%2$d)</string> <string name="range_hint">(%1$d%2$d)</string>
<string name="delete_active_message">無法刪除正在使用的隧道。</string> <string name="delete_active_message">無法刪除正在使用的隧道。</string>
@@ -260,7 +256,6 @@
<string name="new_tunnel">新隧道</string> <string name="new_tunnel">新隧道</string>
<string name="kill_switch">kill switch</string> <string name="kill_switch">kill switch</string>
<string name="configuration">組態</string> <string name="configuration">組態</string>
<string name="global_dns_servers">全域 DNS 伺服器</string>
<string name="contact">聯絡</string> <string name="contact">聯絡</string>
<string name="ports_must_differ">失敗。代理必須具有不同連接埠。</string> <string name="ports_must_differ">失敗。代理必須具有不同連接埠。</string>
<string name="backup_and_restore">備份和還原</string> <string name="backup_and_restore">備份和還原</string>
@@ -295,7 +290,6 @@
<string name="mimic_sip">模仿 SIP</string> <string name="mimic_sip">模仿 SIP</string>
<string name="unavailable_in_mode">當前模式下不可用</string> <string name="unavailable_in_mode">當前模式下不可用</string>
<string name="server_port">伺服器:連接埠</string> <string name="server_port">伺服器:連接埠</string>
<string name="enabled_app_shortcuts">啟用應用程式捷徑</string>
<string name="preferred_tunnel">首選隧道</string> <string name="preferred_tunnel">首選隧道</string>
<string name="tunnel_running_name_message">隧道正在使用時無法變更名稱。</string> <string name="tunnel_running_name_message">隧道正在使用時無法變更名稱。</string>
<string name="donation_closing">為您全職開發本專案是我的夢想。</string> <string name="donation_closing">為您全職開發本專案是我的夢想。</string>
@@ -391,7 +385,6 @@
<string name="dns_error_empty">Endpoint cannot be empty</string> <string name="dns_error_empty">Endpoint cannot be empty</string>
<string name="tunnel_state_connected">Connected</string> <string name="tunnel_state_connected">Connected</string>
<string name="dns_error_invalid_scheme">DoH must use HTTPS</string> <string name="dns_error_invalid_scheme">DoH must use HTTPS</string>
<string name="configuration_globals">Configuration globals</string>
<string name="private_dns_hostname">Private DNS: hostname (%1$s)</string> <string name="private_dns_hostname">Private DNS: hostname (%1$s)</string>
<string name="app">App</string> <string name="app">App</string>
<string name="refresh_rate">Statistics refresh rate</string> <string name="refresh_rate">Statistics refresh rate</string>
@@ -433,7 +426,6 @@
<string name="included_apps">%1$s apps included</string> <string name="included_apps">%1$s apps included</string>
<string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string> <string name="error_http_port_unavailable">HTTP listener port %1$d is already in use.\nPlease choose a different port.</string>
<string name="current_system_dns">Current system DNS</string> <string name="current_system_dns">Current system DNS</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="status_template">status: %1$s</string> <string name="status_template">status: %1$s</string>
@@ -467,7 +459,4 @@
<string name="i4" translatable="false">I4</string> <string name="i4" translatable="false">I4</string>
<string name="s1" translatable="false">S1</string> <string name="s1" translatable="false">S1</string>
<string name="h4" translatable="false">H4</string> <string name="h4" translatable="false">H4</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string>
<string name="no_tunnels_yet">Nothing here… yet.</string>
</resources> </resources>
+23 -13
View File
@@ -59,7 +59,6 @@
<string name="seconds">Seconds</string> <string name="seconds">Seconds</string>
<string name="persistent_keepalive">Persistent keepalive</string> <string name="persistent_keepalive">Persistent keepalive</string>
<string name="cancel">Cancel</string> <string name="cancel">Cancel</string>
<string name="enabled_app_shortcuts">Enable app shortcuts</string>
<string name="unknown_error">Unknown error occurred</string> <string name="unknown_error">Unknown error occurred</string>
<string name="tunnel_on_wifi">Tunnel on Wi-Fi</string> <string name="tunnel_on_wifi">Tunnel on Wi-Fi</string>
<string name="my_email" translatable="false">support@zaneschepke.com</string> <string name="my_email" translatable="false">support@zaneschepke.com</string>
@@ -83,7 +82,6 @@
<string name="pin_created">Pin successfully created</string> <string name="pin_created">Pin successfully created</string>
<string name="enter_pin">Enter PIN</string> <string name="enter_pin">Enter PIN</string>
<string name="create_pin">Create PIN</string> <string name="create_pin">Create PIN</string>
<string name="enable_app_lock">Enable app lock</string>
<string name="set_primary_tunnel">Tunnel used when no preferred tunnel is configured</string> <string name="set_primary_tunnel">Tunnel used when no preferred tunnel is configured</string>
<string name="edit_tunnel">Edit tunnel</string> <string name="edit_tunnel">Edit tunnel</string>
<string name="settings">Settings</string> <string name="settings">Settings</string>
@@ -129,8 +127,7 @@
<string name="donate">Donate to project</string> <string name="donate">Donate to project</string>
<string name="local_logging">Local logging</string> <string name="local_logging">Local logging</string>
<string name="add_from_clipboard">Add from clipboard</string> <string name="add_from_clipboard">Add from clipboard</string>
<string name="stop_on_no_internet">Stop on no internet</string> <string name="stop_on_no_internet">Stop on no network</string>
<string name="stop_on_internet_loss">Stop tunnel on internet loss</string>
<string name="native_kill_switch">Native kill switch</string> <string name="native_kill_switch">Native kill switch</string>
<string name="allow_lan_traffic">Allow LAN traffic</string> <string name="allow_lan_traffic">Allow LAN traffic</string>
<string name="bypass_lan_for_kill_switch">Bypass LAN for kill switch</string> <string name="bypass_lan_for_kill_switch">Bypass LAN for kill switch</string>
@@ -138,7 +135,7 @@
<string name="auto_tunnel_channel_id" translatable="false">Auto-tunnel Channel</string> <string name="auto_tunnel_channel_id" translatable="false">Auto-tunnel Channel</string>
<string name="auto_tunnel_channel_description">Notifications for auto-tunnel events.</string> <string name="auto_tunnel_channel_description">Notifications for auto-tunnel events.</string>
<string name="stop">Stop</string> <string name="stop">Stop</string>
<string name="splt_tunneling">Split tunneling</string> <string name="splt_tunneling">App split tunneling</string>
<string name="show_scripts">Show scripts</string> <string name="show_scripts">Show scripts</string>
<string name="pre_up">Pre up</string> <string name="pre_up">Pre up</string>
<string name="post_up">Post up</string> <string name="post_up">Post up</string>
@@ -179,7 +176,6 @@
<string name="dns_resolve_error">DNS resolution failed</string> <string name="dns_resolve_error">DNS resolution failed</string>
<string name="auth_error">Unauthorized</string> <string name="auth_error">Unauthorized</string>
<string name="service_running_error">Service not running</string> <string name="service_running_error">Service not running</string>
<string name="enable_remote_app_control">Enable remote app control</string>
<string name="select_all">Select all</string> <string name="select_all">Select all</string>
<string name="export_success">Export success</string> <string name="export_success">Export success</string>
<string name="download">Download</string> <string name="download">Download</string>
@@ -276,7 +272,7 @@
<string name="active_tunnel_update_failed">Active tunnel update failed</string> <string name="active_tunnel_update_failed">Active tunnel update failed</string>
<string name="ddns_auto_update">Dynamic DNS (DDNS)</string> <string name="ddns_auto_update">Dynamic DNS (DDNS)</string>
<string name="ddns_auto_update_description"> <string name="ddns_auto_update_description">
Re-resolves the server hostname and updates peer endpoints on DDNS changes Automatically update the server endpoint when its IP changes
</string> </string>
<string name="mode_disabled_template">Feature unavailable in %1$s mode.</string> <string name="mode_disabled_template">Feature unavailable in %1$s mode.</string>
<string name="lockdown">Lockdown</string> <string name="lockdown">Lockdown</string>
@@ -358,9 +354,6 @@
<string name="metered_tunnel">Metered tunnel</string> <string name="metered_tunnel">Metered tunnel</string>
<string name="lockdown_settings">Lockdown settings</string> <string name="lockdown_settings">Lockdown settings</string>
<string name="unavailable_in_mode">Unavailable in current mode</string> <string name="unavailable_in_mode">Unavailable in current mode</string>
<string name="global_split_tunneling">Global split tunneling</string>
<string name="global_dns_servers">Global DNS servers</string>
<string name="global_amnezia_configuration">Global Amnezia configuration</string>
<string name="dual_stack">Dual-stack</string> <string name="dual_stack">Dual-stack</string>
<string name="dual_stack_description">Tunnels must support IPv4 and IPv6</string> <string name="dual_stack_description">Tunnels must support IPv4 and IPv6</string>
<string name="save_changes">Save changes</string> <string name="save_changes">Save changes</string>
@@ -475,7 +468,6 @@
<string name="error">Error</string> <string name="error">Error</string>
<string name="tunnel_globals">Tunnel globals</string> <string name="tunnel_globals">Tunnel globals</string>
<string name="configuration_globals">Configuration globals</string>
<string name="screen_recording_protection">Screen recording protection</string> <string name="screen_recording_protection">Screen recording protection</string>
<string name="security">Security</string> <string name="security">Security</string>
<string name="ready">Ready</string> <string name="ready">Ready</string>
@@ -504,14 +496,32 @@
<string name="tunnel_state_establishing_connection">Establishing connection</string> <string name="tunnel_state_establishing_connection">Establishing connection</string>
<string name="peer_endpoints">Peer endpoints</string> <string name="peer_endpoints">Peer endpoints</string>
<string name="prefer_ipv6">Prefer IPv6</string> <string name="prefer_ipv6">Prefer IPv6</string>
<string name="prefer_ipv6_desc">Use IPv6 endpoints when the network supports it.</string> <string name="prefer_ipv6_desc">Use IPv6 endpoints when the network supports it</string>
<string name="fallback_to_ipv4">Fallback to IPv4</string> <string name="fallback_to_ipv4">Fallback to IPv4</string>
<string name="fallback_to_ipv4_desc">Switch to IPv4 if IPv6 fails, without restarting the tunnel.</string> <string name="fallback_to_ipv4_desc">Switch to IPv4 if IPv6 fails, without restarting the tunnel</string>
<string name="restore_ipv6">Restore IPv6</string> <string name="restore_ipv6">Restore IPv6</string>
<string name="restore_ipv6_desc">Switch back to IPv6 when an IPv6 network is detected.</string> <string name="restore_ipv6_desc">Switch back to IPv6 when an IPv6 network is detected.</string>
<string name="no_tunnels_yet">Nothing here… yet.</string> <string name="no_tunnels_yet">Nothing here… yet.</string>
<string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string> <string name="getting_started_guidance">Use the + button to import a tunnel, or view the getting started guide.</string>
<string name="getting_started_guide_link">view the getting started guide</string> <string name="getting_started_guide_link">view the getting started guide</string>
<string name="metered_tunnel_desc">Mark this tunnel as metered so apps treat it like mobile data</string>
<string name="local_proxy">Local proxy</string>
<string name="local_proxy_desc">Expose the tunnel as a local SOCKS5/HTTP proxy</string>
<string name="lockdown_desc">Permanent kill switch that blocks all non-tunnel traffic</string>
<string name="vpn_desc">Standard system-wide VPN mode</string>
<string name="stop_on_no_internet_desc">Stop the tunnel when the device has no network connection</string>
<string name="start_on_boot_desc">Start auto-tunnel automatically on boot</string>
<string name="local_logging_desc">Local log storage with live viewer</string>
<string name="tunnel_globals_desc">Global configuration overrides for all tunnels</string>
<string name="tunnel_configuration">Tunnel configuration</string>
<string name="amnezia_configuration">Amnezia configuration</string>
<string name="screen_recording_protection_desc">Block screen recording on screens with sensitive data</string>
<string name="app_lock">App lock</string>
<string name="app_lock_desc">Require a PIN to access the app</string>
<string name="app_shortcuts">App shortcuts</string>
<string name="app_shortcuts_desc">Add quick actions to the app icon</string>
<string name="remote_control">Remote control</string>
<string name="remote_control_desc">Allow other apps (like Tasker) to control tunnels</string>
</resources> </resources>
@@ -0,0 +1,11 @@
What's new:
- Improved tunnel reliability and leak protection with new deferred endpoint bootstrapping
- Improved tunnel status monitoring with real time handshake status monitoring
- Kernel mode and ping monitoring removed
- Full config and live tunnel view in quick format
- Automatic ipv4/ipv6 endpoint fallback and recovery
- Added support got DoT and custom endpoints for peer resolution DNS
- Amnezia tunnel globals
- Improved notifications
- UI improvements with better feature descriptions
- Various bug fix and app performance improvements
@@ -1,5 +1,5 @@
Co nowego: What's new:
- Obsługa Amnezia 2.0. - Amnezia 2.0 support
- Kopiowanie aplikacji rozdzielonego tunelu z istniejącej konfiguracji. - Copy split tunnel apps from existing config
- Naprawiono błąd uruchamiania rejestratora. - Logger start bugfix
- Naprawiono błąd synchronizacji dodanego szybkiego kafelka. - Quick tile added sync bugfix
@@ -1,3 +1,3 @@
Co nowego: What's new:
- Naprawiono błąd powodujący, że ekran autotunelowania nie wczytywał się bez połączenia z Wi-Fi. - Auto-tunnel screen not loading without connecting to Wi-Fi bugfix
- Naprawiono błąd związany z importowaniem tunelu przez adres URL. - Import tunnel via URL bugfix
@@ -1,6 +1,6 @@
Co nowego: What's new:
- Naprawiono błąd trybu blokady profilu prywatnego. - Private profile lockdown mode bugfix
- Optymalizacja wydajności interfejsu użytkownika. - UI performance optimizations
- Naprawiono błąd powodujący awarię nawigacji wstecznej w niektórych scenariuszach. - Back navigation crash in certain scenarios bugfix
- Naprawiono błąd automatycznego wyścigu tunelowania po zmianach w Amnezia 2.0. - Auto tunneling race after Amnezia 2.0 changes bugfix
- Lokalizacje. - Localizations
@@ -1,7 +1,7 @@
Co nowego: What's new:
- Naprawiono uzgadnianie trybu drzemki. - Doze mode handshake fix
- Opcjonalnie naprawiono błąd I2-5. - Optional I2-5 bugfix
- Naprawiono błąd powodujący awarię podczas tworzenia od podstaw. - Create from scratch crash bugfix
- Wyświetlanie statystyk tunelu w powiadomieniach. - Show tunnel statistics in notification
- Filtrowanie tunelu według opóźnienia. - Filter tunnel by latency
- Tłumaczenia. - Translations
@@ -21,6 +21,19 @@ interface Tunnel {
data object Down : State data object Down : State
data object Starting : State data object Starting : State
data object Stopping : State
companion object {
fun fromNative(code: Int): State? {
return when (code) {
0 -> Up.Healthy
1 -> Up.HandshakeFailure
99 -> Down
else -> null
}
}
}
} }
sealed interface IpStrategy { sealed interface IpStrategy {
@@ -8,7 +8,6 @@ import com.zaneschepke.tunnel.model.DnsBoostrapMode
import com.zaneschepke.tunnel.model.KillSwitchConfig import com.zaneschepke.tunnel.model.KillSwitchConfig
import com.zaneschepke.tunnel.service.VpnService import com.zaneschepke.tunnel.service.VpnService
import com.zaneschepke.tunnel.state.BackendStatus import com.zaneschepke.tunnel.state.BackendStatus
import kotlin.reflect.KClass
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
interface Backend { interface Backend {
@@ -27,9 +26,6 @@ interface Backend {
suspend fun setBootstrapDnsMode(mode: DnsBoostrapMode) suspend fun setBootstrapDnsMode(mode: DnsBoostrapMode)
// Emergency synchronous teardown to be called only from Service.onDestroy()
fun emergencyStopAllOfTypeSync(modeClass: KClass<out BackendMode>)
suspend fun stopAllActiveTunnels(): Result<Unit> suspend fun stopAllActiveTunnels(): Result<Unit>
val status: Flow<BackendStatus> val status: Flow<BackendStatus>
@@ -1,16 +0,0 @@
package com.zaneschepke.tunnel.backend
import com.zaneschepke.tunnel.state.EngineState
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
internal class DefaultEngineStateProvider : EngineStateProvider {
private val _state = MutableStateFlow(EngineState())
override val state: Flow<EngineState> = _state
fun update(transform: (EngineState) -> EngineState) {
_state.value = transform(_state.value)
}
}
@@ -1,8 +0,0 @@
package com.zaneschepke.tunnel.backend
import com.zaneschepke.tunnel.state.EngineState
import kotlinx.coroutines.flow.Flow
internal interface EngineStateProvider {
val state: Flow<EngineState>
}
@@ -12,9 +12,10 @@ object RootShell {
fun requestRootPermission(): Boolean { fun requestRootPermission(): Boolean {
return try { return try {
val result = Shell.cmd("true").exec() val shell = Shell.cmd("su").exec()
result.isSuccess shell.isSuccess
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e, "Root permission request failed or timed out")
false false
} }
} }
@@ -2,46 +2,26 @@ package com.zaneschepke.tunnel.backend
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import com.zaneschepke.tunnel.ProxyBackend
import com.zaneschepke.tunnel.StatusCallback
import com.zaneschepke.tunnel.VpnBackend
import com.zaneschepke.tunnel.service.TunnelService import com.zaneschepke.tunnel.service.TunnelService
import com.zaneschepke.tunnel.service.VpnService import com.zaneschepke.tunnel.service.VpnService
import com.zaneschepke.tunnel.state.NativeTunnelStatus
import com.zaneschepke.tunnel.util.BackendException import com.zaneschepke.tunnel.util.BackendException
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import java.util.concurrent.CompletableFuture import kotlin.time.Duration.Companion.milliseconds
import java.util.concurrent.TimeUnit import kotlinx.coroutines.CompletableDeferred
import java.util.concurrent.TimeoutException import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlin.concurrent.atomics.AtomicBoolean import kotlinx.coroutines.TimeoutCancellationException
import kotlin.concurrent.atomics.ExperimentalAtomicApi import kotlinx.coroutines.withTimeout
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.withTimeoutOrNull
import kotlinx.coroutines.flow.asSharedFlow
import timber.log.Timber import timber.log.Timber
internal class ServiceHolder(val context: Context) { internal class ServiceHolder(val context: Context) {
internal val uapiPath = context.dataDir.absolutePath internal val uapiPath = context.dataDir.absolutePath
@OptIn(ExperimentalAtomicApi::class) @Volatile private var vpnService = CompletableDeferred<VpnService>()
private val nativeCallbacksRegistered = AtomicBoolean(false) @Volatile private var tunnelService = CompletableDeferred<TunnelService>()
@Volatile private var vpnServiceDestroyed = CompletableDeferred<Unit>()
private val _nativeStatuses = MutableSharedFlow<NativeTunnelStatus>(extraBufferCapacity = 64) @Volatile private var tunnelServiceDestroyed = CompletableDeferred<Unit>()
val nativeStatuses = _nativeStatuses.asSharedFlow()
private val statusCallback = StatusCallback { handle, code ->
val status = NativeTunnelStatus.NativeTunnelStatusCode.from(code)
if (status == null) {
Timber.d("Unknown native status code: $code")
return@StatusCallback
}
Timber.d("Native Callback - Handle: $handle, Code: $status")
_nativeStatuses.tryEmit(NativeTunnelStatus(handle = handle, code = status))
}
fun set(service: VpnService) { fun set(service: VpnService) {
vpnService.complete(service) vpnService.complete(service)
@@ -51,117 +31,81 @@ internal class ServiceHolder(val context: Context) {
tunnelService.complete(service) tunnelService.complete(service)
} }
fun getVpnService(): VpnService { fun signalVpnServiceDestroyed() {
vpnServiceDestroyed.complete(Unit)
}
vpnService.getNow(null)?.let { fun signalTunnelServiceDestroyed() {
return it tunnelServiceDestroyed.complete(Unit)
}
@OptIn(ExperimentalCoroutinesApi::class)
suspend fun getVpnService(): VpnService {
if (vpnService.isCompleted && !vpnService.isCancelled) {
return vpnService.getCompleted()
} }
try { if (android.net.VpnService.prepare(context) != null) {
if (android.net.VpnService.prepare(context) != null) { throw BackendException.Unauthorized("Permission unavailable to use VpnService")
throw BackendException.Unauthorized("Permission unavailable to use VpnService")
}
context.startForegroundService(Intent(context, VpnService::class.java))
} catch (e: Exception) {
Timber.e(e, "Error starting VPN service")
} }
context.startForegroundService(Intent(context, VpnService::class.java))
return try { return try {
vpnService.get(2, TimeUnit.SECONDS) withTimeout(3_000L.milliseconds) { vpnService.await() }
} catch (e: TimeoutException) { } catch (e: TimeoutCancellationException) {
Timber.e(e, "Timed out getting VpnService") Timber.e(e, "Timed out getting VpnService")
throw BackendException.InternalError("Failed to get VpnService") throw BackendException.InternalError("Failed to get VpnService")
} }
} }
fun getTunnelService(): TunnelService { @OptIn(ExperimentalCoroutinesApi::class)
suspend fun getTunnelService(): TunnelService {
tunnelService.getNow(null)?.let { if (tunnelService.isCompleted && !tunnelService.isCancelled) {
return it return tunnelService.getCompleted()
} }
try { context.startForegroundService(Intent(context, TunnelService::class.java))
context.startForegroundService(Intent(context, TunnelService::class.java))
} catch (e: Exception) {
Timber.e(e, "Error starting TunnelService")
}
return try { return try {
tunnelService.get(2, TimeUnit.SECONDS) withTimeout(3_000L.milliseconds) { tunnelService.await() }
} catch (e: TimeoutException) { } catch (e: TimeoutCancellationException) {
Timber.e(e, "Timed out getting TunnelService") Timber.e(e, "Timed out getting TunnelService")
throw BackendException.InternalError("Failed to get TunnelService") throw BackendException.InternalError("Failed to get TunnelService")
} }
} }
fun stopVpnService() { @OptIn(ExperimentalCoroutinesApi::class)
val service = vpnService.getNow(null) ?: return suspend fun stopTunnelService() {
val service =
if (tunnelService.isCompleted && !tunnelService.isCancelled) {
tunnelService.getCompleted()
} else return
Timber.d("Stopping VpnService") tunnelServiceDestroyed = CompletableDeferred()
ProxyBackend.setSocketProtector(null)
service.stopSelf() service.stopSelf()
tunnelService = CompletableDeferred()
withTimeoutOrNull(1_000L.milliseconds) { tunnelServiceDestroyed.await() }
} }
fun stopTunnelService() { @OptIn(ExperimentalCoroutinesApi::class)
val service = tunnelService.getNow(null) ?: return suspend fun stopVpnService() {
val service =
if (vpnService.isCompleted && !vpnService.isCancelled) {
vpnService.getCompleted()
} else return
Timber.d("Stopping TunnelService") vpnServiceDestroyed = CompletableDeferred()
service.stopSelf() service.stopSelf()
} vpnService = CompletableDeferred()
withTimeoutOrNull(1_000L.milliseconds) { vpnServiceDestroyed.await() }
@OptIn(ExperimentalAtomicApi::class)
fun ensureNativeCallbacksRegistered() {
if (!nativeCallbacksRegistered.compareAndSet(expectedValue = false, newValue = true)) {
return
}
VpnBackend.setStatusCallback(statusCallback)
Timber.d("Registered native status callback")
}
@OptIn(ExperimentalAtomicApi::class)
fun maybeUnregisterNativeCallbacks() {
val vpnAlive = vpnService.getNow(null) != null
val tunnelAlive = tunnelService.getNow(null) != null
if (vpnAlive || tunnelAlive) {
return
}
if (!nativeCallbacksRegistered.compareAndSet(expectedValue = true, newValue = false)) {
return
}
VpnBackend.setStatusCallback(null)
Timber.d("Unregistered native status callback")
}
fun clear(service: VpnService) {
if (vpnService.getNow(null) === service) {
vpnService = CompletableFuture()
}
maybeUnregisterNativeCallbacks()
}
fun clear(service: TunnelService) {
if (tunnelService.getNow(null) === service) {
tunnelService = CompletableFuture()
}
maybeUnregisterNativeCallbacks()
} }
companion object { companion object {
const val DEFAULT_MTU = 1280 const val DEFAULT_MTU = 1280
// for consumer to set AOVPN callback // for consumer to set AOVPN callback
var alwaysOnCallback: WeakReference<VpnService.AlwaysOnCallback>? = null var alwaysOnCallback: WeakReference<VpnService.AlwaysOnCallback>? = null
@Volatile var vpnService = CompletableFuture<VpnService>()
@Volatile var tunnelService = CompletableFuture<TunnelService>()
} }
} }
@@ -1,784 +0,0 @@
package com.zaneschepke.tunnel.backend
import com.zaneschepke.networkmonitor.StableNetworkEngine
import com.zaneschepke.tunnel.DnsConfigManager
import com.zaneschepke.tunnel.Tunnel
import com.zaneschepke.tunnel.event.ActorEvent
import com.zaneschepke.tunnel.event.ActorEvent.ActiveConfigUpdated
import com.zaneschepke.tunnel.event.ActorEvent.BootstrapConfigUpdated
import com.zaneschepke.tunnel.event.ActorEvent.BootstrapStateChanged
import com.zaneschepke.tunnel.event.ActorEvent.EngineStatus
import com.zaneschepke.tunnel.event.ActorEvent.KillSwitchStateChanged
import com.zaneschepke.tunnel.event.ActorEvent.PeersUpdated
import com.zaneschepke.tunnel.event.ActorEvent.ResolvedPeersApplied
import com.zaneschepke.tunnel.event.ActorEvent.TunnelStarted
import com.zaneschepke.tunnel.event.ActorEvent.TunnelStopped
import com.zaneschepke.tunnel.event.TunnelEvent
import com.zaneschepke.tunnel.event.TunnelEvent.NoRootShellAccess
import com.zaneschepke.tunnel.model.BackendMode
import com.zaneschepke.tunnel.model.DnsBootstrapResult
import com.zaneschepke.tunnel.model.PublicKey
import com.zaneschepke.tunnel.model.RunningTunnel
import com.zaneschepke.tunnel.model.TunnelCommand
import com.zaneschepke.tunnel.state.ActiveTunnel
import com.zaneschepke.tunnel.state.ActorState
import com.zaneschepke.tunnel.state.BootstrapState
import com.zaneschepke.tunnel.state.NativeTunnelStatus
import com.zaneschepke.tunnel.state.TunnelRuntimeState
import com.zaneschepke.tunnel.util.RootShellException
import com.zaneschepke.tunnel.util.buildResolvedPeers
import com.zaneschepke.tunnel.util.exponentialBackoffForever
import com.zaneschepke.wireguardautotunnel.parser.ActiveConfig
import kotlin.reflect.KClass
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.supervisorScope
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeout
import timber.log.Timber
internal class TunnelActor(
private val scope: CoroutineScope,
private val engine: TunnelEngine,
private val stableNetworkEngine: StableNetworkEngine,
) {
private val inbox = Channel<TunnelCommand>(Channel.UNLIMITED)
// track running hooks to prevent service shutdown until post down hooks complete
private val _runningPostDownHooks = MutableStateFlow(0)
val runningHooks = _runningPostDownHooks.asStateFlow()
private val _state =
MutableStateFlow(ActorState(byTunnelId = emptyMap(), byHandle = emptyMap()))
val state: StateFlow<ActorState> = _state.asStateFlow()
private val _events = MutableSharedFlow<TunnelEvent>(extraBufferCapacity = 32)
val events = _events.asSharedFlow()
private val tunnelJobs = mutableMapOf<Int, Job>()
init {
scope.launch {
engine.status.distinctUntilChanged().collect { status ->
when (status.code) {
NativeTunnelStatus.NativeTunnelStatusCode.STOPPED -> {
val tunnelId = _state.value.byHandle[status.handle] ?: return@collect
stopTunnel(tunnelId, status.handle)
}
NativeTunnelStatus.NativeTunnelStatusCode.HEALTHY,
NativeTunnelStatus.NativeTunnelStatusCode.HANDSHAKE_FAILURE -> {
apply(EngineStatus(status))
}
}
}
}
scope.launch {
for (cmd in inbox) {
try {
when (cmd) {
is TunnelCommand.Start -> {
val result = engine.start(cmd.tunnel, cmd.mode)
apply(TunnelStarted(result, cmd))
val runtime = _state.value.byTunnelId[result.tunnelId] ?: continue
val job =
startTunnelJobs(
tunnelId = result.tunnelId,
runtime = runtime,
removedPeerEndpoint = result.removedPeerEndpoint,
)
tunnelJobs[result.tunnelId] = job
job.invokeOnCompletion { tunnelJobs.remove(result.tunnelId, job) }
}
is TunnelCommand.Stop -> {
val runtime = _state.value.byTunnelId[cmd.tunnelId] ?: continue
engine.stop(runtime.running.handle, runtime.running.mode)
}
is TunnelCommand.SetBootstrapConfig -> {
apply(BootstrapConfigUpdated(cmd.config))
}
is TunnelCommand.UpdatePeers -> {
val runtime = _state.value.byTunnelId[cmd.tunnelId] ?: continue
val running = runtime.running
val peers = running.buildResolvedPeers(preferIpv6 = cmd.preferIpv6)
engine.updatePeers(
handle = running.handle,
mode = running.mode,
peers = peers,
)
apply(
PeersUpdated(
tunnelId = cmd.tunnelId,
peers = peers,
preferIpv6 = cmd.preferIpv6,
)
)
}
is TunnelCommand.ApplyResolvedPeers -> {
val runtime = _state.value.byTunnelId[cmd.tunnelId] ?: continue
val running = runtime.running
engine.updatePeers(
handle = running.handle,
mode = running.mode,
peers = cmd.peers,
)
apply(
ResolvedPeersApplied(
tunnelId = cmd.tunnelId,
cache = cmd.cache,
peers = cmd.peers,
)
)
}
is TunnelCommand.UpdateActiveConfig -> {
val runtime = _state.value.byTunnelId[cmd.tunnelId] ?: continue
val running = runtime.running
val activeConfig = engine.getActiveConfig(running.handle, running.mode)
if (runtime.active.activeConfig == activeConfig) {
continue
}
apply(
ActiveConfigUpdated(
tunnelId = cmd.tunnelId,
activeConfig = activeConfig,
)
)
}
is TunnelCommand.SetBootstrapState -> {
apply(
BootstrapStateChanged(
tunnelId = cmd.tunnelId,
bootstrapState = cmd.state,
)
)
}
is TunnelCommand.UpdateKillSwitch -> {
apply(KillSwitchStateChanged(cmd.enabled))
}
is TunnelCommand.RunHook -> {
val isPostDown = cmd.phase == TunnelCommand.RunHook.Phase.PostDown
if (isPostDown) {
_runningPostDownHooks.update { it + 1 }
}
try {
cmd.cmds?.forEach { cmd ->
withTimeout(3_000.milliseconds) {
withContext(Dispatchers.IO) { RootShell.run(cmd) }
}
}
} catch (t: Throwable) {
Timber.w(t, "Root shell commands failed")
if (t is RootShellException.NoRootAccess) {
_events.emit(NoRootShellAccess(tunnelId = cmd.tunnelId))
}
} finally {
if (isPostDown) {
_runningPostDownHooks.update { (it - 1).coerceAtLeast(0) }
}
}
}
}
} catch (t: Throwable) {
Timber.e(t, "Tunnel command failed: $cmd")
}
}
}
}
suspend fun send(cmd: TunnelCommand) {
inbox.send(cmd)
}
private fun apply(event: ActorEvent) {
_state.value = reduce(_state.value, event)
}
private fun startTunnelJobs(
tunnelId: Int,
runtime: TunnelRuntimeState,
removedPeerEndpoint: Boolean,
): Job {
return scope.launch {
supervisorScope {
val running = runtime.running
if (!running.mode.config.peers.all { it.isStaticallyConfigured }) {
startDnsBootstrapJob(tunnelId)
}
if (removedPeerEndpoint) {
when (val strategy = running.tunnel.ipStrategy) {
Tunnel.IpStrategy.Ipv4Only -> Unit
is Tunnel.IpStrategy.PreferIpv6 -> {
if (strategy.recoveryEnabled || strategy.fallbackToIpv4Enabled) {
startIpv6Job(tunnelId, strategy)
}
}
}
}
running.tunnel.features.forEach { feature ->
when (feature) {
is Tunnel.Feature.ActiveConfigMonitor -> {
startActiveConfigJob(tunnelId, feature.intervalSeconds)
}
Tunnel.Feature.DynamicDNS -> {
startDynamicDnsJob(tunnelId)
}
}
}
awaitCancellation()
}
}
}
private fun stopTunnel(tunnelId: Int, handle: Int) {
tunnelJobs.remove(tunnelId)?.cancel()
apply(TunnelStopped(tunnelId, handle))
}
private fun CoroutineScope.startDnsBootstrapJob(tunnelId: Int) = launch {
send(TunnelCommand.SetBootstrapState(tunnelId, BootstrapState.ResolvingDns))
val runtime = state.value.byTunnelId[tunnelId] ?: return@launch
val running = runtime.running
val cache = resolvePeers(running)
ensureActive()
val updatedRunning = running.copy(peerBootstrapCache = cache)
val networkHasIpv6 = stableNetworkEngine.stableState.value?.state?.hasIpv6 ?: false
val peers =
updatedRunning.buildResolvedPeers(
preferIpv6 = running.currentPreferIpv6 && networkHasIpv6
)
send(TunnelCommand.SetBootstrapState(tunnelId, BootstrapState.UpdatingPeers))
send(TunnelCommand.ApplyResolvedPeers(tunnelId = tunnelId, cache = cache, peers = peers))
send(TunnelCommand.SetBootstrapState(tunnelId, BootstrapState.Complete))
}
private fun CoroutineScope.startDynamicDnsJob(tunnelId: Int) = launch {
val controller =
DynamicDnsController(
stabilityWindowMs = DDNS_STABILITY_WINDOW,
failureWindowMs = DDNS_FAILURE_WINDOW,
minCheckIntervalMs = DDNS_MIN_CHECK_INTERVAL,
)
combine(
stableNetworkEngine.stableState.filterNotNull(),
state.mapNotNull { it.byTunnelId[tunnelId]?.active },
) { stable, activeTunnel ->
stable to activeTunnel
}
.collect { (stable, activeTunnel) ->
val runtime = state.value.byTunnelId[tunnelId] ?: return@collect
if (!stable.state.hasInternet()) return@collect
val now = System.currentTimeMillis()
val isHealthy = activeTunnel.transportState is Tunnel.State.Up.Healthy
val isHandshakeFailure =
activeTunnel.transportState is Tunnel.State.Up.HandshakeFailure
if (!controller.shouldCheck(now, isHealthy, isHandshakeFailure)) return@collect
// Fresh DNS resolve
val freshDns = resolvePeers(runtime.running)
if (freshDns.isEmpty()) {
controller.markChecked(now)
return@collect
}
ensureActive()
// Query live state from WireGuard UAPI
val activeConfig =
try {
engine.getActiveConfig(runtime.running.handle, runtime.running.mode)
} catch (t: Throwable) {
Timber.w(t, "UAPI query failed during DDNS check")
controller.markChecked(now)
return@collect
}
val mismatches = findEndpointMismatches(freshDns, activeConfig, runtime.running)
if (mismatches.isEmpty()) {
controller.markChecked(now)
return@collect
}
controller.markChecked(now)
Timber.i("Dynamic DNS drift detected for peers: $mismatches — updating")
_events.emit(
TunnelEvent.DynamicDnsUpdate(tunnelId = tunnelId, changedPeers = mismatches)
)
val latestRuntime = state.value.byTunnelId[tunnelId] ?: return@collect
val updatedCache = latestRuntime.running.peerBootstrapCache + freshDns
send(
TunnelCommand.ApplyResolvedPeers(
tunnelId = tunnelId,
cache = updatedCache,
peers =
latestRuntime.running
.copy(peerBootstrapCache = updatedCache)
.buildResolvedPeers(
preferIpv6 = latestRuntime.running.currentPreferIpv6
),
)
)
}
}
private fun findEndpointMismatches(
freshDns: Map<PublicKey, DnsBootstrapResult>,
activeConfig: ActiveConfig?,
running: RunningTunnel,
): List<PublicKey> {
if (activeConfig == null) return emptyList()
val currentEndpoints = activeConfig.peers.associateBy { it.publicKey }
return freshDns.mapNotNull { (pubKey, dnsResult) ->
val current = currentEndpoints[pubKey] ?: return@mapNotNull null
val currentEndpoint = current.endpoint ?: return@mapNotNull null
val normalizedCurrent = normalizeEndpointForComparison(currentEndpoint)
// Choose which fresh address to compare against
val freshAddress =
if (running.currentPreferIpv6 && dnsResult.ipv6.isNotEmpty()) {
dnsResult.ipv6.first()
} else {
dnsResult.ipv4.firstOrNull() ?: dnsResult.ipv6.firstOrNull()
} ?: return@mapNotNull null
if (freshAddress != normalizedCurrent) pubKey else null
}
}
/** Normalizes an endpoint string so IPv6 addresses have brackets. */
private fun normalizeEndpointForComparison(endpoint: String): String {
val host = endpoint.substringBeforeLast(":")
val port = endpoint.substringAfterLast(":")
return if (host.contains(":")) {
// Looks like IPv6
if (host.startsWith("[")) endpoint else "[$host]:$port"
} else {
endpoint
}
}
private fun CoroutineScope.startIpv6Job(tunnelId: Int, strategy: Tunnel.IpStrategy.PreferIpv6) =
launch {
var currentNetworkKey: String? = null
var hasRecoveredOnThisNetwork = false
var hasFallenBackOnThisNetwork = false
var healthySinceMs: Long? = null
var failureCount = 0
var firstFailureTime = 0L
var ipv6Bad = false
combine(
stableNetworkEngine.stableState.filterNotNull(),
state.mapNotNull { it.byTunnelId[tunnelId]?.active },
) { stable, activeTunnel ->
val newKey = stable.key
if (newKey != currentNetworkKey) {
currentNetworkKey = newKey
hasRecoveredOnThisNetwork = false
hasFallenBackOnThisNetwork = false
healthySinceMs = null
failureCount = 0
firstFailureTime = 0L
ipv6Bad = false
Timber.d("Stable network changed resetting IPv6 state ($newKey)")
}
val now = System.currentTimeMillis()
val isUsingIpv6 =
activeTunnel.activeConfig?.peers?.any {
it.endpoint?.startsWith("[") == true
} ?: false
val isHealthy = activeTunnel.transportState is Tunnel.State.Up.Healthy
val isHandshakeFailure =
activeTunnel.transportState is Tunnel.State.Up.HandshakeFailure
healthySinceMs = if (isHealthy) healthySinceMs ?: now else null
val healthyDuration = healthySinceMs?.let { now - it } ?: 0L
Timber.d(
"IPv6 strategy | net=$newKey | usingIPv6=$isUsingIpv6 | healthy=$isHealthy | healthyDuration=${healthyDuration}ms | hasRecovered=$hasRecoveredOnThisNetwork | hasFallback=$hasFallenBackOnThisNetwork | hasIPv6=${stable.state.hasIpv6} | ipv6Bad=$ipv6Bad | state=${activeTunnel.transportState}"
)
if (!isHandshakeFailure) {
failureCount = 0
firstFailureTime = 0L
}
// Fallback IPv6 to IPv4
if (
strategy.fallbackToIpv4Enabled &&
isHandshakeFailure &&
isUsingIpv6 &&
!hasFallenBackOnThisNetwork
) {
if (failureCount == 0) firstFailureTime = now
failureCount++
val failureDuration = now - firstFailureTime
Timber.d(
"IPv6 strategy | Fallback check: failureCount=$failureCount duration=${failureDuration}ms"
)
if (
failureCount >= IPV4_FALLBACK_FAILURE_COUNT &&
failureDuration >= IPV4_FALLBACK_FAILURE_DURATION
) {
hasFallenBackOnThisNetwork = true
ipv6Bad = true
Timber.d("Fallback to IPv4 triggered on $newKey (marking IPv6 bad)")
_events.emit(TunnelEvent.FallbackToIpv4(tunnelId))
send(TunnelCommand.UpdatePeers(tunnelId, preferIpv6 = false))
}
}
// Recovery IPv4 to IPv6
if (
strategy.recoveryEnabled &&
!isUsingIpv6 &&
!hasRecoveredOnThisNetwork &&
healthySinceMs != null &&
stable.state.hasIpv6 &&
!ipv6Bad
) {
Timber.d(
"IPv6 strategy | Recovery check: healthy for ${healthyDuration}ms (need >= ${RECOVERY_STABILITY_WINDOW}ms)"
)
if (healthyDuration >= RECOVERY_STABILITY_WINDOW) {
hasRecoveredOnThisNetwork = true
Timber.d(
"Recovered to IPv6 on $newKey (healthy for ${healthyDuration}ms)"
)
_events.emit(TunnelEvent.RecoveredToIpv6(tunnelId))
send(TunnelCommand.UpdatePeers(tunnelId, preferIpv6 = true))
}
}
}
.collect {}
}
private fun CoroutineScope.startActiveConfigJob(tunnelId: Int, interval: Int) = launch {
while (isActive) {
send(TunnelCommand.UpdateActiveConfig(tunnelId))
delay(interval.seconds)
}
}
private fun reduce(state: ActorState, event: ActorEvent): ActorState {
return when (event) {
is EngineStatus -> {
val tunnelId = state.byHandle[event.status.handle] ?: return state
val runtime = state.byTunnelId[tunnelId] ?: return state
val newTransportState =
when (event.status.code) {
NativeTunnelStatus.NativeTunnelStatusCode.HEALTHY -> Tunnel.State.Up.Healthy
NativeTunnelStatus.NativeTunnelStatusCode.HANDSHAKE_FAILURE ->
Tunnel.State.Up.HandshakeFailure
NativeTunnelStatus.NativeTunnelStatusCode.STOPPED ->
return state // should never happen
}
val now = System.currentTimeMillis()
val updatedActive =
runtime.active.copy(
transportState = newTransportState,
lastStateChangeMs = now,
lastHealthChangeMs =
if (newTransportState is Tunnel.State.Up.Healthy) {
now
} else {
runtime.active.lastHealthChangeMs
},
)
state.copy(
byTunnelId =
state.byTunnelId + (tunnelId to runtime.copy(active = updatedActive))
)
}
is TunnelStarted -> {
val result = event.result
val cmd = event.cmd
val running =
RunningTunnel(
handle = result.handle,
interfaceName = result.interfaceName,
mode = result.mode,
tunnel = cmd.tunnel,
currentPreferIpv6 = cmd.tunnel.ipStrategy is Tunnel.IpStrategy.PreferIpv6,
)
val runtime =
TunnelRuntimeState(
running = running,
active =
ActiveTunnel(
transportState = Tunnel.State.Starting,
interfaceName = result.interfaceName,
mode = result.mode,
uptime = System.currentTimeMillis(),
activeConfig = null,
),
)
state.copy(
byTunnelId = state.byTunnelId + (result.tunnelId to runtime),
byHandle = state.byHandle + (result.handle to result.tunnelId),
)
}
is TunnelStopped -> {
state.copy(
byTunnelId = state.byTunnelId - event.tunnelId,
byHandle = state.byHandle - event.handle,
)
}
is PeersUpdated -> {
val runtime = state.byTunnelId[event.tunnelId] ?: return state
val now = System.currentTimeMillis()
val updatedActive = runtime.active.copy(lastPeerUpdateMs = now)
val updatedRunning =
runtime.running.copy(
currentPreferIpv6 = event.preferIpv6,
resolvedPeers = event.peers,
)
state.copy(
byTunnelId =
state.byTunnelId +
(event.tunnelId to
runtime.copy(running = updatedRunning, active = updatedActive))
)
}
is ResolvedPeersApplied -> {
val runtime = state.byTunnelId[event.tunnelId] ?: return state
val running = runtime.running
val now = System.currentTimeMillis()
val updatedActive = runtime.active.copy(lastPeerUpdateMs = now)
val updatedRunning =
running.copy(resolvedPeers = event.peers, peerBootstrapCache = event.cache)
state.copy(
byTunnelId =
state.byTunnelId +
(event.tunnelId to
runtime.copy(running = updatedRunning, active = updatedActive))
)
}
is ActiveConfigUpdated -> {
val runtime = state.byTunnelId[event.tunnelId] ?: return state
val updated =
runtime.copy(active = runtime.active.copy(activeConfig = event.activeConfig))
state.copy(byTunnelId = state.byTunnelId + (event.tunnelId to updated))
}
is BootstrapStateChanged -> {
val runtime = state.byTunnelId[event.tunnelId] ?: return state
val updated =
runtime.copy(
active = runtime.active.copy(bootstrapState = event.bootstrapState)
)
state.copy(byTunnelId = state.byTunnelId + (event.tunnelId to updated))
}
is KillSwitchStateChanged -> {
state.copy(killSwitchEnabled = event.enabled)
}
is BootstrapConfigUpdated -> {
state.copy(dnsConfig = event.config)
}
}
}
fun emergencyStop(tunnelId: Int) {
val runtime = _state.value.byTunnelId[tunnelId] ?: return
val handle = runtime.running.handle
val mode = runtime.running.mode
Timber.d("Emergency stop tunnel $tunnelId (handle=$handle, mode=$mode)")
engine.stop(handle, mode)
// Immediately clean up actor state
stopTunnel(tunnelId, handle)
}
// Convenience method for services
fun emergencyStopAllOfType(modeClass: KClass<out BackendMode>) {
_state.value.byTunnelId
.filter { (_, runtime) -> modeClass.isInstance(runtime.running.mode) }
.keys
.forEach { emergencyStop(it) }
}
suspend fun resolvePeers(runningTunnel: RunningTunnel): Map<PublicKey, DnsBootstrapResult> {
val peersToResolve = runningTunnel.mode.config.peers.filter { !it.isStaticallyConfigured }
if (peersToResolve.isEmpty()) return emptyMap()
val results = mutableMapOf<PublicKey, DnsBootstrapResult>()
exponentialBackoffForever {
val bypassNeeded =
runningTunnel.mode is BackendMode.Vpn || state.value.killSwitchEnabled
Timber.d("Peer resolution attempt (resolved=${results.size}/${peersToResolve.size})")
for (peer in peersToResolve) {
// already resolved
if (results.containsKey(peer.publicKey)) continue
val endpoint = peer.endpoint ?: continue
val host = endpoint.substringBeforeLast(":")
val dnsConfig = state.value.dnsConfig
val dnsResult =
try {
DnsConfigManager.resolveHostBootstrap(
host = host,
dnsConfig.protocol,
dnsConfig.upstream,
bypass = bypassNeeded,
)
} catch (e: Exception) {
Timber.w(e, "DNS failed for $host")
continue
}
if (dnsResult.ipv4.isEmpty() && dnsResult.ipv6.isEmpty()) {
Timber.w("No IPs for $host")
continue
}
results[peer.publicKey] =
dnsResult.copy(
ipv4 = dnsResult.ipv4,
// normalize
ipv6 = dnsResult.ipv6.map { "[$it]" },
)
Timber.d("Resolved $host to ${results[peer.publicKey]}")
}
// exit
if (results.size == peersToResolve.size) {
return@exponentialBackoffForever
}
// force retry
throw IllegalStateException("Incomplete resolution, retrying...")
}
return results
}
companion object {
private const val DDNS_MIN_CHECK_INTERVAL = 30_000L
private const val DDNS_FAILURE_WINDOW = 15_000L
private const val DDNS_STABILITY_WINDOW = 15_000L
private const val IPV4_FALLBACK_FAILURE_COUNT = 4
private const val IPV4_FALLBACK_FAILURE_DURATION = 10_000L
private const val RECOVERY_STABILITY_WINDOW = 5_000L
}
}
@@ -4,36 +4,62 @@ import com.zaneschepke.networkmonitor.ActiveNetwork
import com.zaneschepke.networkmonitor.DnsInfo import com.zaneschepke.networkmonitor.DnsInfo
import com.zaneschepke.networkmonitor.NetworkMonitor import com.zaneschepke.networkmonitor.NetworkMonitor
import com.zaneschepke.networkmonitor.PrivateDnsMode import com.zaneschepke.networkmonitor.PrivateDnsMode
import com.zaneschepke.networkmonitor.StableNetworkEngine
import com.zaneschepke.tunnel.DnsConfigManager
import com.zaneschepke.tunnel.NotificationProvider import com.zaneschepke.tunnel.NotificationProvider
import com.zaneschepke.tunnel.ProxyBackend import com.zaneschepke.tunnel.StatusCallback
import com.zaneschepke.tunnel.Tunnel import com.zaneschepke.tunnel.Tunnel
import com.zaneschepke.tunnel.VpnBackend
import com.zaneschepke.tunnel.event.TunnelEvent import com.zaneschepke.tunnel.event.TunnelEvent
import com.zaneschepke.tunnel.model.BackendMode import com.zaneschepke.tunnel.model.BackendMode
import com.zaneschepke.tunnel.model.DnsBoostrapConfig import com.zaneschepke.tunnel.model.DnsBoostrapConfig
import com.zaneschepke.tunnel.model.DnsBoostrapMode import com.zaneschepke.tunnel.model.DnsBoostrapMode
import com.zaneschepke.tunnel.model.DnsBootstrapResult
import com.zaneschepke.tunnel.model.Host
import com.zaneschepke.tunnel.model.KillSwitchConfig import com.zaneschepke.tunnel.model.KillSwitchConfig
import com.zaneschepke.tunnel.model.TunnelCommand import com.zaneschepke.tunnel.model.PublicKey
import com.zaneschepke.tunnel.service.VpnService import com.zaneschepke.tunnel.service.VpnService
import com.zaneschepke.tunnel.state.ActiveTunnel
import com.zaneschepke.tunnel.state.BackendStatus import com.zaneschepke.tunnel.state.BackendStatus
import com.zaneschepke.tunnel.state.BootstrapState
import com.zaneschepke.tunnel.state.EngineStartResult
import com.zaneschepke.tunnel.state.KillSwitchState import com.zaneschepke.tunnel.state.KillSwitchState
import com.zaneschepke.tunnel.state.RuntimeDnsConfig import com.zaneschepke.tunnel.state.RuntimeDnsConfig
import com.zaneschepke.tunnel.util.BackendException import com.zaneschepke.tunnel.util.RootShellException
import com.zaneschepke.tunnel.util.buildResolvedPeers
import com.zaneschepke.tunnel.util.exponentialBackoffForever
import com.zaneschepke.tunnel.util.isLastTunnelOfServiceType
import com.zaneschepke.tunnel.util.toHostMap
import com.zaneschepke.wireguardautotunnel.parser.ActiveConfig
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import kotlin.reflect.KClass import java.util.concurrent.ConcurrentHashMap
import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.delay
import kotlinx.coroutines.ensureActive import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChangedBy import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.supervisorScope
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeout
import kotlinx.coroutines.withTimeoutOrNull import kotlinx.coroutines.withTimeoutOrNull
import org.koin.java.KoinJavaComponent.inject import org.koin.java.KoinJavaComponent.inject
import timber.log.Timber import timber.log.Timber
@@ -42,10 +68,11 @@ class TunnelBackend(
private val scope: CoroutineScope, private val scope: CoroutineScope,
private val networkMonitor: NetworkMonitor, private val networkMonitor: NetworkMonitor,
override val notificationProvider: NotificationProvider, override val notificationProvider: NotificationProvider,
private val stableNetworkEngine: StableNetworkEngine,
) : Backend { ) : Backend {
private val serviceHolder: ServiceHolder by inject(ServiceHolder::class.java) private val serviceHolder: ServiceHolder by inject(ServiceHolder::class.java)
private val actor: TunnelActor by inject(TunnelActor::class.java) private val engine: TunnelEngine by inject(TunnelEngine::class.java)
private val _status = MutableStateFlow(BackendStatus()) private val _status = MutableStateFlow(BackendStatus())
override val status: Flow<BackendStatus> = _status.asStateFlow() override val status: Flow<BackendStatus> = _status.asStateFlow()
@@ -53,100 +80,98 @@ class TunnelBackend(
private val _events = MutableSharedFlow<TunnelEvent>(extraBufferCapacity = 32) private val _events = MutableSharedFlow<TunnelEvent>(extraBufferCapacity = 32)
override val events = _events.asSharedFlow() override val events = _events.asSharedFlow()
private val tunnelMutex = Mutex()
private val tunnelJobs = ConcurrentHashMap<Int, Job>()
private val byHandle = ConcurrentHashMap<Int, Int>()
private val byTunnelId = ConcurrentHashMap<Int, Int>()
private val peerUpdateMutexes = ConcurrentHashMap<Int, Mutex>()
enum class PeerUpdateReason {
DDNS_CHECK,
IPV4_FALLBACK,
IPV6_RECOVERY,
NETWORK_CHANGE_RESET,
}
private var dnsConfigJob: Job? = null private var dnsConfigJob: Job? = null
init { private val statusCallback = StatusCallback { handle, code ->
scope.launch { val state = Tunnel.State.fromNative(code)
var hadVpnTunnels = false state?.let { nativeState ->
var hadProxyTunnels = false val tunnelId = byHandle[handle] ?: return@let
updateTunnelTransportState(tunnelId, nativeState)
}
}
var lastActiveTunnelIds: Set<Int> = emptySet() override suspend fun start(tunnel: Tunnel, mode: BackendMode): Result<Unit> =
tunnelMutex.withLock {
runCatching {
if (_status.value.activeTunnels.containsKey(tunnel.id)) {
Timber.d("Tunnel ${tunnel.id} already running — ignoring start")
return@runCatching
}
actor.state.collect { actorState -> val isFirst = _status.value.activeTunnels.isEmpty()
val hasVpnNow =
actorState.byTunnelId.values.any { it.running.mode is BackendMode.Vpn }
val hasProxyNow = addOrReplaceActiveTunnel(
actorState.byTunnelId.values.any { it.running.mode is BackendMode.Proxy } tunnel.id,
ActiveTunnel(
val activeTunnels = actorState.byTunnelId.mapValues { it.value.active } tunnel = tunnel,
transportState = Tunnel.State.Starting,
_status.update { current -> current.copy(activeTunnels = activeTunnels) } mode = mode,
),
val currentTunnelIds = activeTunnels.keys )
// update tile
if (currentTunnelIds != lastActiveTunnelIds) {
notificationProvider.refreshTile(serviceHolder.context) notificationProvider.refreshTile(serviceHolder.context)
lastActiveTunnelIds = currentTunnelIds
val scriptsEnabled = tunnel.scriptsEnabled
if (isFirst) VpnBackend.setStatusCallback(statusCallback)
if (scriptsEnabled)
mode.config.`interface`.preUp?.let { runScripts(it, tunnel.id) }
val result = engine.start(tunnel, mode)
onEngineStartResult(tunnel.id, result)
if (scriptsEnabled)
mode.config.`interface`.postUp?.let { runScripts(it, tunnel.id) }
tunnelJobs[result.tunnelId] =
startTunnelJobs(result.handle, tunnel, mode, result.removedPeerEndpoint)
} }
.onFailure { cleanup(tunnel.id) }
}
// VPN cleanup private fun onEngineStartResult(tunnelId: Int, result: EngineStartResult) {
if (hadVpnTunnels && !hasVpnNow) { updateActiveTunnel(tunnelId) {
it.copy(interfaceName = result.interfaceName, uptime = System.currentTimeMillis())
}
byHandle[result.handle] = tunnelId
byTunnelId[tunnelId] = result.handle
}
ensureActive() private fun cleanup(tunnelId: Int) {
actor.runningHooks.first { it == 0 } tunnelJobs.remove(tunnelId)?.cancel()
removeActiveTunnel(tunnelId)
byTunnelId[tunnelId]?.let { byHandle.remove(it) }
byTunnelId.remove(tunnelId)
peerUpdateMutexes.remove(tunnelId)
}
val latestState = actor.state.value private suspend fun runScripts(commands: List<String>, tunnelId: Int) {
val stillHasVpn = try {
latestState.byTunnelId.values.any { it.running.mode is BackendMode.Vpn } commands.forEach { cmd ->
withTimeout(3_000.milliseconds) {
if (!shouldKeepVpnServiceAlive(stillHasVpn)) { withContext(Dispatchers.IO) { RootShell.run(cmd) }
Timber.d("Stopping VPN service after hooks completed")
ProxyBackend.setSocketProtector(null)
serviceHolder.stopVpnService()
} else {
Timber.d("VPN shutdown aborted — state changed during hook wait")
}
} }
// Proxy cleanup
if (hadProxyTunnels && !hasProxyNow) {
ensureActive()
actor.runningHooks.first { it == 0 }
val latestState = actor.state.value
val stillHasProxy =
latestState.byTunnelId.values.any { it.running.mode is BackendMode.Proxy }
if (!stillHasProxy) {
Timber.d("Stopping tunnel service after hooks completed")
serviceHolder.stopTunnelService()
} else {
Timber.d("Proxy shutdown aborted — state changed during hook wait")
}
}
hadVpnTunnels = hasVpnNow
hadProxyTunnels = hasProxyNow
} }
} } catch (t: Throwable) {
} Timber.w(t, "Root shell commands failed")
if (t is RootShellException.NoRootAccess) {
private fun shouldKeepVpnServiceAlive(hasVpnTunnels: Boolean): Boolean { _events.emit(TunnelEvent.NoRootShellAccess(tunnelId = tunnelId))
return hasVpnTunnels || _status.value.killSwitch.enabled }
}
override suspend fun start(tunnel: Tunnel, mode: BackendMode): Result<Unit> = runCatching {
val existing = actor.state.value.byTunnelId[tunnel.id]
if (existing != null) {
Timber.d("Tunnel ${tunnel.id} already running — ignoring start")
return@runCatching
}
val scriptsEnabled = tunnel.scriptsEnabled
val preUp = mode.config.`interface`.preUp
if (!preUp.isNullOrEmpty() && scriptsEnabled) {
actor.send(TunnelCommand.RunHook(tunnel.id, TunnelCommand.RunHook.Phase.PreUp, preUp))
}
actor.send(TunnelCommand.Start(tunnel, mode))
val postUp = mode.config.`interface`.postUp
if (!postUp.isNullOrEmpty() && scriptsEnabled) {
actor.send(TunnelCommand.RunHook(tunnel.id, TunnelCommand.RunHook.Phase.PostUp, postUp))
} }
} }
@@ -154,36 +179,48 @@ class TunnelBackend(
ServiceHolder.alwaysOnCallback = WeakReference(alwaysOnCallback) ServiceHolder.alwaysOnCallback = WeakReference(alwaysOnCallback)
} }
override suspend fun stop(id: Int): Result<Unit> = runCatching { override suspend fun stop(id: Int): Result<Unit> = tunnelMutex.withLock {
// TODO need a clean localized message for this passed by provider, but this error should runCatching {
// never happen val activeTun = _status.value.activeTunnels[id] ?: return@runCatching
val runtime = val mode = activeTun.mode ?: return@runCatching
actor.state.value.byTunnelId[id] updateTunnelTransportState(id, Tunnel.State.Stopping)
?: throw BackendException.InternalError(
"Tunnel $id is not active or no longer exists"
)
val scriptsEnabled = runtime.running.tunnel.scriptsEnabled val isLast = _status.value.activeTunnels.size == 1
val mode = runtime.running.mode val isLastOfServiceType = _status.value.isLastTunnelOfServiceType(id)
val preDown = mode.config.`interface`.preDown try {
if (!preDown.isNullOrEmpty() && scriptsEnabled) { stopTunnelInternal(id, activeTun)
actor.send(TunnelCommand.RunHook(id, TunnelCommand.RunHook.Phase.PreDown, preDown)) } finally {
notificationProvider.refreshTile(serviceHolder.context)
if (isLast) VpnBackend.setStatusCallback(null)
if (isLastOfServiceType) {
when (mode) {
is BackendMode.Proxy.KillSwitchPrimary,
is BackendMode.Vpn -> serviceHolder.stopVpnService()
is BackendMode.Proxy.Standard -> serviceHolder.stopTunnelService()
}
}
}
} }
actor.send(TunnelCommand.Stop(id)) }
val postDown = mode.config.`interface`.postDown private suspend fun stopTunnelInternal(tunnelId: Int, activeTunnel: ActiveTunnel) {
if (!postDown.isNullOrEmpty() && scriptsEnabled) { updateTunnelTransportState(tunnelId, Tunnel.State.Stopping)
actor.send(TunnelCommand.RunHook(id, TunnelCommand.RunHook.Phase.PostDown, postDown)) val handle = byTunnelId[tunnelId] ?: return
val scriptsEnabled = activeTunnel.tunnel?.scriptsEnabled == true
val mode = activeTunnel.mode ?: return
try {
if (scriptsEnabled) mode.config.`interface`.preDown?.let { runScripts(it, tunnelId) }
engine.stop(handle, activeTunnel.mode)
if (scriptsEnabled) mode.config.`interface`.postDown?.let { runScripts(it, tunnelId) }
} finally {
cleanup(tunnelId)
} }
} }
override suspend fun setKillSwitch(config: KillSwitchConfig) = runCatching { override suspend fun setKillSwitch(config: KillSwitchConfig) = runCatching {
val service = serviceHolder.getVpnService() val service = serviceHolder.getVpnService()
service.setKillSwitch(config) service.setKillSwitch(config)
actor.send(TunnelCommand.UpdateKillSwitch(true))
_status.update { current -> _status.update { current ->
current.copy(killSwitch = current.killSwitch.copy(enabled = true, config = config)) current.copy(killSwitch = current.killSwitch.copy(enabled = true, config = config))
} }
@@ -192,9 +229,6 @@ class TunnelBackend(
override suspend fun disableKillSwitch() = runCatching { override suspend fun disableKillSwitch() = runCatching {
val service = serviceHolder.getVpnService() val service = serviceHolder.getVpnService()
service.setKillSwitch(null) service.setKillSwitch(null)
actor.send(TunnelCommand.UpdateKillSwitch(false))
_status.update { current -> _status.update { current ->
current.copy( current.copy(
killSwitch = killSwitch =
@@ -209,17 +243,14 @@ class TunnelBackend(
override suspend fun setBootstrapDnsMode(mode: DnsBoostrapMode) { override suspend fun setBootstrapDnsMode(mode: DnsBoostrapMode) {
_status.update { it.copy(dnsMode = mode) } _status.update { it.copy(dnsMode = mode) }
when (mode) { when (mode) {
is DnsBoostrapMode.Custom -> { is DnsBoostrapMode.Custom -> {
Timber.d( Timber.d(
"DNS Bootstrap mode set to custom: ${mode.config.protocol} -> ${mode.config.upstream}" "DNS Bootstrap mode set to custom: ${mode.config.protocol} -> ${mode.config.upstream}"
) )
dnsConfigJob?.cancel() dnsConfigJob?.cancel()
dnsConfigJob = null dnsConfigJob = null
updateRuntimeDnsConfig(mode.config)
actor.send(TunnelCommand.SetBootstrapConfig(RuntimeDnsConfig.from(mode.config)))
} }
DnsBoostrapMode.System -> { DnsBoostrapMode.System -> {
Timber.d("DNS Bootstrap mode set to System") Timber.d("DNS Bootstrap mode set to System")
@@ -229,6 +260,15 @@ class TunnelBackend(
} }
} }
override suspend fun stopAllActiveTunnels() = tunnelMutex.withLock {
_status.value.activeTunnels.forEach { (id, tunnel) -> stopTunnelInternal(id, tunnel) }
notificationProvider.refreshTile(serviceHolder.context)
VpnBackend.setStatusCallback(null)
serviceHolder.stopTunnelService()
serviceHolder.stopVpnService()
Result.success(Unit)
}
private suspend fun emitInitialSystemDnsConfig() { private suspend fun emitInitialSystemDnsConfig() {
val state = val state =
withTimeoutOrNull(2_500L.milliseconds) { withTimeoutOrNull(2_500L.milliseconds) {
@@ -244,20 +284,7 @@ class TunnelBackend(
val config = determineSystemDnsBoostrapConfig(dns) val config = determineSystemDnsBoostrapConfig(dns)
Timber.d("DNS initial emission: protocol=${config.protocol} upstream=${config.upstream}") Timber.d("DNS initial emission: protocol=${config.protocol} upstream=${config.upstream}")
updateRuntimeDnsConfig(config)
actor.send(TunnelCommand.SetBootstrapConfig(RuntimeDnsConfig.from(config)))
}
override fun emergencyStopAllOfTypeSync(modeClass: KClass<out BackendMode>) {
actor.emergencyStopAllOfType(modeClass)
_status.update { it.copy(activeTunnels = emptyMap()) }
notificationProvider.refreshTile(serviceHolder.context)
}
override suspend fun stopAllActiveTunnels(): Result<Unit> = runCatching {
_status.value.activeTunnels.forEach { (id, _) -> stop(id) }
_status.update { it.copy(activeTunnels = emptyMap()) }
notificationProvider.refreshTile(serviceHolder.context)
} }
private fun determineSystemDnsBoostrapConfig(dnsInfo: DnsInfo): DnsBoostrapConfig { private fun determineSystemDnsBoostrapConfig(dnsInfo: DnsInfo): DnsBoostrapConfig {
@@ -287,8 +314,7 @@ class TunnelBackend(
} }
private fun startSystemDnsMonitoring() { private fun startSystemDnsMonitoring() {
if (dnsConfigJob?.isActive == true) return dnsConfigJob?.cancel()
dnsConfigJob = scope.launch { dnsConfigJob = scope.launch {
networkMonitor.connectivityStateFlow networkMonitor.connectivityStateFlow
.distinctUntilChangedBy { it.underlyingDnsInfo } .distinctUntilChangedBy { it.underlyingDnsInfo }
@@ -302,9 +328,499 @@ class TunnelBackend(
) )
val config = determineSystemDnsBoostrapConfig(dns) val config = determineSystemDnsBoostrapConfig(dns)
updateRuntimeDnsConfig(config)
actor.send(TunnelCommand.SetBootstrapConfig(RuntimeDnsConfig.from(config)))
} }
} }
} }
private fun updateStatus(transform: (BackendStatus) -> BackendStatus) {
_status.update(transform)
}
private fun updateRuntimeDnsConfig(boostrapConfig: DnsBoostrapConfig) {
_status.update { current ->
current.copy(runtimeDnsConfig = RuntimeDnsConfig.from(boostrapConfig))
}
}
fun addOrReplaceActiveTunnel(id: Int, tunnel: ActiveTunnel) {
updateStatus { current ->
current.copy(activeTunnels = current.activeTunnels + (id to tunnel))
}
}
fun updateActiveTunnel(id: Int, transform: (ActiveTunnel) -> ActiveTunnel) {
updateStatus { current ->
val existing = current.activeTunnels[id] ?: return@updateStatus current
current.copy(activeTunnels = current.activeTunnels + (id to transform(existing)))
}
}
fun removeActiveTunnel(id: Int) {
updateStatus { current -> current.copy(activeTunnels = current.activeTunnels - id) }
}
fun updateTunnelTransportState(id: Int, newState: Tunnel.State) {
updateActiveTunnel(id) { tunnel ->
val stateChanged = tunnel.transportState != newState
tunnel.copy(
transportState = newState,
lastStateChangeMs =
if (stateChanged) {
System.currentTimeMillis()
} else {
tunnel.lastStateChangeMs
},
lastHealthChangeMs =
if (newState is Tunnel.State.Up.Healthy) {
if (stateChanged || tunnel.lastHealthChangeMs == 0L)
System.currentTimeMillis()
else tunnel.lastHealthChangeMs
} else {
tunnel.lastHealthChangeMs
},
)
}
}
fun updateTunnelBootstrapState(id: Int, newState: BootstrapState) {
updateActiveTunnel(id) { tunnel -> tunnel.copy(bootstrapState = newState) }
}
fun markPeerUpdate(id: Int) {
updateActiveTunnel(id) { tunnel ->
tunnel.copy(lastPeerUpdateMs = System.currentTimeMillis())
}
}
private fun startTunnelJobs(
handle: Int,
tunnel: Tunnel,
mode: BackendMode,
removedPeerEndpoint: Boolean,
): Job {
return scope.launch {
supervisorScope {
if (removedPeerEndpoint) {
updateTunnelBootstrapState(tunnel.id, BootstrapState.ResolvingDns)
startDnsBootstrapJob(handle, tunnel, mode)
}
if (removedPeerEndpoint) {
when (val strategy = tunnel.ipStrategy) {
Tunnel.IpStrategy.Ipv4Only -> Unit
is Tunnel.IpStrategy.PreferIpv6 -> {
if (strategy.recoveryEnabled || strategy.fallbackToIpv4Enabled) {
startIpv6Job(handle, tunnel.id, strategy)
}
}
}
}
tunnel.features.forEach { feature ->
when (feature) {
is Tunnel.Feature.ActiveConfigMonitor -> {
startActiveConfigJob(handle, tunnel.id, mode, feature.intervalSeconds)
}
Tunnel.Feature.DynamicDNS -> {
startDynamicDnsJob(handle, tunnel.id)
}
}
}
awaitCancellation()
}
}
}
private fun CoroutineScope.startDnsBootstrapJob(
handle: Int,
tunnel: Tunnel,
mode: BackendMode,
) = launch {
updateTunnelBootstrapState(tunnel.id, BootstrapState.ResolvingDns)
val resultMap = resolvePeers(mode)
ensureActive()
val networkHasIpv6 = stableNetworkEngine.stableState.value?.state?.hasIpv6 ?: false
val hostMap =
resultMap.toHostMap(
preferIpv6 = tunnel.ipStrategy is Tunnel.IpStrategy.PreferIpv6 && networkHasIpv6
)
val resolvedPeers = mode.config.buildResolvedPeers(hostMap)
updateTunnelBootstrapState(tunnel.id, BootstrapState.UpdatingPeers)
engine.updatePeers(handle, mode, resolvedPeers)
markPeerUpdate(tunnel.id)
updateTunnelBootstrapState(tunnel.id, BootstrapState.Complete)
}
suspend fun resolvePeers(mode: BackendMode): Map<PublicKey, DnsBootstrapResult> {
val peersToResolve = mode.config.peers.filter { !it.isStaticallyConfigured }
if (peersToResolve.isEmpty()) return emptyMap()
val results = mutableMapOf<PublicKey, DnsBootstrapResult>()
// Wait until we have internet before starting any resolution
stableNetworkEngine.stableState.first { it?.state?.hasInternet() == true }
exponentialBackoffForever {
// If we lose internet while inside the backoff loop, wait again until it comes back
if (stableNetworkEngine.stableState.value?.state?.hasInternet() != true) {
Timber.d("No internet — waiting for connectivity before next resolution attempt")
stableNetworkEngine.stableState.first { it?.state?.hasInternet() == true }
Timber.d("Internet restored — resuming peer resolution")
}
Timber.d("Peer resolution attempt (resolved=${results.size}/${peersToResolve.size})")
for (peer in peersToResolve) {
if (results.containsKey(peer.publicKey)) continue
val endpoint = peer.endpoint ?: continue
val host = endpoint.substringBeforeLast(":")
val dnsConfig = _status.value.runtimeDnsConfig
val bypassNeeded = mode is BackendMode.Vpn || _status.value.killSwitch.enabled
val dnsResult =
try {
DnsConfigManager.resolveHostBootstrap(
host = host,
protocol = dnsConfig.protocol,
upstream = dnsConfig.upstream,
bypass = bypassNeeded,
)
} catch (e: Exception) {
Timber.w(e, "DNS resolution failed for host=$host")
continue
}
if (dnsResult.ipv4.isEmpty() && dnsResult.ipv6.isEmpty()) {
Timber.w("No IP addresses returned for host=$host")
continue
}
results[peer.publicKey] =
dnsResult.copy(ipv4 = dnsResult.ipv4, ipv6 = dnsResult.ipv6.map { "[$it]" })
Timber.d("Successfully resolved $host${results[peer.publicKey]}")
}
if (results.size == peersToResolve.size) {
return@exponentialBackoffForever
}
throw IllegalStateException("Incomplete peer resolution, will retry with backoff")
}
return results
}
private fun CoroutineScope.startActiveConfigJob(
handle: Int,
tunnelId: Int,
mode: BackendMode,
interval: Int,
) = launch {
while (isActive) {
val activeConfig = engine.getActiveConfig(handle, mode)
updateActiveTunnel(tunnelId) { it.copy(activeConfig = activeConfig) }
delay(interval.seconds)
}
}
private fun CoroutineScope.startDynamicDnsJob(handle: Int, tunnelId: Int) = launch {
val controller =
DynamicDnsController(
stabilityWindowMs = DDNS_STABILITY_WINDOW,
failureWindowMs = DDNS_FAILURE_WINDOW,
minCheckIntervalMs = DDNS_MIN_CHECK_INTERVAL,
)
combine(
stableNetworkEngine.stableState.filterNotNull(),
status.mapNotNull { it.activeTunnels[tunnelId] },
) { stable, activeTunnel ->
stable to activeTunnel
}
.collect { (stable, activeTunnel) ->
if (!stable.state.hasInternet()) return@collect
val now = System.currentTimeMillis()
val isHealthy = activeTunnel.transportState is Tunnel.State.Up.Healthy
val isHandshakeFailure =
activeTunnel.transportState is Tunnel.State.Up.HandshakeFailure
if (!controller.shouldCheck(now, isHealthy, isHandshakeFailure)) return@collect
controller.markChecked(now)
val mode = activeTunnel.mode ?: return@collect
reconcilePeers(
tunnelId = tunnelId,
handle = handle,
mode = mode,
reason = PeerUpdateReason.DDNS_CHECK,
)
}
}
private fun findEndpointMismatches(
freshDns: Map<PublicKey, DnsBootstrapResult>,
activeConfig: ActiveConfig,
preferIpv6: Boolean,
): Map<PublicKey, Host> {
val currentEndpoints = activeConfig.peers.associateBy { it.publicKey }
return freshDns
.mapNotNull { (pubKey, dnsResult) ->
val current = currentEndpoints[pubKey] ?: return@mapNotNull null
val currentEndpoint = current.endpoint ?: return@mapNotNull null
val normalizedCurrent = normalizeEndpointForComparison(currentEndpoint)
val freshAddress =
if (preferIpv6 && dnsResult.ipv6.isNotEmpty()) {
dnsResult.ipv6.first()
} else {
dnsResult.ipv4.firstOrNull() ?: dnsResult.ipv6.firstOrNull()
} ?: return@mapNotNull null
if (freshAddress != normalizedCurrent) {
pubKey to freshAddress
} else {
null
}
}
.toMap()
}
private fun normalizeEndpointForComparison(endpoint: String): String {
val host = endpoint.substringBeforeLast(":")
val port = endpoint.substringAfterLast(":")
return if (host.contains(":")) {
// Looks like IPv6
if (host.startsWith("[")) endpoint else "[$host]:$port"
} else {
endpoint
}
}
private fun CoroutineScope.startIpv6Job(
handle: Int,
tunnelId: Int,
strategy: Tunnel.IpStrategy.PreferIpv6,
) = launch {
var currentNetworkKey: String? = null
var hasRecoveredOnThisNetwork = false
var hasFallenBackOnThisNetwork = false
var healthySinceMs: Long? = null
var failureCount = 0
var firstFailureTime = 0L
var ipv6Bad = false
combine(
stableNetworkEngine.stableState.filterNotNull(),
status.mapNotNull { it.activeTunnels[tunnelId] },
) { stable, activeTunnel ->
stable to activeTunnel
}
.collect { (stable, activeTunnel) ->
val newKey = stable.key
val mode = activeTunnel.mode ?: return@collect
// Reset state upon network transition
if (newKey != currentNetworkKey) {
// Capture the old state before we reset local variables
val wasFallenBack = activeTunnel.isFallenBackToIpv4ForNetwork
currentNetworkKey = newKey
hasRecoveredOnThisNetwork = false
hasFallenBackOnThisNetwork = false
healthySinceMs = null
failureCount = 0
firstFailureTime = 0L
ipv6Bad = false
Timber.d("Stable network changed resetting IPv6 state ($newKey)")
// Optimistically revert to IPv6 on a new network
if (wasFallenBack) {
Timber.d(
"Network changed while in IPv4 fallback. Attempting optimistic reset."
)
reconcilePeers(
tunnelId = tunnelId,
handle = handle,
mode = mode,
reason = PeerUpdateReason.NETWORK_CHANGE_RESET,
)
}
}
val now = System.currentTimeMillis()
// Determine if currently using IPv6
val isUsingIpv6 =
activeTunnel.activeConfig?.peers?.any { it.endpoint?.startsWith("[") == true }
?: mode.config.peers.any { it.endpoint?.startsWith("[") == true }
val isHealthy = activeTunnel.transportState is Tunnel.State.Up.Healthy
val isHandshakeFailure =
activeTunnel.transportState is Tunnel.State.Up.HandshakeFailure
healthySinceMs = if (isHealthy) healthySinceMs ?: now else null
val healthyDuration = healthySinceMs?.let { now - it } ?: 0L
if (!isHandshakeFailure) {
failureCount = 0
firstFailureTime = 0L
}
// Fallback
if (
strategy.fallbackToIpv4Enabled &&
isHandshakeFailure &&
isUsingIpv6 &&
!hasFallenBackOnThisNetwork
) {
if (failureCount == 0) firstFailureTime = now
failureCount++
val failureDuration = now - firstFailureTime
Timber.d(
"IPv6 strategy | Fallback check: failureCount=$failureCount duration=${failureDuration}ms"
)
if (
failureCount >= IPV4_FALLBACK_FAILURE_COUNT &&
failureDuration >= IPV4_FALLBACK_FAILURE_DURATION
) {
hasFallenBackOnThisNetwork = true
ipv6Bad = true
Timber.d("Fallback to IPv4 triggered on $newKey (marking IPv6 bad)")
reconcilePeers(
tunnelId = tunnelId,
handle = handle,
mode = mode,
reason = PeerUpdateReason.IPV4_FALLBACK,
)
}
}
// Recovery
if (
strategy.recoveryEnabled &&
!isUsingIpv6 &&
!hasRecoveredOnThisNetwork &&
healthySinceMs != null &&
stable.state.hasIpv6 &&
!ipv6Bad
) {
Timber.d(
"IPv6 strategy | Recovery check: healthy for ${healthyDuration}ms (need >= ${RECOVERY_STABILITY_WINDOW}ms)"
)
if (healthyDuration >= RECOVERY_STABILITY_WINDOW) {
hasRecoveredOnThisNetwork = true
Timber.d("Recovered to IPv6 on $newKey (healthy for ${healthyDuration}ms)")
reconcilePeers(
tunnelId = tunnelId,
handle = handle,
mode = mode,
reason = PeerUpdateReason.IPV6_RECOVERY,
)
}
}
}
}
private suspend fun reconcilePeers(
tunnelId: Int,
handle: Int,
mode: BackendMode,
reason: PeerUpdateReason,
) {
val mutex = peerUpdateMutexes.getOrPut(tunnelId) { Mutex() }
mutex.withLock {
when (reason) {
PeerUpdateReason.IPV4_FALLBACK -> {
updateActiveTunnel(tunnelId) { it.copy(isFallenBackToIpv4ForNetwork = true) }
}
PeerUpdateReason.IPV6_RECOVERY,
PeerUpdateReason.NETWORK_CHANGE_RESET -> {
updateActiveTunnel(tunnelId) { it.copy(isFallenBackToIpv4ForNetwork = false) }
if (reason == PeerUpdateReason.NETWORK_CHANGE_RESET) return
}
PeerUpdateReason.DDNS_CHECK -> Unit
}
val updatedActiveTunnel = _status.value.activeTunnels[tunnelId] ?: return
val tunnel = updatedActiveTunnel.tunnel ?: return
val results = resolvePeers(mode)
if (results.isEmpty()) return
val networkHasIpv6 = stableNetworkEngine.stableState.value?.state?.hasIpv6 == true
val preferIpv6 =
tunnel.ipStrategy is Tunnel.IpStrategy.PreferIpv6 &&
networkHasIpv6 &&
!updatedActiveTunnel.isFallenBackToIpv4ForNetwork
val activeConfig =
try {
engine.getActiveConfig(handle, mode)
} catch (t: Throwable) {
Timber.w(t, "UAPI query failed during peer reconciliation")
return
} ?: return
val mismatches = findEndpointMismatches(results, activeConfig, preferIpv6)
Timber.d("Reconciliation complete for $reason. Mismatches found: ${mismatches.size}")
if (mismatches.isNotEmpty()) {
Timber.i("Updating peers due to $reason: $mismatches")
val resolvedPeers = mode.config.buildResolvedPeers(mismatches)
try {
engine.updatePeers(handle, mode, resolvedPeers)
markPeerUpdate(tunnelId)
when (reason) {
PeerUpdateReason.IPV4_FALLBACK ->
_events.emit(TunnelEvent.FallbackToIpv4(tunnelId))
PeerUpdateReason.IPV6_RECOVERY ->
_events.emit(TunnelEvent.RecoveredToIpv6(tunnelId))
PeerUpdateReason.DDNS_CHECK ->
_events.emit(
TunnelEvent.DynamicDnsUpdate(tunnelId, mismatches.keys.toList())
)
PeerUpdateReason.NETWORK_CHANGE_RESET -> Unit
}
} catch (t: Throwable) {
Timber.e(t, "Failed to apply peer updates to WireGuard engine")
}
} else {
Timber.d("No mismatches found, skipping event emission.")
}
}
}
companion object {
private const val DDNS_MIN_CHECK_INTERVAL = 30_000L
private const val DDNS_FAILURE_WINDOW = 15_000L
private const val DDNS_STABILITY_WINDOW = 15_000L
private const val IPV4_FALLBACK_FAILURE_COUNT = 4
private const val IPV4_FALLBACK_FAILURE_DURATION = 10_000L
private const val RECOVERY_STABILITY_WINDOW = 5_000L
}
} }
@@ -3,20 +3,14 @@ package com.zaneschepke.tunnel.backend
import com.zaneschepke.tunnel.Tunnel import com.zaneschepke.tunnel.Tunnel
import com.zaneschepke.tunnel.model.BackendMode import com.zaneschepke.tunnel.model.BackendMode
import com.zaneschepke.tunnel.state.EngineStartResult import com.zaneschepke.tunnel.state.EngineStartResult
import com.zaneschepke.tunnel.state.EngineState
import com.zaneschepke.tunnel.state.NativeTunnelStatus
import com.zaneschepke.wireguardautotunnel.parser.ActiveConfig import com.zaneschepke.wireguardautotunnel.parser.ActiveConfig
import com.zaneschepke.wireguardautotunnel.parser.PeerSection import com.zaneschepke.wireguardautotunnel.parser.PeerSection
import kotlinx.coroutines.flow.Flow
internal interface TunnelEngine { internal interface TunnelEngine {
val status: Flow<NativeTunnelStatus> suspend fun start(tunnel: Tunnel, mode: BackendMode): EngineStartResult
val state: Flow<EngineState>
fun start(tunnel: Tunnel, mode: BackendMode): EngineStartResult suspend fun stop(handle: Int, mode: BackendMode)
fun stop(handle: Int, mode: BackendMode)
suspend fun updatePeers(handle: Int, mode: BackendMode, peers: List<PeerSection>) suspend fun updatePeers(handle: Int, mode: BackendMode, peers: List<PeerSection>)
@@ -9,32 +9,32 @@ import com.zaneschepke.tunnel.model.ProxyConfig
import com.zaneschepke.tunnel.service.VpnService import com.zaneschepke.tunnel.service.VpnService
import com.zaneschepke.tunnel.service.VpnService.Companion.HEV_BRIDGE_TRAFFIC_TAG import com.zaneschepke.tunnel.service.VpnService.Companion.HEV_BRIDGE_TRAFFIC_TAG
import com.zaneschepke.tunnel.state.EngineStartResult import com.zaneschepke.tunnel.state.EngineStartResult
import com.zaneschepke.tunnel.state.EngineState
import com.zaneschepke.tunnel.state.NativeTunnelStatus
import com.zaneschepke.tunnel.util.BackendException import com.zaneschepke.tunnel.util.BackendException
import com.zaneschepke.wireguardautotunnel.parser.ActiveConfig import com.zaneschepke.wireguardautotunnel.parser.ActiveConfig
import com.zaneschepke.wireguardautotunnel.parser.Config import com.zaneschepke.wireguardautotunnel.parser.Config
import com.zaneschepke.wireguardautotunnel.parser.PeerSection import com.zaneschepke.wireguardautotunnel.parser.PeerSection
import java.io.IOException import java.io.IOException
import java.net.DatagramSocket
import java.net.ServerSocket import java.net.ServerSocket
import java.net.SocketException
import java.util.UUID import java.util.UUID
import kotlinx.coroutines.flow.Flow import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.delay
internal class WireGuardTunnelEngine( internal class WireGuardTunnelEngine(private val serviceHolder: ServiceHolder) : TunnelEngine {
private val serviceHolder: ServiceHolder,
stateProvider: EngineStateProvider,
) : TunnelEngine {
override val status: Flow<NativeTunnelStatus> = serviceHolder.nativeStatuses override suspend fun start(tunnel: Tunnel, mode: BackendMode): EngineStartResult {
override val state: Flow<EngineState> = stateProvider.state
override fun start(tunnel: Tunnel, mode: BackendMode): EngineStartResult {
val ifName = WGT_INTERFACE_PREFIX + tunnel.id val ifName = WGT_INTERFACE_PREFIX + tunnel.id
val (config, removedPeerEndpoint) = buildConfig(mode) val (config, removedPeerEndpoint) = buildConfig(mode)
// guard against static listenPort issues
val listenPort = config.`interface`.listenPort
if (listenPort != null) {
waitForUdpPortAvailable(listenPort)
}
val handle = val handle =
when (mode) { when (mode) {
is BackendMode.Proxy.KillSwitchPrimary -> { is BackendMode.Proxy.KillSwitchPrimary -> {
@@ -154,7 +154,7 @@ internal class WireGuardTunnelEngine(
return peer.copy(endpoint = null) return peer.copy(endpoint = null)
} }
override fun stop(handle: Int, mode: BackendMode) { override suspend fun stop(handle: Int, mode: BackendMode) {
when (mode) { when (mode) {
is BackendMode.Proxy.Standard -> stopProxyTunnel(handle) is BackendMode.Proxy.Standard -> stopProxyTunnel(handle)
is BackendMode.Vpn -> stopVpnTunnel(handle) is BackendMode.Vpn -> stopVpnTunnel(handle)
@@ -162,7 +162,7 @@ internal class WireGuardTunnelEngine(
} }
} }
private fun stopKillSwitchPrimaryTunnel(handle: Int) { private suspend fun stopKillSwitchPrimaryTunnel(handle: Int) {
ProxyBackend.awgTurnProxyTunnelOff(handle) ProxyBackend.awgTurnProxyTunnelOff(handle)
val service = serviceHolder.getVpnService() val service = serviceHolder.getVpnService()
service.stopHevSocks5Bridge() service.stopHevSocks5Bridge()
@@ -176,7 +176,7 @@ internal class WireGuardTunnelEngine(
VpnBackend.awgTurnOff(handle) VpnBackend.awgTurnOff(handle)
} }
private fun startVpnTunnel(tunnel: Tunnel, ifName: String, config: Config): Int { private suspend fun startVpnTunnel(tunnel: Tunnel, ifName: String, config: Config): Int {
val service = serviceHolder.getVpnService() val service = serviceHolder.getVpnService()
@@ -197,7 +197,29 @@ internal class WireGuardTunnelEngine(
return handle return handle
} }
private fun startProxyTunnel( private fun isUdpPortAvailable(port: Int): Boolean {
if (port !in 1..65_535) return false
return try {
DatagramSocket(port).use { true }
} catch (_: SocketException) {
false
}
}
@Throws(BackendException::class)
private suspend fun waitForUdpPortAvailable(port: Int, timeoutMs: Long = 3000L) {
val deadline = System.currentTimeMillis() + timeoutMs
while (System.currentTimeMillis() < deadline) {
if (isUdpPortAvailable(port)) return
delay(50.milliseconds)
}
throw BackendException.ListenPortUnavailable(
"UDP ListenPort $port is still in use after waiting $timeoutMs ms",
port,
)
}
private suspend fun startProxyTunnel(
ifName: String, ifName: String,
config: Config, config: Config,
proxyConfig: ProxyConfig, proxyConfig: ProxyConfig,
@@ -2,10 +2,7 @@ package com.zaneschepke.tunnel.di
import com.zaneschepke.tunnel.TunnelLibraryInitializer import com.zaneschepke.tunnel.TunnelLibraryInitializer
import com.zaneschepke.tunnel.backend.Backend import com.zaneschepke.tunnel.backend.Backend
import com.zaneschepke.tunnel.backend.DefaultEngineStateProvider
import com.zaneschepke.tunnel.backend.EngineStateProvider
import com.zaneschepke.tunnel.backend.ServiceHolder import com.zaneschepke.tunnel.backend.ServiceHolder
import com.zaneschepke.tunnel.backend.TunnelActor
import com.zaneschepke.tunnel.backend.TunnelBackend import com.zaneschepke.tunnel.backend.TunnelBackend
import com.zaneschepke.tunnel.backend.TunnelEngine import com.zaneschepke.tunnel.backend.TunnelEngine
import com.zaneschepke.tunnel.backend.WireGuardTunnelEngine import com.zaneschepke.tunnel.backend.WireGuardTunnelEngine
@@ -23,10 +20,8 @@ val tunnelModule = module {
single { ServiceHolder(androidContext()) } single { ServiceHolder(androidContext()) }
// expect networkMonitor and NotificationProvider to be available to koin from app // expect networkMonitor and NotificationProvider to be available to koin from app
single<Backend> { TunnelBackend(get(named(CoroutineScopes.IO_SCOPE)), get(), get()) } single<Backend> { TunnelBackend(get(named(CoroutineScopes.IO_SCOPE)), get(), get(), get()) }
single<TunnelActor> { TunnelActor(get(named(CoroutineScopes.IO_SCOPE)), get(), get()) } single<TunnelEngine> { WireGuardTunnelEngine(get()) }
single<EngineStateProvider> { DefaultEngineStateProvider() }
single<TunnelEngine> { WireGuardTunnelEngine(get(), get()) }
} }
enum class CoroutineScopes { enum class CoroutineScopes {
@@ -1,42 +0,0 @@
package com.zaneschepke.tunnel.event
import com.zaneschepke.tunnel.model.DnsBootstrapResult
import com.zaneschepke.tunnel.model.PublicKey
import com.zaneschepke.tunnel.model.TunnelCommand
import com.zaneschepke.tunnel.state.BootstrapState
import com.zaneschepke.tunnel.state.EngineStartResult
import com.zaneschepke.tunnel.state.NativeTunnelStatus
import com.zaneschepke.tunnel.state.RuntimeDnsConfig
import com.zaneschepke.wireguardautotunnel.parser.ActiveConfig
import com.zaneschepke.wireguardautotunnel.parser.PeerSection
sealed class ActorEvent {
data class EngineStatus(val status: NativeTunnelStatus) : ActorEvent()
data class TunnelStarted(val result: EngineStartResult, val cmd: TunnelCommand.Start) :
ActorEvent()
data class TunnelStopped(val tunnelId: Int, val handle: Int) : ActorEvent()
data class PeersUpdated(
val tunnelId: Int,
val peers: List<PeerSection>,
val preferIpv6: Boolean,
) : ActorEvent()
data class BootstrapConfigUpdated(val config: RuntimeDnsConfig) : ActorEvent()
data class ResolvedPeersApplied(
val tunnelId: Int,
val cache: Map<PublicKey, DnsBootstrapResult>,
val peers: List<PeerSection>,
) : ActorEvent()
data class ActiveConfigUpdated(val tunnelId: Int, val activeConfig: ActiveConfig?) :
ActorEvent()
data class BootstrapStateChanged(val tunnelId: Int, val bootstrapState: BootstrapState) :
ActorEvent()
data class KillSwitchStateChanged(val enabled: Boolean) : ActorEvent()
}
@@ -0,0 +1,5 @@
package com.zaneschepke.tunnel.model
typealias PublicKey = String
typealias Host = String
@@ -1,16 +0,0 @@
package com.zaneschepke.tunnel.model
import com.zaneschepke.tunnel.Tunnel
import com.zaneschepke.wireguardautotunnel.parser.PeerSection
typealias PublicKey = String
data class RunningTunnel(
val handle: Int,
val interfaceName: String,
val tunnel: Tunnel,
val mode: BackendMode,
val currentPreferIpv6: Boolean = false,
val resolvedPeers: List<PeerSection>? = null,
val peerBootstrapCache: Map<PublicKey, DnsBootstrapResult> = emptyMap(),
)
@@ -1,39 +0,0 @@
package com.zaneschepke.tunnel.model
import com.zaneschepke.tunnel.Tunnel
import com.zaneschepke.tunnel.state.BootstrapState
import com.zaneschepke.tunnel.state.RuntimeDnsConfig
import com.zaneschepke.wireguardautotunnel.parser.PeerSection
sealed class TunnelCommand {
data class Start(val tunnel: Tunnel, val mode: BackendMode) : TunnelCommand()
data class Stop(val tunnelId: Int) : TunnelCommand()
data class UpdateActiveConfig(val tunnelId: Int) : TunnelCommand()
data class UpdateKillSwitch(val enabled: Boolean) : TunnelCommand()
data class ApplyResolvedPeers(
val tunnelId: Int,
val cache: Map<PublicKey, DnsBootstrapResult>,
val peers: List<PeerSection>,
) : TunnelCommand()
data class UpdatePeers(val tunnelId: Int, val preferIpv6: Boolean) : TunnelCommand()
data class SetBootstrapState(val tunnelId: Int, val state: BootstrapState) : TunnelCommand()
data class SetBootstrapConfig(val config: RuntimeDnsConfig) : TunnelCommand()
data class RunHook(val tunnelId: Int, val phase: Phase, val cmds: List<String>?) :
TunnelCommand() {
enum class Phase {
PreUp,
PostUp,
PreDown,
PostDown,
}
}
}
@@ -6,7 +6,6 @@ import androidx.lifecycle.LifecycleService
import com.zaneschepke.tunnel.backend.Backend import com.zaneschepke.tunnel.backend.Backend
import com.zaneschepke.tunnel.backend.ServiceHolder import com.zaneschepke.tunnel.backend.ServiceHolder
import com.zaneschepke.tunnel.backend.ServiceHolder.Companion.alwaysOnCallback import com.zaneschepke.tunnel.backend.ServiceHolder.Companion.alwaysOnCallback
import com.zaneschepke.tunnel.model.BackendMode
import org.koin.java.KoinJavaComponent.inject import org.koin.java.KoinJavaComponent.inject
import timber.log.Timber import timber.log.Timber
@@ -16,15 +15,14 @@ class TunnelService : LifecycleService() {
private val serviceHolder: ServiceHolder by inject(ServiceHolder::class.java) private val serviceHolder: ServiceHolder by inject(ServiceHolder::class.java)
override fun onCreate() { override fun onCreate() {
ServiceHolder.tunnelService.complete(this) serviceHolder.set(this)
serviceHolder.ensureNativeCallbacksRegistered()
launchForegroundNotification() launchForegroundNotification()
super.onCreate() super.onCreate()
} }
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId) super.onStartCommand(intent, flags, startId)
ServiceHolder.tunnelService.complete(this) serviceHolder.set(this)
launchForegroundNotification() launchForegroundNotification()
// Service restarted by system, reuse always-on VPN callback // Service restarted by system, reuse always-on VPN callback
@@ -41,10 +39,8 @@ class TunnelService : LifecycleService() {
} }
override fun onDestroy() { override fun onDestroy() {
backend.emergencyStopAllOfTypeSync(BackendMode.Proxy.Standard::class)
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE) ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
serviceHolder.clear(this) serviceHolder.signalTunnelServiceDestroyed()
super.onDestroy() super.onDestroy()
} }
@@ -15,14 +15,13 @@ import com.zaneschepke.tunnel.backend.KillSwitch
import com.zaneschepke.tunnel.backend.ServiceHolder import com.zaneschepke.tunnel.backend.ServiceHolder
import com.zaneschepke.tunnel.backend.ServiceHolder.Companion.DEFAULT_MTU import com.zaneschepke.tunnel.backend.ServiceHolder.Companion.DEFAULT_MTU
import com.zaneschepke.tunnel.backend.ServiceHolder.Companion.alwaysOnCallback import com.zaneschepke.tunnel.backend.ServiceHolder.Companion.alwaysOnCallback
import com.zaneschepke.tunnel.backend.ServiceHolder.Companion.vpnService
import com.zaneschepke.tunnel.backend.SocketProtector import com.zaneschepke.tunnel.backend.SocketProtector
import com.zaneschepke.tunnel.model.BackendMode
import com.zaneschepke.tunnel.model.KillSwitchConfig import com.zaneschepke.tunnel.model.KillSwitchConfig
import com.zaneschepke.tunnel.util.parseDns import com.zaneschepke.tunnel.util.parseDns
import com.zaneschepke.tunnel.util.parseInetNetwork import com.zaneschepke.tunnel.util.parseInetNetwork
import com.zaneschepke.wireguardautotunnel.parser.Config import com.zaneschepke.wireguardautotunnel.parser.Config
import java.io.IOException import java.io.IOException
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@@ -47,10 +46,8 @@ class VpnService : android.net.VpnService(), KillSwitch, SocketProtector {
get() = Builder() get() = Builder()
override fun onCreate() { override fun onCreate() {
vpnService.complete(this) serviceHolder.set(this)
// We call this for all backend modes as it is shared for bootstrapping bypass
ProxyBackend.setSocketProtector(this) ProxyBackend.setSocketProtector(this)
serviceHolder.ensureNativeCallbacksRegistered()
launchForegroundNotification() launchForegroundNotification()
super.onCreate() super.onCreate()
} }
@@ -66,30 +63,21 @@ class VpnService : android.net.VpnService(), KillSwitch, SocketProtector {
override fun onDestroy() { override fun onDestroy() {
Timber.d("VpnService destroyed") Timber.d("VpnService destroyed")
try { try {
ProxyBackend.setSocketProtector(null) ProxyBackend.setSocketProtector(null)
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE) ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
disableKillSwitch() disableKillSwitch()
hevBridgeJob?.cancel() hevBridgeJob?.cancel()
serviceScope.cancel() serviceScope.cancel()
backend.emergencyStopAllOfTypeSync(BackendMode.Vpn::class)
backend.emergencyStopAllOfTypeSync(BackendMode.Proxy.KillSwitchPrimary::class)
stopHevSocks5Bridge() stopHevSocks5Bridge()
serviceHolder.clear(this)
} finally { } finally {
serviceHolder.signalVpnServiceDestroyed()
super.onDestroy() super.onDestroy()
} }
} }
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
vpnService.complete(this) serviceHolder.set(this)
launchForegroundNotification() launchForegroundNotification()
// Service restarted by system or Always-on VPN started // Service restarted by system or Always-on VPN started
@@ -141,7 +129,7 @@ class VpnService : android.net.VpnService(), KillSwitch, SocketProtector {
if (attempt % 5 == 0) { if (attempt % 5 == 0) {
Timber.d("SOCKS5 not ready yet, retrying...") Timber.d("SOCKS5 not ready yet, retrying...")
} }
delay(300) delay(300.milliseconds)
} }
} }
Timber.e("Timed out waiting for SOCKS5 proxy to be ready") Timber.e("Timed out waiting for SOCKS5 proxy to be ready")
@@ -1,21 +1,21 @@
package com.zaneschepke.tunnel.state package com.zaneschepke.tunnel.state
import com.zaneschepke.pinger.model.PingStats
import com.zaneschepke.tunnel.Tunnel import com.zaneschepke.tunnel.Tunnel
import com.zaneschepke.tunnel.model.BackendMode import com.zaneschepke.tunnel.model.BackendMode
import com.zaneschepke.wireguardautotunnel.parser.ActiveConfig import com.zaneschepke.wireguardautotunnel.parser.ActiveConfig
data class ActiveTunnel( data class ActiveTunnel(
val tunnel: Tunnel? = null,
val mode: BackendMode? = null,
val transportState: Tunnel.State = Tunnel.State.Down, val transportState: Tunnel.State = Tunnel.State.Down,
val bootstrapState: BootstrapState = BootstrapState.None, val bootstrapState: BootstrapState = BootstrapState.None,
val lastStateChangeMs: Long = System.currentTimeMillis(), val lastStateChangeMs: Long = System.currentTimeMillis(),
val lastHealthChangeMs: Long = 0L, val lastHealthChangeMs: Long = 0L,
val interfaceName: String? = null, val interfaceName: String? = null,
val activeConfig: ActiveConfig? = null, val activeConfig: ActiveConfig? = null,
val pingStats: PingStats? = null,
val mode: BackendMode? = null,
val uptime: Long? = null, val uptime: Long? = null,
val lastPeerUpdateMs: Long = 0L, val lastPeerUpdateMs: Long = 0L,
val isFallenBackToIpv4ForNetwork: Boolean = false,
) { ) {
val isPeerUpdating: Boolean val isPeerUpdating: Boolean
get() = System.currentTimeMillis() - lastPeerUpdateMs < PEER_UPDATE_GRACE_MS get() = System.currentTimeMillis() - lastPeerUpdateMs < PEER_UPDATE_GRACE_MS
@@ -1,8 +0,0 @@
package com.zaneschepke.tunnel.state
data class ActorState(
val byTunnelId: Map<Int, TunnelRuntimeState>,
val byHandle: Map<Int, Int>,
val killSwitchEnabled: Boolean = false,
val dnsConfig: RuntimeDnsConfig = RuntimeDnsConfig(),
)
@@ -6,4 +6,5 @@ data class BackendStatus(
val killSwitch: KillSwitchState = KillSwitchState(), val killSwitch: KillSwitchState = KillSwitchState(),
val activeTunnels: Map<Int, ActiveTunnel> = emptyMap(), val activeTunnels: Map<Int, ActiveTunnel> = emptyMap(),
val dnsMode: DnsBoostrapMode = DnsBoostrapMode.System, val dnsMode: DnsBoostrapMode = DnsBoostrapMode.System,
val runtimeDnsConfig: RuntimeDnsConfig = RuntimeDnsConfig(),
) )
@@ -1,7 +0,0 @@
package com.zaneschepke.tunnel.state
internal data class EngineState(
val tunnels: Map<Int, TunnelRuntimeState> = emptyMap(),
val killSwitch: KillSwitchState = KillSwitchState(),
val dns: DnsState = DnsState(),
)
@@ -1,15 +0,0 @@
package com.zaneschepke.tunnel.state
data class NativeTunnelStatus(val handle: Int, val code: NativeTunnelStatusCode) {
enum class NativeTunnelStatusCode(val code: Int) {
HEALTHY(0),
HANDSHAKE_FAILURE(1),
STOPPED(99);
companion object {
fun from(code: Int): NativeTunnelStatusCode? {
return entries.firstOrNull { it.code == code }
}
}
}
}
@@ -1,5 +0,0 @@
package com.zaneschepke.tunnel.state
import com.zaneschepke.tunnel.model.RunningTunnel
data class TunnelRuntimeState(val running: RunningTunnel, val active: ActiveTunnel)
@@ -9,4 +9,6 @@ sealed class BackendException : Exception() {
class Socks5PortUnavailable(override val message: String, val port: Int) : BackendException() class Socks5PortUnavailable(override val message: String, val port: Int) : BackendException()
class HttpPortUnavailable(override val message: String, val port: Int) : BackendException() class HttpPortUnavailable(override val message: String, val port: Int) : BackendException()
class ListenPortUnavailable(override val message: String, val port: Int) : BackendException()
} }
@@ -1,9 +1,13 @@
package com.zaneschepke.tunnel.util package com.zaneschepke.tunnel.util
import android.os.Build import android.os.Build
import com.zaneschepke.tunnel.model.BackendMode
import com.zaneschepke.tunnel.model.DnsBootstrapResult import com.zaneschepke.tunnel.model.DnsBootstrapResult
import com.zaneschepke.tunnel.model.DnsConfig import com.zaneschepke.tunnel.model.DnsConfig
import com.zaneschepke.tunnel.model.RunningTunnel import com.zaneschepke.tunnel.model.Host
import com.zaneschepke.tunnel.model.PublicKey
import com.zaneschepke.tunnel.state.BackendStatus
import com.zaneschepke.wireguardautotunnel.parser.Config
import com.zaneschepke.wireguardautotunnel.parser.PeerSection import com.zaneschepke.wireguardautotunnel.parser.PeerSection
import java.net.Inet4Address import java.net.Inet4Address
import java.net.InetAddress import java.net.InetAddress
@@ -63,27 +67,37 @@ internal fun String.parseDns(): DnsConfig {
return DnsConfig(servers, domains) return DnsConfig(servers, domains)
} }
internal fun RunningTunnel.buildResolvedPeers(preferIpv6: Boolean): List<PeerSection> { internal fun Config.buildResolvedPeers(hostMap: Map<PublicKey, Host>): List<PeerSection> {
return this.peers.map { peer ->
fun selectIp(cache: DnsBootstrapResult, preferIpv6: Boolean): String? { val updatedHost = hostMap[peer.publicKey] ?: return@map peer
val port = peer.endpoint?.substringAfterLast(":") ?: return@map peer
val ipv4 = cache.ipv4.firstOrNull() peer.copy(endpoint = "$updatedHost:$port")
val ipv6 = cache.ipv6.firstOrNull() }
}
return when {
preferIpv6 -> ipv6 ?: ipv4 fun Map<PublicKey, DnsBootstrapResult>.toHostMap(preferIpv6: Boolean): Map<PublicKey, Host> =
else -> ipv4 ?: ipv6 mapNotNull { (pubKey, result) ->
} val host =
} if (preferIpv6) {
result.ipv6.firstOrNull() ?: result.ipv4.firstOrNull()
return mode.config.peers.map { peer -> } else {
val endpoint = peer.endpoint ?: return@map peer result.ipv4.firstOrNull() ?: result.ipv6.firstOrNull()
val port = endpoint.substringAfterLast(":") }
host?.let { pubKey to it }
val dnsCache = peerBootstrapCache[peer.publicKey] ?: return@map peer }
.toMap()
val selectedIp = selectIp(cache = dnsCache, preferIpv6 = preferIpv6) ?: return@map peer
fun BackendStatus.isLastTunnelOfServiceType(tunnelId: Int): Boolean {
peer.copy(endpoint = "$selectedIp:$port") val mode = activeTunnels[tunnelId]?.mode ?: return false
return when (mode) {
is BackendMode.Vpn,
is BackendMode.Proxy.KillSwitchPrimary -> {
activeTunnels.values.count {
it.mode is BackendMode.Vpn || it.mode is BackendMode.Proxy.KillSwitchPrimary
} == 1
}
is BackendMode.Proxy.Standard -> {
activeTunnels.values.count { it.mode is BackendMode.Proxy.Standard } == 1
}
} }
} }
+87 -38
View File
@@ -9,7 +9,6 @@ import (
"net" "net"
"sync" "sync"
"syscall" "syscall"
"time"
"github.com/amnezia-vpn/amneziawg-go/conn" "github.com/amnezia-vpn/amneziawg-go/conn"
"github.com/amnezia-vpn/amneziawg-go/device" "github.com/amnezia-vpn/amneziawg-go/device"
@@ -25,6 +24,7 @@ var (
cancelFuncs map[int32]context.CancelFunc cancelFuncs map[int32]context.CancelFunc
tag string tag string
virtualTunnelHandles map[int32]*wireproxyawg.VirtualTun virtualTunnelHandles map[int32]*wireproxyawg.VirtualTun
tunnelMu sync.RWMutex
) )
func init() { func init() {
@@ -35,7 +35,6 @@ func init() {
//export awgStartProxy //export awgStartProxy
func awgStartProxy(interfaceName string, config string, uapiPath string, bypass int32) int32 { func awgStartProxy(interfaceName string, config string, uapiPath string, bypass int32) int32 {
conf, err := wireproxyawg.ParseConfigString(config) conf, err := wireproxyawg.ParseConfigString(config)
if err != nil { if err != nil {
shared.LogError(tag, "Invalid config file", err) shared.LogError(tag, "Invalid config file", err)
@@ -49,22 +48,32 @@ func awgStartProxy(interfaceName string, config string, uapiPath string, bypass
} }
setting, err := wireproxyawg.CreateIPCRequest(conf.Device, false) setting, err := wireproxyawg.CreateIPCRequest(conf.Device, false)
if err != nil { if err != nil {
shared.LogError(tag, "Create IPC request failed", err) shared.LogError(tag, "Create IPC request failed")
shared.ReleaseHandle(handle)
return -1 return -1
} }
tun, tnet, err := netstack.CreateNetTUN(setting.DeviceAddr, setting.DNS, setting.MTU) tun, tnet, err := netstack.CreateNetTUN(
setting.DeviceAddr,
setting.DNS,
setting.MTU,
)
if err != nil { if err != nil {
shared.LogError(tag, "Create TUN failed", err) shared.LogError(tag, "Create TUN failed")
shared.ReleaseHandle(handle)
return -1 return -1
} }
name, err := tun.Name() name, err := tun.Name()
if err != nil {
shared.LogError(tag, "Failed to get TUN name: %v", err)
shared.ReleaseHandle(handle)
tun.Close()
return -1
}
var bind conn.Bind var bind conn.Bind
if bypass == 1 { if bypass == 1 {
bind = conn.NewStdNetBindWithControl(protectControlFunc) bind = conn.NewStdNetBindWithControl(protectControlFunc)
} else { } else {
@@ -75,28 +84,34 @@ func awgStartProxy(interfaceName string, config string, uapiPath string, bypass
go C.awgNotifyStatus(C.int32_t(handle), C.int32_t(code)) go C.awgNotifyStatus(C.int32_t(handle), C.int32_t(code))
} }
dev := device.NewDevice(tun, bind, shared.NewLogger("Tun/"+interfaceName), statusCB) dev := device.NewDevice(
tun,
bind,
shared.NewLogger("Tun/"+interfaceName),
statusCB,
)
dev.DisableSomeRoamingForBrokenMobileSemantics() dev.DisableSomeRoamingForBrokenMobileSemantics()
err = dev.IpcSet(setting.IpcRequest) if err = dev.IpcSet(setting.IpcRequest); err != nil {
shared.LogError(tag, "Ipc setting failed")
if err != nil { shared.ReleaseHandle(handle)
shared.LogError(tag, "Ipc setting failed", err) dev.Close()
return -1 return -1
} }
uapiFile, err := ipc.UAPIOpen(uapiPath, name)
var uapi net.Listener var uapi net.Listener
uapiFile, uapiErr := ipc.UAPIOpen(uapiPath, name)
if err != nil { if uapiErr != nil {
shared.LogError(tag, "UAPIOpen: %v", err) shared.LogError(tag, "UAPIOpen: %v", uapiErr)
} else { } else {
uapi, err = ipc.UAPIListen(uapiPath, name, uapiFile) uapi, err = ipc.UAPIListen(uapiPath, name, uapiFile)
if err != nil { if err != nil {
uapiFile.Close()
shared.LogError(tag, "UAPIListen: %v", err) shared.LogError(tag, "UAPIListen: %v", err)
uapiFile.Close()
uapi = nil
} else { } else {
go func() { go func() {
for { for {
@@ -110,10 +125,18 @@ func awgStartProxy(interfaceName string, config string, uapiPath string, bypass
} }
} }
err = dev.Up() if err = dev.Up(); err != nil {
if err != nil { shared.LogError(tag, "Failed to bring up device")
shared.LogError(tag, "Failed to bring up device", err)
uapiFile.Close() if uapiFile != nil {
uapiFile.Close()
}
if uapi != nil {
uapi.Close()
}
shared.ReleaseHandle(handle)
dev.Close() dev.Close()
return -1 return -1
} }
@@ -128,15 +151,14 @@ func awgStartProxy(interfaceName string, config string, uapiPath string, bypass
PingRecordLock: new(sync.Mutex), PingRecordLock: new(sync.Mutex),
} }
virtualTunnelHandles[handle] = virtualTun
// Create cancellable context
tunnelCtx, tunnelCancel := context.WithCancel(context.Background()) tunnelCtx, tunnelCancel := context.WithCancel(context.Background())
cancelFuncs[handle] = tunnelCancel
// Spawn all routines with context tunnelMu.Lock()
virtualTunnelHandles[handle] = virtualTun
cancelFuncs[handle] = tunnelCancel
tunnelMu.Unlock()
for _, spawner := range conf.Routines { for _, spawner := range conf.Routines {
shared.LogDebug(tag, "Spawning routine..")
go func(s wireproxyawg.RoutineSpawner) { go func(s wireproxyawg.RoutineSpawner) {
if err := s.SpawnRoutine(tunnelCtx, virtualTun); err != nil { if err := s.SpawnRoutine(tunnelCtx, virtualTun); err != nil {
shared.LogError(tag, "Routine failed: %v", err) shared.LogError(tag, "Routine failed: %v", err)
@@ -144,13 +166,16 @@ func awgStartProxy(interfaceName string, config string, uapiPath string, bypass
}(spawner) }(spawner)
} }
shared.LogDebug(tag, "Done starting proxy and tunnel") shared.LogDebug(tag, "Done starting proxy and tunnel for handle %d", handle)
return handle return handle
} }
//export awgUpdateProxyTunnelPeers //export awgUpdateProxyTunnelPeers
func awgUpdateProxyTunnelPeers(tunnelHandle int32, settings string) int32 { func awgUpdateProxyTunnelPeers(tunnelHandle int32, settings string) int32 {
tunnelMu.RLock()
handle, ok := virtualTunnelHandles[tunnelHandle] handle, ok := virtualTunnelHandles[tunnelHandle]
tunnelMu.RUnlock()
if !ok { if !ok {
shared.LogError(tag, "Tunnel is not up") shared.LogError(tag, "Tunnel is not up")
return -1 return -1
@@ -180,7 +205,9 @@ func awgUpdateProxyTunnelPeers(tunnelHandle int32, settings string) int32 {
//export awgGetProxyConfig //export awgGetProxyConfig
func awgGetProxyConfig(tunnelHandle int32) *C.char { func awgGetProxyConfig(tunnelHandle int32) *C.char {
tunnelMu.RLock()
handle, ok := virtualTunnelHandles[tunnelHandle] handle, ok := virtualTunnelHandles[tunnelHandle]
tunnelMu.RUnlock()
if !ok { if !ok {
shared.LogError(tag, "Tunnel is not up") shared.LogError(tag, "Tunnel is not up")
return nil return nil
@@ -212,20 +239,38 @@ func protectControlFunc(network, address string, c syscall.RawConn) error {
//export awgTurnProxyTunnelOff //export awgTurnProxyTunnelOff
func awgTurnProxyTunnelOff(virtualTunnelHandle int32) { func awgTurnProxyTunnelOff(virtualTunnelHandle int32) {
tunnelMu.Lock()
virtualTun, ok := virtualTunnelHandles[virtualTunnelHandle] virtualTun, ok := virtualTunnelHandles[virtualTunnelHandle]
if !ok { if !ok {
shared.LogError(tag, "Tunnel handle %d not found", virtualTunnelHandle) tunnelMu.Unlock()
shared.LogError(
tag,
"Tunnel handle %d not found",
virtualTunnelHandle,
)
return return
} }
shared.LogDebug(tag, "Tearing down tunnel %d", virtualTunnelHandle)
if cancel, exists := cancelFuncs[virtualTunnelHandle]; exists { cancel := cancelFuncs[virtualTunnelHandle]
delete(virtualTunnelHandles, virtualTunnelHandle)
delete(cancelFuncs, virtualTunnelHandle)
tunnelMu.Unlock()
shared.LogDebug(
tag,
"Tearing down tunnel %d",
virtualTunnelHandle,
)
if cancel != nil {
cancel() cancel()
delete(cancelFuncs, virtualTunnelHandle)
time.Sleep(50 * time.Millisecond)
} }
// Close UAPI listener and underlying file
if virtualTun.Uapi != nil { if virtualTun.Uapi != nil {
virtualTun.Uapi.Close() virtualTun.Uapi.Close()
} }
@@ -234,12 +279,16 @@ func awgTurnProxyTunnelOff(virtualTunnelHandle int32) {
virtualTun.Dev.Close() virtualTun.Dev.Close()
} }
go C.awgNotifyStatus( shared.ReleaseHandle(virtualTunnelHandle)
C.awgNotifyStatus(
C.int32_t(virtualTunnelHandle), C.int32_t(virtualTunnelHandle),
C.int32_t(shared.StatusStop), C.int32_t(shared.StatusStop),
) )
delete(virtualTunnelHandles, virtualTunnelHandle) shared.LogDebug(
shared.ReleaseHandle(virtualTunnelHandle) tag,
shared.LogDebug(tag, "Tunnel %d fully closed (UAPI/Dev/Bind purged)", virtualTunnelHandle) "Tunnel handle %d fully closed",
virtualTunnelHandle,
)
} }
+75 -34
View File
@@ -13,6 +13,7 @@ import (
"net" "net"
"runtime/debug" "runtime/debug"
"strings" "strings"
"sync"
"github.com/amnezia-vpn/amneziawg-go/conn" "github.com/amnezia-vpn/amneziawg-go/conn"
"github.com/amnezia-vpn/amneziawg-go/device" "github.com/amnezia-vpn/amneziawg-go/device"
@@ -31,6 +32,7 @@ type TunnelHandle struct {
var ( var (
tag string tag string
tunnelHandles = make(map[int32]TunnelHandle) tunnelHandles = make(map[int32]TunnelHandle)
tunnelMu sync.RWMutex
) )
func init() { func init() {
@@ -40,7 +42,6 @@ func init() {
//export awgTurnOn //export awgTurnOn
func awgTurnOn(interfaceName string, tunFd int32, settings string, uapiPath string) int32 { func awgTurnOn(interfaceName string, tunFd int32, settings string, uapiPath string) int32 {
tunnel, name, err := tun.CreateUnmonitoredTUNFromFD(int(tunFd)) tunnel, name, err := tun.CreateUnmonitoredTUNFromFD(int(tunFd))
if err != nil { if err != nil {
unix.Close(int(tunFd)) unix.Close(int(tunFd))
shared.LogError(tag, "CreateUnmonitoredTUNFromFD: %v", err) shared.LogError(tag, "CreateUnmonitoredTUNFromFD: %v", err)
@@ -50,52 +51,56 @@ func awgTurnOn(interfaceName string, tunFd int32, settings string, uapiPath stri
conf, err := wireproxyawg.ParseConfigString(settings) conf, err := wireproxyawg.ParseConfigString(settings)
if err != nil { if err != nil {
shared.LogError(tag, "Invalid config file", err) shared.LogError(tag, "Invalid config file", err)
unix.Close(int(tunFd))
if tunnel != nil { if tunnel != nil {
tunnel.Close() tunnel.Close()
} }
return -1 return -1
} }
shared.LogDebug(tag, "Creating device with domain blocking enabled: %v", conf.Device.DomainBlockingEnabled) handle, err := shared.GenerateUniqueHandle()
if err != nil {
handle, err2 := shared.GenerateUniqueHandle() shared.LogError(tag, "Unable to generate handle: %v", err)
if tunnel != nil {
tunnel.Close()
}
return -1
}
statusCB := func(code device.StatusCode) { statusCB := func(code device.StatusCode) {
go C.awgNotifyStatus(C.int32_t(handle), C.int32_t(code)) go C.awgNotifyStatus(C.int32_t(handle), C.int32_t(code))
} }
tunDevice := device.NewDevice(tunnel, conn.NewStdNetBind(), shared.NewLogger("Tun/"+interfaceName), statusCB) tunDevice := device.NewDevice(tunnel, conn.NewStdNetBind(), shared.NewLogger("Tun/"+interfaceName), statusCB)
tunDevice.DisableSomeRoamingForBrokenMobileSemantics() tunDevice.DisableSomeRoamingForBrokenMobileSemantics()
ipcRequest, err := wireproxyawg.CreateIPCRequest(conf.Device, false) ipcRequest, err := wireproxyawg.CreateIPCRequest(conf.Device, false)
if err != nil { if err != nil {
shared.LogError(tag, "CreateIPCRequest: %v", err) shared.LogError(tag, "CreateIPCRequest: %v", err)
unix.Close(int(tunFd))
shared.ReleaseHandle(handle) shared.ReleaseHandle(handle)
tunDevice.Close()
return -1 return -1
} }
err = tunDevice.IpcSet(ipcRequest.IpcRequest) err = tunDevice.IpcSet(ipcRequest.IpcRequest)
if err != nil { if err != nil {
unix.Close(int(tunFd))
shared.ReleaseHandle(handle)
shared.LogError(tag, "IpcSet: %v", err) shared.LogError(tag, "IpcSet: %v", err)
shared.ReleaseHandle(handle)
tunDevice.Close()
return -1 return -1
} }
var uapi net.Listener var uapi net.Listener
uapiFile, uapiErr := ipc.UAPIOpen(uapiPath, name)
uapiFile, err := ipc.UAPIOpen(uapiPath, name) if uapiErr != nil {
shared.LogError(tag, "UAPIOpen: %v", uapiErr)
if err != nil { uapiFile = nil
shared.LogError(tag, "UAPIOpen: %v", err)
} else { } else {
uapi, err = ipc.UAPIListen(uapiPath, name, uapiFile) // uapiPath as rootdir, name as interface uapi, err = ipc.UAPIListen(uapiPath, name, uapiFile)
if err != nil { if err != nil {
uapiFile.Close()
shared.LogError(tag, "UAPIListen: %v", err) shared.LogError(tag, "UAPIListen: %v", err)
uapiFile.Close()
uapiFile = nil
uapi = nil
} else { } else {
go func() { go func() {
for { for {
@@ -112,29 +117,33 @@ func awgTurnOn(interfaceName string, tunFd int32, settings string, uapiPath stri
err = tunDevice.Up() err = tunDevice.Up()
if err != nil { if err != nil {
shared.LogError(tag, "Unable to bring up device: %v", err) shared.LogError(tag, "Unable to bring up device: %v", err)
uapiFile.Close() if uapiFile != nil {
shared.ReleaseHandle(handle) uapiFile.Close()
tunDevice.Close() }
return -1 if uapi != nil {
} uapi.Close()
shared.LogDebug(tag, "Device started") }
if err2 != nil {
shared.LogError(tag, "Unable to find empty handle", err2)
uapiFile.Close()
shared.ReleaseHandle(handle) shared.ReleaseHandle(handle)
tunDevice.Close() tunDevice.Close()
return -1 return -1
} }
tunnelHandles[handle] = TunnelHandle{device: tunDevice, uapi: uapi} shared.LogDebug(tag, "Tunnel started successfully for handle %d", handle)
tunnelMu.Lock()
tunnelHandles[handle] = TunnelHandle{
device: tunDevice,
uapi: uapi,
}
tunnelMu.Unlock()
return handle return handle
} }
//export awgUpdateTunnelPeers //export awgUpdateTunnelPeers
func awgUpdateTunnelPeers(tunnelHandle int32, settings string) int32 { func awgUpdateTunnelPeers(tunnelHandle int32, settings string) int32 {
tunnelMu.RLock()
handle, ok := tunnelHandles[tunnelHandle] handle, ok := tunnelHandles[tunnelHandle]
tunnelMu.RUnlock()
if !ok { if !ok {
shared.LogError(tag, "Tunnel is not up") shared.LogError(tag, "Tunnel is not up")
return -1 return -1
@@ -158,75 +167,107 @@ func awgUpdateTunnelPeers(tunnelHandle int32, settings string) int32 {
return -1 return -1
} }
shared.LogDebug(tag, "Configuration updated successfully") shared.LogDebug(tag, "Configuration updated successfully with handle %d", handle)
return 0 return 0
} }
//export awgTurnOff //export awgTurnOff
func awgTurnOff(tunnelHandle int32) { func awgTurnOff(tunnelHandle int32) {
tunnelMu.Lock()
handle, ok := tunnelHandles[tunnelHandle] handle, ok := tunnelHandles[tunnelHandle]
if !ok { if !ok {
tunnelMu.Unlock()
shared.LogError(tag, "Tunnel is not up") shared.LogError(tag, "Tunnel is not up")
return return
} }
go C.awgNotifyStatus(
C.int32_t(tunnelHandle),
C.int32_t(shared.StatusStop),
)
delete(tunnelHandles, tunnelHandle) delete(tunnelHandles, tunnelHandle)
tunnelMu.Unlock()
if handle.uapi != nil { if handle.uapi != nil {
handle.uapi.Close() handle.uapi.Close()
} }
handle.device.Close()
if handle.device != nil {
handle.device.Close()
}
shared.ReleaseHandle(tunnelHandle) shared.ReleaseHandle(tunnelHandle)
C.awgNotifyStatus(
C.int32_t(tunnelHandle),
C.int32_t(shared.StatusStop),
)
} }
//export awgGetSocketV4 //export awgGetSocketV4
func awgGetSocketV4(tunnelHandle int32) int32 { func awgGetSocketV4(tunnelHandle int32) int32 {
tunnelMu.RLock()
handle, ok := tunnelHandles[tunnelHandle] handle, ok := tunnelHandles[tunnelHandle]
tunnelMu.RUnlock()
if !ok { if !ok {
return -1 return -1
} }
bind, _ := handle.device.Bind().(conn.PeekLookAtSocketFd) bind, _ := handle.device.Bind().(conn.PeekLookAtSocketFd)
if bind == nil { if bind == nil {
return -1 return -1
} }
fd, err := bind.PeekLookAtSocketFd4() fd, err := bind.PeekLookAtSocketFd4()
if err != nil { if err != nil {
return -1 return -1
} }
return int32(fd) return int32(fd)
} }
//export awgGetSocketV6 //export awgGetSocketV6
func awgGetSocketV6(tunnelHandle int32) int32 { func awgGetSocketV6(tunnelHandle int32) int32 {
tunnelMu.RLock()
handle, ok := tunnelHandles[tunnelHandle] handle, ok := tunnelHandles[tunnelHandle]
tunnelMu.RUnlock()
if !ok { if !ok {
return -1 return -1
} }
bind, _ := handle.device.Bind().(conn.PeekLookAtSocketFd) bind, _ := handle.device.Bind().(conn.PeekLookAtSocketFd)
if bind == nil { if bind == nil {
return -1 return -1
} }
fd, err := bind.PeekLookAtSocketFd6() fd, err := bind.PeekLookAtSocketFd6()
if err != nil { if err != nil {
return -1 return -1
} }
return int32(fd) return int32(fd)
} }
//export awgGetConfig //export awgGetConfig
func awgGetConfig(tunnelHandle int32) *C.char { func awgGetConfig(tunnelHandle int32) *C.char {
tunnelMu.RLock()
handle, ok := tunnelHandles[tunnelHandle] handle, ok := tunnelHandles[tunnelHandle]
tunnelMu.RUnlock()
if !ok { if !ok {
return nil return nil
} }
settings, err := handle.device.IpcGet() settings, err := handle.device.IpcGet()
if err != nil { if err != nil {
return nil return nil
} }
return C.CString(settings) return C.CString(settings)
} }