feat: user logger toggle control
Allow users to toggle on and off local logger. This will hopefully help address #437 Allow shortcuts activation on AndroidTV closes #451
This commit is contained in:
parent
b2938c3ac8
commit
f9ef308f8a
|
@ -6,6 +6,7 @@ import android.os.StrictMode
|
||||||
import android.os.StrictMode.ThreadPolicy
|
import android.os.StrictMode.ThreadPolicy
|
||||||
import com.zaneschepke.logcatter.LogReader
|
import com.zaneschepke.logcatter.LogReader
|
||||||
import com.zaneschepke.wireguardautotunnel.data.datastore.LocaleStorage
|
import com.zaneschepke.wireguardautotunnel.data.datastore.LocaleStorage
|
||||||
|
import com.zaneschepke.wireguardautotunnel.data.repository.AppStateRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.module.ApplicationScope
|
import com.zaneschepke.wireguardautotunnel.module.ApplicationScope
|
||||||
import com.zaneschepke.wireguardautotunnel.module.IoDispatcher
|
import com.zaneschepke.wireguardautotunnel.module.IoDispatcher
|
||||||
import com.zaneschepke.wireguardautotunnel.util.LocaleUtil
|
import com.zaneschepke.wireguardautotunnel.util.LocaleUtil
|
||||||
|
@ -32,6 +33,9 @@ class WireGuardAutoTunnel : Application() {
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var logReader: LogReader
|
lateinit var logReader: LogReader
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var appStateRepository: AppStateRepository
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@IoDispatcher
|
@IoDispatcher
|
||||||
lateinit var ioDispatcher: CoroutineDispatcher
|
lateinit var ioDispatcher: CoroutineDispatcher
|
||||||
|
@ -54,10 +58,13 @@ class WireGuardAutoTunnel : Application() {
|
||||||
}
|
}
|
||||||
if (!isRunningOnTv()) {
|
if (!isRunningOnTv()) {
|
||||||
applicationScope.launch(ioDispatcher) {
|
applicationScope.launch(ioDispatcher) {
|
||||||
|
if (appStateRepository.isLocalLogsEnabled()) {
|
||||||
|
Timber.d("Starting logger")
|
||||||
logReader.start()
|
logReader.start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun attachBaseContext(base: Context) {
|
override fun attachBaseContext(base: Context) {
|
||||||
super.attachBaseContext(LocaleUtil.getLocalizedContext(base, LocaleStorage(base).getPreferredLocale()))
|
super.attachBaseContext(LocaleUtil.getLocalizedContext(base, LocaleStorage(base).getPreferredLocale()))
|
||||||
|
|
|
@ -26,6 +26,7 @@ class DataStoreManager(
|
||||||
val currentSSID = stringPreferencesKey("CURRENT_SSID")
|
val currentSSID = stringPreferencesKey("CURRENT_SSID")
|
||||||
val pinLockEnabled = booleanPreferencesKey("PIN_LOCK_ENABLED")
|
val pinLockEnabled = booleanPreferencesKey("PIN_LOCK_ENABLED")
|
||||||
val tunnelStatsExpanded = booleanPreferencesKey("TUNNEL_STATS_EXPANDED")
|
val tunnelStatsExpanded = booleanPreferencesKey("TUNNEL_STATS_EXPANDED")
|
||||||
|
val isLocalLogsEnabled = booleanPreferencesKey("LOCAL_LOGS_ENABLED")
|
||||||
val theme = stringPreferencesKey("THEME")
|
val theme = stringPreferencesKey("THEME")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ data class GeneralState(
|
||||||
val isBatteryOptimizationDisableShown: Boolean = BATTERY_OPTIMIZATION_DISABLE_SHOWN_DEFAULT,
|
val isBatteryOptimizationDisableShown: Boolean = BATTERY_OPTIMIZATION_DISABLE_SHOWN_DEFAULT,
|
||||||
val isPinLockEnabled: Boolean = PIN_LOCK_ENABLED_DEFAULT,
|
val isPinLockEnabled: Boolean = PIN_LOCK_ENABLED_DEFAULT,
|
||||||
val isTunnelStatsExpanded: Boolean = IS_TUNNEL_STATS_EXPANDED,
|
val isTunnelStatsExpanded: Boolean = IS_TUNNEL_STATS_EXPANDED,
|
||||||
|
val isLocalLogsEnabled: Boolean = IS_LOGS_ENABLED_DEFAULT,
|
||||||
val theme: Theme = Theme.AUTOMATIC,
|
val theme: Theme = Theme.AUTOMATIC,
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -14,5 +15,6 @@ data class GeneralState(
|
||||||
const val BATTERY_OPTIMIZATION_DISABLE_SHOWN_DEFAULT = false
|
const val BATTERY_OPTIMIZATION_DISABLE_SHOWN_DEFAULT = false
|
||||||
const val PIN_LOCK_ENABLED_DEFAULT = false
|
const val PIN_LOCK_ENABLED_DEFAULT = false
|
||||||
const val IS_TUNNEL_STATS_EXPANDED = false
|
const val IS_TUNNEL_STATS_EXPANDED = false
|
||||||
|
const val IS_LOGS_ENABLED_DEFAULT = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,5 +29,9 @@ interface AppStateRepository {
|
||||||
|
|
||||||
suspend fun getTheme(): Theme
|
suspend fun getTheme(): Theme
|
||||||
|
|
||||||
|
suspend fun isLocalLogsEnabled(): Boolean
|
||||||
|
|
||||||
|
suspend fun setLocalLogsEnabled(enabled: Boolean)
|
||||||
|
|
||||||
val generalStateFlow: Flow<GeneralState>
|
val generalStateFlow: Flow<GeneralState>
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,14 @@ class DataStoreAppStateRepository(
|
||||||
} ?: Theme.AUTOMATIC
|
} ?: Theme.AUTOMATIC
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun isLocalLogsEnabled(): Boolean {
|
||||||
|
return dataStoreManager.getFromStore(DataStoreManager.isLocalLogsEnabled) ?: GeneralState.IS_LOGS_ENABLED_DEFAULT
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun setLocalLogsEnabled(enabled: Boolean) {
|
||||||
|
dataStoreManager.saveToDataStore(DataStoreManager.isLocalLogsEnabled, enabled)
|
||||||
|
}
|
||||||
|
|
||||||
override val generalStateFlow: Flow<GeneralState> =
|
override val generalStateFlow: Flow<GeneralState> =
|
||||||
dataStoreManager.preferencesFlow.map { prefs ->
|
dataStoreManager.preferencesFlow.map { prefs ->
|
||||||
prefs?.let { pref ->
|
prefs?.let { pref ->
|
||||||
|
@ -84,6 +92,7 @@ class DataStoreAppStateRepository(
|
||||||
pref[DataStoreManager.pinLockEnabled]
|
pref[DataStoreManager.pinLockEnabled]
|
||||||
?: GeneralState.PIN_LOCK_ENABLED_DEFAULT,
|
?: GeneralState.PIN_LOCK_ENABLED_DEFAULT,
|
||||||
isTunnelStatsExpanded = pref[DataStoreManager.tunnelStatsExpanded] ?: GeneralState.IS_TUNNEL_STATS_EXPANDED,
|
isTunnelStatsExpanded = pref[DataStoreManager.tunnelStatsExpanded] ?: GeneralState.IS_TUNNEL_STATS_EXPANDED,
|
||||||
|
isLocalLogsEnabled = pref[DataStoreManager.isLocalLogsEnabled] ?: GeneralState.IS_LOGS_ENABLED_DEFAULT,
|
||||||
theme = getTheme(),
|
theme = getTheme(),
|
||||||
)
|
)
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
|
|
|
@ -49,7 +49,7 @@ constructor(
|
||||||
vpnState.copy(
|
vpnState.copy(
|
||||||
tunnelConfig = tunnels.firstOrNull { it.id == vpnState.tunnelConfig?.id },
|
tunnelConfig = tunnels.firstOrNull { it.id == vpnState.tunnelConfig?.id },
|
||||||
)
|
)
|
||||||
}.stateIn(applicationScope, SharingStarted.Lazily, VpnState())
|
}.stateIn(applicationScope, SharingStarted.Eagerly, VpnState())
|
||||||
|
|
||||||
private var statsJob: Job? = null
|
private var statsJob: Job? = null
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.wireguard.android.backend.WgQuickBackend
|
import com.wireguard.android.backend.WgQuickBackend
|
||||||
import com.wireguard.android.util.RootShell
|
import com.wireguard.android.util.RootShell
|
||||||
|
import com.zaneschepke.logcatter.LogReader
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
|
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
|
||||||
import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository
|
import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository
|
||||||
|
@ -25,6 +26,7 @@ import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.onCompletion
|
import kotlinx.coroutines.flow.onCompletion
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.flow.takeWhile
|
import kotlinx.coroutines.flow.takeWhile
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.plus
|
import kotlinx.coroutines.plus
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
@ -41,6 +43,7 @@ constructor(
|
||||||
@IoDispatcher private val ioDispatcher: CoroutineDispatcher,
|
@IoDispatcher private val ioDispatcher: CoroutineDispatcher,
|
||||||
@AppShell private val rootShell: Provider<RootShell>,
|
@AppShell private val rootShell: Provider<RootShell>,
|
||||||
private val serviceManager: ServiceManager,
|
private val serviceManager: ServiceManager,
|
||||||
|
private val logReader: LogReader,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
val uiState =
|
val uiState =
|
||||||
|
@ -64,11 +67,14 @@ constructor(
|
||||||
AppUiState(),
|
AppUiState(),
|
||||||
)
|
)
|
||||||
|
|
||||||
private val _isAppReady = MutableStateFlow<Boolean>(false)
|
private val _isAppReady = MutableStateFlow(false)
|
||||||
val isAppReady = _isAppReady.asStateFlow()
|
val isAppReady = _isAppReady.asStateFlow()
|
||||||
|
|
||||||
|
private val _configurationChange = MutableStateFlow(false)
|
||||||
|
val configurationChange = _configurationChange.asStateFlow()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch(ioDispatcher) {
|
||||||
initPin()
|
initPin()
|
||||||
initAutoTunnel()
|
initAutoTunnel()
|
||||||
initTunnel()
|
initTunnel()
|
||||||
|
@ -84,7 +90,6 @@ constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun initTunnel() {
|
private suspend fun initTunnel() {
|
||||||
if (tunnelService.get().getState() == TunnelState.UP) tunnelService.get().startStatsJob()
|
|
||||||
val activeTunnels = appDataRepository.tunnels.getActive()
|
val activeTunnels = appDataRepository.tunnels.getActive()
|
||||||
if (activeTunnels.isNotEmpty() &&
|
if (activeTunnels.isNotEmpty() &&
|
||||||
tunnelService.get().getState() == TunnelState.DOWN
|
tunnelService.get().getState() == TunnelState.DOWN
|
||||||
|
@ -116,6 +121,22 @@ constructor(
|
||||||
appDataRepository.appState.setLocationDisclosureShown(true)
|
appDataRepository.appState.setLocationDisclosureShown(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onToggleLocalLogging() = viewModelScope.launch(ioDispatcher) {
|
||||||
|
with(uiState.value.generalState) {
|
||||||
|
val toggledOn = !isLocalLogsEnabled
|
||||||
|
appDataRepository.appState.setLocalLogsEnabled(toggledOn)
|
||||||
|
if (!toggledOn) onLoggerStop()
|
||||||
|
_configurationChange.update {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun onLoggerStop() {
|
||||||
|
logReader.stop()
|
||||||
|
logReader.deleteAndClearLogs()
|
||||||
|
}
|
||||||
|
|
||||||
fun onToggleAlwaysOnVPN() = viewModelScope.launch {
|
fun onToggleAlwaysOnVPN() = viewModelScope.launch {
|
||||||
with(uiState.value.settings) {
|
with(uiState.value.settings) {
|
||||||
appDataRepository.settings.save(
|
appDataRepository.settings.save(
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.zaneschepke.wireguardautotunnel.ui
|
package com.zaneschepke.wireguardautotunnel.ui
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
|
@ -24,9 +25,7 @@ import androidx.compose.material3.surfaceColorAtElevation
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
|
||||||
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 androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||||
|
@ -42,7 +41,6 @@ import com.zaneschepke.wireguardautotunnel.data.repository.AppStateRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
|
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.BottomNavBar
|
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.BottomNavBar
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.BottomNavItem
|
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.BottomNavItem
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.LocalFocusRequester
|
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.LocalNavController
|
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.LocalNavController
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.CustomSnackBar
|
import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.CustomSnackBar
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarControllerProvider
|
import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarControllerProvider
|
||||||
|
@ -65,8 +63,8 @@ import com.zaneschepke.wireguardautotunnel.util.LocaleUtil
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.requestAutoTunnelTileServiceUpdate
|
import com.zaneschepke.wireguardautotunnel.util.extensions.requestAutoTunnelTileServiceUpdate
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.requestTunnelTileServiceStateUpdate
|
import com.zaneschepke.wireguardautotunnel.util.extensions.requestTunnelTileServiceStateUpdate
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import timber.log.Timber
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
@ -83,11 +81,11 @@ class MainActivity : AppCompatActivity() {
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var tunnelService: TunnelService
|
lateinit var tunnelService: TunnelService
|
||||||
|
|
||||||
private val viewModel by viewModels<AppViewModel>()
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
val viewModel by viewModels<AppViewModel>()
|
||||||
|
|
||||||
installSplashScreen().apply {
|
installSplashScreen().apply {
|
||||||
setKeepOnScreenCondition {
|
setKeepOnScreenCondition {
|
||||||
!viewModel.isAppReady.value
|
!viewModel.isAppReady.value
|
||||||
|
@ -95,15 +93,23 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
val appUiState by viewModel.uiState.collectAsStateWithLifecycle(lifecycle = this.lifecycle)
|
val appUiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
|
val configurationChange by viewModel.configurationChange.collectAsStateWithLifecycle()
|
||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
val rootItemFocusRequester = remember { FocusRequester() }
|
|
||||||
|
|
||||||
LaunchedEffect(appUiState.tunnels) {
|
LaunchedEffect(appUiState.tunnels) {
|
||||||
Timber.d("Updating launched")
|
|
||||||
requestTunnelTileServiceStateUpdate()
|
requestTunnelTileServiceStateUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(configurationChange) {
|
||||||
|
if (configurationChange) {
|
||||||
|
Intent(this@MainActivity, MainActivity::class.java).also {
|
||||||
|
startActivity(it)
|
||||||
|
exitProcess(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(appUiState.autoTunnelActive) {
|
LaunchedEffect(appUiState.autoTunnelActive) {
|
||||||
requestAutoTunnelTileServiceUpdate()
|
requestAutoTunnelTileServiceUpdate()
|
||||||
}
|
}
|
||||||
|
@ -114,7 +120,6 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CompositionLocalProvider(LocalFocusRequester provides rootItemFocusRequester) {
|
|
||||||
CompositionLocalProvider(LocalNavController provides navController) {
|
CompositionLocalProvider(LocalNavController provides navController) {
|
||||||
SnackbarControllerProvider { host ->
|
SnackbarControllerProvider { host ->
|
||||||
WireguardAutoTunnelTheme(theme = appUiState.generalState.theme) {
|
WireguardAutoTunnelTheme(theme = appUiState.generalState.theme) {
|
||||||
|
@ -191,7 +196,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
DisplayScreen(appUiState)
|
DisplayScreen(appUiState)
|
||||||
}
|
}
|
||||||
composable<Route.Support> {
|
composable<Route.Support> {
|
||||||
SupportScreen()
|
SupportScreen(appUiState, viewModel)
|
||||||
}
|
}
|
||||||
composable<Route.Logs> {
|
composable<Route.Logs> {
|
||||||
LogsScreen()
|
LogsScreen()
|
||||||
|
@ -225,7 +230,6 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override fun attachBaseContext(newBase: Context) {
|
override fun attachBaseContext(newBase: Context) {
|
||||||
oldPrefLocaleCode = LocaleStorage(newBase).getPreferredLocale()
|
oldPrefLocaleCode = LocaleStorage(newBase).getPreferredLocale()
|
||||||
|
@ -241,9 +245,4 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
super.onResume()
|
super.onResume()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
tunnelService.cancelStatsJob()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
package com.zaneschepke.wireguardautotunnel.ui.common.navigation
|
package com.zaneschepke.wireguardautotunnel.ui.common.navigation
|
||||||
|
|
||||||
import androidx.compose.runtime.compositionLocalOf
|
import androidx.compose.runtime.compositionLocalOf
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
|
|
||||||
val LocalNavController = compositionLocalOf<NavHostController> {
|
val LocalNavController = compositionLocalOf<NavHostController> {
|
||||||
error("NavController was not provided")
|
error("NavController was not provided")
|
||||||
}
|
}
|
||||||
|
|
||||||
val LocalFocusRequester = compositionLocalOf<FocusRequester> { error("FocusRequester is not provided") }
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ package com.zaneschepke.wireguardautotunnel.ui.screens.options
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
@ -53,7 +52,6 @@ import com.zaneschepke.wireguardautotunnel.util.extensions.isValidIpv4orIpv6Addr
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight
|
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledWidth
|
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledWidth
|
||||||
|
|
||||||
@OptIn(ExperimentalLayoutApi::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun OptionsScreen(optionsViewModel: OptionsViewModel = hiltViewModel(), appUiState: AppUiState, tunnelId: Int) {
|
fun OptionsScreen(optionsViewModel: OptionsViewModel = hiltViewModel(), appUiState: AppUiState, tunnelId: Int) {
|
||||||
val navController = LocalNavController.current
|
val navController = LocalNavController.current
|
||||||
|
|
|
@ -5,7 +5,6 @@ import androidx.compose.foundation.focusable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
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
|
||||||
|
@ -30,13 +29,11 @@ import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.focus.focusRequester
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalFocusManager
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
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 androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelState
|
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelState
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.AppUiState
|
import com.zaneschepke.wireguardautotunnel.ui.AppUiState
|
||||||
|
@ -45,7 +42,6 @@ import com.zaneschepke.wireguardautotunnel.ui.Route
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
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.SelectionItem
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelectionGroupButton
|
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelectionGroupButton
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.LocalFocusRequester
|
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.LocalNavController
|
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.LocalNavController
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.prompt.AuthorizationPrompt
|
import com.zaneschepke.wireguardautotunnel.ui.common.prompt.AuthorizationPrompt
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarController
|
import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarController
|
||||||
|
@ -59,17 +55,12 @@ import com.zaneschepke.wireguardautotunnel.util.extensions.scaledWidth
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.showToast
|
import com.zaneschepke.wireguardautotunnel.util.extensions.showToast
|
||||||
import xyz.teamgravity.pin_lock_compose.PinManager
|
import xyz.teamgravity.pin_lock_compose.PinManager
|
||||||
|
|
||||||
@OptIn(
|
|
||||||
ExperimentalPermissionsApi::class,
|
|
||||||
ExperimentalLayoutApi::class,
|
|
||||||
)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel: AppViewModel, uiState: AppUiState) {
|
fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel: AppViewModel, uiState: AppUiState) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val navController = LocalNavController.current
|
val navController = LocalNavController.current
|
||||||
val focusManager = LocalFocusManager.current
|
val focusManager = LocalFocusManager.current
|
||||||
val snackbar = SnackbarController.current
|
val snackbar = SnackbarController.current
|
||||||
val rootFocusRequester = LocalFocusRequester.current
|
|
||||||
val isRunningOnTv = remember { context.isRunningOnTv() }
|
val isRunningOnTv = remember { context.isRunningOnTv() }
|
||||||
|
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
|
@ -135,16 +126,14 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel:
|
||||||
navController.navigate(Route.AutoTunnel)
|
navController.navigate(Route.AutoTunnel)
|
||||||
},
|
},
|
||||||
trailing = {
|
trailing = {
|
||||||
ForwardButton(Modifier.focusable().focusRequester(rootFocusRequester)) { navController.navigate(Route.AutoTunnel) }
|
ForwardButton(Modifier.focusable()) { navController.navigate(Route.AutoTunnel) }
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
SurfaceSelectionGroupButton(
|
SurfaceSelectionGroupButton(
|
||||||
buildList {
|
buildList {
|
||||||
if (!isRunningOnTv) {
|
add(
|
||||||
addAll(
|
|
||||||
listOf(
|
|
||||||
SelectionItem(
|
SelectionItem(
|
||||||
Icons.Filled.AppShortcut,
|
Icons.Filled.AppShortcut,
|
||||||
{
|
{
|
||||||
|
@ -161,6 +150,10 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel:
|
||||||
},
|
},
|
||||||
onClick = { appViewModel.onToggleShortcutsEnabled() },
|
onClick = { appViewModel.onToggleShortcutsEnabled() },
|
||||||
),
|
),
|
||||||
|
)
|
||||||
|
if (!isRunningOnTv) {
|
||||||
|
addAll(
|
||||||
|
listOf(
|
||||||
SelectionItem(
|
SelectionItem(
|
||||||
Icons.Outlined.VpnLock,
|
Icons.Outlined.VpnLock,
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,12 +9,17 @@ import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.AttachMoney
|
import androidx.compose.material.icons.filled.AttachMoney
|
||||||
import androidx.compose.material.icons.filled.Book
|
import androidx.compose.material.icons.filled.Book
|
||||||
import androidx.compose.material.icons.filled.LineStyle
|
|
||||||
import androidx.compose.material.icons.filled.Mail
|
import androidx.compose.material.icons.filled.Mail
|
||||||
import androidx.compose.material.icons.filled.Policy
|
import androidx.compose.material.icons.filled.Policy
|
||||||
|
import androidx.compose.material.icons.filled.ViewTimeline
|
||||||
|
import androidx.compose.material.icons.outlined.ViewHeadline
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
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.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
@ -24,24 +29,42 @@ import androidx.compose.ui.res.vectorResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.zaneschepke.wireguardautotunnel.BuildConfig
|
import com.zaneschepke.wireguardautotunnel.BuildConfig
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
|
import com.zaneschepke.wireguardautotunnel.ui.AppUiState
|
||||||
|
import com.zaneschepke.wireguardautotunnel.ui.AppViewModel
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.Route
|
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.SelectionItem
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelectionGroupButton
|
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelectionGroupButton
|
||||||
|
import com.zaneschepke.wireguardautotunnel.ui.common.dialog.InfoDialog
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.label.GroupLabel
|
import com.zaneschepke.wireguardautotunnel.ui.common.label.GroupLabel
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.label.VersionLabel
|
import com.zaneschepke.wireguardautotunnel.ui.common.label.VersionLabel
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.LocalNavController
|
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.LocalNavController
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.ForwardButton
|
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.ForwardButton
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.theme.topPadding
|
import com.zaneschepke.wireguardautotunnel.ui.theme.topPadding
|
||||||
|
import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.launchSupportEmail
|
import com.zaneschepke.wireguardautotunnel.util.extensions.launchSupportEmail
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.openWebUrl
|
import com.zaneschepke.wireguardautotunnel.util.extensions.openWebUrl
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight
|
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledWidth
|
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledWidth
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SupportScreen() {
|
fun SupportScreen(appUiState: AppUiState, appViewModel: AppViewModel) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val navController = LocalNavController.current
|
val navController = LocalNavController.current
|
||||||
|
|
||||||
|
var showDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
if (showDialog) {
|
||||||
|
InfoDialog(onAttest = {
|
||||||
|
showDialog = false
|
||||||
|
appViewModel.onToggleLocalLogging()
|
||||||
|
}, onDismiss = {
|
||||||
|
showDialog = false
|
||||||
|
}, title = {
|
||||||
|
Text(stringResource(R.string.configuration_change))
|
||||||
|
}, body = { Text(stringResource(R.string.requires_app_relaunch)) }, confirmText = { Text(stringResource(R.string.yes)) })
|
||||||
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.Start,
|
horizontalAlignment = Alignment.Start,
|
||||||
verticalArrangement = Arrangement.spacedBy(24.dp.scaledHeight(), Alignment.Top),
|
verticalArrangement = Arrangement.spacedBy(24.dp.scaledHeight(), Alignment.Top),
|
||||||
|
@ -54,7 +77,8 @@ fun SupportScreen() {
|
||||||
) {
|
) {
|
||||||
GroupLabel(stringResource(R.string.thank_you))
|
GroupLabel(stringResource(R.string.thank_you))
|
||||||
SurfaceSelectionGroupButton(
|
SurfaceSelectionGroupButton(
|
||||||
listOf(
|
buildList {
|
||||||
|
add(
|
||||||
SelectionItem(
|
SelectionItem(
|
||||||
Icons.Filled.Book,
|
Icons.Filled.Book,
|
||||||
title = {
|
title = {
|
||||||
|
@ -70,8 +94,40 @@ fun SupportScreen() {
|
||||||
context.openWebUrl(context.getString(R.string.docs_url))
|
context.openWebUrl(context.getString(R.string.docs_url))
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
)
|
||||||
|
if (!context.isRunningOnTv()) {
|
||||||
|
add(
|
||||||
SelectionItem(
|
SelectionItem(
|
||||||
Icons.Filled.LineStyle,
|
Icons.Outlined.ViewHeadline,
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.local_logging),
|
||||||
|
style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
description = {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.enable_local_logging),
|
||||||
|
style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.outline),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
trailing = {
|
||||||
|
ScaledSwitch(
|
||||||
|
appUiState.generalState.isLocalLogsEnabled,
|
||||||
|
onClick = {
|
||||||
|
showDialog = true
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onClick = {
|
||||||
|
showDialog = true
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if (appUiState.generalState.isLocalLogsEnabled) {
|
||||||
|
add(
|
||||||
|
SelectionItem(
|
||||||
|
Icons.Filled.ViewTimeline,
|
||||||
title = {
|
title = {
|
||||||
Text(
|
Text(
|
||||||
stringResource(R.string.read_logs),
|
stringResource(R.string.read_logs),
|
||||||
|
@ -87,6 +143,10 @@ fun SupportScreen() {
|
||||||
navController.navigate(Route.Logs)
|
navController.navigate(Route.Logs)
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add(
|
||||||
SelectionItem(
|
SelectionItem(
|
||||||
Icons.Filled.Policy,
|
Icons.Filled.Policy,
|
||||||
title = {
|
title = {
|
||||||
|
@ -102,8 +162,8 @@ fun SupportScreen() {
|
||||||
context.openWebUrl(context.getString(R.string.privacy_policy_url))
|
context.openWebUrl(context.getString(R.string.privacy_policy_url))
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
)
|
||||||
),
|
},
|
||||||
)
|
)
|
||||||
SurfaceSelectionGroupButton(
|
SurfaceSelectionGroupButton(
|
||||||
buildList {
|
buildList {
|
||||||
|
|
|
@ -229,4 +229,8 @@
|
||||||
<string name="tunnel_running">Tunnel running</string>
|
<string name="tunnel_running">Tunnel running</string>
|
||||||
<string name="monitoring_state_changes">Monitoring state changes</string>
|
<string name="monitoring_state_changes">Monitoring state changes</string>
|
||||||
<string name="donate">Donate to project</string>
|
<string name="donate">Donate to project</string>
|
||||||
|
<string name="local_logging">Local logging</string>
|
||||||
|
<string name="enable_local_logging">Enable local logging</string>
|
||||||
|
<string name="configuration_change">Configuration change</string>
|
||||||
|
<string name="requires_app_relaunch">This change requires an app relaunch. Would you like to proceed?</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in New Issue