Compare commits

..

5 Commits

Author SHA1 Message Date
Zane Schepke 4da05e23f1 chore: release v4.1.5 2025-11-07 23:58:45 -05:00
Zane Schepke 6749719e21 chore: bump deps, update app description 2025-11-07 23:50:07 -05:00
Zane Schepke 1c160ff5f9 fix: network monitor should ignore default network VPN events
#1038
2025-11-07 21:54:16 -05:00
Zane Schepke 861440b7db fix: disable metered option for Android 9 and lower
closes #1044

#1031
2025-11-07 20:49:32 -05:00
Zane Schepke bdb0d27b53 ci: add aab build workflow 2025-11-05 00:47:46 -05:00
8 changed files with 201 additions and 60 deletions
+130
View File
@@ -0,0 +1,130 @@
name: build-aab
permissions:
contents: read
on:
workflow_dispatch:
inputs:
build_type:
type: choice
description: "Build type"
required: true
default: release
options:
- release
flavor:
type: choice
description: "Product flavor"
required: true
default: google
options:
- google
secrets:
SIGNING_KEY_ALIAS:
required: false
SIGNING_KEY_PASSWORD:
required: false
SIGNING_STORE_PASSWORD:
required: false
SERVICE_ACCOUNT_JSON:
required: false
KEYSTORE:
required: false
workflow_call:
inputs:
build_type:
type: string
description: "Build type"
required: true
default: release
flavor:
type: string
description: "Product flavor"
required: false
default: google
secrets:
SIGNING_KEY_ALIAS:
required: false
SIGNING_KEY_PASSWORD:
required: false
SIGNING_STORE_PASSWORD:
required: false
SERVICE_ACCOUNT_JSON:
required: false
KEYSTORE:
required: false
env:
UPLOAD_DIR_ANDROID: android_artifacts
jobs:
build:
runs-on: ubuntu-latest
env:
SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS }}
SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }}
SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }}
KEY_STORE_FILE: 'android_keystore.jks'
KEY_STORE_LOCATION: ${{ github.workspace }}/app/keystore/
outputs:
UPLOAD_DIR_ANDROID: ${{ env.UPLOAD_DIR_ANDROID }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Set up JDK 17
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '17'
cache: gradle
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Decode Keystore
id: decode_keystore
uses: timheuer/base64-to-file@v1.2
with:
fileName: ${{ env.KEY_STORE_FILE }}
fileDir: ${{ env.KEY_STORE_LOCATION }}
encodedString: ${{ secrets.KEYSTORE }}
- name: Create keystore path env var
if: ${{ inputs.build_type != 'debug' }}
run: |
store_path=${{ env.KEY_STORE_LOCATION }}${{ env.KEY_STORE_FILE }}
echo "KEY_STORE_PATH=$store_path" >> $GITHUB_ENV
- name: Build AAB (noSplits=true)
run: |
flavor=${{ inputs.flavor }}
build_type=${{ inputs.build_type }}
case $build_type in
"release")
./gradlew :app:bundle${flavor^}Release \
-PnoSplits=true \
--info
;;
esac
- name: Get release AAB path
id: aab-path
run: |
AAB_PATH=$(find app/build/outputs/bundle -iname "*google*release*.aab" -type f | head -1)
if [ -z "$AAB_PATH" ]; then
echo "Error: AAB not found!" >&2
exit 1
fi
echo "Found AAB: $AAB_PATH"
echo "path=$AAB_PATH" >> $GITHUB_OUTPUT
- name: Upload AAB Artifact
uses: actions/upload-artifact@v5
with:
name: google-play-aab
path: ${{ steps.aab-path.outputs.path }}
retention-days: 7
if-no-files-found: error
@@ -1,5 +1,6 @@
package com.zaneschepke.wireguardautotunnel.ui.screens.settings.lockdown
import android.os.Build
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
@@ -103,12 +104,14 @@ fun LockdownSettingsScreen(viewModel: LockdownViewModel = hiltViewModel()) {
trailing = { ThemedSwitch(checked = bypassLan, onClick = { bypassLan = it }) },
onClick = { bypassLan = !bypassLan },
)
SurfaceRow(
leading = { Icon(Icons.Outlined.DataUsage, contentDescription = null) },
title = stringResource(R.string.metered_tunnel),
trailing = { ThemedSwitch(checked = metered, onClick = { metered = it }) },
onClick = { metered = !metered },
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
SurfaceRow(
leading = { Icon(Icons.Outlined.DataUsage, contentDescription = null) },
title = stringResource(R.string.metered_tunnel),
trailing = { ThemedSwitch(checked = metered, onClick = { metered = it }) },
onClick = { metered = !metered },
)
}
SurfaceRow(
leading = {
Icon(ImageVector.vectorResource(R.drawable.host), contentDescription = null)
@@ -1,5 +1,6 @@
package com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.settings
import android.os.Build
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
@@ -148,36 +149,38 @@ fun TunnelSettingsScreen(viewModel: TunnelViewModel) {
},
onClick = { viewModel.setIpv4Preferred(!tunnel.isIpv4Preferred) },
)
SurfaceRow(
leading = {
Icon(
Icons.Outlined.DataUsage,
contentDescription = null,
tint =
if (sharedUiState.proxyEnabled) Disabled
else MaterialTheme.colorScheme.onSurface,
)
},
title = stringResource(R.string.metered_tunnel),
enabled = !sharedUiState.proxyEnabled,
description =
if (sharedUiState.proxyEnabled) {
{
DescriptionText(
stringResource(R.string.unavailable_in_mode),
disabled = true,
)
}
} else null,
trailing = {
ThemedSwitch(
checked = tunnel.isMetered,
onClick = { viewModel.setMetered(it) },
enabled = !sharedUiState.proxyEnabled,
)
},
onClick = { viewModel.setMetered(!tunnel.isMetered) },
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
SurfaceRow(
leading = {
Icon(
Icons.Outlined.DataUsage,
contentDescription = null,
tint =
if (sharedUiState.proxyEnabled) Disabled
else MaterialTheme.colorScheme.onSurface,
)
},
title = stringResource(R.string.metered_tunnel),
enabled = !sharedUiState.proxyEnabled,
description =
if (sharedUiState.proxyEnabled) {
{
DescriptionText(
stringResource(R.string.unavailable_in_mode),
disabled = true,
)
}
} else null,
trailing = {
ThemedSwitch(
checked = tunnel.isMetered,
onClick = { viewModel.setMetered(it) },
enabled = !sharedUiState.proxyEnabled,
)
},
onClick = { viewModel.setMetered(!tunnel.isMetered) },
)
}
}
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
object Constants {
const val VERSION_NAME = "4.1.4"
const val VERSION_CODE = 40104
const val VERSION_NAME = "4.1.5"
const val VERSION_CODE = 40105
const val TARGET_SDK = 36
const val MIN_SDK = 26
const val APP_ID = "com.zaneschepke.wireguardautotunnel"
@@ -0,0 +1,3 @@
What's new:
- Fixes crash on older Android versions where metered tunnel override is unavailable
- Fixes auto-tunnel network monitor incorrectly detecting VPN changes
@@ -1,15 +1,13 @@
- Tunnel Import Methods: Easily add tunnels using .conf files, ZIP archives, manual entry, or QR code scanning.
- Auto-Tunneling: Automatically activate tunnels based on Wi-Fi SSID, Ethernet connections, or mobile data networks.
- Split Tunneling: Flexible support for routing specific apps or traffic through the VPN.
- WireGuard Modes: Full compatibility with WireGuard in both kernel and userspace implementations.
- AmneziaWG Integration: Userspace mode for AmneziaWG, providing robust censorship evasion.
- Always-On VPN: Ensures continuous protection with Android's Always-On VPN feature.
- Quick Controls: Quick Settings tile and home screen shortcuts for easy VPN toggling.
- Automation Support: Intent-based automation for controlling tunnels.
- Auto-Restore: Seamlessly restores auto-tunneling and active tunnels after device restarts or app updates.
- Proxying Options: Built-in HTTP and SOCKS5 proxy support within tunnels.
- Lockdown Mode: Custom kill switch for maximum leak prevention and security.
- Dynamic DNS Handling: Detects and updates DNS changes without tunnel restarts.
- Monitoring Tools: Advanced tunnel monitoring features for tunnel performance monitoring.
- Android TV Support: Android TV support for secure streaming and browsing.
- Advanced DNS: DNS over HTTPS support for tunnel endpoint resolutions.
WG Tunnel is a WireGuard VPN client that strikes the balance between simplicity and robustness, making it the ideal client for casual and power users alike.
Whether you simply want to automate when you're connected to your VPN or you're a power user with advanced privacy use cases, WG Tunnel has you covered.
- **Auto-Tunneling:** Automatically activate tunnels based on Wi-Fi SSID, Ethernet connections, or mobile data networks.
- **Split Tunneling:** Flexible support for routing specific apps or traffic through the VPN.
- **App Modes:** Support for multiple tunnel modes, including standard VPN, kernel, lockdown (custom kill switch), and proxy modes.
- **AmneziaWG Integration:** Full support for AmneziaWG, providing robust censorship evasion.
- **Proxying Options:** Built-in HTTP and SOCKS5 proxy support allowing third-party apps to tunnel their traffic.
- **Quick Controls:** Quick Settings tile and home screen shortcuts for easy toggling actions.
- **Automation Support:** Intent-based automation for controlling tunnels and auto-tunneling.
- **Dynamic DNS Handling:** Detects and updates DNS changes without tunnel restarts.
- **Monitoring Tools:** Advanced tunnel monitoring features for tunnel performance monitoring.
- **Android TV Support:** Android TV support for nearly all app features.
+7 -7
View File
@@ -1,7 +1,7 @@
[versions]
accompanist = "0.37.3"
activityCompose = "1.11.0"
amneziawgAndroid = "2.2.1"
amneziawgAndroid = "2.2.2"
androidx-junit = "1.3.0"
icmp4a = "1.0.0"
ipaddress = "5.5.1"
@@ -11,18 +11,18 @@ roomdatabasebackup = "1.1.0"
shizuku = "13.1.5"
appcompat = "1.7.1"
coreKtx = "1.17.0"
datastorePreferences = "1.2.0-beta01"
datastorePreferences = "1.2.0-rc01"
desugar_jdk_libs = "2.1.5"
espressoCore = "3.7.0"
hiltAndroid = "2.57.2"
hiltCompiler = "1.3.0"
hiltNavigationCompose = "1.3.0"
navigation3 = "1.0.0-beta01"
navigation3 = "1.0.0-rc01"
junit = "4.13.2"
kotlinx-serialization-json = "1.9.0"
ktorClientCore = "3.3.1"
lifecycle-runtime-compose = "2.9.4"
material3 = "1.5.0-alpha07"
material3 = "1.5.0-alpha08"
pinLockCompose = "1.0.5"
qrose = "1.0.1"
roomVersion = "2.8.3"
@@ -33,19 +33,19 @@ tunnel = "1.4.0"
androidGradlePlugin = "8.12.3"
kotlin = "2.2.21"
ksp = "2.3.0"
composeBom = "2025.10.01"
composeBom = "2025.11.00"
compose = "1.9.4"
icons = "1.7.8"
workRuntimeKtxVersion = "2.11.0"
zxingAndroidEmbedded = "4.3.0"
coreSplashscreen = "1.0.1"
coreSplashscreen = "1.2.0"
gradlePlugins-grgit = "5.3.3"
reorderable = "3.0.0"
material = "1.13.0"
storage = "1.6.0"
ktfmt = "0.25.0"
licensee = "1.14.1"
lifecycleViewmodelNavigation3 = "2.10.0-beta01"
lifecycleViewmodelNavigation3 = "2.10.0-rc01"
[bundles]
# Core AndroidX foundations
@@ -98,6 +98,10 @@ class AndroidNetworkMonitor(
network: Network,
caps: NetworkCapabilities,
) {
if (caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
Timber.d("Ignoring VPN default network change: $network")
return
}
trySend(TransportEvent.CapabilitiesChanged(network, caps))
}
}