diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/navigation/components/BottomNavbar.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/navigation/components/BottomNavbar.kt
index b74f2240..a2e1a085 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/navigation/components/BottomNavbar.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/navigation/components/BottomNavbar.kt
@@ -1,5 +1,7 @@
package com.zaneschepke.wireguardautotunnel.ui.navigation.components
+import android.R.attr.button
+import android.R.attr.theme
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
@@ -7,11 +9,13 @@ import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.size
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
+import com.zaneschepke.wireguardautotunnel.ui.LocalIsAndroidTV
import com.zaneschepke.wireguardautotunnel.ui.common.animations.AnimatedFloatIcon
import com.zaneschepke.wireguardautotunnel.ui.navigation.Tab
import com.zaneschepke.wireguardautotunnel.ui.theme.SilverTree
@@ -19,6 +23,9 @@ import com.zaneschepke.wireguardautotunnel.ui.theme.SilverTree
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun BottomNavbar(isAutoTunnelActive: Boolean, currentTab: Tab, onTabSelected: (Tab) -> Unit) {
+ val isTv = LocalIsAndroidTV.current
+ val rippleTheme = LocalRippleConfiguration.current
+ val theme = remember { if (isTv) rippleTheme else null }
FlexibleBottomAppBar(
containerColor = MaterialTheme.colorScheme.surface,
contentColor = MaterialTheme.colorScheme.onSurface,
@@ -31,29 +38,31 @@ fun BottomNavbar(isAutoTunnelActive: Boolean, currentTab: Tab, onTabSelected: (T
val hasBadge = tab == Tab.AUTOTUNNEL && isAutoTunnelActive
val button =
@Stable @Composable {
- IconButton(
- onClick = { onTabSelected(tab) },
- colors =
- IconButtonDefaults.iconButtonColors(
- containerColor = Color.Transparent,
- contentColor =
- if (isSelected) {
- MaterialTheme.colorScheme.primary
- } else {
- MaterialTheme.colorScheme.onSurfaceVariant
- },
- disabledContainerColor = Color.Transparent,
- disabledContentColor =
- MaterialTheme.colorScheme.onSurfaceVariant,
- ),
- interactionSource = interactionSource,
- ) {
- AnimatedFloatIcon(
- activeIcon = tab.activeIcon,
- inactiveIcon = tab.inactiveIcon,
- isSelected = isSelected,
- modifier = Modifier.size(24.dp),
- )
+ CompositionLocalProvider(LocalRippleConfiguration provides theme) {
+ IconButton(
+ onClick = { onTabSelected(tab) },
+ colors =
+ IconButtonDefaults.iconButtonColors(
+ containerColor = Color.Transparent,
+ contentColor =
+ if (isSelected) {
+ MaterialTheme.colorScheme.primary
+ } else {
+ MaterialTheme.colorScheme.onSurfaceVariant
+ },
+ disabledContainerColor = Color.Transparent,
+ disabledContentColor =
+ MaterialTheme.colorScheme.onSurfaceVariant,
+ ),
+ interactionSource = interactionSource,
+ ) {
+ AnimatedFloatIcon(
+ activeIcon = tab.activeIcon,
+ inactiveIcon = tab.inactiveIcon,
+ isSelected = isSelected,
+ modifier = Modifier.size(24.dp),
+ )
+ }
}
}
if (hasBadge) {
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 f0605c82..91fdf93b 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
@@ -200,17 +200,17 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel()) {
onClick = { navController.push(Route.ProxySettings) },
)
}
+ SurfaceRow(
+ leading = { Icon(Icons.Outlined.Android, null) },
+ title = stringResource(R.string.android_integrations),
+ onClick = { navController.push(Route.AndroidIntegrations) },
+ )
}
Column {
GroupLabel(
stringResource(R.string.general),
modifier = Modifier.padding(horizontal = 16.dp),
)
- SurfaceRow(
- leading = { Icon(Icons.Outlined.Android, null) },
- title = stringResource(R.string.android_integrations),
- onClick = { navController.push(Route.AndroidIntegrations) },
- )
SurfaceRow(
leading = {
Icon(Icons.AutoMirrored.Outlined.ViewQuilt, contentDescription = null)
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/integrations/AndroidIntegrationsScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/integrations/AndroidIntegrationsScreen.kt
index 5ed06686..c0acceea 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/integrations/AndroidIntegrationsScreen.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/integrations/AndroidIntegrationsScreen.kt
@@ -1,25 +1,19 @@
package com.zaneschepke.wireguardautotunnel.ui.screens.settings.integrations
import androidx.compose.animation.AnimatedVisibility
-import androidx.compose.foundation.clickable
-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.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.Launch
import androidx.compose.material.icons.filled.AppShortcut
import androidx.compose.material.icons.filled.SmartToy
-import androidx.compose.material.icons.outlined.AdminPanelSettings
-import androidx.compose.material.icons.outlined.Restore
-import androidx.compose.material.icons.outlined.VpnLock
+import androidx.compose.material.icons.outlined.*
import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
+import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
@@ -34,6 +28,7 @@ import com.zaneschepke.wireguardautotunnel.ui.common.button.SurfaceRow
import com.zaneschepke.wireguardautotunnel.ui.common.functions.rememberClipboardHelper
import com.zaneschepke.wireguardautotunnel.ui.common.label.GroupLabel
import com.zaneschepke.wireguardautotunnel.ui.common.security.SecureScreenFromRecording
+import com.zaneschepke.wireguardautotunnel.ui.common.text.DescriptionText
import com.zaneschepke.wireguardautotunnel.util.extensions.launchVpnSettings
import com.zaneschepke.wireguardautotunnel.viewmodel.SettingsViewModel
@@ -74,6 +69,7 @@ fun AndroidIntegrationsScreen(viewModel: SettingsViewModel = hiltViewModel()) {
onClick = {
viewModel.setAlwaysOnVpnEnabled(!settingsState.settings.isAlwaysOnVpnEnabled)
},
+ description = { DescriptionText(stringResource(R.string.aovpn_description)) },
)
}
Column {
@@ -95,6 +91,7 @@ fun AndroidIntegrationsScreen(viewModel: SettingsViewModel = hiltViewModel()) {
!settingsState.settings.isRestoreOnBootEnabled
)
},
+ description = { DescriptionText(stringResource(R.string.tunnel_boot_description)) },
)
SurfaceRow(
leading = { Icon(Icons.Filled.AppShortcut, contentDescription = null) },
@@ -117,25 +114,50 @@ fun AndroidIntegrationsScreen(viewModel: SettingsViewModel = hiltViewModel()) {
onClick = { viewModel.setRemoteEnabled(it) },
)
},
- description = {
- settingsState.remoteKey?.let { key ->
- AnimatedVisibility(visible = settingsState.isRemoteEnabled) {
+ title = stringResource(R.string.enable_remote_app_control),
+ onClick = { viewModel.setRemoteEnabled(!settingsState.isRemoteEnabled) },
+ )
+ AnimatedVisibility(settingsState.isRemoteEnabled) {
+ settingsState.remoteKey?.let { key ->
+ var passwordProtected by remember { mutableStateOf(true) }
+ val keyText by
+ remember(passwordProtected) {
+ derivedStateOf {
+ if (passwordProtected) "•".repeat(key.length) else key
+ }
+ }
+ SurfaceRow(
+ leading = { Icon(Icons.Outlined.Key, contentDescription = null) },
+ title = stringResource(R.string.remote_key),
+ description = {
Text(
- text = stringResource(R.string.remote_key_template, key),
+ text = keyText,
style =
MaterialTheme.typography.bodySmall.copy(
color = MaterialTheme.colorScheme.outline
),
- maxLines = 1,
- overflow = TextOverflow.Ellipsis,
- modifier = Modifier.clickable { clipboard.copy(key) },
+ overflow = TextOverflow.Clip,
)
- }
- }
- },
- title = stringResource(R.string.enable_remote_app_control),
- onClick = { viewModel.setRemoteEnabled(!settingsState.isRemoteEnabled) },
- )
+ },
+ trailing = {
+ Row {
+ IconButton(onClick = { passwordProtected = !passwordProtected }) {
+ Icon(
+ Icons.Outlined.RemoveRedEye,
+ contentDescription = stringResource(R.string.show_password),
+ )
+ }
+ IconButton(onClick = { clipboard.copy(key) }) {
+ Icon(
+ Icons.Outlined.ContentCopy,
+ contentDescription = stringResource(R.string.copy),
+ )
+ }
+ }
+ },
+ )
+ }
+ }
}
}
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index fa0bb9f0..672788d3 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -101,7 +101,7 @@
if you are unsure how to proceed
See the
getting started guide
- Restart on boot
+ Start on boot
Permission Denied
VPN system settings
VPN connection permission has been denied. Please check the
@@ -409,5 +409,8 @@
Auto-tunnel and tunnels must be disabled to perform this action.
Dynamic DNS endpoint successfully updated.
Reliability
- Delay between rapid changes
+ Delay between rapid network changes
+ Allows Android to start tunnels
+ Start the default tunnel on boot
+ Remote key