From 777a948244c3befa6eb40c5b7963b3ff19d78ae0 Mon Sep 17 00:00:00 2001 From: Zane Schepke Date: Sun, 17 Nov 2024 01:30:13 -0500 Subject: [PATCH] fix: add back per tunnel ping settings --- .../wireguardautotunnel/ui/AppViewModel.kt | 83 ++++++ .../config/SubmitConfigurationTextBox.kt | 33 ++- .../ui/common/textbox/CustomTextField.kt | 3 +- .../ui/screens/options/OptionsScreen.kt | 259 +++++++++++------- .../ui/screens/settings/SettingsScreen.kt | 16 +- .../ui/screens/settings/SettingsViewModel.kt | 96 ------- .../components/TrustNetworksTextBox.kt | 3 + 7 files changed, 279 insertions(+), 214 deletions(-) diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/AppViewModel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/AppViewModel.kt index f47149e..8ca0d21 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/AppViewModel.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/AppViewModel.kt @@ -2,13 +2,19 @@ package com.zaneschepke.wireguardautotunnel.ui import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.wireguard.android.backend.WgQuickBackend +import com.wireguard.android.util.RootShell +import com.zaneschepke.wireguardautotunnel.R import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository +import com.zaneschepke.wireguardautotunnel.module.AppShell import com.zaneschepke.wireguardautotunnel.module.IoDispatcher import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelState +import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarController import com.zaneschepke.wireguardautotunnel.util.Constants +import com.zaneschepke.wireguardautotunnel.util.StringValue import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.MutableStateFlow @@ -21,6 +27,7 @@ import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.takeWhile import kotlinx.coroutines.launch import kotlinx.coroutines.plus +import kotlinx.coroutines.withContext import xyz.teamgravity.pin_lock_compose.PinManager import javax.inject.Inject import javax.inject.Provider @@ -32,6 +39,7 @@ constructor( private val appDataRepository: AppDataRepository, private val tunnelService: Provider, @IoDispatcher private val ioDispatcher: CoroutineDispatcher, + @AppShell private val rootShell: Provider, private val serviceManager: ServiceManager, ) : ViewModel() { @@ -107,4 +115,79 @@ constructor( fun setLocationDisclosureShown() = viewModelScope.launch { appDataRepository.appState.setLocationDisclosureShown(true) } + + fun onToggleAlwaysOnVPN() = viewModelScope.launch { + with(uiState.value.settings) { + appDataRepository.settings.save( + copy( + isAlwaysOnVpnEnabled = !isAlwaysOnVpnEnabled, + ), + ) + } + } + + fun onToggleRestartAtBoot() = viewModelScope.launch { + with(uiState.value.settings) { + appDataRepository.settings.save( + copy( + isRestoreOnBootEnabled = !isRestoreOnBootEnabled, + ), + ) + } + } + + fun onToggleShortcutsEnabled() = viewModelScope.launch { + with(uiState.value.settings) { + appDataRepository.settings.save( + this.copy( + isShortcutsEnabled = !isShortcutsEnabled, + ), + ) + } + } + + private fun saveKernelMode(enabled: Boolean) = viewModelScope.launch { + with(uiState.value.settings) { + appDataRepository.settings.save( + this.copy( + isKernelEnabled = enabled, + ), + ) + } + } + + fun onToggleKernelMode() = viewModelScope.launch { + with(uiState.value.settings) { + if (!isKernelEnabled) { + requestRoot().onSuccess { + if (!isKernelSupported()) return@onSuccess SnackbarController.showMessage(StringValue.StringResource(R.string.kernel_not_supported)) + appDataRepository.settings.save( + copy( + isKernelEnabled = true, + isAmneziaEnabled = false, + ), + ) + } + } else { + saveKernelMode(enabled = false) + } + } + } + + private suspend fun isKernelSupported(): Boolean { + return withContext(ioDispatcher) { + WgQuickBackend.hasKernelSupport() + } + } + + private suspend fun requestRoot(): Result { + return withContext(ioDispatcher) { + kotlin.runCatching { + rootShell.get().start() + SnackbarController.showMessage(StringValue.StringResource(R.string.root_accepted)) + }.onFailure { + SnackbarController.showMessage(StringValue.StringResource(R.string.error_root_denied)) + } + } + } } diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/config/SubmitConfigurationTextBox.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/config/SubmitConfigurationTextBox.kt index ce23ed9..afa4d25 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/config/SubmitConfigurationTextBox.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/config/SubmitConfigurationTextBox.kt @@ -3,6 +3,7 @@ package com.zaneschepke.wireguardautotunnel.ui.common.config import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.collectIsFocusedAsState import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons @@ -10,7 +11,6 @@ import androidx.compose.material.icons.outlined.Save import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -20,10 +20,11 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalSoftwareKeyboardController -import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardCapitalization -import com.zaneschepke.wireguardautotunnel.R +import androidx.compose.ui.unit.dp +import com.zaneschepke.wireguardautotunnel.ui.common.textbox.CustomTextField +import com.zaneschepke.wireguardautotunnel.util.extensions.scaledWidth @Composable fun SubmitConfigurationTextBox( @@ -44,17 +45,22 @@ fun SubmitConfigurationTextBox( var stateValue by remember { mutableStateOf(value ?: "") } - OutlinedTextField( + CustomTextField( isError = isErrorValue(stateValue), - modifier = Modifier - .fillMaxWidth(), + textStyle = MaterialTheme.typography.bodySmall, value = stateValue, - singleLine = true, - interactionSource = interactionSource, onValueChange = { stateValue = it }, + interactionSource = interactionSource, label = { Text(label) }, - maxLines = 1, - placeholder = { Text(hint) }, + containerColor = MaterialTheme.colorScheme.surface, + placeholder = { Text(hint, style = MaterialTheme.typography.bodySmall) }, + modifier = + Modifier + .padding( + top = 5.dp, + bottom = 10.dp, + ).fillMaxWidth().padding(end = 16.dp.scaledWidth()), + singleLine = true, keyboardOptions = keyboardOptions, keyboardActions = KeyboardActions( onDone = { @@ -62,16 +68,17 @@ fun SubmitConfigurationTextBox( keyboardController?.hide() }, ), - trailingIcon = { + trailing = { if (!isErrorValue(stateValue) && isFocused) { IconButton(onClick = { onSubmit(stateValue) keyboardController?.hide() focusManager.clearFocus() }) { + val icon = Icons.Outlined.Save Icon( - imageVector = Icons.Outlined.Save, - contentDescription = stringResource(R.string.save_changes), + imageVector = icon, + contentDescription = icon.name, tint = MaterialTheme.colorScheme.primary, ) } diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/textbox/CustomTextField.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/textbox/CustomTextField.kt index 16e7bd2..95d19b0 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/textbox/CustomTextField.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/textbox/CustomTextField.kt @@ -10,7 +10,6 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.SolidColor @@ -37,8 +36,8 @@ fun CustomTextField( isError: Boolean = false, readOnly: Boolean = false, enabled: Boolean = true, + interactionSource: MutableInteractionSource, ) { - val interactionSource = remember { MutableInteractionSource() } val space = " " BasicTextField( value = value, diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/options/OptionsScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/options/OptionsScreen.kt index c74b39b..5eb3715 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/options/OptionsScreen.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/options/OptionsScreen.kt @@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Edit @@ -30,6 +31,8 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import com.zaneschepke.wireguardautotunnel.R @@ -38,12 +41,15 @@ import com.zaneschepke.wireguardautotunnel.ui.Route import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelectionGroupButton +import com.zaneschepke.wireguardautotunnel.ui.common.config.SubmitConfigurationTextBox import com.zaneschepke.wireguardautotunnel.ui.common.label.GroupLabel import com.zaneschepke.wireguardautotunnel.ui.common.navigation.LocalNavController import com.zaneschepke.wireguardautotunnel.ui.common.navigation.TopNavBar import com.zaneschepke.wireguardautotunnel.ui.screens.settings.autotunnel.components.TrustedNetworkTextBox import com.zaneschepke.wireguardautotunnel.ui.screens.settings.autotunnel.components.WildcardsLabel import com.zaneschepke.wireguardautotunnel.ui.theme.iconSize +import com.zaneschepke.wireguardautotunnel.util.Constants +import com.zaneschepke.wireguardautotunnel.util.extensions.isValidIpv4orIpv6Address import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight import com.zaneschepke.wireguardautotunnel.util.extensions.scaledWidth @@ -88,112 +94,175 @@ fun OptionsScreen(optionsViewModel: OptionsViewModel = hiltViewModel(), appUiSta ) { GroupLabel(stringResource(R.string.auto_tunneling)) SurfaceSelectionGroupButton( - listOf( - SelectionItem( - Icons.Outlined.Star, - title = { - Text( - stringResource(R.string.primary_tunnel), - style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface), - ) - }, - description = { - Text( - stringResource(R.string.set_primary_tunnel), - style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.outline), - ) - }, - trailing = { - ScaledSwitch( - config.isPrimaryTunnel, + buildList { + addAll( + listOf( + SelectionItem( + Icons.Outlined.Star, + title = { + Text( + stringResource(R.string.primary_tunnel), + style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface), + ) + }, + description = { + Text( + stringResource(R.string.set_primary_tunnel), + style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.outline), + ) + }, + trailing = { + ScaledSwitch( + config.isPrimaryTunnel, + onClick = { optionsViewModel.onTogglePrimaryTunnel(config) }, + ) + }, onClick = { optionsViewModel.onTogglePrimaryTunnel(config) }, - ) - }, - onClick = { optionsViewModel.onTogglePrimaryTunnel(config) }, - ), - SelectionItem( - Icons.Outlined.PhoneAndroid, - title = { Text(stringResource(R.string.mobile_tunnel), style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) }, - description = { - Text( - stringResource(R.string.mobile_data_tunnel), - style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.outline), - ) - }, - trailing = { - ScaledSwitch( - config.isMobileDataTunnel, + ), + SelectionItem( + Icons.Outlined.PhoneAndroid, + title = { + Text( + stringResource(R.string.mobile_tunnel), + style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface), + ) + }, + description = { + Text( + stringResource(R.string.mobile_data_tunnel), + style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.outline), + ) + }, + trailing = { + ScaledSwitch( + config.isMobileDataTunnel, + onClick = { optionsViewModel.onToggleIsMobileDataTunnel(config) }, + ) + }, onClick = { optionsViewModel.onToggleIsMobileDataTunnel(config) }, - ) - }, - onClick = { optionsViewModel.onToggleIsMobileDataTunnel(config) }, - ), - SelectionItem( - Icons.Outlined.NetworkPing, - title = { - Text( - stringResource(R.string.restart_on_ping), - style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface), - ) - }, - trailing = { - ScaledSwitch( - checked = config.isPingEnabled, + ), + SelectionItem( + Icons.Outlined.NetworkPing, + title = { + Text( + stringResource(R.string.restart_on_ping), + style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface), + ) + }, + trailing = { + ScaledSwitch( + checked = config.isPingEnabled, + onClick = { optionsViewModel.onToggleRestartOnPing(config) }, + ) + }, onClick = { optionsViewModel.onToggleRestartOnPing(config) }, - ) - }, - onClick = { optionsViewModel.onToggleRestartOnPing(config) }, - ), - SelectionItem( - title = { - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp.scaledHeight()), - ) { + ), + ), + ) + if (config.isPingEnabled || appUiState.settings.isPingEnabled) { + add( + SelectionItem( + title = {}, + description = { + SubmitConfigurationTextBox( + config.pingIp, + stringResource(R.string.set_custom_ping_ip), + stringResource(R.string.default_ping_ip), + isErrorValue = { !it.isNullOrBlank() && !it.isValidIpv4orIpv6Address() }, + onSubmit = { + optionsViewModel.saveTunnelChanges( + config.copy(pingIp = it.ifBlank { null }), + ) + }, + ) + fun isSecondsError(seconds: String?): Boolean { + return seconds?.let { value -> if (value.isBlank()) false else value.toLong() >= Long.MAX_VALUE / 1000 } ?: false + } + SubmitConfigurationTextBox( + config.pingInterval?.let { (it / 1000).toString() }, + stringResource(R.string.set_custom_ping_internal), + "(${stringResource(R.string.optional_default)} ${Constants.PING_INTERVAL / 1000})", + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Done, + ), + isErrorValue = ::isSecondsError, + onSubmit = { + optionsViewModel.saveTunnelChanges( + config.copy(pingInterval = if (it.isBlank()) null else it.toLong() * 1000), + ) + }, + ) + SubmitConfigurationTextBox( + config.pingCooldown?.let { (it / 1000).toString() }, + stringResource(R.string.set_custom_ping_cooldown), + "(${stringResource(R.string.optional_default)} ${Constants.PING_COOLDOWN / 1000})", + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + ), + isErrorValue = ::isSecondsError, + onSubmit = { + optionsViewModel.saveTunnelChanges( + config.copy(pingCooldown = if (it.isBlank()) null else it.toLong() * 1000), + ) + }, + ) + }, + ), + ) + } + add( + SelectionItem( + title = { Row( verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .weight(4f, false) - .fillMaxWidth(), + modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp.scaledHeight()), ) { - val icon = Icons.Outlined.Security - Icon( - icon, - icon.name, - modifier = Modifier.size(iconSize), - ) - Column( - horizontalAlignment = Alignment.Start, - verticalArrangement = Arrangement.spacedBy(2.dp, Alignment.CenterVertically), + Row( + verticalAlignment = Alignment.CenterVertically, modifier = Modifier - .fillMaxWidth() - .padding(start = 16.dp.scaledWidth()) - .padding(vertical = 6.dp.scaledHeight()), + .weight(4f, false) + .fillMaxWidth(), ) { - Text( - stringResource(R.string.use_tunnel_on_wifi_name), - style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface), + val icon = Icons.Outlined.Security + Icon( + icon, + icon.name, + modifier = Modifier.size(iconSize), ) + Column( + horizontalAlignment = Alignment.Start, + verticalArrangement = Arrangement.spacedBy(2.dp, Alignment.CenterVertically), + modifier = Modifier + .fillMaxWidth() + .padding(start = 16.dp.scaledWidth()) + .padding(vertical = 6.dp.scaledHeight()), + ) { + Text( + stringResource(R.string.use_tunnel_on_wifi_name), + style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface), + ) + } } } - } - }, - description = { - TrustedNetworkTextBox( - config.tunnelNetworks, - onDelete = { optionsViewModel.onDeleteRunSSID(it, config) }, - currentText = currentText, - onSave = { optionsViewModel.onSaveRunSSID(it, config) }, - onValueChange = { currentText = it }, - supporting = { - if (appUiState.settings.isWildcardsEnabled) { - WildcardsLabel() - } - }, - ) - }, - ), - ), + }, + description = { + TrustedNetworkTextBox( + config.tunnelNetworks, + onDelete = { optionsViewModel.onDeleteRunSSID(it, config) }, + currentText = currentText, + onSave = { optionsViewModel.onSaveRunSSID(it, config) }, + onValueChange = { currentText = it }, + supporting = { + if (appUiState.settings.isWildcardsEnabled) { + WildcardsLabel() + } + }, + ) + }, + ), + ) + }, ) } } 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 08027f7..566b47d 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 @@ -150,7 +150,7 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel: { ScaledSwitch( uiState.settings.isShortcutsEnabled, - onClick = { viewModel.onToggleShortcutsEnabled() }, + onClick = { appViewModel.onToggleShortcutsEnabled() }, ) }, title = { @@ -159,7 +159,7 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel: style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface), ) }, - onClick = { viewModel.onToggleShortcutsEnabled() }, + onClick = { appViewModel.onToggleShortcutsEnabled() }, ), SelectionItem( Icons.Outlined.VpnLock, @@ -173,7 +173,7 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel: ) && uiState.settings.isAutoTunnelEnabled ), - onClick = { viewModel.onToggleAlwaysOnVPN() }, + onClick = { appViewModel.onToggleAlwaysOnVPN() }, checked = uiState.settings.isAlwaysOnVpnEnabled, ) }, @@ -183,7 +183,7 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel: style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface), ) }, - onClick = { viewModel.onToggleAlwaysOnVPN() }, + onClick = { appViewModel.onToggleAlwaysOnVPN() }, ), SelectionItem( Icons.Outlined.AdminPanelSettings, @@ -209,7 +209,7 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel: { ScaledSwitch( uiState.settings.isRestoreOnBootEnabled, - onClick = { viewModel.onToggleRestartAtBoot() }, + onClick = { appViewModel.onToggleRestartAtBoot() }, ) }, title = { @@ -218,7 +218,7 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel: style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface), ) }, - onClick = { viewModel.onToggleRestartAtBoot() }, + onClick = { appViewModel.onToggleRestartAtBoot() }, ), ) }, @@ -294,7 +294,7 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel: trailing = { ScaledSwitch( uiState.settings.isKernelEnabled, - onClick = { viewModel.onToggleKernelMode() }, + onClick = { appViewModel.onToggleKernelMode() }, enabled = !( uiState.settings.isAutoTunnelEnabled || uiState.settings.isAlwaysOnVpnEnabled || @@ -303,7 +303,7 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel: ) }, onClick = { - viewModel.onToggleKernelMode() + appViewModel.onToggleKernelMode() }, ), ), 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 4de62be..c86d6f4 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 @@ -4,119 +4,23 @@ import android.content.Context import androidx.core.content.FileProvider import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.wireguard.android.backend.WgQuickBackend -import com.wireguard.android.util.RootShell import com.zaneschepke.wireguardautotunnel.R -import com.zaneschepke.wireguardautotunnel.data.domain.Settings import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository -import com.zaneschepke.wireguardautotunnel.module.AppShell -import com.zaneschepke.wireguardautotunnel.module.IoDispatcher -import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarController import com.zaneschepke.wireguardautotunnel.util.FileUtils -import com.zaneschepke.wireguardautotunnel.util.StringValue import com.zaneschepke.wireguardautotunnel.util.extensions.launchShareFile import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import java.time.Instant import javax.inject.Inject -import javax.inject.Provider @HiltViewModel class SettingsViewModel @Inject constructor( private val appDataRepository: AppDataRepository, - @AppShell private val rootShell: Provider, private val fileUtils: FileUtils, - @IoDispatcher private val ioDispatcher: CoroutineDispatcher, ) : ViewModel() { - private val settings = appDataRepository.settings.getSettingsFlow() - .stateIn(viewModelScope, SharingStarted.Eagerly, Settings()) - - fun setLocationDisclosureShown() = viewModelScope.launch { - appDataRepository.appState.setLocationDisclosureShown(true) - } - - fun onToggleAlwaysOnVPN() = viewModelScope.launch { - with(settings.value) { - appDataRepository.settings.save( - copy( - isAlwaysOnVpnEnabled = !isAlwaysOnVpnEnabled, - ), - ) - } - } - - fun onToggleShortcutsEnabled() = viewModelScope.launch { - with(settings.value) { - appDataRepository.settings.save( - this.copy( - isShortcutsEnabled = !isShortcutsEnabled, - ), - ) - } - } - - private fun saveKernelMode(enabled: Boolean) = viewModelScope.launch { - with(settings.value) { - appDataRepository.settings.save( - this.copy( - isKernelEnabled = enabled, - ), - ) - } - } - - fun onToggleKernelMode() = viewModelScope.launch { - with(settings.value) { - if (!isKernelEnabled) { - requestRoot().onSuccess { - if (!isKernelSupported()) return@onSuccess SnackbarController.showMessage(StringValue.StringResource(R.string.kernel_not_supported)) - appDataRepository.settings.save( - copy( - isKernelEnabled = true, - isAmneziaEnabled = false, - ), - ) - } - } else { - saveKernelMode(enabled = false) - } - } - } - - private suspend fun isKernelSupported(): Boolean { - return withContext(ioDispatcher) { - WgQuickBackend.hasKernelSupport() - } - } - - fun onToggleRestartAtBoot() = viewModelScope.launch { - with(settings.value) { - appDataRepository.settings.save( - copy( - isRestoreOnBootEnabled = !isRestoreOnBootEnabled, - ), - ) - } - } - - private suspend fun requestRoot(): Result { - return withContext(ioDispatcher) { - kotlin.runCatching { - rootShell.get().start() - SnackbarController.showMessage(StringValue.StringResource(R.string.root_accepted)) - }.onFailure { - SnackbarController.showMessage(StringValue.StringResource(R.string.error_root_denied)) - } - } - } - fun exportAllConfigs(context: Context) = viewModelScope.launch { kotlin.runCatching { val shareFile = fileUtils.createNewShareFile("wg-export_${Instant.now().epochSecond}.zip") diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/components/TrustNetworksTextBox.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/components/TrustNetworksTextBox.kt index 30a7430..81869af 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/components/TrustNetworksTextBox.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/components/TrustNetworksTextBox.kt @@ -1,5 +1,6 @@ package com.zaneschepke.wireguardautotunnel.ui.screens.settings.autotunnel.components +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi @@ -16,6 +17,7 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -66,6 +68,7 @@ fun TrustedNetworkTextBox( textStyle = MaterialTheme.typography.bodySmall, value = currentText, onValueChange = onValueChange, + interactionSource = remember { MutableInteractionSource() }, label = { Text(stringResource(R.string.add_wifi_name)) }, containerColor = MaterialTheme.colorScheme.surface, supportingText = supporting,