diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 202299a..c52f965 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -151,6 +151,7 @@ dependencies {
implementation(libs.androidx.compose.ui.tooling.preview)
implementation(libs.androidx.material3)
implementation(libs.androidx.appcompat)
+ implementation(libs.material)
// test
testImplementation(libs.junit)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 1ae1216..180c821 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -40,6 +40,12 @@
+
+
+
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/datastore/DataStoreManager.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/datastore/DataStoreManager.kt
index 5ed880d..54c80c3 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/datastore/DataStoreManager.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/datastore/DataStoreManager.kt
@@ -26,6 +26,7 @@ class DataStoreManager(
val currentSSID = stringPreferencesKey("CURRENT_SSID")
val pinLockEnabled = booleanPreferencesKey("PIN_LOCK_ENABLED")
val tunnelStatsExpanded = booleanPreferencesKey("TUNNEL_STATS_EXPANDED")
+ val wildcardsEnabled = booleanPreferencesKey("WILDCARDS_ENABLED")
val theme = stringPreferencesKey("THEME")
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/domain/GeneralState.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/domain/GeneralState.kt
index f53ca1f..db7be88 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/domain/GeneralState.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/domain/GeneralState.kt
@@ -7,6 +7,7 @@ data class GeneralState(
val isBatteryOptimizationDisableShown: Boolean = BATTERY_OPTIMIZATION_DISABLE_SHOWN_DEFAULT,
val isPinLockEnabled: Boolean = PIN_LOCK_ENABLED_DEFAULT,
val isTunnelStatsExpanded: Boolean = IS_TUNNEL_STATS_EXPANDED,
+ val isWildcardsEnabled: Boolean = IS_WILDCARDS_ENABLED,
val theme: Theme = Theme.AUTOMATIC
) {
companion object {
@@ -14,5 +15,6 @@ data class GeneralState(
const val BATTERY_OPTIMIZATION_DISABLE_SHOWN_DEFAULT = false
const val PIN_LOCK_ENABLED_DEFAULT = false
const val IS_TUNNEL_STATS_EXPANDED = false
+ const val IS_WILDCARDS_ENABLED = false
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/repository/AppStateRepository.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/repository/AppStateRepository.kt
index 82e2cfd..142465c 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/repository/AppStateRepository.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/repository/AppStateRepository.kt
@@ -13,6 +13,10 @@ interface AppStateRepository {
suspend fun setPinLockEnabled(enabled: Boolean)
+ suspend fun isWildcardsEnabled(): Boolean
+
+ suspend fun setWildcardsEnabled(enabled: Boolean)
+
suspend fun isBatteryOptimizationDisableShown(): Boolean
suspend fun setBatteryOptimizationDisableShown(shown: Boolean)
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/repository/DataStoreAppStateRepository.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/repository/DataStoreAppStateRepository.kt
index 06ac943..58260a6 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/repository/DataStoreAppStateRepository.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/repository/DataStoreAppStateRepository.kt
@@ -29,6 +29,14 @@ class DataStoreAppStateRepository(
dataStoreManager.saveToDataStore(DataStoreManager.pinLockEnabled, enabled)
}
+ override suspend fun isWildcardsEnabled(): Boolean {
+ return dataStoreManager.getFromStore(DataStoreManager.wildcardsEnabled) ?: GeneralState.IS_WILDCARDS_ENABLED
+ }
+
+ override suspend fun setWildcardsEnabled(enabled: Boolean) {
+ dataStoreManager.saveToDataStore(DataStoreManager.wildcardsEnabled, enabled)
+ }
+
override suspend fun isBatteryOptimizationDisableShown(): Boolean {
return dataStoreManager.getFromStore(DataStoreManager.batteryDisableShown)
?: GeneralState.BATTERY_OPTIMIZATION_DISABLE_SHOWN_DEFAULT
@@ -84,6 +92,7 @@ class DataStoreAppStateRepository(
pref[DataStoreManager.pinLockEnabled]
?: GeneralState.PIN_LOCK_ENABLED_DEFAULT,
isTunnelStatsExpanded = pref[DataStoreManager.tunnelStatsExpanded] ?: GeneralState.IS_TUNNEL_STATS_EXPANDED,
+ isWildcardsEnabled = pref[DataStoreManager.wildcardsEnabled] ?: GeneralState.IS_WILDCARDS_ENABLED,
theme = getTheme()
)
} catch (e: IllegalArgumentException) {
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 cad3e47..468303a 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/AppViewModel.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/AppViewModel.kt
@@ -37,34 +37,17 @@ class AppViewModel
constructor(
private val appDataRepository: AppDataRepository,
private val tunnelService: Provider,
- private val rootShell: Provider,
@IoDispatcher private val ioDispatcher: CoroutineDispatcher,
) : ViewModel() {
- private val _appUiState = MutableStateFlow(AppUiState())
-
- val appUiState = _appUiState.onStart {
- _appUiState.update {
- it.copy(
- isRooted = isRooted(),
- isKernelAvailable = isKernelSupported(),
- )
- }
- }.stateIn(
- viewModelScope + ioDispatcher,
- SharingStarted.WhileSubscribed(Constants.SUBSCRIPTION_TIMEOUT),
- AppUiState(),
- )
-
val uiState =
combine(
appDataRepository.settings.getSettingsFlow(),
appDataRepository.tunnels.getTunnelConfigsFlow(),
tunnelService.get().vpnState,
appDataRepository.appState.generalStateFlow,
- appUiState,
- ) { settings, tunnels, tunnelState, generalState, appUiState ->
- appUiState.copy(
+ ) { settings, tunnels, tunnelState, generalState ->
+ AppUiState(
settings,
tunnels,
tunnelState,
@@ -73,14 +56,13 @@ constructor(
}.stateIn(
viewModelScope + ioDispatcher,
SharingStarted.WhileSubscribed(Constants.SUBSCRIPTION_TIMEOUT),
- _appUiState.value,
+ AppUiState(),
)
private val _isAppReady = MutableStateFlow(false)
val isAppReady = _isAppReady.asStateFlow()
init {
-
viewModelScope.launch {
initPin()
initAutoTunnel()
@@ -116,14 +98,6 @@ constructor(
if (settings.isAutoTunnelEnabled) ServiceManager.startWatcherService(WireGuardAutoTunnel.instance)
}
- fun setTunnels(tunnels: TunnelConfigs) = viewModelScope.launch(ioDispatcher) {
- _appUiState.emit(
- _appUiState.value.copy(
- tunnels = tunnels,
- ),
- )
- }
-
fun onPinLockDisabled() = viewModelScope.launch(ioDispatcher) {
PinManager.clearPin()
appDataRepository.appState.setPinLockEnabled(false)
@@ -133,20 +107,7 @@ constructor(
appDataRepository.appState.setPinLockEnabled(true)
}
- private suspend fun isKernelSupported(): Boolean {
- return withContext(ioDispatcher) {
- WgQuickBackend.hasKernelSupport()
- }
- }
-
- private suspend fun isRooted(): Boolean {
- return try {
- withContext(ioDispatcher) {
- rootShell.get().start()
- }
- true
- } catch (_: Exception) {
- false
- }
+ fun setLocationDisclosureShown() = viewModelScope.launch {
+ appDataRepository.appState.setLocationDisclosureShown(true)
}
}
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 be51b7b..b8d1583 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/MainActivity.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/MainActivity.kt
@@ -8,14 +8,10 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
-import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.WindowInsets
-import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.systemBars
-import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Home
import androidx.compose.material.icons.rounded.QuestionMark
@@ -31,14 +27,12 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
-import androidx.compose.ui.focus.focusProperties
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
-import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import androidx.navigation.toRoute
import com.zaneschepke.wireguardautotunnel.R
@@ -50,9 +44,8 @@ import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelState
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.BottomNavBar
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.TopNavBar
-import com.zaneschepke.wireguardautotunnel.ui.common.navigation.isCurrentRoute
import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.CustomSnackBar
import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarControllerProvider
import com.zaneschepke.wireguardautotunnel.ui.screens.config.ConfigScreen
@@ -65,6 +58,7 @@ import com.zaneschepke.wireguardautotunnel.ui.screens.settings.appearance.Appear
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.appearance.display.DisplayScreen
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.appearance.language.LanguageScreen
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.autotunnel.AutoTunnelScreen
+import com.zaneschepke.wireguardautotunnel.ui.screens.settings.disclosure.LocationDisclosureScreen
import com.zaneschepke.wireguardautotunnel.ui.screens.support.SupportScreen
import com.zaneschepke.wireguardautotunnel.ui.screens.support.logs.LogsScreen
import com.zaneschepke.wireguardautotunnel.ui.theme.WireguardAutoTunnelTheme
@@ -104,7 +98,7 @@ class MainActivity : AppCompatActivity() {
setContent {
val appUiState by viewModel.uiState.collectAsStateWithLifecycle(lifecycle = this.lifecycle)
val navController = rememberNavController()
- val navBackStackEntry by navController.currentBackStackEntryAsState()
+ val rootItemFocusRequester = remember { FocusRequester() }
LaunchedEffect(appUiState.vpnState.status) {
val context = this@MainActivity
@@ -121,122 +115,109 @@ class MainActivity : AppCompatActivity() {
}
}
- CompositionLocalProvider(LocalNavController provides navController) {
- SnackbarControllerProvider { host ->
- WireguardAutoTunnelTheme(theme = appUiState.generalState.theme){
- val focusRequester = remember { FocusRequester() }
- Scaffold(
- contentWindowInsets = WindowInsets(0.dp),
- snackbarHost = {
- SnackbarHost(host) { snackbarData: SnackbarData ->
- CustomSnackBar(
- snackbarData.visuals.message,
- isRtl = false,
- containerColor =
- MaterialTheme.colorScheme.surfaceColorAtElevation(
- 2.dp,
- ),
- )
- }
- },
- modifier =
- Modifier
- .focusable()
- .focusProperties {
- if (navBackStackEntry?.isCurrentRoute(Route.Lock) == true) {
- Unit
- } else {
- up = focusRequester
+ CompositionLocalProvider(LocalFocusRequester provides rootItemFocusRequester) {
+ CompositionLocalProvider(LocalNavController provides navController) {
+ SnackbarControllerProvider { host ->
+ WireguardAutoTunnelTheme(theme = appUiState.generalState.theme) {
+ Scaffold(
+ contentWindowInsets = WindowInsets(0.dp),
+ snackbarHost = {
+ SnackbarHost(host) { snackbarData: SnackbarData ->
+ CustomSnackBar(
+ snackbarData.visuals.message,
+ isRtl = false,
+ containerColor =
+ MaterialTheme.colorScheme.surfaceColorAtElevation(
+ 2.dp,
+ ),
+ )
}
},
- bottomBar = {
- BottomNavBar(
- navController,
- listOf(
- BottomNavItem(
- name = stringResource(R.string.tunnels),
- route = Route.Main,
- icon = Icons.Rounded.Home,
+ bottomBar = {
+ BottomNavBar(
+ navController,
+ listOf(
+ BottomNavItem(
+ name = stringResource(R.string.tunnels),
+ route = Route.Main,
+ icon = Icons.Rounded.Home,
+ ),
+ BottomNavItem(
+ name = stringResource(R.string.settings),
+ route = Route.Settings,
+ icon = Icons.Rounded.Settings,
+ ),
+ BottomNavItem(
+ name = stringResource(R.string.support),
+ route = Route.Support,
+ icon = Icons.Rounded.QuestionMark,
+ ),
),
- BottomNavItem(
- name = stringResource(R.string.settings),
- route = Route.Settings,
- icon = Icons.Rounded.Settings,
- ),
- BottomNavItem(
- name = stringResource(R.string.support),
- route = Route.Support,
- icon = Icons.Rounded.QuestionMark,
- ),
- ),
- )
- },
- ) {
- Box(modifier = Modifier.fillMaxSize().padding(it)) {
- NavHost(
- navController,
- enterTransition = { fadeIn(tween(Constants.TRANSITION_ANIMATION_TIME)) },
- exitTransition = { fadeOut(tween(Constants.TRANSITION_ANIMATION_TIME)) },
- startDestination = (if (appUiState.generalState.isPinLockEnabled == true) Route.Lock else Route.Main),
- ) {
- composable {
- MainScreen(
- focusRequester = focusRequester,
- uiState = appUiState,
- )
- }
- composable {
- SettingsScreen(
- appViewModel = viewModel,
- uiState = appUiState,
- focusRequester = focusRequester,
- )
- }
- composable {
- AutoTunnelScreen(
- appUiState,
- )
- }
- composable {
- AppearanceScreen()
- }
- composable {
- LanguageScreen(localeStorage)
- }
- composable {
- DisplayScreen(appUiState)
- }
- composable {
- SupportScreen(
- focusRequester = focusRequester,
- appUiState = appUiState,
- )
- }
- composable {
- LogsScreen()
- }
- composable {
- val args = it.toRoute()
- ConfigScreen(
- focusRequester = focusRequester,
- tunnelId = args.id,
- )
- }
- composable {
- val args = it.toRoute()
- OptionsScreen(
- tunnelId = args.id,
- focusRequester = focusRequester,
- appUiState = appUiState,
- )
- }
- composable {
- PinLockScreen(
- appViewModel = viewModel,
- )
- }
- composable {
- ScannerScreen()
+ )
+ },
+ ) {
+ Box(modifier = Modifier.fillMaxSize().padding(it)) {
+ NavHost(
+ navController,
+ enterTransition = { fadeIn(tween(Constants.TRANSITION_ANIMATION_TIME)) },
+ exitTransition = { fadeOut(tween(Constants.TRANSITION_ANIMATION_TIME)) },
+ startDestination = (if (appUiState.generalState.isPinLockEnabled == true) Route.Lock else Route.Main),
+ ) {
+ composable {
+ MainScreen(
+ uiState = appUiState,
+ )
+ }
+ composable {
+ SettingsScreen(
+ appViewModel = viewModel,
+ uiState = appUiState,
+ )
+ }
+ composable {
+ LocationDisclosureScreen(viewModel, appUiState)
+ }
+ composable {
+ AutoTunnelScreen(
+ appUiState,
+ )
+ }
+ composable {
+ AppearanceScreen()
+ }
+ composable {
+ LanguageScreen(localeStorage)
+ }
+ composable {
+ DisplayScreen(appUiState)
+ }
+ composable {
+ SupportScreen()
+ }
+ composable {
+ LogsScreen()
+ }
+ composable {
+ val args = it.toRoute()
+ ConfigScreen(
+ tunnelId = args.id,
+ )
+ }
+ composable {
+ val args = it.toRoute()
+ OptionsScreen(
+ tunnelId = args.id,
+ appUiState = appUiState,
+ )
+ }
+ composable {
+ PinLockScreen(
+ appViewModel = viewModel,
+ )
+ }
+ composable {
+ ScannerScreen()
+ }
}
}
}
@@ -267,3 +248,4 @@ class MainActivity : AppCompatActivity() {
tunnelService.cancelStatsJob()
}
}
+
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/Route.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/Route.kt
index 0d0e7d3..4a7d714 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/Route.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/Route.kt
@@ -12,6 +12,9 @@ sealed class Route {
@Serializable
data object AutoTunnel : Route()
+ @Serializable
+ data object LocationDisclosure : Route()
+
@Serializable
data object Appearance : Route()
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/ClickableIconButton.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/ClickableIconButton.kt
index cd1ed79..5cf74b2 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/ClickableIconButton.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/ClickableIconButton.kt
@@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.size
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
@@ -17,7 +18,7 @@ fun ClickableIconButton(onClick: () -> Unit, onIconClick: () -> Unit, text: Stri
onClick = onClick,
enabled = enabled,
) {
- Text(text, Modifier.weight(1f, false))
+ Text(text, Modifier.weight(1f, false), style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.primary)
Spacer(modifier = Modifier.size(ButtonDefaults.IconSpacing))
Icon(
imageVector = icon,
@@ -30,7 +31,7 @@ fun ClickableIconButton(onClick: () -> Unit, onIconClick: () -> Unit, text: Stri
if (enabled) {
onIconClick()
}
- },
+ },
)
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/ExpandingRowListItem.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/ExpandingRowListItem.kt
index a83a97d..0109326 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/ExpandingRowListItem.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/ExpandingRowListItem.kt
@@ -31,12 +31,10 @@ fun ExpandingRowListItem(
trailing: @Composable () -> Unit,
isExpanded: Boolean,
expanded: @Composable () -> Unit = {},
- focusRequester: FocusRequester,
) {
Box(
modifier =
Modifier
- .focusRequester(focusRequester)
.animateContentSize()
.clip(RoundedCornerShape(30.dp))
.combinedClickable(
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/button/IconSurfaceButton.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/button/IconSurfaceButton.kt
index f12fede..6ceb0c4 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/button/IconSurfaceButton.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/button/IconSurfaceButton.kt
@@ -2,16 +2,24 @@ package com.zaneschepke.wireguardautotunnel.ui.common.button
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.clickable
+import androidx.compose.foundation.focusable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
+import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Card
+import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.dp
@@ -27,63 +35,61 @@ fun IconSurfaceButton(title: String, onClick: () -> Unit, selected: Boolean, lea
1.dp,
MaterialTheme.colorScheme.primary
) else null
- val interactionSource =
- androidx.compose.runtime.remember { androidx.compose.foundation.interaction.MutableInteractionSource() }
- androidx.compose.material3.Card(
+ Card(
modifier =
Modifier
.fillMaxWidth()
- .height(IntrinsicSize.Min)
- .clickable(interactionSource = interactionSource, indication = null) {
- onClick()
- },
- shape = androidx.compose.foundation.shape.RoundedCornerShape(8.dp),
+ .height(IntrinsicSize.Min),
+ shape = RoundedCornerShape(8.dp),
border = border,
- colors = androidx.compose.material3.CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface),
+ colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface),
) {
- Column(
- modifier =
- Modifier
- .padding(horizontal = 8.dp.scaledWidth(), vertical = 10.dp.scaledHeight())
- .padding(end = 16.dp.scaledWidth()).padding(start = 8.dp.scaledWidth())
- .fillMaxSize(),
- verticalArrangement = androidx.compose.foundation.layout.Arrangement.Center,
- horizontalAlignment = androidx.compose.ui.Alignment.Companion.Start,
- ) {
- androidx.compose.foundation.layout.Row(
- verticalAlignment = androidx.compose.ui.Alignment.Companion.CenterVertically,
- horizontalArrangement = androidx.compose.foundation.layout.Arrangement.spacedBy(16.dp.scaledWidth()),
- ) {
- androidx.compose.foundation.layout.Row(
- horizontalArrangement = androidx.compose.foundation.layout.Arrangement.spacedBy(
- 16.dp.scaledWidth()
- ),
- verticalAlignment = androidx.compose.ui.Alignment.Companion.CenterVertically,
- modifier = Modifier.padding(vertical = if (description == null) 10.dp.scaledHeight() else 0.dp),
- ) {
- leadingIcon?.let {
- Icon(
- leadingIcon,
- leadingIcon.name,
- Modifier.Companion.size(iconSize.scaledWidth()),
- if (selected) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface,
- )
- }
- Column {
- Text(
- title,
- style = MaterialTheme.typography.titleMedium
- )
- description?.let {
- Text(
- description,
- color = MaterialTheme.colorScheme.onSurfaceVariant,
- style = MaterialTheme.typography.bodyMedium,
- )
- }
- }
- }
- }
- }
+ Box(modifier = Modifier.clickable { onClick() }
+ .fillMaxWidth()) {
+ Column(
+ modifier =
+ Modifier
+ .padding(horizontal = 8.dp.scaledWidth(), vertical = 10.dp.scaledHeight())
+ .padding(end = 16.dp.scaledWidth()).padding(start = 8.dp.scaledWidth())
+ .fillMaxSize(),
+ verticalArrangement = Arrangement.Center,
+ horizontalAlignment = Alignment.Start,
+ ) {
+ Row(
+ verticalAlignment = Alignment.Companion.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(16.dp.scaledWidth()),
+ ) {
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(
+ 16.dp.scaledWidth()
+ ),
+ verticalAlignment = Alignment.Companion.CenterVertically,
+ modifier = Modifier.padding(vertical = if (description == null) 10.dp.scaledHeight() else 0.dp),
+ ) {
+ leadingIcon?.let {
+ Icon(
+ leadingIcon,
+ leadingIcon.name,
+ Modifier.size(iconSize),
+ if (selected) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface,
+ )
+ }
+ Column {
+ Text(
+ title,
+ style = MaterialTheme.typography.titleMedium
+ )
+ description?.let {
+ Text(
+ description,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ style = MaterialTheme.typography.bodyMedium,
+ )
+ }
+ }
+ }
+ }
+ }
+ }
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/button/ScaledSwitch.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/button/ScaledSwitch.kt
index ec4c24e..f34fa3a 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/button/ScaledSwitch.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/button/ScaledSwitch.kt
@@ -8,11 +8,11 @@ import androidx.compose.ui.unit.dp
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight
@Composable
-fun ScaledSwitch(checked: Boolean, onClick: (checked: Boolean) -> Unit, enabled: Boolean = true) {
+fun ScaledSwitch(checked: Boolean, onClick: (checked: Boolean) -> Unit, enabled: Boolean = true, modifier: Modifier = Modifier) {
Switch(
checked,
{ onClick(it) },
- Modifier.scale((52.dp.scaledHeight() / 52.dp)),
- enabled = enabled
+ modifier.scale((52.dp.scaledHeight() / 52.dp)),
+ enabled = enabled,
)
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/button/surface/SelectionItem.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/button/surface/SelectionItem.kt
index f92eec4..0260594 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/button/surface/SelectionItem.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/button/surface/SelectionItem.kt
@@ -1,6 +1,7 @@
package com.zaneschepke.wireguardautotunnel.ui.common.button.surface
import androidx.compose.runtime.Composable
+import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.graphics.vector.ImageVector
data class SelectionItem(
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/button/surface/SurfaceSelectionGroupButton.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/button/surface/SurfaceSelectionGroupButton.kt
index 9f3fa47..6dcedbb 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/button/surface/SurfaceSelectionGroupButton.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/button/surface/SurfaceSelectionGroupButton.kt
@@ -1,7 +1,6 @@
package com.zaneschepke.wireguardautotunnel.ui.common.button.surface
import androidx.compose.foundation.clickable
-import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -10,8 +9,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.automirrored.filled.ArrowRight
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.HorizontalDivider
@@ -27,67 +24,65 @@ import com.zaneschepke.wireguardautotunnel.util.extensions.scaledWidth
@Composable
fun SurfaceSelectionGroupButton(items: List) {
- Card(
- modifier = Modifier.fillMaxWidth(),
- shape = RoundedCornerShape(8.dp),
- colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface),
- ) {
- items.mapIndexed { index, it ->
- Box(
- contentAlignment = Alignment.Center,
- modifier = (it.onClick?.let {
- Modifier
- .clickable {
- it()
- }
- } ?: Modifier).fillMaxWidth()
- ) {
- Row(
- verticalAlignment = Alignment.CenterVertically,
- modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp.scaledHeight()),
+ Card(
+ modifier = Modifier.fillMaxWidth(),
+ shape = RoundedCornerShape(8.dp),
+ colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface),
+ ) {
+ items.mapIndexed { index, item ->
+ Box(
+ contentAlignment = Alignment.Center,
+ modifier = Modifier
+ .then(item.onClick?.let { Modifier.clickable { it() }} ?: Modifier)
+ .fillMaxWidth()
) {
Row(
verticalAlignment = Alignment.CenterVertically,
- modifier = Modifier
- .padding(start = 16.dp.scaledWidth())
- .weight(4f, false)
- .fillMaxWidth(),
+ modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp.scaledHeight()),
) {
- it.leadingIcon?.let { icon ->
- Icon(
- icon,
- icon.name,
- modifier = Modifier.size(iconSize.scaledWidth()),
- )
- }
- Column(
- horizontalAlignment = Alignment.Start,
- verticalArrangement = Arrangement.spacedBy(2.dp, Alignment.CenterVertically),
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
- .fillMaxWidth()
- .padding(start = if (it.leadingIcon != null) 16.dp.scaledWidth() else 0.dp)
- .padding(vertical = if (it.description == null) 16.dp.scaledHeight() else 6.dp.scaledHeight()),
+ .padding(start = 16.dp.scaledWidth())
+ .weight(4f, false)
+ .fillMaxWidth(),
) {
- it.title()
- it.description?.let {
+ item.leadingIcon?.let { icon ->
+ Icon(
+ icon,
+ icon.name,
+ modifier = Modifier.size(iconSize),
+ )
+ }
+ Column(
+ horizontalAlignment = Alignment.Start,
+ verticalArrangement = Arrangement.spacedBy(2.dp, Alignment.CenterVertically),
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(start = if (item.leadingIcon != null) 16.dp.scaledWidth() else 0.dp)
+ .padding(vertical = if (item.description == null) 16.dp.scaledHeight() else 6.dp.scaledHeight()),
+ ) {
+ item.title()
+ item.description?.let {
+ it()
+ }
+ }
+ }
+ item.trailing?.let {
+ Box(
+ contentAlignment = Alignment.CenterEnd,
+ modifier = Modifier
+ .padding(end = 24.dp.scaledWidth(), start = 16.dp.scaledWidth())
+ .weight(1f),
+ ) {
it()
}
}
}
- it.trailing?.let {
- Box(
- contentAlignment = Alignment.CenterEnd,
- modifier = Modifier
- .padding(end = 24.dp.scaledWidth(), start = 16.dp.scaledWidth())
- .weight(1f),
- ) {
- it()
- }
- }
}
+ if (index + 1 != items.size) HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant)
}
- if (index + 1 != items.size) HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant)
}
}
-}
+
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 ea94ce8..bb78fe2 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
@@ -32,7 +32,6 @@ fun SubmitConfigurationTextBox(
value: String?,
label: String,
hint: String,
- focusRequester: FocusRequester,
isErrorValue: (value: String?) -> Boolean,
onSubmit: (value: String) -> Unit,
keyboardOptions: KeyboardOptions = KeyboardOptions(
@@ -50,8 +49,7 @@ fun SubmitConfigurationTextBox(
OutlinedTextField(
isError = isErrorValue(stateValue),
modifier = Modifier
- .fillMaxWidth()
- .focusRequester(focusRequester),
+ .fillMaxWidth(),
value = stateValue,
singleLine = true,
interactionSource = interactionSource,
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/label/GroupLabel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/label/GroupLabel.kt
new file mode 100644
index 0000000..fd2eea4
--- /dev/null
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/label/GroupLabel.kt
@@ -0,0 +1,22 @@
+package com.zaneschepke.wireguardautotunnel.ui.common.label
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+
+@Composable
+fun GroupLabel(title: String) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Start,
+ ) {
+ Text(
+ title,
+ style = MaterialTheme.typography.titleMedium,
+ )
+ }
+}
+
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/label/VersionLabel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/label/VersionLabel.kt
new file mode 100644
index 0000000..16051a7
--- /dev/null
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/label/VersionLabel.kt
@@ -0,0 +1,33 @@
+package com.zaneschepke.wireguardautotunnel.ui.common.label
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalClipboardManager
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.AnnotatedString
+import com.zaneschepke.wireguardautotunnel.BuildConfig
+import com.zaneschepke.wireguardautotunnel.R
+
+@Composable
+fun VersionLabel() {
+ val clipboardManager = LocalClipboardManager.current
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Start,
+ ) {
+ Text(
+ "${stringResource(R.string.version)}: ${BuildConfig.VERSION_NAME}",
+ style = MaterialTheme.typography.labelMedium,
+ color = MaterialTheme.colorScheme.outline,
+ modifier = Modifier.clickable {
+ clipboardManager.setText(AnnotatedString(BuildConfig.VERSION_NAME))
+ }
+ )
+ }
+}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/navigation/BottomNavBar.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/navigation/BottomNavBar.kt
index d8bbfaa..4155b95 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/navigation/BottomNavBar.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/navigation/BottomNavBar.kt
@@ -8,11 +8,21 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.onFocusChanged
+import androidx.compose.ui.input.key.Key
+import androidx.compose.ui.input.key.KeyEventType
+import androidx.compose.ui.input.key.key
+import androidx.compose.ui.input.key.onKeyEvent
+import androidx.compose.ui.input.key.type
import androidx.navigation.NavController
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.compose.currentBackStackEntryAsState
+import timber.log.Timber
@Composable
fun BottomNavBar(navController: NavController, bottomNavItems: List) {
@@ -20,15 +30,16 @@ fun BottomNavBar(navController: NavController, bottomNavItems: List
- val selected = navBackStackEntry.isCurrentRoute(item.route)
+ bottomNavItems.forEachIndexed { index, item ->
+ val selected = navBackStackEntry.isCurrentRoute(item.route::class)
NavigationBarItem(
selected = selected,
onClick = {
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/navigation/Extensions.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/navigation/Extensions.kt
index ac518d5..23cd41e 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/navigation/Extensions.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/navigation/Extensions.kt
@@ -5,10 +5,11 @@ import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavDestination.Companion.hasRoute
import androidx.navigation.NavDestination.Companion.hierarchy
import com.zaneschepke.wireguardautotunnel.ui.Route
+import kotlin.reflect.KClass
@SuppressLint("RestrictedApi")
-fun NavBackStackEntry?.isCurrentRoute(route: Route): Boolean {
+fun NavBackStackEntry?.isCurrentRoute(cls: KClass): Boolean {
return this?.destination?.hierarchy?.any {
- it.hasRoute(route = route::class)
+ it.hasRoute(route = cls)
} == true
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/navigation/LocalNavController.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/navigation/LocalNavController.kt
index be5c806..5e896b8 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/navigation/LocalNavController.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/navigation/LocalNavController.kt
@@ -1,8 +1,13 @@
package com.zaneschepke.wireguardautotunnel.ui.common.navigation
import androidx.compose.runtime.compositionLocalOf
+import androidx.compose.runtime.staticCompositionLocalOf
+import androidx.compose.ui.focus.FocusRequester
import androidx.navigation.NavHostController
val LocalNavController = compositionLocalOf {
error("NavController was not provided")
}
+
+val LocalFocusRequester = compositionLocalOf { error("FocusRequester is not provided") }
+
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/navigation/TopNavBar.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/navigation/TopNavBar.kt
index 05f479e..9feb69b 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/navigation/TopNavBar.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/navigation/TopNavBar.kt
@@ -11,14 +11,14 @@ import androidx.compose.runtime.Composable
@OptIn(ExperimentalMaterial3Api::class)
@Composable
-fun TopNavBar(title: String, trailing: @Composable () -> Unit = {}) {
+fun TopNavBar(title: String, trailing: @Composable () -> Unit = {}, showBack: Boolean = true) {
val navController = LocalNavController.current
CenterAlignedTopAppBar(
title = {
Text(title)
},
navigationIcon = {
- IconButton(onClick = { navController.popBackStack() }) {
+ if(showBack) IconButton(onClick = { navController.popBackStack() }) {
val icon = Icons.AutoMirrored.Outlined.ArrowBack
Icon(
imageVector = icon,
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/config/ConfigScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/config/ConfigScreen.kt
index 637038e..a2a859f 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/config/ConfigScreen.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/config/ConfigScreen.kt
@@ -72,7 +72,7 @@ import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight
import kotlinx.coroutines.delay
@Composable
-fun ConfigScreen(tunnelId: Int, focusRequester: FocusRequester) {
+fun ConfigScreen(tunnelId: Int) {
val viewModel = hiltViewModel { factory ->
factory.create(tunnelId)
}
@@ -102,18 +102,6 @@ fun ConfigScreen(tunnelId: Int, focusRequester: FocusRequester) {
}
}
- LaunchedEffect(Unit) {
- if (!uiState.loading && context.isRunningOnTv()) {
- delay(Constants.FOCUS_REQUEST_DELAY)
- kotlin.runCatching {
- focusRequester.requestFocus()
- }.onFailure {
- delay(Constants.FOCUS_REQUEST_DELAY)
- focusRequester.requestFocus()
- }
- }
- }
-
LaunchedEffect(Unit) {
delay(2_000L)
viewModel.cleanUpUninstalledApps()
@@ -194,7 +182,7 @@ fun ConfigScreen(tunnelId: Int, focusRequester: FocusRequester) {
}
},
) {
- Column(Modifier.padding(top = 24.dp.scaledHeight()).padding(it)) {
+ Column(Modifier.padding(it)) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Top,
@@ -219,7 +207,7 @@ fun ConfigScreen(tunnelId: Int, focusRequester: FocusRequester) {
Modifier.fillMaxWidth(fillMaxWidth)
}
)
- .padding(bottom = 10.dp),
+ .padding(bottom = 10.dp.scaledHeight()).padding(top = 24.dp.scaledHeight()),
) {
Column(
horizontalAlignment = Alignment.Start,
@@ -237,7 +225,6 @@ fun ConfigScreen(tunnelId: Int, focusRequester: FocusRequester) {
stringResource(id = R.string.show_amnezia_properties),
checked = derivedConfigType.value == ConfigType.AMNEZIA,
onCheckChanged = { configType = if (it) ConfigType.AMNEZIA else ConfigType.WIREGUARD },
- modifier = Modifier.focusRequester(focusRequester),
)
ConfigurationTextBox(
value = uiState.tunnelName,
@@ -248,7 +235,6 @@ fun ConfigScreen(tunnelId: Int, focusRequester: FocusRequester) {
modifier =
Modifier
.fillMaxWidth()
- .focusRequester(focusRequester),
)
OutlinedTextField(
modifier =
@@ -362,7 +348,6 @@ fun ConfigScreen(tunnelId: Int, focusRequester: FocusRequester) {
modifier =
Modifier
.fillMaxWidth()
- .focusRequester(focusRequester),
)
ConfigurationTextBox(
value = uiState.interfaceProxy.junkPacketMinSize,
@@ -376,7 +361,6 @@ fun ConfigScreen(tunnelId: Int, focusRequester: FocusRequester) {
modifier =
Modifier
.fillMaxWidth()
- .focusRequester(focusRequester),
)
ConfigurationTextBox(
value = uiState.interfaceProxy.junkPacketMaxSize,
@@ -390,7 +374,6 @@ fun ConfigScreen(tunnelId: Int, focusRequester: FocusRequester) {
modifier =
Modifier
.fillMaxWidth()
- .focusRequester(focusRequester),
)
ConfigurationTextBox(
value = uiState.interfaceProxy.initPacketJunkSize,
@@ -401,7 +384,6 @@ fun ConfigScreen(tunnelId: Int, focusRequester: FocusRequester) {
modifier =
Modifier
.fillMaxWidth()
- .focusRequester(focusRequester),
)
ConfigurationTextBox(
value = uiState.interfaceProxy.responsePacketJunkSize,
@@ -415,7 +397,6 @@ fun ConfigScreen(tunnelId: Int, focusRequester: FocusRequester) {
modifier =
Modifier
.fillMaxWidth()
- .focusRequester(focusRequester),
)
ConfigurationTextBox(
value = uiState.interfaceProxy.initPacketMagicHeader,
@@ -429,7 +410,6 @@ fun ConfigScreen(tunnelId: Int, focusRequester: FocusRequester) {
modifier =
Modifier
.fillMaxWidth()
- .focusRequester(focusRequester),
)
ConfigurationTextBox(
value = uiState.interfaceProxy.responsePacketMagicHeader,
@@ -443,7 +423,6 @@ fun ConfigScreen(tunnelId: Int, focusRequester: FocusRequester) {
modifier =
Modifier
.fillMaxWidth()
- .focusRequester(focusRequester),
)
ConfigurationTextBox(
value = uiState.interfaceProxy.underloadPacketMagicHeader,
@@ -457,7 +436,6 @@ fun ConfigScreen(tunnelId: Int, focusRequester: FocusRequester) {
modifier =
Modifier
.fillMaxWidth()
- .focusRequester(focusRequester),
)
ConfigurationTextBox(
value = uiState.interfaceProxy.transportPacketMagicHeader,
@@ -471,7 +449,6 @@ fun ConfigScreen(tunnelId: Int, focusRequester: FocusRequester) {
modifier =
Modifier
.fillMaxWidth()
- .focusRequester(focusRequester),
)
}
Row(
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 1282852..db40bfd 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
@@ -1,6 +1,5 @@
package com.zaneschepke.wireguardautotunnel.ui.screens.main
-import android.annotation.SuppressLint
import android.net.VpnService
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
@@ -9,23 +8,22 @@ import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.gestures.ScrollableDefaults
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.systemBars
-import androidx.compose.foundation.layout.windowInsetsPadding
+import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.overscroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.outlined.Add
import androidx.compose.material3.FabPosition
import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -33,7 +31,6 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
@@ -47,6 +44,7 @@ import com.zaneschepke.wireguardautotunnel.ui.common.NestedScrollListener
import com.zaneschepke.wireguardautotunnel.ui.common.dialog.InfoDialog
import com.zaneschepke.wireguardautotunnel.ui.common.functions.rememberFileImportLauncherForResult
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.LocalNavController
+import com.zaneschepke.wireguardautotunnel.ui.common.navigation.TopNavBar
import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarController
import com.zaneschepke.wireguardautotunnel.ui.screens.main.components.AutoTunnelRowItem
import com.zaneschepke.wireguardautotunnel.ui.screens.main.components.GettingStartedLabel
@@ -58,12 +56,10 @@ import com.zaneschepke.wireguardautotunnel.util.Constants
import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv
import com.zaneschepke.wireguardautotunnel.util.extensions.openWebUrl
import com.zaneschepke.wireguardautotunnel.util.extensions.startTunnelBackground
-import kotlinx.coroutines.delay
-@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@OptIn(ExperimentalFoundationApi::class)
@Composable
-fun MainScreen(viewModel: MainViewModel = hiltViewModel(), uiState: AppUiState, focusRequester: FocusRequester) {
+fun MainScreen(viewModel: MainViewModel = hiltViewModel(), uiState: AppUiState) {
val context = LocalContext.current
val navController = LocalNavController.current
val snackbar = SnackbarController.current
@@ -73,6 +69,7 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel(), uiState: AppUiState,
var isFabVisible by rememberSaveable { mutableStateOf(true) }
var showDeleteTunnelAlertDialog by remember { mutableStateOf(false) }
var selectedTunnel by remember { mutableStateOf(null) }
+ val isRunningOnTv = remember { context.isRunningOnTv() }
val nestedScrollConnection = remember {
NestedScrollListener({ isFabVisible = false }, { isFabVisible = true })
@@ -86,18 +83,6 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel(), uiState: AppUiState,
},
)
- LaunchedEffect(Unit) {
- if (context.isRunningOnTv()) {
- delay(Constants.FOCUS_REQUEST_DELAY)
- runCatching {
- focusRequester.requestFocus()
- }.onFailure {
- delay(Constants.FOCUS_REQUEST_DELAY)
- focusRequester.requestFocus()
- }
- }
- }
-
val tunnelFileImportResultLauncher = rememberFileImportLauncherForResult(onNoFileExplorer = {
snackbar.showMessage(
context.getString(R.string.error_no_file_explorer),
@@ -149,20 +134,37 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel(), uiState: AppUiState,
selectedTunnel = null
},
)
- }.windowInsetsPadding(WindowInsets.systemBars),
+ },
floatingActionButtonPosition = FabPosition.End,
floatingActionButton = {
- ScrollDismissFab({
+ if(!isRunningOnTv) ScrollDismissFab({
val icon = Icons.Filled.Add
Icon(
imageVector = icon,
contentDescription = icon.name,
tint = MaterialTheme.colorScheme.onPrimary,
)
- }, focusRequester, isVisible = isFabVisible, onClick = {
+ }, isVisible = isFabVisible, onClick = {
showBottomSheet = true
})
},
+ topBar = {
+ if(isRunningOnTv) TopNavBar(
+ showBack = false,
+ title = stringResource(R.string.app_name),
+ trailing = {
+ IconButton(onClick = {
+ showBottomSheet = true
+ }) {
+ val icon = Icons.Outlined.Add
+ Icon(
+ imageVector = icon,
+ contentDescription = icon.name,
+ )
+ }
+ }
+ )
+ }
) {
TunnelImportSheet(
showBottomSheet,
@@ -180,7 +182,7 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel(), uiState: AppUiState,
verticalArrangement = Arrangement.Top,
modifier =
Modifier
- .fillMaxSize()
+ .fillMaxSize().padding(it)
.overscroll(ScrollableDefaults.overscrollEffect())
.nestedScroll(nestedScrollConnection),
state = rememberLazyListState(0, uiState.tunnels.count()),
@@ -192,10 +194,9 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel(), uiState: AppUiState,
item {
GettingStartedLabel(onClick = { context.openWebUrl(it) })
}
- }
- if (uiState.settings.isAutoTunnelEnabled) {
+ } else {
item {
- AutoTunnelRowItem(uiState.settings, { viewModel.onToggleAutoTunnelingPause() }, focusRequester)
+ AutoTunnelRowItem(uiState.settings, { viewModel.onToggleAutoTunnel(context) })
}
}
items(
@@ -218,7 +219,6 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel(), uiState: AppUiState,
onDelete = { showDeleteTunnelAlertDialog = true },
onCopy = { viewModel.onCopyTunnel(tunnel) },
onSwitchClick = { onTunnelToggle(it, tunnel) },
- focusRequester = focusRequester,
)
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainViewModel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainViewModel.kt
index 4733e78..5454e47 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainViewModel.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainViewModel.kt
@@ -160,6 +160,20 @@ constructor(
}
}
+ fun onToggleAutoTunnel(context: Context) = viewModelScope.launch {
+ val settings = appDataRepository.settings.getSettings()
+ if (settings.isAutoTunnelEnabled) {
+ ServiceManager.stopWatcherService(context)
+ } else {
+ ServiceManager.startWatcherService(context)
+ }
+ appDataRepository.settings.save(
+ settings.copy(
+ isAutoTunnelEnabled = !settings.isAutoTunnelEnabled,
+ ),
+ )
+ }
+
private suspend fun saveTunnelsFromZipUri(uri: Uri, context: Context) {
ZipInputStream(getInputStreamFromUri(uri, context)).use { zip ->
generateSequence { zip.nextEntry }
@@ -186,13 +200,6 @@ constructor(
saveTunnelConfigFromStream(stream, name)
}
- fun onToggleAutoTunnelingPause() = viewModelScope.launch {
- val settings = appDataRepository.settings.getSettings()
- appDataRepository.settings.save(
- settings.copy(isAutoTunnelPaused = !settings.isAutoTunnelPaused),
- )
- }
-
private fun saveTunnel(tunnelConfig: TunnelConfig) = viewModelScope.launch {
appDataRepository.tunnels.save(tunnelConfig)
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/AutoTunnelRowItem.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/AutoTunnelRowItem.kt
index 54f6170..bf2fe1e 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/AutoTunnelRowItem.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/AutoTunnelRowItem.kt
@@ -16,31 +16,20 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.buildAnnotatedString
+import androidx.compose.ui.unit.dp
import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.data.domain.Settings
import com.zaneschepke.wireguardautotunnel.ui.common.ExpandingRowListItem
+import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
import com.zaneschepke.wireguardautotunnel.ui.theme.SilverTree
import com.zaneschepke.wireguardautotunnel.ui.theme.iconSize
import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv
+import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight
@Composable
-fun AutoTunnelRowItem(settings: Settings, onToggle: () -> Unit, focusRequester: FocusRequester) {
+fun AutoTunnelRowItem(settings: Settings, onToggle: () -> Unit) {
val context = LocalContext.current
val itemFocusRequester = remember { FocusRequester() }
- val autoTunnelingLabel =
- buildAnnotatedString {
- append(stringResource(id = R.string.auto_tunneling))
- append(": ")
- if (settings.isAutoTunnelPaused) {
- append(
- stringResource(id = R.string.paused),
- )
- } else {
- append(
- stringResource(id = R.string.active),
- )
- }
- }
ExpandingRowListItem(
leading = {
val icon = Icons.Rounded.Bolt
@@ -49,23 +38,23 @@ fun AutoTunnelRowItem(settings: Settings, onToggle: () -> Unit, focusRequester:
icon.name,
modifier =
Modifier
- .size(iconSize).scale(1.5f),
+ .size(16.dp.scaledHeight()).scale(1.5f),
tint =
- if (settings.isAutoTunnelPaused) {
+ if (!settings.isAutoTunnelEnabled) {
Color.Gray
} else {
SilverTree
},
)
},
- text = autoTunnelingLabel.text,
+ text = stringResource(R.string.auto_tunneling),
trailing = {
- TextButton(
- modifier = Modifier.focusRequester(itemFocusRequester),
- onClick = { onToggle() },
- ) {
- Text(stringResource(id = if (settings.isAutoTunnelPaused) R.string.resume else R.string.pause))
- }
+ ScaledSwitch(
+ settings.isAutoTunnelEnabled,
+ onClick = {
+ onToggle()
+ }
+ )
},
onClick = {
if (context.isRunningOnTv()) {
@@ -73,6 +62,5 @@ fun AutoTunnelRowItem(settings: Settings, onToggle: () -> Unit, focusRequester:
}
},
isExpanded = false,
- focusRequester = focusRequester,
)
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/ScrollDismissMultiFab.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/ScrollDismissMultiFab.kt
index fb4706a..dc750fd 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/ScrollDismissMultiFab.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/ScrollDismissMultiFab.kt
@@ -14,14 +14,13 @@ import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.unit.dp
@Composable
-fun ScrollDismissFab(icon: @Composable () -> Unit, focusRequester: FocusRequester, isVisible: Boolean, onClick: () -> Unit) {
+fun ScrollDismissFab(icon: @Composable () -> Unit, isVisible: Boolean, onClick: () -> Unit) {
AnimatedVisibility(
visible = isVisible,
enter = slideInVertically(initialOffsetY = { it * 2 }),
exit = slideOutVertically(targetOffsetY = { it * 2 }),
modifier =
Modifier
- .focusRequester(focusRequester)
.focusGroup(),
) {
FloatingActionButton(
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/TunnelRowItem.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/TunnelRowItem.kt
index 6cd35fe..35011ff 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/TunnelRowItem.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/TunnelRowItem.kt
@@ -13,7 +13,6 @@ import androidx.compose.material.icons.rounded.Smartphone
import androidx.compose.material.icons.rounded.Star
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
-import androidx.compose.material3.Switch
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
@@ -23,16 +22,18 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalHapticFeedback
+import androidx.compose.ui.unit.dp
import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig
import com.zaneschepke.wireguardautotunnel.service.tunnel.VpnState
import com.zaneschepke.wireguardautotunnel.ui.Route
import com.zaneschepke.wireguardautotunnel.ui.common.ExpandingRowListItem
+import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.LocalNavController
import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarController
-import com.zaneschepke.wireguardautotunnel.ui.theme.iconSize
import com.zaneschepke.wireguardautotunnel.util.extensions.asColor
import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv
+import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight
@Composable
fun TunnelRowItem(
@@ -46,7 +47,6 @@ fun TunnelRowItem(
onCopy: () -> Unit,
onDelete: () -> Unit,
onSwitchClick: (checked: Boolean) -> Unit,
- focusRequester: FocusRequester,
) {
val leadingIconColor = if (!isActive) Color.Gray else vpnState.statistics.asColor()
val context = LocalContext.current
@@ -69,7 +69,7 @@ fun TunnelRowItem(
icon,
icon.name,
tint = leadingIconColor,
- modifier = Modifier.size(iconSize),
+ modifier = Modifier.size(16.dp.scaledHeight()),
)
},
text = tunnel.name,
@@ -89,7 +89,6 @@ fun TunnelRowItem(
},
isExpanded = expanded && isActive,
expanded = { if (isActive && expanded) TunnelStatisticsRow(vpnState.statistics, tunnel) },
- focusRequester = focusRequester,
trailing = {
if (
isSelected &&
@@ -143,7 +142,6 @@ fun TunnelRowItem(
)
}
IconButton(
- modifier = Modifier.focusRequester(focusRequester),
onClick = {
if (isActive) {
onClick()
@@ -181,21 +179,17 @@ fun TunnelRowItem(
icon.name,
)
}
- Switch(
+ ScaledSwitch(
modifier = Modifier.focusRequester(itemFocusRequester),
checked = isActive,
- onCheckedChange = { checked ->
- onSwitchClick(checked)
- },
+ onClick = onSwitchClick
)
}
} else {
- Switch(
+ ScaledSwitch(
modifier = Modifier.focusRequester(itemFocusRequester),
checked = isActive,
- onCheckedChange = { checked ->
- onSwitchClick(checked)
- },
+ onClick = onSwitchClick
)
}
}
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 0a0bd25..d60c2db 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
@@ -1,101 +1,74 @@
package com.zaneschepke.wireguardautotunnel.ui.screens.options
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
-import androidx.compose.foundation.layout.FlowRow
-import androidx.compose.foundation.layout.IntrinsicSize
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.systemBars
+import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.foundation.text.KeyboardActions
-import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Close
-import androidx.compose.material.icons.outlined.Add
import androidx.compose.material.icons.outlined.Edit
+import androidx.compose.material.icons.outlined.NetworkPing
+import androidx.compose.material.icons.outlined.PhoneAndroid
+import androidx.compose.material.icons.outlined.Security
+import androidx.compose.material.icons.outlined.Star
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
-import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
-import androidx.compose.ui.focus.focusRequester
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalFocusManager
+import androidx.compose.ui.input.key.Key
+import androidx.compose.ui.input.key.KeyEventType
+import androidx.compose.ui.input.key.key
+import androidx.compose.ui.input.key.onKeyEvent
+import androidx.compose.ui.input.key.type
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.font.FontStyle
-import androidx.compose.ui.text.input.ImeAction
-import androidx.compose.ui.text.input.KeyboardCapitalization
-import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.ui.AppUiState
import com.zaneschepke.wireguardautotunnel.ui.Route
-import com.zaneschepke.wireguardautotunnel.ui.common.ClickableIconButton
-import com.zaneschepke.wireguardautotunnel.ui.common.config.ConfigurationToggle
-import com.zaneschepke.wireguardautotunnel.ui.common.config.SubmitConfigurationTextBox
+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.label.GroupLabel
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.LocalNavController
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.TopNavBar
-import com.zaneschepke.wireguardautotunnel.ui.common.text.SectionTitle
-import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.WildcardSupportingLabel
-import com.zaneschepke.wireguardautotunnel.util.Constants
-import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv
-import com.zaneschepke.wireguardautotunnel.util.extensions.isValidIpv4orIpv6Address
-import com.zaneschepke.wireguardautotunnel.util.extensions.openWebUrl
+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.extensions.scaledHeight
+import com.zaneschepke.wireguardautotunnel.util.extensions.scaledWidth
import kotlinx.coroutines.delay
@OptIn(ExperimentalLayoutApi::class)
@Composable
-fun OptionsScreen(optionsViewModel: OptionsViewModel = hiltViewModel(), focusRequester: FocusRequester, appUiState: AppUiState, tunnelId: Int) {
- val scrollState = rememberScrollState()
- val context = LocalContext.current
+fun OptionsScreen(optionsViewModel: OptionsViewModel = hiltViewModel(), appUiState: AppUiState, tunnelId: Int) {
val navController = LocalNavController.current
val config = appUiState.tunnels.first { it.id == tunnelId }
- val interactionSource = remember { MutableInteractionSource() }
- val focusManager = LocalFocusManager.current
- val screenPadding = 5.dp
- val fillMaxWidth = .85f
-
var currentText by remember { mutableStateOf("") }
- LaunchedEffect(Unit) {
- if (context.isRunningOnTv()) {
- delay(Constants.FOCUS_REQUEST_DELAY)
- kotlin.runCatching {
- focusRequester.requestFocus()
- }.onFailure {
- delay(Constants.FOCUS_REQUEST_DELAY)
- focusRequester.requestFocus()
- }
- }
+ LaunchedEffect(config.tunnelNetworks) {
+ currentText = ""
}
-
- fun saveTrustedSSID() {
- if (currentText.isNotEmpty()) {
- optionsViewModel.onSaveRunSSID(currentText, config)
- currentText = ""
- }
- }
-
Scaffold(
topBar = {
TopNavBar(config.name, trailing = {
@@ -111,219 +84,121 @@ fun OptionsScreen(optionsViewModel: OptionsViewModel = hiltViewModel(), focusReq
)
}
})
- },
+ }
) {
Column(
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.Top,
+ horizontalAlignment = Alignment.Start,
+ verticalArrangement = Arrangement.spacedBy(24.dp.scaledHeight(), Alignment.Top),
modifier =
Modifier
- .fillMaxSize().padding(it)
- .verticalScroll(scrollState)
- .clickable(
- indication = null,
- interactionSource = interactionSource,
- ) {
- focusManager.clearFocus()
- },
+ .fillMaxSize()
+ .padding(it)
+ .padding(top = 24.dp.scaledHeight())
+ .padding(horizontal = 24.dp.scaledWidth()),
) {
- Surface(
- tonalElevation = 2.dp,
- shadowElevation = 2.dp,
- shape = RoundedCornerShape(12.dp),
- color = MaterialTheme.colorScheme.surface,
- modifier =
- (
- if (context.isRunningOnTv()) {
- Modifier
- .height(IntrinsicSize.Min)
- .fillMaxWidth(fillMaxWidth)
- .padding(top = 10.dp)
- } else {
- Modifier
- .fillMaxWidth(fillMaxWidth)
- .padding(top = 20.dp)
- }
- )
- .padding(bottom = 10.dp),
- ) {
- Column(
- horizontalAlignment = Alignment.Start,
- verticalArrangement = Arrangement.Top,
- modifier = Modifier.padding(15.dp),
- ) {
- SectionTitle(
- title = stringResource(id = R.string.general),
- padding = screenPadding,
- )
- ConfigurationToggle(
- stringResource(R.string.set_primary_tunnel),
- enabled = true,
- checked = config.isPrimaryTunnel,
- modifier =
- Modifier
- .focusRequester(focusRequester),
- onCheckChanged = { optionsViewModel.onTogglePrimaryTunnel(config) },
- )
- }
- }
- Surface(
- tonalElevation = 2.dp,
- shadowElevation = 2.dp,
- shape = RoundedCornerShape(12.dp),
- color = MaterialTheme.colorScheme.surface,
- modifier =
- (
- if (context.isRunningOnTv()) {
- Modifier
- .height(IntrinsicSize.Min)
- .fillMaxWidth(fillMaxWidth)
- .padding(top = 10.dp)
- } else {
- Modifier
- .fillMaxWidth(fillMaxWidth)
- .padding(top = 20.dp)
- }
- )
- .padding(bottom = 10.dp),
- ) {
- Column(
- horizontalAlignment = Alignment.Start,
- verticalArrangement = Arrangement.Top,
- modifier = Modifier.padding(15.dp),
- ) {
- SectionTitle(
- title = stringResource(id = R.string.auto_tunneling),
- padding = screenPadding,
- )
- ConfigurationToggle(
- stringResource(R.string.mobile_data_tunnel),
- enabled = true,
- checked = config.isMobileDataTunnel,
- onCheckChanged = { optionsViewModel.onToggleIsMobileDataTunnel(config) },
- )
- Column {
- FlowRow(
- modifier =
- Modifier
- .padding(screenPadding)
- .fillMaxWidth(),
- horizontalArrangement = Arrangement.spacedBy(5.dp),
- ) {
- config.tunnelNetworks.forEach { ssid ->
- ClickableIconButton(
- onClick = {
- if (context.isRunningOnTv()) {
- focusRequester.requestFocus()
- optionsViewModel.onDeleteRunSSID(ssid, config)
- }
- },
- onIconClick = {
- if (context.isRunningOnTv()) focusRequester.requestFocus()
- optionsViewModel.onDeleteRunSSID(ssid, config)
- },
- text = ssid,
- icon = Icons.Filled.Close,
- enabled = true,
- )
- }
- if (config.tunnelNetworks.isEmpty()) {
- Text(
- stringResource(R.string.no_wifi_names_configured),
- fontStyle = FontStyle.Italic,
- color = MaterialTheme.colorScheme.onSurface,
- )
- }
- }
- OutlinedTextField(
- enabled = true,
- value = currentText,
- onValueChange = { currentText = it },
- label = { Text(stringResource(id = R.string.use_tunnel_on_wifi_name)) },
- supportingText = { WildcardSupportingLabel { context.openWebUrl(it) } },
- modifier =
- Modifier
- .padding(
- start = screenPadding,
- top = 5.dp,
- bottom = 10.dp,
- ),
- maxLines = 1,
- keyboardOptions =
- KeyboardOptions(
- capitalization = KeyboardCapitalization.None,
- imeAction = ImeAction.Done,
- ),
- keyboardActions = KeyboardActions(onDone = { saveTrustedSSID() }),
- trailingIcon = {
- if (currentText != "") {
- IconButton(onClick = { saveTrustedSSID() }) {
- Icon(
- imageVector = Icons.Outlined.Add,
- contentDescription = stringResource(R.string.save_changes),
- tint = MaterialTheme.colorScheme.primary,
+ 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,
+ 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,
+ 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,
+ onClick = { optionsViewModel.onToggleRestartOnPing(config) },
+ )
+ },
+ onClick = { optionsViewModel.onToggleRestartOnPing(config) }
+ ),
+ SelectionItem(
+ title = {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp.scaledHeight()),
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier
+ .weight(4f, false)
+ .fillMaxWidth(),
+ ) {
+ 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),
)
}
+
}
- },
- )
- ConfigurationToggle(
- stringResource(R.string.restart_on_ping),
- enabled = !appUiState.settings.isPingEnabled,
- checked = config.isPingEnabled || appUiState.settings.isPingEnabled,
- onCheckChanged = { optionsViewModel.onToggleRestartOnPing(config) },
- )
- if (config.isPingEnabled || appUiState.settings.isPingEnabled) {
- SubmitConfigurationTextBox(
- config.pingIp,
- stringResource(R.string.set_custom_ping_ip),
- stringResource(R.string.default_ping_ip),
- focusRequester,
- 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})",
- focusRequester,
- keyboardOptions = KeyboardOptions(
- keyboardType = KeyboardType.Number,
- imeAction = ImeAction.Done,
- ),
- isErrorValue = ::isSecondsError,
- onSubmit = {
- optionsViewModel.saveTunnelChanges(
- config.copy(pingInterval = if (it.isBlank()) null else it.toLong() * 1000),
- )
- },
+
+ },
+ description = {
+ TrustedNetworkTextBox(
+ config.tunnelNetworks, onDelete = { optionsViewModel.onDeleteRunSSID(it, config) },
+ currentText = currentText,
+ onSave = { optionsViewModel.onSaveRunSSID(it, config) },
+ onValueChange = { currentText = it },
+ supporting = { if(appUiState.generalState.isWildcardsEnabled) {
+ WildcardsLabel()
+ }}
)
- SubmitConfigurationTextBox(
- config.pingCooldown?.let { (it / 1000).toString() },
- stringResource(R.string.set_custom_ping_cooldown),
- "(${stringResource(R.string.optional_default)} ${Constants.PING_COOLDOWN / 1000})",
- focusRequester,
- keyboardOptions = KeyboardOptions(
- keyboardType = KeyboardType.Number,
- ),
- isErrorValue = ::isSecondsError,
- onSubmit = {
- optionsViewModel.saveTunnelChanges(
- config.copy(pingCooldown = if (it.isBlank()) null else it.toLong() * 1000),
- )
- },
- )
- }
- }
- }
- }
+ },
+ )
+ )
+ )
}
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/options/OptionsViewModel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/options/OptionsViewModel.kt
index eecd3df..d7d3236 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/options/OptionsViewModel.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/options/OptionsViewModel.kt
@@ -32,6 +32,7 @@ constructor(
}
fun onSaveRunSSID(ssid: String, tunnelConfig: TunnelConfig) = viewModelScope.launch {
+ if(ssid.isBlank()) return@launch
val trimmed = ssid.trim()
val tunnelsWithName = appDataRepository.tunnels.findByTunnelNetworksName(trimmed)
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 5373b14..09a4067 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
@@ -11,6 +11,7 @@ import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity.RESULT_OK
import androidx.compose.foundation.clickable
+import androidx.compose.foundation.focusable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -23,15 +24,9 @@ import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.automirrored.outlined.ArrowForward
-import androidx.compose.material.icons.automirrored.outlined.ArrowRight
import androidx.compose.material.icons.automirrored.outlined.ViewQuilt
import androidx.compose.material.icons.filled.AppShortcut
-import androidx.compose.material.icons.filled.Bolt
-import androidx.compose.material.icons.filled.Restore
-import androidx.compose.material.icons.filled.VpnLock
import androidx.compose.material.icons.outlined.AdminPanelSettings
-import androidx.compose.material.icons.outlined.AppShortcut
import androidx.compose.material.icons.outlined.Bolt
import androidx.compose.material.icons.outlined.Code
import androidx.compose.material.icons.outlined.FolderZip
@@ -39,7 +34,6 @@ import androidx.compose.material.icons.outlined.Notifications
import androidx.compose.material.icons.outlined.Pin
import androidx.compose.material.icons.outlined.Restore
import androidx.compose.material.icons.outlined.VpnLock
-import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -49,7 +43,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
@@ -58,7 +52,6 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.zaneschepke.wireguardautotunnel.R
-import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelState
import com.zaneschepke.wireguardautotunnel.ui.AppUiState
import com.zaneschepke.wireguardautotunnel.ui.AppViewModel
@@ -66,13 +59,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.navigation.LocalFocusRequester
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.LocalNavController
import com.zaneschepke.wireguardautotunnel.ui.common.prompt.AuthorizationPrompt
import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarController
import com.zaneschepke.wireguardautotunnel.ui.screens.main.components.VpnDeniedDialog
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.BackgroundLocationDialog
-import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.BackgroundLocationDisclosure
+import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.ForwardButton
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.LocationServicesDialog
+import com.zaneschepke.wireguardautotunnel.ui.theme.topPadding
import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv
import com.zaneschepke.wireguardautotunnel.util.extensions.launchAppSettings
import com.zaneschepke.wireguardautotunnel.util.extensions.launchNotificationSettings
@@ -87,13 +82,14 @@ import xyz.teamgravity.pin_lock_compose.PinManager
ExperimentalLayoutApi::class,
)
@Composable
-fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel: AppViewModel, uiState: AppUiState, focusRequester: FocusRequester) {
+fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel: AppViewModel, uiState: AppUiState) {
val context = LocalContext.current
val navController = LocalNavController.current
val focusManager = LocalFocusManager.current
val snackbar = SnackbarController.current
+ val rootFocusRequester = LocalFocusRequester.current
+ val isRunningOnTv = remember { context.isRunningOnTv() }
- val scrollState = rememberScrollState()
val interactionSource = remember { MutableInteractionSource() }
val settingsUiState by viewModel.uiState.collectAsStateWithLifecycle()
@@ -119,9 +115,7 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel:
ActivityResultContracts.StartActivityForResult(),
onResult = {
val accepted = (it.resultCode == RESULT_OK)
- if (accepted) {
- viewModel.onToggleAutoTunnel(context)
- } else {
+ if (!accepted) {
showVpnPermissionDialog = true
}
},
@@ -141,20 +135,23 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel:
startForResult.launch(intent)
}
- fun handleAutoTunnelToggle() {
- if (!uiState.generalState.isBatteryOptimizationDisableShown &&
- !isBatteryOptimizationsDisabled() && !context.isRunningOnTv()
- ) {
- return requestBatteryOptimizationsDisabled()
- }
- val intent = if (!uiState.settings.isKernelEnabled) {
- VpnService.prepare(context)
- } else {
- null
- }
- if (intent != null) return vpnActivityResultState.launch(intent)
- viewModel.onToggleAutoTunnel(context)
- }
+// fun handleAutoTunnelToggle() {
+// if (!uiState.generalState.isBatteryOptimizationDisableShown &&
+// !isBatteryOptimizationsDisabled() && !isRunningOnTv
+// ) {
+// return requestBatteryOptimizationsDisabled()
+// }
+// val intent = if (!uiState.settings.isKernelEnabled) {
+// VpnService.prepare(context)
+// } else {
+// null
+// }
+// if (intent != null) return vpnActivityResultState.launch(intent)
+// viewModel.onToggleAutoTunnel(context)
+// }
+
+
+
// fun checkFineLocationGranted() {
// isBackgroundLocationGranted =
@@ -188,18 +185,6 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel:
// if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
// checkFineLocationGranted()
// }
- if (!uiState.generalState.isLocationDisclosureShown) {
- BackgroundLocationDisclosure(
- onDismiss = { viewModel.setLocationDisclosureShown() },
- onAttest = {
- context.launchAppSettings()
- viewModel.setLocationDisclosureShown()
- },
- scrollState,
- focusRequester,
- )
- return
- }
BackgroundLocationDialog(
showLocationDialog,
@@ -207,11 +192,11 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel:
onAttest = { showLocationDialog = false },
)
- LocationServicesDialog(
- showLocationServicesAlertDialog,
- onDismiss = { showVpnPermissionDialog = false },
- onAttest = { handleAutoTunnelToggle() },
- )
+// LocationServicesDialog(
+// showLocationServicesAlertDialog,
+// onDismiss = { showVpnPermissionDialog = false },
+// onAttest = { handleAutoTunnelToggle() },
+// )
VpnDeniedDialog(showVpnPermissionDialog, onDismiss = { showVpnPermissionDialog = false })
@@ -243,121 +228,139 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel:
Modifier
.verticalScroll(rememberScrollState())
.fillMaxSize()
- .padding(top = 24.dp.scaledHeight())
- .padding(horizontal = 24.dp.scaledWidth()).clickable(
+ .padding(top = topPadding)
+ .padding(bottom = 40.dp.scaledHeight())
+ .padding(horizontal = 24.dp.scaledWidth())
+ .then(if(!isRunningOnTv) Modifier.clickable(
indication = null,
interactionSource = interactionSource,
) {
focusManager.clearFocus()
- }.windowInsetsPadding(WindowInsets.systemBars),
+ } else Modifier)
) {
SurfaceSelectionGroupButton(
listOf(
SelectionItem(
Icons.Outlined.Bolt,
- title = { Text(stringResource(R.string.auto_tunneling), style = MaterialTheme.typography.bodyLarge.copy(MaterialTheme.colorScheme.onSurface)) },
+ title = { Text(stringResource(R.string.auto_tunneling), style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
description = {
Text(
- "Configure on demand tunnel rules",
- style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.outline),
+ stringResource(R.string.on_demand_rules),
+ style = MaterialTheme.typography.bodySmall.copy(MaterialTheme.colorScheme.outline),
)
},
onClick = {
+ if(!uiState.generalState.isLocationDisclosureShown) return@SelectionItem navController.navigate(Route.LocationDisclosure)
navController.navigate(Route.AutoTunnel)
},
trailing = {
- val icon = Icons.AutoMirrored.Outlined.ArrowForward
- Icon(icon, icon.name)
- }
- ),
- ),
+ ForwardButton(Modifier.focusable().focusRequester(rootFocusRequester)) { navController.navigate(Route.AutoTunnel) }
+ },
+ )
+ )
+ )
+ SurfaceSelectionGroupButton(
+ buildList {
+ if (!isRunningOnTv) addAll(
+ listOf(
+ SelectionItem(
+ Icons.Filled.AppShortcut,
+ {
+ ScaledSwitch(
+ uiState.settings.isShortcutsEnabled,
+ onClick = { viewModel.onToggleShortcutsEnabled() },
+ )
+ },
+ title = {
+ Text(
+ stringResource(R.string.enabled_app_shortcuts),
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface))
+ },
+ onClick = { viewModel.onToggleShortcutsEnabled() }
+ ),
+ SelectionItem(
+ Icons.Outlined.VpnLock,
+ {
+ ScaledSwitch(
+ enabled = !(
+ (
+ uiState.settings.isTunnelOnWifiEnabled ||
+ uiState.settings.isTunnelOnEthernetEnabled ||
+ uiState.settings.isTunnelOnMobileDataEnabled
+ ) &&
+ uiState.settings.isAutoTunnelEnabled
+ ),
+ onClick = { viewModel.onToggleAlwaysOnVPN() },
+ checked = uiState.settings.isAlwaysOnVpnEnabled,
+ )
+ },
+ title = {
+ Text(
+ stringResource(R.string.always_on_vpn_support),
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface))
+ },
+ onClick = { viewModel.onToggleAlwaysOnVPN() }
+ ),
+ SelectionItem(
+ Icons.Outlined.AdminPanelSettings,
+ title = {
+ Text(
+ stringResource(R.string.kill_switch),
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface))
+ },
+ onClick = {
+ context.launchVpnSettings()
+ },
+ trailing = {
+ ForwardButton { context.launchVpnSettings() }
+ },
+ )
+ )
+ )
+ add(
+ SelectionItem(
+ Icons.Outlined.Restore,
+ {
+ ScaledSwitch(
+ uiState.settings.isRestoreOnBootEnabled,
+ onClick = { viewModel.onToggleRestartAtBoot() },
+ )
+ },
+ title = {
+ Text(
+ stringResource(R.string.restart_at_boot),
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface))
+ },
+ onClick = { viewModel.onToggleRestartAtBoot() }
+ )
+ )
+ }
)
SurfaceSelectionGroupButton(
- listOf(
- SelectionItem(
- Icons.Filled.AppShortcut,
- {
- ScaledSwitch(
- uiState.settings.isShortcutsEnabled,
- onClick = { viewModel.onToggleShortcutsEnabled() },
- )
- },
- title = {
- Text(stringResource(R.string.enabled_app_shortcuts), style = MaterialTheme.typography.bodyLarge.copy(MaterialTheme.colorScheme.onSurface))
- },
- ),
- SelectionItem(
- Icons.Outlined.VpnLock,
- {
- ScaledSwitch(
- enabled = !(
- (
- uiState.settings.isTunnelOnWifiEnabled ||
- uiState.settings.isTunnelOnEthernetEnabled ||
- uiState.settings.isTunnelOnMobileDataEnabled
- ) &&
- uiState.settings.isAutoTunnelEnabled
- ),
- onClick = { viewModel.onToggleAlwaysOnVPN() },
- checked = uiState.settings.isAlwaysOnVpnEnabled,
- )
- },
- title = {
- Text(stringResource(R.string.always_on_vpn_support), style = MaterialTheme.typography.bodyLarge.copy(MaterialTheme.colorScheme.onSurface))
- },
- ),
- SelectionItem(
- Icons.Outlined.AdminPanelSettings,
- title = { Text(stringResource(R.string.kill_switch), style = MaterialTheme.typography.bodyLarge.copy(MaterialTheme.colorScheme.onSurface)) },
- onClick = {
- context.launchVpnSettings()
- },
- trailing = {
- val icon = Icons.AutoMirrored.Outlined.ArrowForward
- Icon(icon, icon.name)
- }
- ),
- SelectionItem(
- Icons.Outlined.Restore,
- {
- ScaledSwitch(
- uiState.settings.isRestoreOnBootEnabled,
- onClick = { viewModel.onToggleRestartAtBoot() },
- )
- },
- title = { Text(stringResource(R.string.restart_at_boot), style = MaterialTheme.typography.bodyLarge.copy(MaterialTheme.colorScheme.onSurface)) },
- ),
+ listOf(SelectionItem(
+ Icons.AutoMirrored.Outlined.ViewQuilt,
+ title = { Text(stringResource(R.string.appearance), style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
+ onClick = {
+ navController.navigate(Route.Appearance)
+ },
+ trailing = {
+ ForwardButton { navController.navigate(Route.Appearance) }
+ },
),
- )
-
- SurfaceSelectionGroupButton(
- mutableListOf(
- SelectionItem(
- Icons.AutoMirrored.Outlined.ViewQuilt,
- title = { Text(stringResource(R.string.appearance), style = MaterialTheme.typography.bodyLarge.copy(MaterialTheme.colorScheme.onSurface)) },
- onClick = {
- navController.navigate(Route.Appearance)
- },
- trailing = {
- val icon = Icons.AutoMirrored.Outlined.ArrowForward
- Icon(icon, icon.name)
- }
- ),
SelectionItem(
Icons.Outlined.Notifications,
- title = { Text(stringResource(R.string.notifications), style = MaterialTheme.typography.bodyLarge.copy(MaterialTheme.colorScheme.onSurface)) },
+ title = { Text(stringResource(R.string.notifications), style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
onClick = {
context.launchNotificationSettings()
},
trailing = {
- val icon = Icons.AutoMirrored.Outlined.ArrowForward
- Icon(icon, icon.name)
- }
+ ForwardButton { context.launchNotificationSettings() }
+ },
),
SelectionItem(
Icons.Outlined.Pin,
- title = { Text(stringResource(R.string.enable_app_lock), style = MaterialTheme.typography.bodyLarge.copy(MaterialTheme.colorScheme.onSurface)) },
+ title = { Text(stringResource(R.string.enable_app_lock), style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
trailing = {
ScaledSwitch(
uiState.generalState.isPinLockEnabled,
@@ -370,52 +373,61 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel:
}
},
)
- }
- ),
- ),
- )
-
- SurfaceSelectionGroupButton(
- listOf(
- SelectionItem(
- Icons.Outlined.Code,
- title = { Text(stringResource(R.string.kernel), style = MaterialTheme.typography.bodyLarge.copy(MaterialTheme.colorScheme.onSurface)) },
- description = {
- Text(
- "Use kernel backend (root only)",
- style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.outline),
- )
},
- trailing = {
- ScaledSwitch(
- uiState.settings.isKernelEnabled,
- onClick = { viewModel.onToggleKernelMode() },
- enabled = !(
- uiState.settings.isAutoTunnelEnabled ||
- uiState.settings.isAlwaysOnVpnEnabled ||
- (uiState.vpnState.status == TunnelState.UP) ||
- !settingsUiState.isKernelAvailable
- ),
- )
- },
- ),
- ),
- )
+ onClick = { if (uiState.generalState.isPinLockEnabled) {
+ appViewModel.onPinLockDisabled()
+ } else {
+ PinManager.initialize(context)
+ navController.navigate(Route.Lock)
+ } }
+ )
+ ))
- SurfaceSelectionGroupButton(
+ if(!isRunningOnTv) SurfaceSelectionGroupButton(listOf(
+ SelectionItem(
+ Icons.Outlined.Code,
+ title = { Text(stringResource(R.string.kernel), style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
+ description = {
+ Text(
+ stringResource(R.string.use_kernel),
+ style = MaterialTheme.typography.bodySmall.copy(MaterialTheme.colorScheme.outline),
+ )
+ },
+ trailing = {
+ ScaledSwitch(
+ uiState.settings.isKernelEnabled,
+ onClick = { viewModel.onToggleKernelMode() },
+ enabled = !(
+ uiState.settings.isAutoTunnelEnabled ||
+ uiState.settings.isAlwaysOnVpnEnabled ||
+ (uiState.vpnState.status == TunnelState.UP) ||
+ !settingsUiState.isKernelAvailable
+ ),
+ )
+ },
+ onClick = {
+ viewModel.onToggleKernelMode()
+ }
+ ),
+ ))
+
+ if(!isRunningOnTv) SurfaceSelectionGroupButton(
listOf(
SelectionItem(
Icons.Outlined.FolderZip,
- title = { Text(stringResource(R.string.export_configs), style = MaterialTheme.typography.bodyLarge.copy(MaterialTheme.colorScheme.onSurface)) },
+ title = { Text(stringResource(R.string.export_configs), style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
onClick = {
if (uiState.tunnels.isEmpty()) return@SelectionItem context.showToast(R.string.tunnel_required)
showAuthPrompt = true
},
- trailing = {},
),
- ),
+ )
)
+
+
+
+
// Surface(
// tonalElevation = 2.dp,
// shadowElevation = 2.dp,
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 992a86a..a5f25b6 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
@@ -60,24 +60,6 @@ constructor(
appDataRepository.appState.setBatteryOptimizationDisableShown(true)
}
- fun onToggleAutoTunnel(context: Context) = viewModelScope.launch {
- with(settings.value) {
- var isAutoTunnelPaused = this.isAutoTunnelPaused
- if (isAutoTunnelEnabled) {
- ServiceManager.stopWatcherService(context)
- } else {
- ServiceManager.startWatcherService(context)
- isAutoTunnelPaused = false
- }
- appDataRepository.settings.save(
- copy(
- isAutoTunnelEnabled = !isAutoTunnelEnabled,
- isAutoTunnelPaused = isAutoTunnelPaused,
- ),
- )
- }
- }
-
fun onToggleAlwaysOnVPN() = viewModelScope.launch {
with(settings.value) {
appDataRepository.settings.save(
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/appearance/AppearanceScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/appearance/AppearanceScreen.kt
index 7a751e4..e25db8f 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/appearance/AppearanceScreen.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/appearance/AppearanceScreen.kt
@@ -2,70 +2,71 @@ package com.zaneschepke.wireguardautotunnel.ui.screens.settings.appearance
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.systemBars
-import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.automirrored.outlined.ArrowForward
import androidx.compose.material.icons.outlined.Contrast
import androidx.compose.material.icons.outlined.Translate
-import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
+import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.ui.Route
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelectionGroupButton
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.LocalNavController
+import com.zaneschepke.wireguardautotunnel.ui.common.navigation.TopNavBar
+import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.ForwardButton
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledWidth
-import com.zaneschepke.wireguardautotunnel.R
@Composable
fun AppearanceScreen() {
val navController = LocalNavController.current
- Column(
- horizontalAlignment = Alignment.Start,
- verticalArrangement = Arrangement.spacedBy(24.dp.scaledHeight(), Alignment.Top),
- modifier =
- Modifier
- .fillMaxSize()
- .windowInsetsPadding(WindowInsets.systemBars)
- .padding(top = 24.dp.scaledHeight())
- .padding(horizontal = 24.dp.scaledWidth()),
- ) {
- SurfaceSelectionGroupButton(
- listOf(
- SelectionItem(
- Icons.Outlined.Translate,
- title = { Text(stringResource(R.string.language), style = MaterialTheme.typography.bodyLarge.copy(MaterialTheme.colorScheme.onSurface)) },
- onClick = { navController.navigate(Route.Language) },
- trailing = {
- val icon = Icons.AutoMirrored.Outlined.ArrowForward
- Icon(icon, icon.name)
- }
+ Scaffold(
+ topBar = {
+ TopNavBar(stringResource(R.string.appearance))
+ }
+ ){
+ Column(
+ horizontalAlignment = Alignment.Start,
+ verticalArrangement = Arrangement.spacedBy(24.dp.scaledHeight(), Alignment.Top),
+ modifier =
+ Modifier
+ .fillMaxSize().padding(it)
+ .padding(top = 24.dp.scaledHeight())
+ .padding(horizontal = 24.dp.scaledWidth()),
+ ) {
+ SurfaceSelectionGroupButton(
+ listOf(
+ SelectionItem(
+ Icons.Outlined.Translate,
+ title = { Text(stringResource(R.string.language), style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
+ onClick = { navController.navigate(Route.Language) },
+ trailing = {
+ ForwardButton { navController.navigate(Route.Language) }
+ }
+ ),
),
- ),
- )
- SurfaceSelectionGroupButton(
- listOf(
- SelectionItem(
- Icons.Outlined.Contrast,
- title = { Text(stringResource(R.string.display_theme), style = MaterialTheme.typography.bodyLarge.copy(MaterialTheme.colorScheme.onSurface)) },
- onClick = { navController.navigate(Route.Display) },
- trailing = {
- val icon = Icons.AutoMirrored.Outlined.ArrowForward
- Icon(icon, icon.name)
- }
+ )
+ SurfaceSelectionGroupButton(
+ listOf(
+ SelectionItem(
+ Icons.Outlined.Contrast,
+ title = { Text(stringResource(R.string.display_theme), style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
+ onClick = { navController.navigate(Route.Display) },
+ trailing = {
+ ForwardButton { navController.navigate(Route.Display) }
+ }
+ ),
),
- ),
- )
+ )
+ }
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/appearance/display/DisplayScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/appearance/display/DisplayScreen.kt
index be76f2c..d6f9c8a 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/appearance/display/DisplayScreen.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/appearance/display/DisplayScreen.kt
@@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.windowInsetsPadding
+import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -16,6 +17,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.ui.AppUiState
import com.zaneschepke.wireguardautotunnel.ui.common.button.IconSurfaceButton
+import com.zaneschepke.wireguardautotunnel.ui.common.navigation.TopNavBar
import com.zaneschepke.wireguardautotunnel.ui.theme.Theme
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledWidth
@@ -23,37 +25,43 @@ import com.zaneschepke.wireguardautotunnel.util.extensions.scaledWidth
@Composable
fun DisplayScreen(appUiState: AppUiState, viewModel: DisplayViewModel = hiltViewModel()) {
- Column(
- horizontalAlignment = Alignment.Start,
- verticalArrangement = Arrangement.spacedBy(24.dp.scaledHeight(), Alignment.Top),
- modifier =
- Modifier
- .fillMaxSize()
- .windowInsetsPadding(WindowInsets.systemBars)
- .padding(top = 24.dp.scaledHeight())
- .padding(horizontal = 24.dp.scaledWidth()),
+ Scaffold(
+ topBar = {
+ TopNavBar(stringResource(R.string.display_theme))
+ }
) {
- IconSurfaceButton(
- title = stringResource(R.string.automatic),
- onClick = {
- viewModel.onThemeChange(Theme.AUTOMATIC)
- },
- selected = appUiState.generalState.theme == Theme.AUTOMATIC,
- )
- IconSurfaceButton(
- title = stringResource(R.string.light),
- onClick = { viewModel.onThemeChange(Theme.LIGHT) },
- selected = appUiState.generalState.theme == Theme.LIGHT,
- )
- IconSurfaceButton(
- title = stringResource(R.string.dark),
- onClick = { viewModel.onThemeChange(Theme.DARK) },
- selected = appUiState.generalState.theme == Theme.DARK,
- )
- IconSurfaceButton(
- title = stringResource(R.string.dynamic),
- onClick = { viewModel.onThemeChange(Theme.DYNAMIC) },
- selected = appUiState.generalState.theme == Theme.DYNAMIC,
- )
+ Column(
+ horizontalAlignment = Alignment.Start,
+ verticalArrangement = Arrangement.spacedBy(24.dp.scaledHeight(), Alignment.Top),
+ modifier =
+ Modifier
+ .fillMaxSize()
+ .padding(it)
+ .padding(top = 24.dp.scaledHeight())
+ .padding(horizontal = 24.dp.scaledWidth()),
+ ) {
+ IconSurfaceButton(
+ title = stringResource(R.string.automatic),
+ onClick = {
+ viewModel.onThemeChange(Theme.AUTOMATIC)
+ },
+ selected = appUiState.generalState.theme == Theme.AUTOMATIC,
+ )
+ IconSurfaceButton(
+ title = stringResource(R.string.light),
+ onClick = { viewModel.onThemeChange(Theme.LIGHT) },
+ selected = appUiState.generalState.theme == Theme.LIGHT,
+ )
+ IconSurfaceButton(
+ title = stringResource(R.string.dark),
+ onClick = { viewModel.onThemeChange(Theme.DARK) },
+ selected = appUiState.generalState.theme == Theme.DARK,
+ )
+ IconSurfaceButton(
+ title = stringResource(R.string.dynamic),
+ onClick = { viewModel.onThemeChange(Theme.DYNAMIC) },
+ selected = appUiState.generalState.theme == Theme.DYNAMIC,
+ )
+ }
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/appearance/language/LanguageScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/appearance/language/LanguageScreen.kt
index 8b97f15..7015783 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/appearance/language/LanguageScreen.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/appearance/language/LanguageScreen.kt
@@ -1,6 +1,7 @@
package com.zaneschepke.wireguardautotunnel.ui.screens.settings.appearance.language
import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBars
@@ -9,6 +10,7 @@ import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
+import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
@@ -24,6 +26,7 @@ import com.zaneschepke.wireguardautotunnel.ui.Route
import com.zaneschepke.wireguardautotunnel.ui.common.SelectedLabel
import com.zaneschepke.wireguardautotunnel.ui.common.button.SelectionItemButton
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.LocalNavController
+import com.zaneschepke.wireguardautotunnel.ui.common.navigation.TopNavBar
import com.zaneschepke.wireguardautotunnel.util.LocaleUtil
import com.zaneschepke.wireguardautotunnel.util.extensions.navigateAndForget
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight
@@ -63,44 +66,50 @@ fun LanguageScreen(localeStorage: LocaleStorage) {
navController.navigateAndForget(Route.Main)
}
- LazyColumn(
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.Top,
- modifier =
- Modifier
- .fillMaxSize()
- .windowInsetsPadding(WindowInsets.systemBars)
- .padding(top = 24.dp.scaledHeight())
- .padding(horizontal = 24.dp.scaledWidth()).windowInsetsPadding(WindowInsets.navigationBars),
- ) {
- item {
- SelectionItemButton(
- buttonText = stringResource(R.string.automatic),
- onClick = {
- onChangeLocale(LocaleUtil.OPTION_PHONE_LANGUAGE)
- },
- trailing = {
- if (currentLocale.value == LocaleUtil.OPTION_PHONE_LANGUAGE) {
- SelectedLabel()
- }
- },
- ripple = false,
- )
+ Scaffold(
+ topBar = {
+ TopNavBar(stringResource(R.string.language))
}
- items(sortedLocales, key = { it }) { locale ->
- SelectionItemButton(
- buttonText = locale.getDisplayLanguage(locale).capitalize(locale) +
- if (locale.toLanguageTag().contains("-")) " (${locale.getDisplayCountry(locale).capitalize(locale)})" else "",
- onClick = {
- onChangeLocale(locale.toLanguageTag())
- },
- trailing = {
- if (locale.toLanguageTag() == currentLocale.value) {
- SelectedLabel()
- }
- },
- ripple = false,
- )
+ ) {
+ LazyColumn(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Top,
+ modifier =
+ Modifier
+ .fillMaxSize().padding(it)
+ .padding(horizontal = 24.dp.scaledWidth()).windowInsetsPadding(WindowInsets.navigationBars),
+ ) {
+ item {
+ Box(modifier = Modifier.padding(top = 24.dp.scaledHeight())) {
+ SelectionItemButton(
+ buttonText = stringResource(R.string.automatic),
+ onClick = {
+ onChangeLocale(LocaleUtil.OPTION_PHONE_LANGUAGE)
+ },
+ trailing = {
+ if (currentLocale.value == LocaleUtil.OPTION_PHONE_LANGUAGE) {
+ SelectedLabel()
+ }
+ },
+ ripple = false,
+ )
+ }
+ }
+ items(sortedLocales, key = { it }) { locale ->
+ SelectionItemButton(
+ buttonText = locale.getDisplayLanguage(locale).capitalize(locale) +
+ if (locale.toLanguageTag().contains("-")) " (${locale.getDisplayCountry(locale).capitalize(locale)})" else "",
+ onClick = {
+ onChangeLocale(locale.toLanguageTag())
+ },
+ trailing = {
+ if (locale.toLanguageTag() == currentLocale.value) {
+ SelectedLabel()
+ }
+ },
+ ripple = false,
+ )
+ }
}
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/AutoTunnelScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/AutoTunnelScreen.kt
index cbe9b99..ad360c3 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/AutoTunnelScreen.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/AutoTunnelScreen.kt
@@ -4,34 +4,21 @@ import android.Manifest
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
-import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.ime
-import androidx.compose.foundation.layout.isImeVisible
-import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.systemBars
-import androidx.compose.foundation.layout.windowInsetsPadding
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.text.KeyboardActions
-import androidx.compose.foundation.text.KeyboardOptions
-import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Close
-import androidx.compose.material.icons.outlined.Add
+import androidx.compose.material.icons.outlined.Filter1
import androidx.compose.material.icons.outlined.NetworkPing
import androidx.compose.material.icons.outlined.Security
import androidx.compose.material.icons.outlined.SettingsEthernet
import androidx.compose.material.icons.outlined.SignalCellular4Bar
import androidx.compose.material.icons.outlined.Wifi
import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -45,8 +32,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontStyle
-import androidx.compose.ui.text.input.ImeAction
-import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.google.accompanist.permissions.ExperimentalPermissionsApi
@@ -54,16 +39,15 @@ import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState
import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.ui.AppUiState
-import com.zaneschepke.wireguardautotunnel.ui.common.ClickableIconButton
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.navigation.TopNavBar
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.autotunnel.components.TrustedNetworkTextBox
-import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.WildcardSupportingLabel
+import com.zaneschepke.wireguardautotunnel.ui.screens.settings.autotunnel.components.WildcardsLabel
+import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.LearnMoreLinkLabel
import com.zaneschepke.wireguardautotunnel.ui.theme.iconSize
import com.zaneschepke.wireguardautotunnel.util.extensions.isLocationServicesEnabled
-import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv
import com.zaneschepke.wireguardautotunnel.util.extensions.openWebUrl
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledWidth
@@ -96,137 +80,190 @@ fun AutoTunnelScreen(uiState: AppUiState, viewModel: AutoTunnelViewModel = hiltV
}
Scaffold(
+ contentWindowInsets = WindowInsets(0.dp),
topBar = {
TopNavBar(stringResource(R.string.auto_tunneling))
- },
+ }
) {
Column(
horizontalAlignment = Alignment.Start,
- verticalArrangement = Arrangement.spacedBy(24.dp, Alignment.Top),
+ verticalArrangement = Arrangement.spacedBy(24.dp.scaledHeight(), Alignment.Top),
modifier =
Modifier
- .verticalScroll(rememberScrollState())
.fillMaxSize()
- .padding(top = 24.dp.scaledHeight()).padding(it)
+ .padding(it)
+ .padding(top = 24.dp.scaledHeight())
.padding(horizontal = 24.dp.scaledWidth()),
) {
SurfaceSelectionGroupButton(
- mutableListOf(
- SelectionItem(
- Icons.Outlined.Wifi,
- title = { Text(stringResource(R.string.tunnel_on_wifi), style = MaterialTheme.typography.bodyLarge.copy(MaterialTheme.colorScheme.onSurface)) },
- description = {
- },
- trailing = {
- ScaledSwitch(
- enabled = !uiState.settings.isAlwaysOnVpnEnabled,
- checked = uiState.settings.isTunnelOnWifiEnabled,
- onClick = { checked ->
- if (!checked || uiState.isRooted) viewModel.onToggleTunnelOnWifi().also { return@ScaledSwitch }
- onAutoTunnelWifiChecked()
- },
- )
- },
- ),
- SelectionItem(
- Icons.Outlined.SignalCellular4Bar,
- title = {
- Text(
- stringResource(R.string.tunnel_mobile_data),
- style = MaterialTheme.typography.bodyLarge.copy(MaterialTheme.colorScheme.onSurface),
- )
- },
- trailing = {
- ScaledSwitch(
- enabled = !uiState.settings.isAlwaysOnVpnEnabled,
- checked = uiState.settings.isTunnelOnMobileDataEnabled,
- onClick = { viewModel.onToggleTunnelOnMobileData() },
- )
- },
- ),
- SelectionItem(
- Icons.Outlined.SettingsEthernet,
- title = {
- Text(
- stringResource(R.string.tunnel_on_ethernet),
- style = MaterialTheme.typography.bodyLarge.copy(MaterialTheme.colorScheme.onSurface),
- )
- },
- trailing = {
- ScaledSwitch(
- enabled = !uiState.settings.isAlwaysOnVpnEnabled,
- checked = uiState.settings.isTunnelOnEthernetEnabled,
- onClick = { viewModel.onToggleTunnelOnEthernet() },
- )
- },
- ),
- SelectionItem(
- Icons.Outlined.NetworkPing,
- title = {
- Text(
- stringResource(R.string.restart_on_ping),
- style = MaterialTheme.typography.bodyLarge.copy(MaterialTheme.colorScheme.onSurface),
- )
- },
- trailing = {
- ScaledSwitch(
- checked = uiState.settings.isPingEnabled,
- onClick = { viewModel.onToggleRestartOnPing() },
- )
- },
- ),
- ).apply {
+ buildList {
+ add(
+ SelectionItem(
+ Icons.Outlined.Wifi,
+ title = {
+ Text(
+ stringResource(R.string.tunnel_on_wifi),
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)
+ )
+ },
+ description = {
+ },
+ trailing = {
+ ScaledSwitch(
+ enabled = !uiState.settings.isAlwaysOnVpnEnabled,
+ checked = uiState.settings.isTunnelOnWifiEnabled,
+ onClick = {
+ if (!uiState.settings.isTunnelOnWifiEnabled || uiState.isRooted) viewModel.onToggleTunnelOnWifi()
+ .also { return@ScaledSwitch }
+ onAutoTunnelWifiChecked()
+ },
+ )
+ },
+ onClick = {
+ if (!uiState.settings.isTunnelOnWifiEnabled || uiState.isRooted) viewModel.onToggleTunnelOnWifi()
+ .also { return@SelectionItem }
+ onAutoTunnelWifiChecked()
+ }
+ )
+ )
if (uiState.settings.isTunnelOnWifiEnabled) {
- add(1,
- SelectionItem(
- title = {
- Row(
- verticalAlignment = Alignment.CenterVertically,
- modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp.scaledHeight()),
- ) {
+ addAll(
+ listOf(
+ SelectionItem(
+ Icons.Outlined.Filter1,
+ title = {
+ Text(
+ stringResource(R.string.use_wildcards),
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)
+ )
+ },
+ description = {
+ LearnMoreLinkLabel({context.openWebUrl(it)}, stringResource(id = R.string.docs_wildcards))
+ },
+ trailing = {
+ ScaledSwitch(
+ checked = uiState.generalState.isWildcardsEnabled,
+ onClick = {
+ viewModel.onToggleWildcards()
+ },
+ )
+ },
+ onClick = {
+ viewModel.onToggleWildcards()
+ }
+ ),
+ 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.scaledWidth()),
- )
- 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(
- "Trusted wifi names",
- style = MaterialTheme.typography.bodyLarge.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.trusted_wifi_names),
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
+ )
+ }
+
}
-
}
- }
- },
- description = {
- TrustedNetworkTextBox(
- uiState.settings.trustedNetworkSSIDs, onDelete = viewModel::onDeleteTrustedSSID,
- currentText = currentText,
- onSave = viewModel::onSaveTrustedSSID,
- onValueChange = { currentText = it }
+ },
+ description = {
+ TrustedNetworkTextBox(
+ uiState.settings.trustedNetworkSSIDs, onDelete = viewModel::onDeleteTrustedSSID,
+ currentText = currentText,
+ onSave = viewModel::onSaveTrustedSSID,
+ onValueChange = { currentText = it },
+ supporting = { if(uiState.generalState.isWildcardsEnabled) {
+ WildcardsLabel()
+ }}
+ )
+ },
+ )
+ ))
+ }
+ }
+ )
+ SurfaceSelectionGroupButton(
+ listOf(
+ SelectionItem(
+ Icons.Outlined.SignalCellular4Bar,
+ title = {
+ Text(
+ stringResource(R.string.tunnel_mobile_data),
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
)
},
+ trailing = {
+ ScaledSwitch(
+ enabled = !uiState.settings.isAlwaysOnVpnEnabled,
+ checked = uiState.settings.isTunnelOnMobileDataEnabled,
+ onClick = { viewModel.onToggleTunnelOnMobileData() },
+ )
+ },
+ onClick = {
+ viewModel.onToggleTunnelOnMobileData()
+ }
+ ),
+ SelectionItem(
+ Icons.Outlined.SettingsEthernet,
+ title = {
+ Text(
+ stringResource(R.string.tunnel_on_ethernet),
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
+ )
+ },
+ trailing = {
+ ScaledSwitch(
+ enabled = !uiState.settings.isAlwaysOnVpnEnabled,
+ checked = uiState.settings.isTunnelOnEthernetEnabled,
+ onClick = { viewModel.onToggleTunnelOnEthernet() },
+ )
+ },
+ onClick = {
+ viewModel.onToggleTunnelOnEthernet()
+ }
+ ),
+ SelectionItem(
+ Icons.Outlined.NetworkPing,
+ title = {
+ Text(
+ stringResource(R.string.restart_on_ping),
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface),
+ )
+ },
+ trailing = {
+ ScaledSwitch(
+ checked = uiState.settings.isPingEnabled,
+ onClick = { viewModel.onToggleRestartOnPing() },
+ )
+ },
+ onClick = {
+ viewModel.onToggleRestartOnPing()
+ }
)
)
- }
- },
- )
+ )
}
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/AutoTunnelViewModel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/AutoTunnelViewModel.kt
index 565fa20..1449c41 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/AutoTunnelViewModel.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/AutoTunnelViewModel.kt
@@ -18,7 +18,6 @@ class AutoTunnelViewModel
@Inject
constructor(
private val appDataRepository: AppDataRepository,
-
) : ViewModel() {
private val settings = appDataRepository.settings.getSettingsFlow()
@@ -44,6 +43,13 @@ constructor(
}
}
+ fun onToggleWildcards() = viewModelScope.launch {
+ val wildcards = appDataRepository.appState.isWildcardsEnabled()
+ appDataRepository.appState.setWildcardsEnabled(
+ !wildcards
+ )
+ }
+
fun onDeleteTrustedSSID(ssid: String) = viewModelScope.launch {
with(settings.value) {
appDataRepository.settings.save(
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 1ac22fb..635be17 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
@@ -16,6 +16,7 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
@@ -25,32 +26,28 @@ import androidx.compose.ui.unit.dp
import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.ui.common.ClickableIconButton
import com.zaneschepke.wireguardautotunnel.ui.common.textbox.CustomTextField
-import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.WildcardSupportingLabel
import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv
-import com.zaneschepke.wireguardautotunnel.util.extensions.openWebUrl
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledWidth
@OptIn(ExperimentalLayoutApi::class)
@Composable
-fun TrustedNetworkTextBox(trustedNetworks: List, onDelete: (ssid: String) -> Unit, currentText: String, onSave : (ssid: String) -> Unit, onValueChange: (network: String) -> Unit) {
+fun TrustedNetworkTextBox(trustedNetworks: List, onDelete: (ssid: String) -> Unit, currentText: String, onSave : (ssid: String) -> Unit, onValueChange: (network: String) -> Unit, supporting: @Composable () -> Unit) {
val context = LocalContext.current
Column(verticalArrangement = Arrangement.spacedBy(10.dp.scaledHeight())){
FlowRow(
modifier =
Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.spacedBy(5.dp),
+ horizontalArrangement = Arrangement.spacedBy(5.dp, Alignment.CenterHorizontally),
) {
trustedNetworks.forEach { ssid ->
ClickableIconButton(
onClick = {
if (context.isRunningOnTv()) {
- //focusRequester.requestFocus()
onDelete(ssid)
}
},
onIconClick = {
- //if (context.isRunningOnTv()) focusRequester.requestFocus()
onDelete(ssid)
},
text = ssid,
@@ -59,17 +56,18 @@ fun TrustedNetworkTextBox(trustedNetworks: List, onDelete: (ssid: String
}
}
CustomTextField(
+ textStyle = MaterialTheme.typography.bodySmall,
value = currentText,
onValueChange = onValueChange,
- label = { Text(stringResource(R.string.add_trusted_ssid)) },
+ label = { Text(stringResource(R.string.add_wifi_name)) },
containerColor = MaterialTheme.colorScheme.surface,
+ supportingText = supporting,
modifier =
Modifier
.padding(
top = 5.dp,
bottom = 10.dp,
).fillMaxWidth().padding(end = 16.dp.scaledWidth()),
- supportingText = { WildcardSupportingLabel { context.openWebUrl(it)} },
singleLine = true,
keyboardOptions =
KeyboardOptions(
@@ -94,7 +92,7 @@ fun TrustedNetworkTextBox(trustedNetworks: List, onDelete: (ssid: String
}
}
},
-
)
+
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/components/WildcardsLabel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/components/WildcardsLabel.kt
new file mode 100644
index 0000000..56039be
--- /dev/null
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/autotunnel/components/WildcardsLabel.kt
@@ -0,0 +1,17 @@
+package com.zaneschepke.wireguardautotunnel.ui.screens.settings.autotunnel.components
+
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontStyle
+import com.zaneschepke.wireguardautotunnel.R
+
+
+@Composable
+fun WildcardsLabel() {
+ Text(
+ stringResource(R.string.wildcards_active),
+ style = MaterialTheme.typography.bodySmall.copy(MaterialTheme.colorScheme.outline, fontStyle = FontStyle.Italic),
+ )
+}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/components/BackgroundLocationDisclosure.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/components/BackgroundLocationDisclosure.kt
deleted file mode 100644
index 107c56c..0000000
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/components/BackgroundLocationDisclosure.kt
+++ /dev/null
@@ -1,88 +0,0 @@
-package com.zaneschepke.wireguardautotunnel.ui.screens.settings.components
-
-import androidx.compose.foundation.ScrollState
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.verticalScroll
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.rounded.LocationOff
-import androidx.compose.material3.Icon
-import androidx.compose.material3.Text
-import androidx.compose.material3.TextButton
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.FocusRequester
-import androidx.compose.ui.focus.focusRequester
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
-import com.zaneschepke.wireguardautotunnel.R
-import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv
-
-@Composable
-fun BackgroundLocationDisclosure(onDismiss: () -> Unit, onAttest: () -> Unit, scrollState: ScrollState, focusRequester: FocusRequester) {
- val context = LocalContext.current
- Column(
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.Top,
- modifier =
- Modifier
- .fillMaxSize()
- .verticalScroll(scrollState),
- ) {
- Icon(
- Icons.Rounded.LocationOff,
- contentDescription = stringResource(id = R.string.map),
- modifier =
- Modifier
- .padding(30.dp)
- .size(128.dp),
- )
- Text(
- stringResource(R.string.prominent_background_location_title),
- textAlign = TextAlign.Center,
- modifier = Modifier.padding(30.dp),
- fontSize = 20.sp,
- )
- Text(
- stringResource(R.string.prominent_background_location_message),
- textAlign = TextAlign.Center,
- modifier = Modifier.padding(30.dp),
- fontSize = 15.sp,
- )
- Row(
- modifier =
- if (context.isRunningOnTv()) {
- Modifier
- .fillMaxWidth()
- .padding(10.dp)
- } else {
- Modifier
- .fillMaxWidth()
- .padding(30.dp)
- },
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.SpaceEvenly,
- ) {
- TextButton(onClick = { onDismiss() }) {
- Text(stringResource(id = R.string.no_thanks))
- }
- TextButton(
- modifier = Modifier.focusRequester(focusRequester),
- onClick = {
- onAttest()
- },
- ) {
- Text(stringResource(id = R.string.turn_on))
- }
- }
- }
-}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/components/ForwardButton.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/components/ForwardButton.kt
new file mode 100644
index 0000000..675b5d3
--- /dev/null
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/components/ForwardButton.kt
@@ -0,0 +1,22 @@
+package com.zaneschepke.wireguardautotunnel.ui.screens.settings.components
+
+import androidx.compose.foundation.focusable
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.outlined.ArrowForward
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.zaneschepke.wireguardautotunnel.ui.theme.iconSize
+
+@Composable
+fun ForwardButton(modifier: Modifier = Modifier.focusable(), onClick: () -> Unit) {
+ IconButton(
+ modifier = modifier,
+ onClick = onClick
+ ) {
+ val icon = Icons.AutoMirrored.Outlined.ArrowForward
+ Icon(icon, icon.name, Modifier.size(iconSize))
+ }
+}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/components/WildcardSupportingLabel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/components/LearnMoreLinkLabel.kt
similarity index 86%
rename from app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/components/WildcardSupportingLabel.kt
rename to app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/components/LearnMoreLinkLabel.kt
index 9c78a4b..453d368 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/components/WildcardSupportingLabel.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/components/LearnMoreLinkLabel.kt
@@ -12,18 +12,18 @@ import androidx.compose.ui.text.withStyle
import com.zaneschepke.wireguardautotunnel.R
@Composable
-fun WildcardSupportingLabel(onClick: (url: String) -> Unit) {
+fun LearnMoreLinkLabel(onClick: (url: String) -> Unit, url : String) {
// TODO update link when docs are fully updated
val gettingStarted =
buildAnnotatedString {
pushStringAnnotation(
tag = "details",
- annotation = stringResource(id = R.string.docs_wildcards),
+ annotation = url,
)
withStyle(
style = SpanStyle(color = MaterialTheme.colorScheme.primary),
) {
- append(stringResource(id = R.string.wildcard_supported))
+ append(stringResource(id = R.string.learn_more))
}
pop()
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/disclosure/LocationDisclosureScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/disclosure/LocationDisclosureScreen.kt
new file mode 100644
index 0000000..63fd324
--- /dev/null
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/disclosure/LocationDisclosureScreen.kt
@@ -0,0 +1,95 @@
+package com.zaneschepke.wireguardautotunnel.ui.screens.settings.disclosure
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.LocationOn
+import androidx.compose.material.icons.rounded.PermScanWifi
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+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.common.button.surface.SelectionItem
+import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelectionGroupButton
+import com.zaneschepke.wireguardautotunnel.ui.common.navigation.LocalNavController
+import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.ForwardButton
+import com.zaneschepke.wireguardautotunnel.ui.theme.topPadding
+import com.zaneschepke.wireguardautotunnel.util.extensions.goFromRoot
+import com.zaneschepke.wireguardautotunnel.util.extensions.launchAppSettings
+import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight
+import com.zaneschepke.wireguardautotunnel.util.extensions.scaledWidth
+
+@Composable
+fun LocationDisclosureScreen(appViewModel: AppViewModel, appUiState: AppUiState) {
+ val context = LocalContext.current
+ val navController = LocalNavController.current
+
+ LaunchedEffect(Unit, appUiState) {
+ if(appUiState.generalState.isLocationDisclosureShown) navController.goFromRoot(Route.AutoTunnel)
+ }
+
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(24.dp.scaledHeight(), Alignment.Top),
+ modifier =
+ Modifier.fillMaxSize().padding(top = topPadding).padding(horizontal = 24.dp.scaledWidth()),
+ ) {
+ val icon = Icons.Rounded.PermScanWifi
+ Icon(
+ icon,
+ contentDescription = icon.name,
+ modifier = Modifier
+ .padding(30.dp.scaledHeight())
+ .size(128.dp.scaledHeight()),
+ )
+ Text(
+ stringResource(R.string.prominent_background_location_title),
+ style = MaterialTheme.typography.titleLarge.copy(fontWeight = FontWeight.Bold),
+ )
+ Text(
+ stringResource(R.string.prominent_background_location_message),
+ style = MaterialTheme.typography.bodyLarge
+ )
+ SurfaceSelectionGroupButton(
+ listOf(
+ SelectionItem(
+ Icons.Outlined.LocationOn,
+ title = { Text(stringResource(R.string.launch_app_settings), style = MaterialTheme.typography.bodyLarge.copy(MaterialTheme.colorScheme.onSurface)) },
+ onClick = { context.launchAppSettings().also {
+ appViewModel.setLocationDisclosureShown()
+ } },
+ trailing = {
+ ForwardButton { context.launchAppSettings().also {
+ appViewModel.setLocationDisclosureShown()
+ } }
+ }
+ ),
+ ),
+ )
+ SurfaceSelectionGroupButton(
+ listOf(
+ SelectionItem(
+ title = { Text(stringResource(R.string.skip), style = MaterialTheme.typography.bodyLarge.copy(MaterialTheme.colorScheme.onSurface)) },
+ onClick = { appViewModel.setLocationDisclosureShown() },
+ trailing = {
+ ForwardButton { appViewModel.setLocationDisclosureShown() }
+ }
+ ),
+ ),
+ )
+ }
+ }
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/support/SupportScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/support/SupportScreen.kt
index ceb885a..561d637 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/support/SupportScreen.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/support/SupportScreen.kt
@@ -1,314 +1,131 @@
package com.zaneschepke.wireguardautotunnel.ui.screens.support
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.IntrinsicSize
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.systemBars
-import androidx.compose.foundation.layout.windowInsetsPadding
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.automirrored.rounded.ArrowForward
-import androidx.compose.material.icons.rounded.Book
-import androidx.compose.material.icons.rounded.FormatListNumbered
-import androidx.compose.material.icons.rounded.Mail
-import androidx.compose.material3.HorizontalDivider
-import androidx.compose.material3.Icon
+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.Policy
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
import androidx.compose.material3.Text
-import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.FocusRequester
-import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.buildAnnotatedString
-import androidx.compose.ui.text.font.FontWeight
-import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
-import com.zaneschepke.wireguardautotunnel.BuildConfig
import com.zaneschepke.wireguardautotunnel.R
-import com.zaneschepke.wireguardautotunnel.ui.AppUiState
import com.zaneschepke.wireguardautotunnel.ui.Route
+import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
+import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelectionGroupButton
+import com.zaneschepke.wireguardautotunnel.ui.common.label.GroupLabel
+import com.zaneschepke.wireguardautotunnel.ui.common.label.VersionLabel
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.LocalNavController
-import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv
+import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.ForwardButton
+import com.zaneschepke.wireguardautotunnel.ui.theme.topPadding
import com.zaneschepke.wireguardautotunnel.util.extensions.launchSupportEmail
import com.zaneschepke.wireguardautotunnel.util.extensions.openWebUrl
+import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight
+import com.zaneschepke.wireguardautotunnel.util.extensions.scaledWidth
@Composable
-fun SupportScreen(focusRequester: FocusRequester, appUiState: AppUiState) {
+fun SupportScreen() {
val context = LocalContext.current
val navController = LocalNavController.current
- val fillMaxWidth = .85f
- Column(
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.Top,
- modifier =
- Modifier
- .fillMaxSize()
- .windowInsetsPadding(WindowInsets.systemBars)
- .verticalScroll(rememberScrollState())
- .focusable(),
- ) {
- Surface(
- tonalElevation = 2.dp,
- shadowElevation = 2.dp,
- shape = RoundedCornerShape(12.dp),
- color = MaterialTheme.colorScheme.surface,
+ Column(
+ horizontalAlignment = Alignment.Start,
+ verticalArrangement = Arrangement.spacedBy(24.dp.scaledHeight(), Alignment.Top),
modifier =
- (
- if (context.isRunningOnTv()) {
- Modifier
- .height(IntrinsicSize.Min)
- .fillMaxWidth(fillMaxWidth)
- .padding(top = 10.dp)
- } else {
- Modifier
- .fillMaxWidth(fillMaxWidth)
- .padding(top = 20.dp)
- }
- )
- .padding(bottom = 25.dp),
+ Modifier
+ .fillMaxSize()
+ .padding(top = topPadding)
+ .padding(horizontal = 24.dp.scaledWidth()),
) {
- Column(modifier = Modifier.padding(20.dp)) {
- val forwardIcon = Icons.AutoMirrored.Rounded.ArrowForward
- Text(
- stringResource(R.string.thank_you),
- textAlign = TextAlign.Start,
- fontWeight = FontWeight.Bold,
- modifier = Modifier.padding(bottom = 20.dp),
- fontSize = 16.sp,
- )
- Text(
- stringResource(id = R.string.support_help_text),
- textAlign = TextAlign.Start,
- fontSize = 16.sp,
- modifier = Modifier.padding(bottom = 20.dp),
- )
- TextButton(
- onClick = {
- context.openWebUrl(
- context.resources.getString(R.string.docs_url),
- )
- },
- modifier =
- Modifier
- .padding(vertical = 5.dp)
- .focusRequester(focusRequester),
- ) {
- Row(
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically,
- modifier = Modifier.fillMaxWidth(),
- ) {
- Row {
- val icon = Icons.Rounded.Book
- Icon(icon, icon.name)
- Text(
- stringResource(id = R.string.docs_description),
- textAlign = TextAlign.Justify,
- modifier =
- Modifier
- .padding(start = 10.dp)
- .weight(
- weight = 1.0f,
- fill = false,
- ),
- softWrap = true,
- )
+ GroupLabel(stringResource(R.string.thank_you))
+ SurfaceSelectionGroupButton(
+ listOf(
+ SelectionItem(
+ Icons.Filled.Book,
+ title = { Text(stringResource(R.string.docs_description), style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
+ trailing = {
+ ForwardButton { context.openWebUrl(context.getString(R.string.docs_url)) }
+ },
+ onClick = {
+ context.openWebUrl(context.getString(R.string.docs_url))
}
- Icon(
- forwardIcon,
- forwardIcon.name,
- )
- }
- }
- HorizontalDivider(
- thickness = 0.5.dp,
- color = MaterialTheme.colorScheme.onBackground,
- )
- TextButton(
- onClick = {
- context.openWebUrl(
- context.resources.getString(R.string.telegram_url),
- )
- },
- modifier = Modifier.padding(vertical = 5.dp),
- ) {
- Row(
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically,
- modifier = Modifier.fillMaxWidth(),
- ) {
- Row {
- val icon = ImageVector.vectorResource(R.drawable.telegram)
- Icon(
- icon,
- icon.name,
- Modifier.size(25.dp),
- )
- Text(
- stringResource(id = R.string.chat_description),
- textAlign = TextAlign.Justify,
- modifier = Modifier.padding(start = 10.dp),
- )
- }
- Icon(
- forwardIcon,
- forwardIcon.name,
- )
- }
- }
- HorizontalDivider(
- thickness = 0.5.dp,
- color = MaterialTheme.colorScheme.onBackground,
- )
- TextButton(
- onClick = {
- context.openWebUrl(
- context.resources.getString(R.string.github_url),
- )
- },
- modifier = Modifier.padding(vertical = 5.dp),
- ) {
- Row(
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically,
- modifier = Modifier.fillMaxWidth(),
- ) {
- Row {
- val icon = ImageVector.vectorResource(R.drawable.github)
- Icon(
- imageVector = icon,
- icon.name,
- Modifier.size(25.dp),
- )
- Text(
- stringResource(id = R.string.open_issue),
- textAlign = TextAlign.Justify,
- modifier = Modifier.padding(start = 10.dp),
- )
- }
- Icon(
- forwardIcon,
- forwardIcon.name,
- )
- }
- }
- HorizontalDivider(
- thickness = 0.5.dp,
- color = MaterialTheme.colorScheme.onBackground,
- )
- TextButton(
- onClick = { context.launchSupportEmail() },
- modifier = Modifier.padding(vertical = 5.dp),
- ) {
- Row(
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically,
- modifier = Modifier.fillMaxWidth(),
- ) {
- Row {
- val icon = Icons.Rounded.Mail
- Icon(icon, icon.name)
- Text(
- stringResource(id = R.string.email_description),
- textAlign = TextAlign.Justify,
- modifier = Modifier.padding(start = 10.dp),
- )
- }
- Icon(
- forwardIcon,
- forwardIcon.name,
- )
- }
- }
- if (!context.isRunningOnTv()) {
- HorizontalDivider(
- thickness = 0.5.dp,
- color = MaterialTheme.colorScheme.onBackground,
- )
- TextButton(
- onClick = { navController.navigate(Route.Logs) },
- modifier = Modifier.padding(vertical = 5.dp),
- ) {
- Row(
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically,
- modifier = Modifier.fillMaxWidth(),
- ) {
- Row {
- val icon = Icons.Rounded.FormatListNumbered
- Icon(icon, icon.name)
- Text(
- stringResource(id = R.string.read_logs),
- textAlign = TextAlign.Justify,
- modifier = Modifier.padding(start = 10.dp),
- )
+ ),
+ SelectionItem(
+ Icons.Filled.LineStyle,
+ title = { Text(stringResource(R.string.read_logs),
+ style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
+ trailing = {
+ ForwardButton {
+ navController.navigate(Route.Logs)
}
- Icon(
- Icons.AutoMirrored.Rounded.ArrowForward,
- stringResource(id = R.string.go),
- )
+ },
+ onClick = {
+ navController.navigate(Route.Logs)
}
- }
- }
- }
- }
- Spacer(modifier = Modifier.weight(1f))
- Text(
- stringResource(id = R.string.privacy_policy),
- style = TextStyle(textDecoration = TextDecoration.Underline),
- fontSize = 16.sp,
- modifier =
- Modifier.clickable {
- context.openWebUrl(
- context.resources.getString(R.string.privacy_policy_url),
+ ),
+ SelectionItem(
+ Icons.Filled.Policy,
+ title = { Text(stringResource(R.string.privacy_policy), style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
+ trailing = {
+ ForwardButton { context.openWebUrl(context.getString(R.string.privacy_policy_url)) }
+ },
+ onClick = {
+ context.openWebUrl(context.getString(R.string.privacy_policy_url))
+ }
+ ),
+
+ )
+ )
+ SurfaceSelectionGroupButton(
+ listOf(
+ SelectionItem(
+ ImageVector.vectorResource(R.drawable.telegram),
+ title = { Text(stringResource(R.string.chat_description), style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
+ trailing = {
+ ForwardButton {
+ context.openWebUrl(context.getString(R.string.telegram_url))
+ }
+ },
+ onClick = {
+ context.openWebUrl(context.getString(R.string.telegram_url))
+ }
+ ),
+ SelectionItem(
+ ImageVector.vectorResource(R.drawable.github),
+ title = { Text(stringResource(R.string.open_issue), style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
+ trailing = {
+ ForwardButton {
+ context.openWebUrl(context.getString(R.string.github_url))
+ }
+ },
+ onClick = {
+ context.openWebUrl(context.getString(R.string.github_url))
+ }
+ ),
+ SelectionItem(
+ Icons.Filled.Mail,
+ title = { Text(stringResource(R.string.email_description), style = MaterialTheme.typography.bodyMedium.copy(MaterialTheme.colorScheme.onSurface)) },
+ trailing = {
+ ForwardButton {
+ context.launchSupportEmail()
+ }
+ },
+ onClick = {
+ context.launchSupportEmail()
+ }
+ ),
)
- },
- )
- Row(
- horizontalArrangement = Arrangement.spacedBy(25.dp),
- verticalAlignment = Alignment.CenterVertically,
- modifier = Modifier.padding(25.dp),
- ) {
- val version =
- buildAnnotatedString {
- append(stringResource(id = R.string.version))
- append(": ")
- append(BuildConfig.VERSION_NAME)
- }
- val mode =
- buildAnnotatedString {
- append(stringResource(R.string.mode))
- append(": ")
- when (appUiState.settings.isKernelEnabled) {
- true -> append(stringResource(id = R.string.kernel))
- false -> append(stringResource(id = R.string.userspace))
- }
- }
- Text(version.text, modifier = Modifier.focusable())
- Text(mode.text)
+ )
+ VersionLabel()
}
}
-}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/theme/Color.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/theme/Color.kt
index 63ac3b4..1caf5e2 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/theme/Color.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/theme/Color.kt
@@ -3,14 +3,16 @@ package com.zaneschepke.wireguardautotunnel.ui.theme
import androidx.compose.ui.graphics.Color
val OffWhite = Color(0xFFE5E1E5)
-val LightGrey = Color(0xFF8D9D9F)
+val LightGrey = Color(0xFFCAC4D0)
val Aqua = Color(0xFF76BEBD)
val SilverTree = Color(0xFF6DB58B)
val Plantation = Color(0xFF264A49)
val Shark = Color(0xFF21272A)
val BalticSea = Color(0xFF1C1B1F)
val Brick = Color(0xFFCE4257)
-val Corn = Color(0xFFFBEC5D)
+val Straw = Color(0xFFD4C483)
+
+
sealed class ThemeColors(
val background: Color,
@@ -19,7 +21,7 @@ sealed class ThemeColors(
val secondary: Color,
val onSurface: Color,
) {
- // TODO fix light theme colors
+
data object Light : ThemeColors(
background = LightGrey,
surface = OffWhite,
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/theme/Size.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/theme/Size.kt
index 21276f5..4900c22 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/theme/Size.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/theme/Size.kt
@@ -4,3 +4,4 @@ import androidx.compose.ui.unit.dp
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight
val iconSize = 24.dp.scaledHeight()
+val topPadding = 80.dp.scaledHeight()
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/theme/Theme.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/theme/Theme.kt
index 8c4eb3c..bb4eeef 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/theme/Theme.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/theme/Theme.kt
@@ -49,12 +49,18 @@ fun WireguardAutoTunnelTheme(
content: @Composable () -> Unit,
) {
val context = LocalContext.current
- val isDark = isSystemInDarkTheme()
+ var isDark = isSystemInDarkTheme()
val autoTheme = if(isDark) DarkColorScheme else LightColorScheme
val colorScheme = when(theme) {
Theme.AUTOMATIC -> autoTheme
- Theme.DARK -> DarkColorScheme
- Theme.LIGHT -> LightColorScheme
+ Theme.DARK -> {
+ isDark = true
+ DarkColorScheme
+ }
+ Theme.LIGHT -> {
+ isDark = false
+ LightColorScheme
+ }
Theme.DYNAMIC -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if (isDark) {
@@ -72,7 +78,7 @@ fun WireguardAutoTunnelTheme(
WindowCompat.setDecorFitsSystemWindows(window, false)
window.statusBarColor = Color.Transparent.toArgb()
window.navigationBarColor = Color.Transparent.toArgb()
- WindowCompat.getInsetsController(window, window.decorView).isAppearanceLightStatusBars = isDark
+ WindowCompat.getInsetsController(window, window.decorView).isAppearanceLightStatusBars = !isDark
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/theme/Type.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/theme/Type.kt
index 5bc690d..ad880ad 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/theme/Type.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/theme/Type.kt
@@ -18,6 +18,7 @@ val inter = FontFamily(
val Typography =
Typography(
bodyLarge = TextStyle(
+ fontFamily = inter,
fontWeight = FontWeight.Normal,
fontSize = 16.sp.scaled(),
lineHeight = 24.sp.scaled(),
@@ -26,12 +27,13 @@ val Typography =
bodySmall = TextStyle(
fontFamily = inter,
fontWeight = FontWeight.Normal,
- fontSize = 13.sp.scaled(),
+ fontSize = 12.sp.scaled(),
lineHeight = 20.sp.scaled(),
letterSpacing = 1.sp,
color = LightGrey,
),
bodyMedium = TextStyle(
+ fontFamily = inter,
fontSize = 14.sp.scaled(),
lineHeight = 20.sp.scaled(),
fontWeight = FontWeight(400),
@@ -54,7 +56,7 @@ val Typography =
titleMedium = TextStyle(
fontFamily = inter,
fontWeight = FontWeight.Bold,
- fontSize = 17.sp.scaled(),
+ fontSize = 16.sp.scaled(),
lineHeight = 21.sp.scaled(),
letterSpacing = 0.sp,
),
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/ContextExtensions.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/ContextExtensions.kt
index 056213c..18a3d2f 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/ContextExtensions.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/ContextExtensions.kt
@@ -66,6 +66,7 @@ fun Context.resizeWidth(dp: Dp): Dp {
}
fun Context.launchNotificationSettings() {
+ if(isRunningOnTv()) return launchAppSettings()
val settingsIntent: Intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/TunnelExtensions.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/TunnelExtensions.kt
index 8764bc4..c39f15c 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/TunnelExtensions.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/TunnelExtensions.kt
@@ -5,7 +5,7 @@ import com.wireguard.android.util.RootShell
import com.wireguard.config.Peer
import com.zaneschepke.wireguardautotunnel.service.tunnel.HandshakeStatus
import com.zaneschepke.wireguardautotunnel.service.tunnel.statistics.TunnelStatistics
-import com.zaneschepke.wireguardautotunnel.ui.theme.Corn
+import com.zaneschepke.wireguardautotunnel.ui.theme.Straw
import com.zaneschepke.wireguardautotunnel.ui.theme.SilverTree
import com.zaneschepke.wireguardautotunnel.util.Constants
import com.zaneschepke.wireguardautotunnel.util.NumberUtils
@@ -58,7 +58,7 @@ fun TunnelStatistics?.asColor(): Color {
?.let { statuses ->
when {
statuses.all { it == HandshakeStatus.HEALTHY } -> SilverTree
- statuses.any { it == HandshakeStatus.STALE } -> Corn
+ statuses.any { it == HandshakeStatus.STALE } -> Straw
statuses.all { it == HandshakeStatus.NOT_STARTED } -> Color.Gray
else -> Color.Gray
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/UiExtensions.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/UiExtensions.kt
index afba1f3..6abc1dc 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/UiExtensions.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/UiExtensions.kt
@@ -3,8 +3,10 @@ package com.zaneschepke.wireguardautotunnel.util.extensions
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit
import androidx.navigation.NavController
+import androidx.navigation.NavGraph.Companion.findStartDestination
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
import com.zaneschepke.wireguardautotunnel.ui.Route
+import com.zaneschepke.wireguardautotunnel.ui.common.navigation.isCurrentRoute
fun NavController.navigateAndForget(route: Route) {
navigate(route) {
@@ -12,6 +14,22 @@ fun NavController.navigateAndForget(route: Route) {
}
}
+fun NavController.goFromRoot(route: Route) {
+ if (currentBackStackEntry?.isCurrentRoute(route::class) == true) return
+ this.navigate(route) {
+ // Pop up to the start destination of the graph to
+ // avoid building up a large stack of destinations
+ // on the back stack as users select items
+ popUpTo(graph.findStartDestination().id) {
+ saveState = true
+ }
+ // Avoid multiple copies of the same destination when
+ // reselecting the same item
+ launchSingleTop = true
+ restoreState = true
+ }
+}
+
fun Dp.scaledHeight(): Dp {
return WireGuardAutoTunnel.instance.resizeHeight(this)
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 8cc001b..48ebb9d 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -23,7 +23,7 @@
Start auto-tunneling
Stop auto-tunneling
Tunnel on mobile data
- View Privacy Policy
+ View privacy policy
Okay
Tunnel on ethernet
This feature requires background location permission to enable Wi-Fi SSID monitoring even while the application is closed. For more details, please see the Privacy Policy linked on the Support screen.
@@ -193,7 +193,6 @@
Ping interval (sec)
"optional, default: "
Ping restart cooldown (sec)
- Learn about supported wildcards.
details
Show Amnezia properties
never
@@ -211,4 +210,14 @@
Language
Display theme
Selected
+ Trusted wifi names
+ Add wifi name
+ On demand tunnel rules
+ Primary tunnel
+ Mobile data tunnel
+ Skip
+ Launch app settings
+ Use name wildcards
+ Learn more
+ Wildcards active