refactor: remove nav ripple on mobile, improve key ui

This commit is contained in:
Zane Schepke
2025-10-17 03:43:41 -04:00
parent 2cc71e657b
commit ff5331bea9
4 changed files with 87 additions and 53 deletions
@@ -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) {
@@ -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)
@@ -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),
)
}
}
},
)
}
}
}
}
}
+5 -2
View File
@@ -101,7 +101,7 @@
<string name="unsure_how">if you are unsure how to proceed</string>
<string name="see_the">See the</string>
<string name="getting_started_guide">getting started guide</string>
<string name="restart_at_boot">Restart on boot</string>
<string name="restart_at_boot">Start on boot</string>
<string name="vpn_denied_dialog_title">Permission Denied</string>
<string name="vpn_settings">VPN system settings</string>
<string name="always_on_message">VPN connection permission has been denied. Please check the</string>
@@ -409,5 +409,8 @@
<string name="all_services_disabled">Auto-tunnel and tunnels must be disabled to perform this action.</string>
<string name="ddns_success_message">Dynamic DNS endpoint successfully updated.</string>
<string name="reliability">Reliability</string>
<string name="debounce_description">Delay between rapid changes</string>
<string name="debounce_description">Delay between rapid network changes</string>
<string name="aovpn_description">Allows Android to start tunnels</string>
<string name="tunnel_boot_description">Start the default tunnel on boot</string>
<string name="remote_key">Remote key</string>
</resources>