Compare commits

..

12 Commits

Author SHA1 Message Date
Zane Schepke 6d8953bf4e fmt 2025-04-29 07:30:37 -04:00
Zane Schepke 375cc978c5 add permissions 2025-04-29 07:25:50 -04:00
Zane Schepke 0f4399bb62 remove push 2025-04-29 07:20:36 -04:00
Zane Schepke 00fb8c7d29 fix: publish 2025-04-29 07:20:11 -04:00
Zane Schepke ab271225de fix: checksum 2025-04-29 07:15:25 -04:00
Zane Schepke 9180344043 fix: file pattern 2025-04-29 07:01:02 -04:00
Zane Schepke c5a2f6e311 fix: upload 2025-04-29 06:40:49 -04:00
Zane Schepke 4780c9bd6f test 2025-04-29 05:43:50 -04:00
Zane Schepke 6350ab6752 dispatch 2025-04-29 05:42:15 -04:00
Zane Schepke f6cf33b996 ci: remove nightly push 2025-04-29 05:40:11 -04:00
Zane Schepke 9872f4da2b ci: fix build upload 2025-04-29 05:39:05 -04:00
Zane Schepke b6fd93eae5 ci: refactor and fix ci bugs 2025-04-29 05:35:16 -04:00
21 changed files with 78 additions and 310 deletions
+3 -3
View File
@@ -110,9 +110,9 @@ android {
} }
compileOptions { compileOptions {
isCoreLibraryDesugaringEnabled = true
sourceCompatibility = JavaVersion.VERSION_17 sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17
isCoreLibraryDesugaringEnabled = true
} }
kotlinOptions { jvmTarget = Constants.JVM_TARGET } kotlinOptions { jvmTarget = Constants.JVM_TARGET }
buildFeatures { buildFeatures {
@@ -124,6 +124,7 @@ android {
licensee { licensee {
Constants.allowedLicenses.forEach { allow(it) } Constants.allowedLicenses.forEach { allow(it) }
allowUrl(Constants.XZING_LICENSE_URL) allowUrl(Constants.XZING_LICENSE_URL)
allowUrl("https://rafaellins.mit-license.org/2021/")
} }
applicationVariants.all { applicationVariants.all {
@@ -197,7 +198,6 @@ dependencies {
implementation(libs.kotlinx.serialization.json) implementation(libs.kotlinx.serialization.json)
implementation(libs.zxing.android.embedded) implementation(libs.zxing.android.embedded)
implementation(libs.material.icons.extended) implementation(libs.material.icons.extended)
implementation(libs.androidx.biometric.ktx) implementation(libs.androidx.biometric.ktx)
@@ -210,7 +210,7 @@ dependencies {
implementation(libs.androidx.work.runtime) implementation(libs.androidx.work.runtime)
implementation(libs.androidx.hilt.work) implementation(libs.androidx.hilt.work)
implementation(libs.qrose) implementation(libs.qrcode.kotlin)
implementation(libs.semver4j) implementation(libs.semver4j)
implementation(libs.ktor.client.core) implementation(libs.ktor.client.core)
+4 -4
View File
@@ -62,10 +62,6 @@
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.App.Start" android:theme="@style/Theme.App.Start"
tools:targetApi="tiramisu"> tools:targetApi="tiramisu">
<activity
android:name="com.journeyapps.barcodescanner.CaptureActivity"
android:screenOrientation="portrait"
tools:replace="screenOrientation" />
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
@@ -82,6 +78,10 @@
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" /> <action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity
android:name="com.journeyapps.barcodescanner.CaptureActivity"
android:screenOrientation="portrait"
tools:replace="screenOrientation" />
<activity <activity
android:name=".core.shortcut.ShortcutsActivity" android:name=".core.shortcut.ShortcutsActivity"
@@ -22,6 +22,7 @@ import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.rounded.Settings
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@@ -53,6 +54,7 @@ import com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.disclosure.Loca
import com.zaneschepke.wireguardautotunnel.ui.screens.main.MainScreen import com.zaneschepke.wireguardautotunnel.ui.screens.main.MainScreen
import com.zaneschepke.wireguardautotunnel.ui.screens.main.autotunnel.TunnelAutoTunnelScreen import com.zaneschepke.wireguardautotunnel.ui.screens.main.autotunnel.TunnelAutoTunnelScreen
import com.zaneschepke.wireguardautotunnel.ui.screens.main.config.ConfigScreen import com.zaneschepke.wireguardautotunnel.ui.screens.main.config.ConfigScreen
import com.zaneschepke.wireguardautotunnel.ui.screens.main.scanner.ScannerScreen
import com.zaneschepke.wireguardautotunnel.ui.screens.main.splittunnel.SplitTunnelScreen import com.zaneschepke.wireguardautotunnel.ui.screens.main.splittunnel.SplitTunnelScreen
import com.zaneschepke.wireguardautotunnel.ui.screens.main.tunneloptions.TunnelOptionsScreen import com.zaneschepke.wireguardautotunnel.ui.screens.main.tunneloptions.TunnelOptionsScreen
import com.zaneschepke.wireguardautotunnel.ui.screens.pin.PinLockScreen import com.zaneschepke.wireguardautotunnel.ui.screens.pin.PinLockScreen
@@ -296,10 +298,11 @@ class MainActivity : AppCompatActivity() {
appUiState.tunnels appUiState.tunnels
.firstOrNull { it.id == args.id } .firstOrNull { it.id == args.id }
?.let { config -> ?.let { config ->
TunnelOptionsScreen(config, viewModel, appViewState) TunnelOptionsScreen(config, viewModel)
} }
} }
composable<Route.Lock> { PinLockScreen(viewModel) } composable<Route.Lock> { PinLockScreen(viewModel) }
composable<Route.Scanner> { ScannerScreen(viewModel) }
composable<Route.KillSwitch> { composable<Route.KillSwitch> {
KillSwitchScreen(appUiState, viewModel) KillSwitchScreen(appUiState, viewModel)
} }
@@ -26,6 +26,7 @@ data class TunnelConf(
val pingIp: String? = null, val pingIp: String? = null,
val isEthernetTunnel: Boolean = false, val isEthernetTunnel: Boolean = false,
val isIpv4Preferred: Boolean = true, val isIpv4Preferred: Boolean = true,
val useCache: Boolean = false,
@Transient private var stateChangeCallback: ((Any) -> Unit)? = null, @Transient private var stateChangeCallback: ((Any) -> Unit)? = null,
) : Tunnel, org.amnezia.awg.backend.Tunnel { ) : Tunnel, org.amnezia.awg.backend.Tunnel {
@@ -110,6 +111,8 @@ data class TunnelConf(
override fun isIpv4ResolutionPreferred(): Boolean = isIpv4Preferred override fun isIpv4ResolutionPreferred(): Boolean = isIpv4Preferred
override fun useCache(): Boolean = useCache
override fun onStateChange(newState: org.amnezia.awg.backend.Tunnel.State) { override fun onStateChange(newState: org.amnezia.awg.backend.Tunnel.State) {
stateChangeCallback?.invoke(newState) stateChangeCallback?.invoke(newState)
} }
@@ -1,6 +1,6 @@
package com.zaneschepke.wireguardautotunnel.domain.enums package com.zaneschepke.wireguardautotunnel.domain.enums
enum class ConfigType { enum class ConfigType {
AM, AMNEZIA,
WG, WG,
} }
@@ -29,6 +29,8 @@ sealed class Route {
@Serializable data object Lock : Route() @Serializable data object Lock : Route()
@Serializable data object Scanner : Route()
@Serializable data object License : Route() @Serializable data object License : Route()
@Serializable data class Config(val id: Int) : Route() @Serializable data class Config(val id: Int) : Route()
@@ -4,7 +4,16 @@ import android.os.Build
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.* import androidx.compose.material.icons.rounded.Add
import androidx.compose.material.icons.rounded.CopyAll
import androidx.compose.material.icons.rounded.Delete
import androidx.compose.material.icons.rounded.Download
import androidx.compose.material.icons.rounded.Edit
import androidx.compose.material.icons.rounded.Menu
import androidx.compose.material.icons.rounded.PlayArrow
import androidx.compose.material.icons.rounded.Save
import androidx.compose.material.icons.rounded.SelectAll
import androidx.compose.material.icons.rounded.Stop
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.Text import androidx.compose.material3.Text
@@ -231,17 +240,8 @@ fun currentNavBackStackEntryAsNavBarState(
showBottom = true, showBottom = true,
topTitle = { tunnel?.name?.let { Text(it) } }, topTitle = { tunnel?.name?.let { Text(it) } },
topTrailing = { topTrailing = {
Row { ActionIconButton(Icons.Rounded.Edit, R.string.edit_tunnel) {
ActionIconButton(Icons.Rounded.QrCode2, R.string.show_qr) { tunnel?.id?.let { navController.navigate(Route.Config(it)) }
tunnel?.id?.let {
viewModel.handleEvent(
AppEvent.SetShowModal(AppViewState.ModalType.QR)
)
}
}
ActionIconButton(Icons.Rounded.Edit, R.string.edit_tunnel) {
tunnel?.id?.let { navController.navigate(Route.Config(it)) }
}
} }
}, },
route = args?.let { Route.TunnelOptions(it.id) }, route = args?.let { Route.TunnelOptions(it.id) },
@@ -9,8 +9,6 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.journeyapps.barcodescanner.ScanContract
import com.journeyapps.barcodescanner.ScanOptions
import com.zaneschepke.wireguardautotunnel.R import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.ui.Route import com.zaneschepke.wireguardautotunnel.ui.Route
import com.zaneschepke.wireguardautotunnel.ui.common.dialog.InfoDialog import com.zaneschepke.wireguardautotunnel.ui.common.dialog.InfoDialog
@@ -47,17 +45,6 @@ fun MainScreen(appUiState: AppUiState, appViewState: AppViewState, viewModel: Ap
onData = { data -> viewModel.handleEvent(AppEvent.ImportTunnelFromFile(data)) }, onData = { data -> viewModel.handleEvent(AppEvent.ImportTunnelFromFile(data)) },
) )
val scanLauncher =
rememberLauncherForActivityResult(
contract = ScanContract(),
onResult = { result ->
{
if (result != null && result.contents.isNotEmpty())
viewModel.handleEvent(AppEvent.ImportTunnelFromQrCode(result.contents))
}
},
)
val requestPermissionLauncher = val requestPermissionLauncher =
rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted
-> ->
@@ -69,7 +56,7 @@ fun MainScreen(appUiState: AppUiState, appViewState: AppViewState, viewModel: Ap
) )
return@rememberLauncherForActivityResult return@rememberLauncherForActivityResult
} }
scanLauncher.launch(ScanOptions().setDesiredBarcodeFormats(ScanOptions.QR_CODE)) navController.navigate(Route.Scanner)
} }
if (appViewState.showModal == AppViewState.ModalType.DELETE) { if (appViewState.showModal == AppViewState.ModalType.DELETE) {
@@ -93,7 +93,7 @@ fun ExportTunnelsBottomSheet(viewModel: AppViewModel) {
ExportOptionRow( ExportOptionRow(
label = stringResource(R.string.export_tunnels_amnezia), label = stringResource(R.string.export_tunnels_amnezia),
onClick = { onClick = {
exportConfigType = ConfigType.AM exportConfigType = ConfigType.AMNEZIA
if (!isAuthorized && !isTv) { if (!isAuthorized && !isTv) {
showAuthPrompt = true showAuthPrompt = true
} else { } else {
@@ -0,0 +1,33 @@
package com.zaneschepke.wireguardautotunnel.ui.screens.main.scanner
import android.app.Activity
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView
import com.journeyapps.barcodescanner.CompoundBarcodeView
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
import com.zaneschepke.wireguardautotunnel.viewmodel.event.AppEvent
@Composable
fun ScannerScreen(viewModel: AppViewModel) {
val context = LocalContext.current
val barcodeView = remember {
CompoundBarcodeView(context).apply {
this.initializeFromIntent((context as Activity).intent)
this.setStatusText("")
this.decodeSingle { result ->
result.text?.let { barCodeOrQr ->
viewModel.handleEvent(AppEvent.ImportTunnelFromQrCode(barCodeOrQr))
}
}
}
}
AndroidView(factory = { barcodeView })
DisposableEffect(Unit) {
barcodeView.resume()
onDispose { barcodeView.pause() }
}
}
@@ -6,53 +6,18 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.* import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
import com.zaneschepke.wireguardautotunnel.ui.common.SectionDivider import com.zaneschepke.wireguardautotunnel.ui.common.SectionDivider
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelectionGroupButton import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelectionGroupButton
import com.zaneschepke.wireguardautotunnel.ui.navigation.LocalIsAndroidTV
import com.zaneschepke.wireguardautotunnel.ui.screens.main.tunneloptions.components.* import com.zaneschepke.wireguardautotunnel.ui.screens.main.tunneloptions.components.*
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.AuthorizationPromptWrapper
import com.zaneschepke.wireguardautotunnel.ui.state.AppViewState
import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel
import com.zaneschepke.wireguardautotunnel.viewmodel.event.AppEvent
@Composable @Composable
fun TunnelOptionsScreen( fun TunnelOptionsScreen(tunnelConf: TunnelConf, viewModel: AppViewModel) {
tunnelConf: TunnelConf,
viewModel: AppViewModel,
appViewState: AppViewState,
) {
val isTv = LocalIsAndroidTV.current
var showAuthPrompt by remember { mutableStateOf(!isTv) }
var isAuthorized by remember { mutableStateOf(isTv) }
if (appViewState.showModal == AppViewState.ModalType.QR) {
// Show authorization prompt if needed
if (showAuthPrompt) {
AuthorizationPromptWrapper(
onDismiss = { showAuthPrompt = false },
onSuccess = {
showAuthPrompt = false
isAuthorized = true
},
viewModel = viewModel,
)
}
if (isAuthorized) {
QrCodeDialog(
tunnelConf = tunnelConf,
onDismiss = {
viewModel.handleEvent(AppEvent.SetShowModal(AppViewState.ModalType.NONE))
},
)
}
}
Column( Column(
horizontalAlignment = Alignment.Start, horizontalAlignment = Alignment.Start,
@@ -1,190 +0,0 @@
package com.zaneschepke.wireguardautotunnel.ui.screens.main.tunneloptions.components
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Check
import androidx.compose.material.icons.outlined.VpnKey
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.MultiChoiceSegmentedButtonRow
import androidx.compose.material3.SegmentedButton
import androidx.compose.material3.SegmentedButtonDefaults
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.DialogProperties
import com.zaneschepke.wireguardautotunnel.MainActivity
import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf
import com.zaneschepke.wireguardautotunnel.domain.enums.ConfigType
import com.zaneschepke.wireguardautotunnel.util.extensions.setScreenBrightness
import io.github.alexzhirkevich.qrose.options.QrBallShape
import io.github.alexzhirkevich.qrose.options.QrBrush
import io.github.alexzhirkevich.qrose.options.QrErrorCorrectionLevel
import io.github.alexzhirkevich.qrose.options.QrFrameShape
import io.github.alexzhirkevich.qrose.options.QrOptions
import io.github.alexzhirkevich.qrose.options.QrPixelShape
import io.github.alexzhirkevich.qrose.options.circle
import io.github.alexzhirkevich.qrose.options.roundCorners
import io.github.alexzhirkevich.qrose.options.solid
import io.github.alexzhirkevich.qrose.rememberQrCodePainter
@Composable
fun QrCodeDialog(tunnelConf: TunnelConf, onDismiss: () -> Unit) {
val context = LocalContext.current
val activity = context as? MainActivity
// Handle screen brightness
DisposableEffect(Unit) {
activity?.setScreenBrightness(1.0f)
onDispose { activity?.setScreenBrightness(-1f) }
}
QrCodeAlertDialog(tunnelConf = tunnelConf, onDismiss = onDismiss)
}
@Composable
private fun QrCodeAlertDialog(tunnelConf: TunnelConf, onDismiss: () -> Unit) {
Surface(color = Color.White, tonalElevation = 0.dp) {
AlertDialog(
containerColor = Color.White,
onDismissRequest = onDismiss,
confirmButton = {
TextButton(onClick = onDismiss) {
Text(stringResource(R.string.done), color = MaterialTheme.colorScheme.surface)
}
},
title = {
Text(
text = tunnelConf.name,
color = Color.Black,
style = MaterialTheme.typography.titleLarge,
)
},
text = { QrCodeContent(tunnelConf = tunnelConf) },
properties = DialogProperties(usePlatformDefaultWidth = true),
)
}
}
@Composable
private fun QrCodeContent(tunnelConf: TunnelConf) {
var selectedOption by remember { mutableStateOf(ConfigType.WG) }
val qrCodeText =
when (selectedOption) {
ConfigType.AM -> tunnelConf.toAmConfig().toAwgQuickString(true)
ConfigType.WG -> tunnelConf.toWgConfig().toWgQuickString(true)
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(12.dp, Alignment.Top),
) {
val qrCodePainter = rememberQrCodePainter(data = qrCodeText, options = createQrOptions())
Image(
painter = qrCodePainter,
contentDescription = stringResource(R.string.show_qr),
modifier =
Modifier.size(300.dp)
.align(Alignment.CenterHorizontally)
.padding(16.dp)
.background(Color.White),
)
ConfigTypeSelector(
selectedOption = selectedOption,
onOptionSelected = { selectedOption = it },
)
}
}
@Composable
private fun ConfigTypeSelector(selectedOption: ConfigType, onOptionSelected: (ConfigType) -> Unit) {
MultiChoiceSegmentedButtonRow(modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)) {
ConfigType.entries.sortedDescending().forEachIndexed { index, entry ->
val isActive = selectedOption == entry
val typeName =
stringResource(
when (entry) {
ConfigType.AM -> R.string.amnezia
ConfigType.WG -> R.string.wireguard
}
)
SegmentedButton(
shape =
SegmentedButtonDefaults.itemShape(
index = index,
count = ConfigType.entries.size,
baseShape = RoundedCornerShape(8.dp),
),
icon = {
SegmentedButtonDefaults.Icon(
active = isActive,
activeContent = {
Icon(
imageVector = Icons.Outlined.Check,
contentDescription = stringResource(R.string.select),
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier.size(SegmentedButtonDefaults.IconSize),
)
},
) {
Icon(
imageVector = Icons.Outlined.VpnKey,
contentDescription = typeName,
tint = Color.Black,
modifier = Modifier.size(SegmentedButtonDefaults.IconSize),
)
}
},
colors =
SegmentedButtonDefaults.colors()
.copy(
activeContainerColor = Color.White,
inactiveContainerColor = Color.White,
),
onCheckedChange = { onOptionSelected(entry) },
checked = isActive,
) {
Text(
text = typeName,
color = Color.Black,
style = MaterialTheme.typography.labelMedium,
)
}
}
}
}
private fun createQrOptions(): QrOptions = QrOptions {
shapes {
darkPixel = QrPixelShape.circle()
ball = QrBallShape.circle()
frame = QrFrameShape.roundCorners(0.2f)
}
colors {
dark = QrBrush.solid(Color.Black)
frame = QrBrush.solid(Color.Black)
ball = QrBrush.solid(Color.Black)
}
errorCorrectionLevel = QrErrorCorrectionLevel.Medium
}
@@ -18,7 +18,6 @@ data class AppViewState(
NONE, NONE,
DELETE, DELETE,
INFO, INFO,
QR,
} }
enum class BottomSheet { enum class BottomSheet {
@@ -1,7 +1,6 @@
package com.zaneschepke.wireguardautotunnel.util.extensions package com.zaneschepke.wireguardautotunnel.util.extensions
import android.Manifest import android.Manifest
import android.app.Activity
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Context.POWER_SERVICE import android.content.Context.POWER_SERVICE
@@ -18,9 +17,6 @@ import android.widget.Toast
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import androidx.core.location.LocationManagerCompat import androidx.core.location.LocationManagerCompat
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import com.zaneschepke.wireguardautotunnel.R import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.core.service.tile.AutoTunnelControlTile import com.zaneschepke.wireguardautotunnel.core.service.tile.AutoTunnelControlTile
import com.zaneschepke.wireguardautotunnel.core.service.tile.TunnelControlTile import com.zaneschepke.wireguardautotunnel.core.service.tile.TunnelControlTile
@@ -229,22 +225,3 @@ fun Context.installApk(apkFile: File) {
} }
startActivity(intent) startActivity(intent)
} }
fun Activity.setScreenBrightness(brightness: Float) {
window.attributes = window.attributes.apply { screenBrightness = brightness }
}
fun Activity.enableImmersiveMode() {
WindowCompat.setDecorFitsSystemWindows(window, false)
val controller = WindowCompat.getInsetsController(window, window.decorView)
controller.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
controller.hide(WindowInsetsCompat.Type.systemBars())
}
fun Activity.disableImmersiveMode() {
WindowCompat.setDecorFitsSystemWindows(window, true)
val controller = WindowCompat.getInsetsController(window, window.decorView)
controller.show(WindowInsetsCompat.Type.systemBars())
window.statusBarColor = android.graphics.Color.TRANSPARENT
}
@@ -39,6 +39,8 @@ import java.time.Instant
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Provider import javax.inject.Provider
import kotlin.collections.component1
import kotlin.collections.component2
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
@@ -400,7 +402,6 @@ constructor(
private suspend fun handleClipboardImport(config: String, tunnels: List<TunnelConf>) { private suspend fun handleClipboardImport(config: String, tunnels: List<TunnelConf>) {
runCatching { runCatching {
Timber.d("Config: $config")
val amConfig = TunnelConf.configFromAmQuick(config) val amConfig = TunnelConf.configFromAmQuick(config)
val tunnelConf = TunnelConf.tunnelConfigFromAmConfig(amConfig) val tunnelConf = TunnelConf.tunnelConfigFromAmConfig(amConfig)
saveTunnel( saveTunnel(
@@ -689,7 +690,7 @@ constructor(
if (tunnels.isEmpty()) return if (tunnels.isEmpty()) return
val (files, shareFileName) = val (files, shareFileName) =
when (configType) { when (configType) {
ConfigType.AM -> { ConfigType.AMNEZIA -> {
val amFiles = fileUtils.createAmFiles(tunnels) val amFiles = fileUtils.createAmFiles(tunnels)
if (amFiles.isEmpty()) { if (amFiles.isEmpty()) {
throw IOException("No valid Amnezia config files created") throw IOException("No valid Amnezia config files created")
-4
View File
@@ -258,8 +258,4 @@
<string name="update_check_unsupported">Update check not supported this build type.</string> <string name="update_check_unsupported">Update check not supported this build type.</string>
<string name="darker">Darker</string> <string name="darker">Darker</string>
<string name="amoled">AMOLED</string> <string name="amoled">AMOLED</string>
<string name="show_qr">Show QR</string>
<string name="amnezia">Amnezia</string>
<string name="wireguard">WireGuard</string>
<string name="done">Done</string>
</resources> </resources>
-2
View File
@@ -6,8 +6,6 @@
<item name="android:colorPrimary">@color/background</item> <item name="android:colorPrimary">@color/background</item>
<item name="android:windowAllowReturnTransitionOverlap">true</item> <item name="android:windowAllowReturnTransitionOverlap">true</item>
<item name="android:windowAllowEnterTransitionOverlap">true</item> <item name="android:windowAllowEnterTransitionOverlap">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style> </style>
<style name="Theme.App.Start" parent="@style/Theme.SplashScreen"> <style name="Theme.App.Start" parent="@style/Theme.SplashScreen">
+1 -1
View File
@@ -8,6 +8,6 @@ repositories {
} }
dependencies { dependencies {
implementation("org.semver4j:semver4j:5.7.0") implementation("org.semver4j:semver4j:5.6.0")
implementation("org.ajoberstar.grgit:grgit-core:5.3.0") implementation("org.ajoberstar.grgit:grgit-core:5.3.0")
} }
+2 -2
View File
@@ -1,7 +1,7 @@
object Constants { object Constants {
const val VERSION_NAME = "3.9.1" const val VERSION_NAME = "3.9.0"
const val JVM_TARGET = "17" const val JVM_TARGET = "17"
const val VERSION_CODE = 39100 const val VERSION_CODE = 38900
const val TARGET_SDK = 35 const val TARGET_SDK = 35
const val MIN_SDK = 26 const val MIN_SDK = 26
const val APP_ID = "com.zaneschepke.wireguardautotunnel" const val APP_ID = "com.zaneschepke.wireguardautotunnel"
@@ -1,6 +0,0 @@
What's new:
- Fix tunnel no connectivity DNS bug
- Fix tunnel stuck starting bug
- Add view tunnel QR code
- Add resolved endpoint to statistics
- Various other bug fixes
+6 -6
View File
@@ -1,7 +1,7 @@
[versions] [versions]
accompanist = "0.37.3" accompanist = "0.37.2"
activityCompose = "1.10.1" activityCompose = "1.10.1"
amneziawgAndroid = "1.4.0" amneziawgAndroid = "1.3.10"
androidx-junit = "1.2.1" androidx-junit = "1.2.1"
appcompat = "1.7.0" appcompat = "1.7.0"
biometricKtx = "1.2.0-alpha05" biometricKtx = "1.2.0-alpha05"
@@ -18,15 +18,15 @@ lifecycle-runtime-compose = "2.8.7"
material3 = "1.3.2" material3 = "1.3.2"
navigationCompose = "2.8.9" navigationCompose = "2.8.9"
pinLockCompose = "1.0.4" pinLockCompose = "1.0.4"
qrose = "1.0.1" qrcodeKotlin = "4.4.1"
roomVersion = "2.7.1" roomVersion = "2.7.1"
semver4j = "3.1.0" semver4j = "3.1.0"
slf4jAndroid = "1.7.36" slf4jAndroid = "1.7.36"
timber = "5.0.1" timber = "5.0.1"
tunnel = "1.3.0" tunnel = "1.2.16"
androidGradlePlugin = "8.9.2" androidGradlePlugin = "8.9.2"
kotlin = "2.1.20" kotlin = "2.1.20"
ksp = "2.1.20-2.0.1" ksp = "2.1.20-2.0.0"
composeBom = "2025.04.01" composeBom = "2025.04.01"
compose = "1.8.0" compose = "1.8.0"
icons = "1.7.8" icons = "1.7.8"
@@ -99,7 +99,7 @@ lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-com
material-icons-extended = { module = "androidx.compose.material:material-icons-extended", version.ref = "icons" } material-icons-extended = { module = "androidx.compose.material:material-icons-extended", version.ref = "icons" }
pin-lock-compose = { module = "com.zaneschepke:pin_lock_compose", version.ref = "pinLockCompose" } pin-lock-compose = { module = "com.zaneschepke:pin_lock_compose", version.ref = "pinLockCompose" }
qrose = { module = "io.github.alexzhirkevich:qrose", version.ref = "qrose" } qrcode-kotlin = { module = "io.github.g0dkar:qrcode-kotlin", version.ref = "qrcodeKotlin" }
semver4j = { module = "com.vdurmont:semver4j", version.ref = "semver4j" } semver4j = { module = "com.vdurmont:semver4j", version.ref = "semver4j" }
slf4j-android = { module = "org.slf4j:slf4j-android", version.ref = "slf4jAndroid" } slf4j-android = { module = "org.slf4j:slf4j-android", version.ref = "slf4jAndroid" }
timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" } timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" }