From 79b5b039b080212772e0b8a93084e4c0dee688b3 Mon Sep 17 00:00:00 2001 From: Zane Schepke Date: Tue, 18 Jun 2024 23:08:15 -0400 Subject: [PATCH] fix: crashing and pin lock Re-enabled pin lock after disablement from crashes. Fixed crashing issues. Closes #237 Fixed bug where pin lock will no longer initialize if never not enabled/in use. Improved tunnel control tile performance. Fix bad address crash when user enters bad addresses into allowedIps. Closes #229 Disabled auto rotate Closes #212 Add restart on boot toggle to make restart of services feature more obvious and configurable. --- README.md | 7 +- app/build.gradle.kts | 6 +- app/src/main/AndroidManifest.xml | 25 ++++--- .../WireGuardAutoTunnel.kt | 24 ------- .../data/datastore/DataStoreManager.kt | 1 + .../data/domain/GeneralState.kt | 8 ++- .../data/repository/AppStateRepository.kt | 3 + .../repository/DataStoreAppStateRepository.kt | 17 ++++- .../receiver/BootReceiver.kt | 49 +++++++------ .../receiver/NotificationActionReceiver.kt | 30 +++++--- .../service/tile/TunnelControlTile.kt | 40 +++++------ .../service/tunnel/WireGuardTunnel.kt | 28 +++++--- .../ui/CaptureActivityPortrait.kt | 5 -- .../wireguardautotunnel/ui/MainActivity.kt | 18 ++--- .../wireguardautotunnel/ui/SplashActivity.kt | 68 +++++++++++++++++++ .../ui/screens/main/MainScreen.kt | 3 - .../ui/screens/settings/SettingsScreen.kt | 39 ++++++----- .../ui/screens/settings/SettingsUiState.kt | 1 + .../ui/screens/settings/SettingsViewModel.kt | 24 ++++++- .../wireguardautotunnel/util/Extensions.kt | 20 ------ app/src/main/res/values/strings.xml | 1 + app/src/main/res/values/themes.xml | 10 ++- buildSrc/src/main/kotlin/Constants.kt | 4 +- .../android/en-US/changelogs/34700.txt | 6 ++ gradle/libs.versions.toml | 8 ++- 25 files changed, 275 insertions(+), 170 deletions(-) delete mode 100644 app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/CaptureActivityPortrait.kt create mode 100644 app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/SplashActivity.kt create mode 100644 fastlane/metadata/android/en-US/changelogs/34700.txt diff --git a/README.md b/README.md index 1345ae7..0fb627e 100644 --- a/README.md +++ b/README.md @@ -69,15 +69,16 @@ and on while on different networks. This app was created to offer a free solutio Want updates faster? -Check out my personal [fdroid repository](https://github.com/zaneschepke/fdroid) to get updates the moment they are released. +Check out my personal [fdroid repository](https://github.com/zaneschepke/fdroid) to get updates the +moment they are released. ## Docs -Information about features, behaviors, and answers to common questions can be found in the app [documentation](https://zaneschepke.com/wgtunnel-docs/overview.html). +Information about features, behaviors, and answers to common questions can be found in the +app [documentation](https://zaneschepke.com/wgtunnel-docs/overview.html). The repository for these docs can be found [here](https://github.com/zaneschepke/wgtunnel-docs). - ## Translation This app is using [Weblate](https://weblate.org) to assist with translations. diff --git a/app/build.gradle.kts b/app/build.gradle.kts index b4a4b1b..bfcfb0c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -112,9 +112,6 @@ android { } create("general") { dimension = Constants.TYPE - if (BuildHelper.isReleaseBuild(gradle) && BuildHelper.isGeneralFlavor(gradle)) { - //any plugins general specific - } } } compileOptions { @@ -211,4 +208,7 @@ dependencies { // shortcuts implementation(libs.androidx.core) implementation(libs.androidx.core.google.shortcuts) + + // splash + implementation(libs.androidx.core.splashscreen) } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f9ad73d..cf86fcb 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -60,31 +60,36 @@ android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" - android:theme="@style/Theme.WireguardAutoTunnel" + android:theme="@style/Theme.AppSplashScreen" tools:targetApi="tiramisu"> + android:theme="@style/Theme.AppSplashScreen"> - + android:name=".ui.MainActivity" + android:exported="true" + android:screenOrientation="portrait" + android:theme="@style/Theme.WireguardAutoTunnel"> + + + + + try { GeneralState( - locationDisclosureShown = pref[DataStoreManager.LOCATION_DISCLOSURE_SHOWN] + isLocationDisclosureShown = pref[DataStoreManager.LOCATION_DISCLOSURE_SHOWN] ?: GeneralState.LOCATION_DISCLOSURE_SHOWN_DEFAULT, - batteryOptimizationDisableShown = pref[DataStoreManager.BATTERY_OPTIMIZE_DISABLE_SHOWN] + isBatteryOptimizationDisableShown = pref[DataStoreManager.BATTERY_OPTIMIZE_DISABLE_SHOWN] ?: GeneralState.BATTERY_OPTIMIZATION_DISABLE_SHOWN_DEFAULT, - tunnelRunningFromManualStart = pref[DataStoreManager.TUNNEL_RUNNING_FROM_MANUAL_START] + isTunnelRunningFromManualStart = pref[DataStoreManager.TUNNEL_RUNNING_FROM_MANUAL_START] + ?: GeneralState.TUNNELING_RUNNING_FROM_MANUAL_START_DEFAULT, + isPinLockEnabled = pref[DataStoreManager.IS_PIN_LOCK_ENABLED] ?: GeneralState.TUNNELING_RUNNING_FROM_MANUAL_START_DEFAULT, ) } catch (e: IllegalArgumentException) { diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/receiver/BootReceiver.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/receiver/BootReceiver.kt index 589aa8e..63381d8 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/receiver/BootReceiver.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/receiver/BootReceiver.kt @@ -4,9 +4,11 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository +import com.zaneschepke.wireguardautotunnel.module.ApplicationScope import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager -import com.zaneschepke.wireguardautotunnel.util.goAsync import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject @@ -19,27 +21,36 @@ class BootReceiver : BroadcastReceiver() { @Inject lateinit var serviceManager: ServiceManager - override fun onReceive(context: Context?, intent: Intent?) = goAsync { - if (Intent.ACTION_BOOT_COMPLETED != intent?.action) return@goAsync + @Inject + @ApplicationScope + lateinit var applicationScope: CoroutineScope + + override fun onReceive(context: Context?, intent: Intent?) { + if (Intent.ACTION_BOOT_COMPLETED != intent?.action) return context?.run { - val settings = appDataRepository.settings.getSettings() - if (settings.isAutoTunnelEnabled) { - Timber.i("Starting watcher service from boot") - serviceManager.startWatcherServiceForeground(context) - } - if (appDataRepository.appState.isTunnelRunningFromManualStart()) { - appDataRepository.appState.getActiveTunnelId()?.let { - Timber.i("Starting tunnel that was active before reboot") - serviceManager.startVpnServiceForeground( - context, - appDataRepository.tunnels.getById(it)?.id, - ) + applicationScope.launch { + val settings = appDataRepository.settings.getSettings() + if(settings.isRestoreOnBootEnabled) { + if (settings.isAutoTunnelEnabled) { + Timber.i("Starting watcher service from boot") + serviceManager.startWatcherServiceForeground(context) + } + if (appDataRepository.appState.isTunnelRunningFromManualStart()) { + appDataRepository.appState.getActiveTunnelId()?.let { + Timber.i("Starting tunnel that was active before reboot") + serviceManager.startVpnServiceForeground( + context, + appDataRepository.tunnels.getById(it)?.id, + ) + return@launch + } + } + if (settings.isAlwaysOnVpnEnabled) { + Timber.i("Starting vpn service from boot AOVPN") + serviceManager.startVpnServiceForeground(context) + } } } - if (settings.isAlwaysOnVpnEnabled) { - Timber.i("Starting vpn service from boot AOVPN") - serviceManager.startVpnServiceForeground(context) - } } } } diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/receiver/NotificationActionReceiver.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/receiver/NotificationActionReceiver.kt index 812da89..cad50de 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/receiver/NotificationActionReceiver.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/receiver/NotificationActionReceiver.kt @@ -4,12 +4,14 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import com.zaneschepke.wireguardautotunnel.data.repository.SettingsRepository +import com.zaneschepke.wireguardautotunnel.module.ApplicationScope import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager import com.zaneschepke.wireguardautotunnel.util.Constants -import com.zaneschepke.wireguardautotunnel.util.goAsync import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.cancel import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject @@ -21,16 +23,22 @@ class NotificationActionReceiver : BroadcastReceiver() { @Inject lateinit var serviceManager: ServiceManager - override fun onReceive(context: Context, intent: Intent?) = goAsync { - try { - //TODO fix for manual start changes when enabled - serviceManager.stopVpnServiceForeground(context) - delay(Constants.TOGGLE_TUNNEL_DELAY) - serviceManager.startVpnServiceForeground(context) - } catch (e: Exception) { - Timber.e(e) - } finally { - cancel() + @Inject + @ApplicationScope + lateinit var applicationScope: CoroutineScope + + override fun onReceive(context: Context, intent: Intent?) { + applicationScope.launch { + try { + //TODO fix for manual start changes when enabled + serviceManager.stopVpnServiceForeground(context) + delay(Constants.TOGGLE_TUNNEL_DELAY) + serviceManager.startVpnServiceForeground(context) + } catch (e: Exception) { + Timber.e(e) + } finally { + cancel() + } } } } diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tile/TunnelControlTile.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tile/TunnelControlTile.kt index 7cc3c6f..b9d8abc 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tile/TunnelControlTile.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tile/TunnelControlTile.kt @@ -11,7 +11,6 @@ import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelState import com.zaneschepke.wireguardautotunnel.service.tunnel.VpnService import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job import kotlinx.coroutines.cancel import kotlinx.coroutines.launch import timber.log.Timber @@ -35,32 +34,27 @@ class TunnelControlTile : TileService() { private var manualStartConfig: TunnelConfig? = null - private var job: Job? = null; - override fun onStartListening() { super.onStartListening() Timber.d("On start listening called") - //TODO Fix this - if (job == null || job?.isCancelled == true) job = applicationScope.launch { - vpnService.vpnState.collect { it -> - when (it.status) { - TunnelState.UP -> { - setActive() - it.tunnelConfig?.name?.let { name -> setTileDescription(name) } - } - - TunnelState.DOWN -> { - setInactive() - val config = appDataRepository.getStartTunnelConfig()?.also { config -> - manualStartConfig = config - } ?: appDataRepository.getPrimaryOrFirstTunnel() - config?.let { - setTileDescription(it.name) - } ?: setUnavailable() - } - - else -> setInactive() + applicationScope.launch { + when (vpnService.getState()) { + TunnelState.UP -> { + setActive() + setTileDescription(vpnService.name) } + + TunnelState.DOWN -> { + setInactive() + val config = appDataRepository.getStartTunnelConfig()?.also { config -> + manualStartConfig = config + } ?: appDataRepository.getPrimaryOrFirstTunnel() + config?.let { + setTileDescription(it.name) + } ?: setUnavailable() + } + + else -> setInactive() } } } diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tunnel/WireGuardTunnel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tunnel/WireGuardTunnel.kt index c8edafe..72309ff 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tunnel/WireGuardTunnel.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tunnel/WireGuardTunnel.kt @@ -45,24 +45,19 @@ constructor( private var statsJob: Job? = null - private lateinit var backend: Backend; - private var backendIsWgUserspace = true private var backendIsAmneziaUserspace = false init { applicationScope.launch(ioDispatcher) { - backend = userspaceBackend.get() appDataRepository.settings.getSettingsFlow().collect { if (it.isKernelEnabled && (backendIsWgUserspace || backendIsAmneziaUserspace)) { Timber.i("Setting kernel backend") - backend = kernelBackend.get() backendIsWgUserspace = false backendIsAmneziaUserspace = false } else if (!it.isKernelEnabled && !it.isAmneziaEnabled && !backendIsWgUserspace) { Timber.i("Setting WireGuard userspace backend") - backend = userspaceBackend.get() backendIsWgUserspace = true backendIsAmneziaUserspace = false } else if (it.isAmneziaEnabled && !backendIsAmneziaUserspace) { @@ -89,7 +84,7 @@ constructor( } else { Timber.i("Using Wg backend") val wgConfig = tunnelConfig?.let { TunnelConfig.configFromWgQuick(it.wgQuick) } - val state = backend.setState( + val state = backend().setState( this, tunnelState.toWgState(), wgConfig, @@ -102,6 +97,7 @@ constructor( return withContext(ioDispatcher) { try { //TODO we need better error handling here + // need to bubble up these errors to the UI val config = tunnelConfig ?: appDataRepository.getPrimaryOrFirstTunnel() if (config != null) { emitTunnelConfig(config) @@ -114,6 +110,22 @@ constructor( } } + private fun backend(): Backend { + return when { + backendIsWgUserspace -> { + userspaceBackend.get() + } + + !backendIsWgUserspace && !backendIsAmneziaUserspace -> { + kernelBackend.get() + } + + else -> { + userspaceBackend.get() + } + } + } + private fun emitTunnelState(state: TunnelState) { _vpnState.tryEmit( _vpnState.value.copy( @@ -162,7 +174,7 @@ constructor( return if (backendIsAmneziaUserspace) TunnelState.from( userspaceAmneziaBackend.get().getState(this), ) - else TunnelState.from(backend.getState(this)) + else TunnelState.from(backend().getState(this)) } override fun getName(): String { @@ -198,7 +210,7 @@ constructor( ), ) } else { - emitBackendStatistics(WireGuardStatistics(backend.getStatistics(this@WireGuardTunnel))) + emitBackendStatistics(WireGuardStatistics(backend().getStatistics(this@WireGuardTunnel))) } delay(Constants.VPN_STATISTIC_CHECK_INTERVAL) } diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/CaptureActivityPortrait.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/CaptureActivityPortrait.kt deleted file mode 100644 index 029ade5..0000000 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/CaptureActivityPortrait.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.zaneschepke.wireguardautotunnel.ui - -import com.journeyapps.barcodescanner.CaptureActivity - -class CaptureActivityPortrait : CaptureActivity() diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/MainActivity.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/MainActivity.kt index 584c864..e8e3fdf 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/MainActivity.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/MainActivity.kt @@ -43,7 +43,7 @@ import com.google.accompanist.permissions.isGranted import com.google.accompanist.permissions.rememberPermissionState import com.zaneschepke.wireguardautotunnel.R import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel -import com.zaneschepke.wireguardautotunnel.data.datastore.DataStoreManager +import com.zaneschepke.wireguardautotunnel.data.repository.AppStateRepository import com.zaneschepke.wireguardautotunnel.data.repository.SettingsRepository import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager import com.zaneschepke.wireguardautotunnel.ui.common.navigation.BottomNavBar @@ -61,14 +61,13 @@ import com.zaneschepke.wireguardautotunnel.util.StringValue import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import xyz.teamgravity.pin_lock_compose.PinManager import javax.inject.Inject @AndroidEntryPoint class MainActivity : AppCompatActivity() { @Inject - lateinit var dataStoreManager: DataStoreManager + lateinit var appStateRepository: AppStateRepository @Inject lateinit var settingsRepository: SettingsRepository @@ -82,17 +81,18 @@ class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + val isPinLockEnabled = intent.extras?.getBoolean(SplashActivity.IS_PIN_LOCK_ENABLED_KEY) + enableEdgeToEdge(navigationBarStyle = SystemBarStyle.dark(Color.Transparent.toArgb())) - // load preferences into memory and init data lifecycleScope.launch { - dataStoreManager.init() WireGuardAutoTunnel.requestTunnelTileServiceStateUpdate() val settings = settingsRepository.getSettings() if (settings.isAutoTunnelEnabled) { serviceManager.startWatcherService(application.applicationContext) } } + setContent { val appViewModel = hiltViewModel() val appUiState by appViewModel.appUiState.collectAsStateWithLifecycle() @@ -201,12 +201,8 @@ class MainActivity : AppCompatActivity() { ) { padding -> NavHost( navController, - startDestination = - //TODO disable pin lock - //(if (PinManager.pinExists()) Screen.Lock.route else Screen.Main.route), - Screen.Main.route, - modifier = - Modifier + startDestination = (if (isPinLockEnabled == true) Screen.Lock.route else Screen.Main.route), + modifier = Modifier .padding(padding) .fillMaxSize(), ) { diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/SplashActivity.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/SplashActivity.kt new file mode 100644 index 0000000..58ef902 --- /dev/null +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/SplashActivity.kt @@ -0,0 +1,68 @@ +package com.zaneschepke.wireguardautotunnel.ui + +import android.annotation.SuppressLint +import android.content.Intent +import android.os.Build +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import com.zaneschepke.logcatter.LocalLogCollector +import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel +import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel.Companion.isRunningOnAndroidTv +import com.zaneschepke.wireguardautotunnel.data.repository.AppStateRepository +import com.zaneschepke.wireguardautotunnel.module.ApplicationScope +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import xyz.teamgravity.pin_lock_compose.PinManager +import javax.inject.Inject + +@SuppressLint("CustomSplashScreen") +@AndroidEntryPoint +class SplashActivity : ComponentActivity() { + + @Inject + lateinit var appStateRepository: AppStateRepository + + @Inject + lateinit var localLogCollector: LocalLogCollector + + @Inject + @ApplicationScope + lateinit var applicationScope: CoroutineScope + + override fun onCreate(savedInstanceState: Bundle?) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + val splashScreen = installSplashScreen() + splashScreen.setKeepOnScreenCondition { true } + } + super.onCreate(savedInstanceState) + + applicationScope.launch { + if (!isRunningOnAndroidTv()) localLogCollector.start() + } + + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.CREATED) { + val pinLockEnabled = appStateRepository.isPinLockEnabled() + if (pinLockEnabled) { + PinManager.initialize(WireGuardAutoTunnel.instance) + } + + val intent = Intent(this@SplashActivity, MainActivity::class.java).apply { + putExtra(IS_PIN_LOCK_ENABLED_KEY, pinLockEnabled) + } + startActivity(intent) + finish() + } + } + } + + companion object { + const val IS_PIN_LOCK_ENABLED_KEY = "is_pin_lock_enabled" + } +} + diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainScreen.kt index a70c0de..e142d93 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainScreen.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainScreen.kt @@ -101,7 +101,6 @@ import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig import com.zaneschepke.wireguardautotunnel.service.tunnel.HandshakeStatus import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelState import com.zaneschepke.wireguardautotunnel.ui.AppViewModel -import com.zaneschepke.wireguardautotunnel.ui.CaptureActivityPortrait import com.zaneschepke.wireguardautotunnel.ui.Screen import com.zaneschepke.wireguardautotunnel.ui.common.RowListItem import com.zaneschepke.wireguardautotunnel.ui.common.screen.LoadingScreen @@ -262,8 +261,6 @@ fun MainScreen( context.getString(R.string.scanning_qr), ) scanOptions.setBeepEnabled(false) - scanOptions.captureActivity = - CaptureActivityPortrait::class.java scanLauncher.launch(scanOptions) } diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsScreen.kt index 8db979b..fa187c6 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsScreen.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsScreen.kt @@ -84,7 +84,6 @@ import com.zaneschepke.wireguardautotunnel.ui.common.text.SectionTitle import com.zaneschepke.wireguardautotunnel.util.getMessage import kotlinx.coroutines.launch import timber.log.Timber -import xyz.teamgravity.pin_lock_compose.PinManager import java.io.File @OptIn( @@ -652,21 +651,29 @@ fun SettingsScreen( onCheckChanged = { viewModel.onToggleShortcutsEnabled() }, ) } - // TODO disable for now -// ConfigurationToggle( -// stringResource(R.string.enable_app_lock), -// enabled = true, -// checked = pinExists.value, -// padding = screenPadding, -// onCheckChanged = { -// if (pinExists.value) { -// PinManager.clearPin() -// pinExists.value = PinManager.pinExists() -// } else { -// navController.navigate(Screen.Lock.route) -// } -// }, -// ) + ConfigurationToggle( + stringResource(R.string.restart_at_boot), + enabled = true, + checked = uiState.settings.isRestoreOnBootEnabled, + padding = screenPadding, + onCheckChanged = { + viewModel.onToggleRestartAtBoot() + }, + ) + ConfigurationToggle( + stringResource(R.string.enable_app_lock), + enabled = true, + checked = uiState.isPinLockEnabled, + padding = screenPadding, + onCheckChanged = { + if (uiState.isPinLockEnabled) { + viewModel.onPinLockDisabled() + } else { + viewModel.onPinLockEnabled() + navController.navigate(Screen.Lock.route) + } + }, + ) if (!WireGuardAutoTunnel.isRunningOnAndroidTv()) { Row( verticalAlignment = Alignment.CenterVertically, diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsUiState.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsUiState.kt index 39b5db3..f51b98c 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsUiState.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsUiState.kt @@ -10,4 +10,5 @@ data class SettingsUiState( val vpnState: VpnState = VpnState(), val isLocationDisclosureShown: Boolean = true, val isBatteryOptimizeDisableShown: Boolean = false, + val isPinLockEnabled: Boolean = false ) diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsViewModel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsViewModel.kt index 9be2d67..828ed32 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsViewModel.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsViewModel.kt @@ -27,6 +27,7 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import timber.log.Timber +import xyz.teamgravity.pin_lock_compose.PinManager import java.io.File import javax.inject.Inject import javax.inject.Provider @@ -57,8 +58,9 @@ constructor( settings, tunnels, tunnelState, - generalState.locationDisclosureShown, - generalState.batteryOptimizationDisableShown, + generalState.isLocationDisclosureShown, + generalState.isBatteryOptimizationDisableShown, + generalState.isPinLockEnabled, ) } .stateIn( @@ -234,4 +236,22 @@ constructor( kernelSupport } } + + fun onPinLockDisabled() = viewModelScope.launch { + PinManager.clearPin() + appDataRepository.appState.setPinLockEnabled(false) + } + + fun onPinLockEnabled() = viewModelScope.launch { + PinManager.initialize(WireGuardAutoTunnel.instance) + appDataRepository.appState.setPinLockEnabled(true) + } + + fun onToggleRestartAtBoot() = viewModelScope.launch { + saveSettings( + uiState.value.settings.copy( + isRestoreOnBootEnabled = !uiState.value.settings.isRestoreOnBootEnabled + ) + ) + } } diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/Extensions.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/Extensions.kt index 6c165c0..f53e5ac 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/Extensions.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/Extensions.kt @@ -1,6 +1,5 @@ package com.zaneschepke.wireguardautotunnel.util -import android.content.BroadcastReceiver import android.content.Context import android.content.pm.PackageInfo import com.zaneschepke.wireguardautotunnel.R @@ -10,7 +9,6 @@ import com.zaneschepke.wireguardautotunnel.service.tunnel.statistics.TunnelStati import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.ObsoleteCoroutinesApi import kotlinx.coroutines.channels.ClosedReceiveChannelException import kotlinx.coroutines.channels.ReceiveChannel @@ -19,7 +17,6 @@ import kotlinx.coroutines.channels.ticker import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.channelFlow -import kotlinx.coroutines.launch import kotlinx.coroutines.selects.whileSelect import org.amnezia.awg.config.Config import timber.log.Timber @@ -27,25 +24,8 @@ import java.math.BigDecimal import java.text.DecimalFormat import java.time.Duration import java.util.concurrent.ConcurrentLinkedQueue -import kotlin.coroutines.CoroutineContext -import kotlin.coroutines.EmptyCoroutineContext import kotlin.coroutines.cancellation.CancellationException -fun BroadcastReceiver.goAsync( - context: CoroutineContext = EmptyCoroutineContext, - block: suspend CoroutineScope.() -> Unit -) { - val pendingResult = goAsync() - @OptIn(DelicateCoroutinesApi::class) // Must run globally; there's no teardown callback. - GlobalScope.launch(context) { - try { - block() - } finally { - pendingResult.finish() - } - } -} - fun BigDecimal.toThreeDecimalPlaceString(): String { val df = DecimalFormat("#.###") return df.format(this) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e69a230..9f136bf 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -176,4 +176,5 @@ Amnezia WireGuard Invalid tunnel config format + Restart on boot \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index b703e9d..58e567b 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -4,4 +4,12 @@ - \ No newline at end of file + + + diff --git a/buildSrc/src/main/kotlin/Constants.kt b/buildSrc/src/main/kotlin/Constants.kt index 8484b92..55f9662 100644 --- a/buildSrc/src/main/kotlin/Constants.kt +++ b/buildSrc/src/main/kotlin/Constants.kt @@ -1,7 +1,7 @@ object Constants { - const val VERSION_NAME = "3.4.6" + const val VERSION_NAME = "3.4.7" const val JVM_TARGET = "17" - const val VERSION_CODE = 34600 + const val VERSION_CODE = 34700 const val TARGET_SDK = 34 const val MIN_SDK = 26 const val APP_ID = "com.zaneschepke.wireguardautotunnel" diff --git a/fastlane/metadata/android/en-US/changelogs/34700.txt b/fastlane/metadata/android/en-US/changelogs/34700.txt new file mode 100644 index 0000000..b74851c --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/34700.txt @@ -0,0 +1,6 @@ +What's new: +- Fix crashing issues +- Improve tile performance +- Re-enable pin lock +- Make restart on boot a setting +- Various performance and bug fixes \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2128c20..68de9e1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -22,12 +22,13 @@ pinLockCompose = "1.0.3" roomVersion = "2.6.1" timber = "5.0.1" tunnel = "1.0.20230706" -androidGradlePlugin = "8.4.1" +androidGradlePlugin = "8.5.0" kotlin = "1.9.24" ksp = "1.9.24-1.0.20" -composeBom = "2024.05.00" -compose = "1.6.7" +composeBom = "2024.06.00" +compose = "1.6.8" zxingAndroidEmbedded = "4.3.0" +coreSplashscreen = "1.0.1" #plugins gradlePlugins-kotlinxSerialization = "1.9.24" @@ -74,6 +75,7 @@ androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "coreKtx" androidx-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "espressoCore" } androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hiltNavigationCompose" } androidx-junit = { module = "androidx.test.ext:junit", version.ref = "androidx-junit" } +androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "coreSplashscreen" } androidx-lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycle-runtime-compose" } androidx-material3 = { module = "androidx.compose.material3:material3", version.ref = "material3" } androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }