Compare commits

..

9 Commits

Author SHA1 Message Date
Zane Schepke a569974beb add Turkish localization 2024-04-16 16:13:41 -04:00
Zane Schepke 87bc89b6f1 Merge branch 'main' of https://github.com/zaneschepke/wgtunnel 2024-04-16 00:50:43 -04:00
Zane Schepke c343220e96 Merge pull request #156 from mikropsoft/main
Add tr locales
2024-04-16 00:41:14 -04:00
Zane Schepke 5447ec73f7 fix: stop tunnel regression
Fixes regression where tunnel is stuck in on state after x amount of toggles. Closes #163

Adds obfuscation of potentially sensitive data from logs.
Closes #160

Adds hiding of FAB on scroll to allow users to toggle tunnels when they have many tunnel configs.
Closes #161
2024-04-16 00:14:06 -04:00
Zane Schepke a2b8eb5b0b ci: update release wording 2024-04-06 11:06:19 -04:00
Zane Schepke e37777e662 fix: improve auto-tunnel reliability
Improved service manager stop service command to improve reliability
Closes #148

Added assets required by Google Play for AndroidTV to fix Google Play release

Added apk fingerprint hash to release to allow verification of apk signature via apksigner

Improve tile sync when tiles are first added

Bump versions
2024-04-06 10:43:00 -04:00
WINZORT ee3fcabcf1 Add tr locales 2024-04-06 15:39:46 +03:00
Zane Schepke 2a8895ffbc docs: update readme, add security 2024-03-30 23:16:19 -04:00
Zane Schepke b1fdb5b9b2 feat: auto-tunneling flexibility
Added tunnel settings feature where users can configure a tunnel to be used on certain SSID or with mobile data.
Closes #50

Added feature where if a tunnel was active when phone restarted, the app will start that tunnel on boot.

Removed automatic auto-tunnel toggling/override from the tunnel tile and app shortcuts as it can cause undesirable behavior.

Added second tile to control auto-tunneling pause/resume state from a tile.

Added two additional static shortcuts to be able to control auto-tunneling pause/resume state from shortcuts.

Fixed bug where crashes can happen from serializing and deserializing tunnel configs by removing the need for serialization of tunnel configs.

Refactored logic of watcher and tunnel service to make state more predictable.
#127

Fixed bug where rapidly toggling tunnels can cause crashes.
Closes #145

Improved how tunnels are manually toggled from one to another.

Improved logic/storage around primary tunnel behavior.

Fixes issue where info level logs were not populating on release builds.

Increase allowed name length displayed in UI.
Closes #143

Fixes bug where androidTV could crash in certain situations.

Bump versions.

Updated screenshots.
2024-03-30 00:00:35 -04:00
39 changed files with 437 additions and 80 deletions
+25
View File
@@ -89,6 +89,31 @@ jobs:
draft: false
prerelease: true
files: ${{ github.workspace }}/${{ steps.apk-path.outputs.path }}
- name: Install apksigner
run: |
sudo apt-get update
sudo apt-get install -y apksigner
- name: Get checksum
id: checksum
run: echo "checksum=$(apksigner verify -print-certs ${{ steps.apk-path.outputs.path }} | grep -Po "(?<=SHA-256 digest:) .*" | tr -d "[:blank:]")" >> $GITHUB_OUTPUT
- name: Append checksum
id: append_checksum
uses: softprops/action-gh-release@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
body: |
SHA256 fingerprint:
```${{ steps.checksum.outputs.checksum }}```
tag_name: ${{ github.ref_name }}
name: ${{ github.ref_name }}
draft: false
prerelease: true
append_body: true
- name: Deploy with fastlane
uses: ruby/setup-ruby@v1
with:
+25
View File
@@ -95,6 +95,31 @@ jobs:
draft: false
prerelease: false
files: ${{ github.workspace }}/${{ steps.apk-path.outputs.path }}
- name: Install apksigner
run: |
sudo apt-get update
sudo apt-get install -y apksigner
- name: Get checksum
id: checksum
run: echo "checksum=$(apksigner verify -print-certs ${{ steps.apk-path.outputs.path }} | grep -Po "(?<=SHA-256 digest:) .*" | tr -d "[:blank:]")" >> $GITHUB_OUTPUT
- name: Append checksum
id: append_checksum
uses: softprops/action-gh-release@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
body: |
SHA256 fingerprint:
```${{ steps.checksum.outputs.checksum }}```
tag_name: ${{ github.ref_name }}
name: ${{ github.ref_name }}
draft: false
prerelease: false
append_body: true
- name: Deploy with fastlane
uses: ruby/setup-ruby@v1
with:
+6 -4
View File
@@ -49,16 +49,18 @@ and on while on different networks. This app was created to offer a free solutio
## Features
* Add tunnels via .conf file, zip, manual entry, or QR code
* Auto connect to VPN based on Wi-Fi SSID, ethernet, or mobile data
* Auto connect to tunnels based on Wi-Fi SSID, ethernet, or mobile data
* Split tunneling by application with search
* WireGuard support for kernel and userspace modes
* Always-On VPN support
* Export tunnels to zip
* Quick tile support for VPN toggling
* Static shortcuts support for primary tunnel for automation integration
* Quick tile support for tunnel toggling, auto-tunneling
* Static shortcuts support for tunnel toggling, auto-tunneling
* Intent automation support for all tunnels
* Automatic service restart after reboot
* Automatic auto-tunneling service restart after reboot
* Automatic tunnel restart after reboot
* Battery preservation measures
* Restart tunnel on ping failure (beta)
## Docs (WIP)
+5
View File
@@ -0,0 +1,5 @@
# Security Policy
## Reporting a Vulnerability
Please report security issues to `support@zaneschepke.com`
+4 -2
View File
@@ -12,6 +12,10 @@ android {
namespace = Constants.APP_ID
compileSdk = Constants.TARGET_SDK
androidResources {
generateLocaleConfig = true
}
defaultConfig {
applicationId = Constants.APP_ID
minSdk = Constants.MIN_SDK
@@ -25,8 +29,6 @@ android {
getByName("debug").assets.srcDirs(files("$projectDir/schemas")) // Room
}
resourceConfigurations.addAll(listOf("en"))
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables { useSupportLibrary = true }
}
+1 -1
View File
@@ -52,7 +52,7 @@
<application
android:name=".WireGuardAutoTunnel"
android:allowBackup="true"
android:banner="@mipmap/ic_banner"
android:banner="@drawable/ic_banner"
android:dataExtractionRules="@xml/data_extraction_rules"
android:enableOnBackInvokedCallback="true"
android:fullBackupContent="@xml/backup_rules"
@@ -24,7 +24,7 @@ class NotificationActionReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent?) = goAsync {
try {
//TODO fix for manual start changes when enabled
serviceManager.stopVpnService(context)
serviceManager.stopVpnServiceForeground(context)
delay(Constants.TOGGLE_TUNNEL_DELAY)
serviceManager.startVpnServiceForeground(context)
} catch (e: Exception) {
@@ -3,5 +3,6 @@ package com.zaneschepke.wireguardautotunnel.service.foreground
enum class Action {
START,
START_FOREGROUND,
STOP
STOP,
STOP_FOREGROUND
}
@@ -24,8 +24,7 @@ open class ForegroundService : LifecycleService() {
when (action) {
Action.START.name,
Action.START_FOREGROUND.name -> startService(intent.extras)
Action.STOP.name -> stopService(intent.extras)
Action.STOP.name, Action.STOP_FOREGROUND.name -> stopService()
Constants.ALWAYS_ON_VPN_ACTION -> {
Timber.i("Always-on VPN starting service")
startService(intent.extras)
@@ -38,29 +37,19 @@ open class ForegroundService : LifecycleService() {
"with a null intent. It has been probably restarted by the system.",
)
}
// by returning this we make sure the service is restarted if the system kills the service
return START_STICKY
}
override fun onDestroy() {
super.onDestroy()
Timber.d("The service has been destroyed")
}
protected open fun startService(extras: Bundle?) {
if (isServiceStarted) return
Timber.d("Starting ${this.javaClass.simpleName}")
isServiceStarted = true
}
protected open fun stopService(extras: Bundle?) {
protected open fun stopService() {
Timber.d("Stopping ${this.javaClass.simpleName}")
try {
stopForeground(STOP_FOREGROUND_REMOVE)
stopSelf()
} catch (e: Exception) {
Timber.e(e)
}
stopForeground(STOP_FOREGROUND_REMOVE)
stopSelf()
isServiceStarted = false
}
}
@@ -23,15 +23,8 @@ class ServiceManager(private val appDataRepository: AppDataRepository) {
intent.component?.javaClass
try {
when (action) {
Action.START_FOREGROUND -> {
context.startForegroundService(intent)
}
Action.START -> {
context.startService(intent)
}
Action.STOP -> context.startService(intent)
Action.START_FOREGROUND, Action.STOP_FOREGROUND -> context.startForegroundService(intent)
Action.START, Action.STOP -> context.startService(intent)
}
} catch (e: Exception) {
Timber.e(e.message)
@@ -52,9 +45,19 @@ class ServiceManager(private val appDataRepository: AppDataRepository) {
)
}
suspend fun stopVpnServiceForeground(context: Context, isManualStop: Boolean = false) {
if (isManualStop) onManualStop()
Timber.i("Stopping vpn service")
actionOnService(
Action.STOP_FOREGROUND,
context,
WireGuardTunnelService::class.java,
)
}
suspend fun stopVpnService(context: Context, isManualStop: Boolean = false) {
if (isManualStop) onManualStop()
Timber.d("Stopping vpn service action")
Timber.i("Stopping vpn service")
actionOnService(
Action.STOP,
context,
@@ -1,6 +0,0 @@
package com.zaneschepke.wireguardautotunnel.service.foreground
enum class ServiceState {
STARTED,
STOPPED,
}
@@ -18,6 +18,7 @@ import com.zaneschepke.wireguardautotunnel.service.notification.NotificationServ
import com.zaneschepke.wireguardautotunnel.service.tunnel.VpnService
import com.zaneschepke.wireguardautotunnel.util.Constants
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
@@ -56,7 +57,7 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
private val networkEventsFlow = MutableStateFlow(WatcherState())
private lateinit var watcherJob: Job
private var watcherJob: Job? = null
private var wakeLock: PowerManager.WakeLock? = null
private val tag = this.javaClass.name
@@ -86,8 +87,8 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
}
}
override fun stopService(extras: Bundle?) {
super.stopService(extras)
override fun stopService() {
super.stopService()
wakeLock?.let {
if (it.isHeld) {
it.release()
@@ -134,8 +135,10 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
}
private fun cancelWatcherJob() {
if (this::watcherJob.isInitialized) {
watcherJob.cancel()
try {
watcherJob?.cancel()
} catch (e : CancellationException) {
Timber.i("Watcher job cancelled")
}
}
@@ -231,7 +234,7 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
}
if (results.contains(false)) {
Timber.i("Restarting VPN for ping failure")
serviceManager.stopVpnService(this)
serviceManager.stopVpnServiceForeground(this)
delay(Constants.VPN_RESTART_DELAY)
serviceManager.startVpnServiceForeground(this)
delay(Constants.PING_COOLDOWN)
@@ -319,7 +322,9 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
)
val ssid = wifiService.getNetworkName(it.networkCapabilities)
ssid?.let {
Timber.i("Detected SSID: $ssid")
if(it.contains(Constants.UNREADABLE_SSID)) {
Timber.w("SSID unreadable: missing permissions")
} else Timber.i("Detected valid SSID")
appDataRepository.appState.setCurrentSsid(ssid)
networkEventsFlow.value =
networkEventsFlow.value.copy(
@@ -376,7 +381,7 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
watcherState.isTunnelOffOnMobileDataConditionMet() -> {
Timber.i("$autoTunnel - tunnel off on mobile data met, turning vpn off")
serviceManager.stopVpnService(this)
serviceManager.stopVpnServiceForeground(this)
}
watcherState.isTunnelNotWifiNamePreferredMet(watcherState.currentNetworkSSID) -> {
@@ -402,17 +407,17 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
watcherState.isTrustedWifiConditionMet() -> {
Timber.i("$autoTunnel - tunnel off on trusted wifi condition met, turning vpn off")
serviceManager.stopVpnService(this)
serviceManager.stopVpnServiceForeground(this)
}
watcherState.isTunnelOffOnWifiConditionMet() -> {
Timber.i("$autoTunnel - tunnel off on wifi condition met, turning vpn off")
serviceManager.stopVpnService(this)
serviceManager.stopVpnServiceForeground(this)
}
watcherState.isTunnelOffOnNoConnectivityMet() -> {
Timber.i("$autoTunnel - tunnel off on no connectivity met, turning vpn off")
serviceManager.stopVpnService(this)
serviceManager.stopVpnServiceForeground(this)
}
else -> {
@@ -16,10 +16,12 @@ import com.zaneschepke.wireguardautotunnel.util.Constants
import com.zaneschepke.wireguardautotunnel.util.handshakeStatus
import com.zaneschepke.wireguardautotunnel.util.mapPeerStats
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@AndroidEntryPoint
@@ -35,7 +37,7 @@ class WireGuardTunnelService : ForegroundService() {
@Inject
lateinit var notificationService: NotificationService
private lateinit var job: Job
private var job: Job? = null
private var didShowConnected = false
@@ -117,8 +119,8 @@ class WireGuardTunnelService : ForegroundService() {
)
}
override fun stopService(extras: Bundle?) {
super.stopService(extras)
override fun stopService() {
super.stopService()
lifecycleScope.launch(Dispatchers.IO) {
vpnService.stopTunnel()
didShowConnected = false
@@ -177,8 +179,10 @@ class WireGuardTunnelService : ForegroundService() {
}
private fun cancelJob() {
if (this::job.isInitialized) {
job.cancel()
try {
job?.cancel()
} catch (e : CancellationException) {
Timber.i("Tunnel job cancelled")
}
}
}
@@ -40,7 +40,7 @@ class ShortcutsActivity : ComponentActivity() {
this@ShortcutsActivity, tunnelConfig?.id, isManualStart = true,
)
Action.STOP.name -> serviceManager.stopVpnService(
Action.STOP.name -> serviceManager.stopVpnServiceForeground(
this@ShortcutsActivity,
isManualStop = true,
)
@@ -53,6 +53,11 @@ class AutoTunnelControlTile : TileService() {
}
}
override fun onTileAdded() {
super.onTileAdded()
onStartListening()
}
override fun onDestroy() {
super.onDestroy()
scope.cancel()
@@ -69,13 +69,21 @@ class TunnelControlTile : TileService() {
scope.cancel()
}
override fun onTileAdded() {
super.onTileAdded()
onStartListening()
}
override fun onClick() {
super.onClick()
unlockAndRun {
scope.launch {
try {
if (vpnService.getState() == Tunnel.State.UP) {
serviceManager.stopVpnService(this@TunnelControlTile, isManualStop = true)
serviceManager.stopVpnServiceForeground(
this@TunnelControlTile,
isManualStop = true,
)
} else {
serviceManager.startVpnServiceForeground(
this@TunnelControlTile, manualStartConfig?.id, isManualStart = true,
@@ -10,6 +10,7 @@ import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository
import com.zaneschepke.wireguardautotunnel.module.Kernel
import com.zaneschepke.wireguardautotunnel.module.Userspace
import com.zaneschepke.wireguardautotunnel.util.Constants
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@@ -33,7 +34,7 @@ constructor(
private val scope = CoroutineScope(Dispatchers.IO)
private lateinit var statsJob: Job
private var statsJob: Job? = null
private var backend: Backend = userspaceBackend
@@ -134,8 +135,10 @@ constructor(
}
}
if (state == State.DOWN) {
if (this::statsJob.isInitialized) {
statsJob.cancel()
try {
statsJob?.cancel()
} catch (e : CancellationException) {
Timber.i("Stats job cancelled")
}
}
}
@@ -118,12 +118,12 @@ constructor(
fun readLogCatOutput() =
viewModelScope.launch(viewModelScope.coroutineContext + Dispatchers.IO) {
launch {
Logcatter.logs {
Logcatter.logs(callback = {
logs.add(it)
if (logs.size > Constants.LOG_BUFFER_SIZE) {
logs.removeRange(0, (logs.size - Constants.LOG_BUFFER_SIZE).toInt())
}
}
})
}
}
@@ -4,13 +4,17 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Home
import androidx.compose.material.icons.rounded.QuestionMark
import androidx.compose.material.icons.rounded.Settings
import androidx.compose.ui.res.stringResource
import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.BottomNavItem
import com.zaneschepke.wireguardautotunnel.util.StringValue
sealed class Screen(val route: String) {
data object Main : Screen("main") {
val navItem =
BottomNavItem(
name = "Tunnels",
name = WireGuardAutoTunnel.instance.getString(R.string.tunnels),
route = route,
icon = Icons.Rounded.Home,
)
@@ -19,7 +23,7 @@ sealed class Screen(val route: String) {
data object Settings : Screen("settings") {
val navItem =
BottomNavItem(
name = "Settings",
name = WireGuardAutoTunnel.instance.getString(R.string.settings),
route = route,
icon = Icons.Rounded.Settings,
)
@@ -28,7 +32,7 @@ sealed class Screen(val route: String) {
data object Support : Screen("support") {
val navItem =
BottomNavItem(
name = "Support",
name = WireGuardAutoTunnel.instance.getString(R.string.support),
route = route,
icon = Icons.Rounded.QuestionMark,
)
@@ -28,7 +28,12 @@ fun ConfigurationToggle(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
) {
Text(label, textAlign = TextAlign.Start)
Text(label, textAlign = TextAlign.Start, modifier = Modifier
.weight(
weight = 1.0f,
fill = false,
),
softWrap = true)
Switch(
modifier = modifier,
enabled = enabled,
@@ -71,8 +71,12 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalHapticFeedback
@@ -124,6 +128,25 @@ fun MainScreen(
val sheetState = rememberModalBottomSheetState()
var showBottomSheet by remember { mutableStateOf(false) }
// Nested scroll for control FAB
val nestedScrollConnection = remember {
object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
// Hide FAB
if (available.y < -1) {
isVisible.value = false
}
// Show FAB
if (available.y > 1) {
isVisible.value = true
}
return Offset.Zero
}
}
}
var showDeleteTunnelAlertDialog by remember { mutableStateOf(false) }
var selectedTunnel by remember { mutableStateOf<TunnelConfig?>(null) }
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
@@ -377,8 +400,8 @@ fun MainScreen(
verticalArrangement = Arrangement.Top,
modifier =
Modifier
.fillMaxWidth()
.overscroll(ScrollableDefaults.overscrollEffect()),
.fillMaxSize()
.overscroll(ScrollableDefaults.overscrollEffect()).nestedScroll(nestedScrollConnection),
state = rememberLazyListState(0, uiState.tunnels.count()),
userScrollEnabled = true,
reverseLayout = false,
@@ -26,6 +26,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
@@ -37,6 +37,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
@@ -122,7 +123,13 @@ fun SupportScreen(
Text(
stringResource(id = R.string.docs_description),
textAlign = TextAlign.Justify,
modifier = Modifier.padding(start = 10.dp),
modifier = Modifier
.padding(start = 10.dp)
.weight(
weight = 1.0f,
fill = false,
),
softWrap = true
)
}
Icon(
@@ -270,8 +277,21 @@ fun SupportScreen(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(25.dp),
) {
Text("Version: ${BuildConfig.VERSION_NAME}", modifier = Modifier.focusable())
Text("Mode: ${if (uiState.settings.isKernelEnabled) "Kernel" else "Userspace"}")
val version = buildAnnotatedString {
append(stringResource(id = R.string.version))
append(": ")
append(BuildConfig.VERSION_NAME)
}
val mode = buildAnnotatedString {
append(stringResource(R.string.mode))
append(": ")
when(uiState.settings.isKernelEnabled){
true -> append(stringResource(id = R.string.kernel))
false -> append(stringResource(id = R.string.userspace))
}
}
Text(version.text, modifier = Modifier.focusable())
Text(mode.text)
}
}
}
@@ -35,4 +35,6 @@ object Constants {
const val TUNNEL_EXTRA_KEY = "tunnelId"
const val UNREADABLE_SSID = "<unknown ssid>"
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_channel_background" />
<foreground android:drawable="@mipmap/ic_channel_foreground" />
</adaptive-icon>
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

+1
View File
@@ -0,0 +1 @@
unqualifiedResLocale=en-US
+191
View File
@@ -0,0 +1,191 @@
<resources>
<string name="app_name">WG Tunnel</string>
<string name="vpn_channel_id">VPN Kanalı</string>
<string name="vpn_channel_name">VPN Bildirim Kanalı</string>
<string name="watcher_channel_id">İzleyici Kanalı</string>
<string name="watcher_channel_name">İzleyici Bildirim Kanalı</string>
<string name="foreground_file">FOREGROUND_FILE</string>
<string name="github_url" translatable="false">https://github.com/zaneschepke/wgtunnel/issues</string>
<string name="docs_url" translatable="false">https://zaneschepke.com/wgtunnel-docs/overview.html</string>
<string name="privacy_policy_url" translatable="false">https://zaneschepke.com/wgtunnel-docs/privacypolicy.html</string>
<string name="error_file_extension">Dosya bir .conf veya .zip değil</string>
<string name="turn_off_tunnel">Eylem tünelin kapalı olmasını gerektiriyor</string>
<string name="no_tunnels">Henüz tünel eklenmedi!</string>
<string name="tunnel_exists">Tünel adı zaten var</string>
<string name="discord_url" translatable="false">https://discord.gg/rbRRNh6H7V</string>
<string name="watcher_notification_title">Bildirim Hizmeti</string>
<string name="watcher_notification_text_active">Ağ durumu değişikliklerini izleme: etkin</string>
<string name="watcher_notification_text_paused">Ağ durumu değişikliklerini izleme: duraklatıldı</string>
<string name="tunnel_start_title">VPN bağlandı</string>
<string name="tunnel_start_text">Tünele bağlı</string>
<string name="vpn_permission_required">Uygulamanın düzgün çalışması için VPN izni gereklidir. Bu izin başlatılmıyorsa, lütfen resmi WireGuard mobil uygulaması için telefon ayarlarınızda \"Her zaman açık VPN\" i devre dışı bırakın ve tekrar deneyin.</string>
<string name="notification_permission_required">Bildirim izni gerekli.</string>
<string name="open_settings">Ayarları</string>
<string name="add_trusted_ssid">Güvenilir wifi adı ekleyin</string>
<string name="tunnels">Tüneller</string>
<string name="enable_auto_tunnel">Otomatik tünellemeyi başlat</string>
<string name="disable_auto_tunnel">Otomatik tünellemeyi durdur</string>
<string name="tunnel_mobile_data">Mobil veri üzerinde tünel</string>
<string name="background_location_reason">Arka planda Wi-Fi SSID\'yi almak için \"Her zaman izin ver\" konum izni gereklidir. Bu özellik için izin gereklidir.</string>
<string name="location_permission_reason">Bu özelliğin düzgün çalışması için konum izni gereklidir.</string>
<string name="one_tunnel_required">Bu özelliği kullanmak için en az bir tünel gereklidir</string>
<string name="retry">"Yeniden dene"</string>
<string name="privacy_policy">Gizlilik Politikasını Görüntüle</string>
<string name="okay">Tamam</string>
<string name="tunnel_on_ethernet">Ethernet üzerinde tünel</string>
<string name="prominent_background_location_message">Bu özellik, uygulama kapalıyken bile Wi-Fi SSID izlemesini etkinleştirmek için arka plan konumu izni gerektirir. Daha fazla bilgi için lütfen Destek ekranında bağlantısı verilen Gizlilik Politikasına bakın.</string>
<string name="prominent_background_location_title">Arka Plan Konum Açıklaması</string>
<string name="thank_you">WG Tunnel\'i kullandığınız için teşekkür ederiz!</string>
<string name="trusted_ssid_empty_description">SSID girin</string>
<string name="trusted_ssid_value_description">SSID\'yi Gönder</string>
<string name="config_validation" translatable="false">[Interface]</string>
<string name="add_tunnels_text">Dosyadan veya ZIP\'ten ekle</string>
<string name="open_file">Dosya Aç</string>
<string name="add_from_qr">QR kodundan ekle</string>
<string name="qr_scan">QR Tarama</string>
<string name="tunnel_edit">Tünel Düzenleme</string>
<string name="tunnel_name">Tünel Adı</string>
<string name="add_tunnel">Tünel Ekle</string>
<string name="exclude">Hariç tut</string>
<string name="include">Dahil et</string>
<string name="tunnel_all">Tünel tüm uygulamalar</string>
<string name="config_changes_saved">Yapılandırma değişiklikleri kaydedildi.</string>
<string name="save_changes">Kaydet</string>
<string name="icon">Simge</string>
<string name="no_thanks">Hayır, teşekkürler</string>
<string name="turn_on"></string>
<string name="map">Harita</string>
<string name="bad_config">Hatalı yapılandırma. Lütfen tekrar deneyin.</string>
<string name="config_interface">Arayüz</string>
<string name="public_key">Genel anahtar</string>
<string name="barcode_downloading">Barkod UI modülünün indirilmesi bekleniyor.</string>
<string name="barcode_downloading_message">Barkod modülü indiriliyor. Tekrar deneyin.</string>
<string name="addresses">Adresler</string>
<string name="dns_servers">DNS sunucuları</string>
<string name="mtu">MTU</string>
<string name="peer">Akran</string>
<string name="allowed_ips">İzin verilen IP\'ler</string>
<string name="endpoint">Uç nokta</string>
<string name="transfer">Nakil</string>
<string name="last_handshake">Son tokalaşma</string>
<string name="name">Ad</string>
<string name="restart">Tüneli Yeniden Başlat</string>
<string name="vpn_connection_failed">Bağlantı kurulamadı</string>
<string name="failed_connection_to">- ile bağlantı kurulamadı</string>
<string name="initial_connection_failure_message">30 saniye boyunca yanıt alınamadıktan sonra sunucuya bağlanmaya çalışılıyor.</string>
<string name="lost_connection_failure_message">Bir dakikadan fazla yanıt alınamadıktan sonra sunucuya yeniden bağlanmaya çalışılıyor.</string>
<string name="always_on_vpn_support">Her Zaman Açık VPN\'e İzin Ver</string>
<string name="select_tunnel_message">Lütfen önce bir tünel seçin</string>
<string name="location_services_not_detected">Konum Hizmetleri Algılanmadı</string>
<string name="check_again">Tekrar kontrol et</string>
<string name="detecting_location_services_disabled">Konum Hizmetlerini Algılama devre dışı</string>
<string name="precise_location_message">Bu özellik, Wi-Fi SSID adına erişmek için kesin konum gerektirir. Lütfen kesin konumu buradan veya uygulama ayarlarından etkinleştirin.</string>
<string name="request">İstek</string>
<string name="toggle_vpn">VPN\'i aç/kapat</string>
<string name="no_tunnel_available">Tünel yok</string>
<string name="hint_search_packages">Paket ara</string>
<string name="clear_icon">Simgeyi Temizle</string>
<string name="search_icon">Simge Ara</string>
<string name="attempt_connection">Bağlantı kurulmaya çalışılıyor..</string>
<string name="vpn_starting">VPN başlatılıyor</string>
<string name="db_name">wg-tunnel-db</string>
<string name="scanning_qr">QR taranıyor</string>
<string name="qr_result_failed">QR taraması başarısız oldu</string>
<string name="none">Güvenilir wifi adı yok</string>
<string name="never">Asla</string>
<string name="stream_failed">Dosya akışı açılamadı.</string>
<string name="other">Diğer</string>
<string name="auto_tunneling">Otomatik tünelleme</string>
<string name="select_tunnel">Kullanılacak tüneli seçin</string>
<string name="vpn_on">VPN açık</string>
<string name="vpn_off">VPN kapalı</string>
<string name="default_vpn_on">Birincil VPN açık</string>
<string name="default_vpn_off">Birincil VPN kapalı</string>
<string name="create_import">Sıfırdan oluştur</string>
<string name="turn_off_auto">Eylem, otomatik tünelin devre dışı bırakılmasını veya duraklatılmasını gerektirir</string>
<string name="turn_on_tunnel">Eylem aktif tünel gerektirir</string>
<string name="add_peer">Akran ekle</string>
<string name="done">Bitti</string>
<string name="interface_">Arayüz</string>
<string name="rotate_keys">Tuşları döndür</string>
<string name="private_key">Özel anahtar</string>
<string name="copy_public_key">Genel anahtarı kopyala</string>
<string name="base64_key">base64 anahtarı</string>
<string name="comma_separated_list">virgülle ayrılmış liste</string>
<string name="listen_port">Portu dinle</string>
<string name="random">(rastgele)</string>
<string name="optional">(isteğe bağlı)</string>
<string name="optional_no_recommend">(isteğe bağlı, önerilmez)</string>
<string name="preshared_key">Ön paylaşımlı anahtar</string>
<string name="seconds">saniye</string>
<string name="persistent_keepalive">Kalıcı canlı tutma</string>
<string name="cancel">İptal</string>
<string name="error_authentication_failed">Kimlik doğrulama başarısız oldu</string>
<string name="error_authorization_failed">Yetkilendirilemedi</string>
<string name="enabled_app_shortcuts">Uygulama kısayollarını etkinleştir</string>
<string name="export_configs">Yapılandırmaları dışa aktar</string>
<string name="battery_saver">Pil tasarrufu (beta)</string>
<string name="location_services_required">Gerekli konum hizmetleri</string>
<string name="background_location_required">Arka plan konumu gerekli</string>
<string name="precise_location_required">Kesin konum gerekli</string>
<string name="unknown_error">Bilinmeyen bir hata oluştu</string>
<string name="exported_configs_message">Yapılandırmalar indirmelere aktarıldı</string>
<string name="tunnel_on_wifi">Güvenilmeyen kablosuz internet bağlantısında tünel</string>
<string name="my_email" translatable="false">support@zaneschepke.com</string>
<string name="email_subject">WG Tunnel Desteği</string>
<string name="email_chooser">Bir e-posta gönderin…</string>
<string name="go">git</string>
<string name="docs_description">Belgeleri okuyun</string>
<string name="discord_description">Topluluğa katılın</string>
<string name="email_description">Bana e-posta gönder</string>
<string name="support_help_text">Sorun yaşıyorsanız, iyileştirme fikirleriniz varsa veya sadece etkileşimde bulunmak istiyorsanız, aşağıdaki kaynaklar mevcuttur:</string>
<string name="kernel">Çekirdek</string>
<string name="use_kernel">Çekirdek modülünü kullan</string>
<string name="error_ssid_exists">SSID zaten var</string>
<string name="error_root_denied">Kök kabuğu reddedildi</string>
<string name="error_no_file_explorer">Yüklü dosya gezgini yok</string>
<string name="error_no_scan">Kod taranmadı</string>
<string name="error_invalid_code">Geçersiz QR kodu</string>
<string name="error_none">Hata yok</string>
<string name="error_file_read">Dosya okunamadı</string>
<string name="location_service_missing">Konum Hizmetleri Algılanmadı</string>
<string name="location_services_missing_message">Uygulama, cihazınızda etkinleştirilen herhangi bir konum hizmeti algılamıyor. Cihaza bağlı olarak bu, güvenilmeyen wifi özelliğinin wifi adını okuyamamasına neden olabilir. Yine de devam etmek ister misiniz?</string>
<string name="auto_tunnel_title">Otomatik Tünel Hizmeti</string>
<string name="delete_tunnel">Tüneli sil</string>
<string name="delete_tunnel_message">Bu tüneli silmek istediğinizden emin misiniz?</string>
<string name="yes">Evet</string>
<string name="resume">Devam et</string>
<string name="pause">Duraklat</string>
<string name="paused">duraklatıldı</string>
<string name="active">aktif</string>
<string name="tunneling_apps">Tünelleme uygulamaları</string>
<string name="included">dahil</string>
<string name="excluded">hariç</string>
<string name="all">tümü</string>
<string name="always_on_disabled">Her zaman açık VPN bir tünel başlatmaya çalıştı, ancak bu özellik ayarlarda devre dışı bırakıldı.</string>
<string name="no_email_detected">E-posta uygulaması algılanmadı</string>
<string name="no_browser_detected">Tarayıcı algılanmadı</string>
<string name="logs_saved">İndirilenlere kaydedilen günlükler</string>
<string name="open_issue">Bir sorun açın</string>
<string name="read_logs">Günlükleri okuyun</string>
<string name="auto">(otomatik)</string>
<string name="config_parse_error">Yapılandırma ayrıştırılamadı</string>
<string name="incorrect_pin">PIN kodu yanlış</string>
<string name="pin_created">Pin başarıyla oluşturuldu</string>
<string name="enter_pin">PIN kodunuzu girin</string>
<string name="create_pin">PIN kodu oluştur</string>
<string name="enable_app_lock">Uygulama kilidi etkinleştirildi</string>
<string name="restart_on_ping">Ping başarısız olduğunda yeniden başlat (beta)</string>
<string name="mobile_data_tunnel">Mobil veri tüneli olarak ayarla</string>
<string name="set_primary_tunnel">Birincil tünel olarak ayarla</string>
<string name="use_tunnel_on_wifi_name">Wifi adında tünel kullan</string>
<string name="no_wifi_names_configured">Bu tünel için yapılandırılmış wifi adı yok</string>
<string name="general">Genel</string>
<string name="edit_tunnel">Tüneli düzenle</string>
<string name="tunnel">Tünel</string>
<string name="disabled">devre dışı</string>
<string name="auto_on">Otomatik ayarlamayı sürdür</string>
<string name="auto_off">Otomatik ayarlamayı duraklat</string>
<string name="auto_tun_on">Otomatik tüneli sürdür</string>
<string name="auto_tun_off">Otomatik tüneli duraklat</string>
</resources>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_channel_background">#1D1A20</color>
</resources>
+6 -1
View File
@@ -135,7 +135,7 @@
<string name="email_subject">WG Tunnel Support</string>
<string name="email_chooser">Send an email…</string>
<string name="go">go</string>
<string name="docs_description">Read the docs (WIP)</string>
<string name="docs_description">Read the docs</string>
<string name="discord_description">Join the community</string>
<string name="email_description">Send me an email</string>
<string name="support_help_text">If you are experiencing issues, have improvement ideas, or just want to engage, the following resources are available:</string>
@@ -188,4 +188,9 @@
<string name="auto_off">Pause auto tun</string>
<string name="auto_tun_on">Resume auto-tunnel</string>
<string name="auto_tun_off">Pause auto-tunnel</string>
<string name="version">Version</string>
<string name="mode">Mode</string>
<string name="userspace">Userspace</string>
<string name="settings">Settings</string>
<string name="support">Support</string>
</resources>
+2 -2
View File
@@ -1,7 +1,7 @@
object Constants {
const val VERSION_NAME = "3.4.0"
const val VERSION_NAME = "3.4.2"
const val JVM_TARGET = "17"
const val VERSION_CODE = 34000
const val VERSION_CODE = 34200
const val TARGET_SDK = 34
const val MIN_SDK = 26
const val APP_ID = "com.zaneschepke.wireguardautotunnel"
@@ -0,0 +1,5 @@
What's new:
- Improved auto tunnel reliability
- Improved tile sync
- Added AndroidTV assets
- Added apk fingerprint
@@ -0,0 +1,5 @@
What's new:
- Fix stop tunnel regression
- Add logs obfuscation
- Add hide FAB on scroll
- Add Turkish localization
+1 -1
View File
@@ -20,7 +20,7 @@ pinLockCompose = "1.0.3"
roomVersion = "2.6.1"
timber = "5.0.1"
tunnel = "1.0.20230706"
androidGradlePlugin = "8.3.1"
androidGradlePlugin = "8.4.0-rc02"
kotlin = "1.9.23"
ksp = "1.9.23-1.0.19"
composeBom = "2024.03.00"
+2 -2
View File
@@ -1,8 +1,8 @@
#Wed Oct 11 22:39:21 EDT 2023
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
distributionSha256Sum=3e1af3ae886920c3ac87f7a91f816c0c7c436f276a6eefdb3da152100fef72ae
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
distributionSha256Sum=9631d53cf3e74bfa726893aee1f8994fee4e060c401335946dba2156f440f24c
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
@@ -3,14 +3,29 @@ package com.zaneschepke.logcatter
import com.zaneschepke.logcatter.model.LogMessage
object Logcatter {
fun logs(callback: (input: LogMessage) -> Unit) {
private val findKeyRegex = """[A-Za-z0-9+/]{42}[AEIMQUYcgkosw480]=""".toRegex()
private val findIpv6AddressRegex = """(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))""".toRegex()
private val findIpv4AddressRegex = """((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}""".toRegex()
private val findTunnelNameRegex = """(?<=tunnel ).*?(?= UP| DOWN)""".toRegex()
fun logs(callback: (input: LogMessage) -> Unit, obfuscator: (log : String) -> String = { log -> this.obfuscator(log)}){
clear()
Runtime.getRuntime().exec("logcat -v epoch")
.inputStream
.bufferedReader()
.useLines { lines ->
lines.forEach { callback(LogMessage.from(it)) }
lines.forEach { callback(LogMessage.from(obfuscator(it))) }
}
}
private fun obfuscator(log : String) : String {
return findKeyRegex.replace(log, "<crypto-key>").let { first ->
findIpv6AddressRegex.replace(first, "<ipv6-address>").let { second ->
findTunnelNameRegex.replace(second, "<tunnel>")
}
}.let{ last -> findIpv4AddressRegex.replace(last,"<ipv4-address>") }
}
fun clear() {