diff --git a/app/src/main/java/com/pyamsoft/tetherfi/info/InfoEntry.kt b/app/src/main/java/com/pyamsoft/tetherfi/info/InfoEntry.kt index 30e96290..ed93f5c2 100644 --- a/app/src/main/java/com/pyamsoft/tetherfi/info/InfoEntry.kt +++ b/app/src/main/java/com/pyamsoft/tetherfi/info/InfoEntry.kt @@ -19,6 +19,7 @@ package com.pyamsoft.tetherfi.info import androidx.activity.ComponentActivity import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import com.pyamsoft.pydroid.arch.SaveStateDisposableEffect import com.pyamsoft.pydroid.ui.inject.ComposableInjector import com.pyamsoft.pydroid.ui.inject.rememberComposableInjector import com.pyamsoft.pydroid.ui.util.rememberNotNull @@ -40,6 +41,14 @@ internal class InfoInjector : ComposableInjector() { } } +/** On mount hooks */ +@Composable +private fun MountHooks( + viewModel: InfoViewModeler, +) { + SaveStateDisposableEffect(viewModel) +} + @Composable fun InfoEntry( modifier: Modifier = Modifier, @@ -52,6 +61,10 @@ fun InfoEntry( val component = rememberComposableInjector { InfoInjector() } val viewModel = rememberNotNull(component.viewModel) + MountHooks( + viewModel = viewModel, + ) + InfoScreen( modifier = modifier, state = viewModel, @@ -61,5 +74,6 @@ fun InfoEntry( onShowQRCode = onShowQRCode, onShowSlowSpeedHelp = onShowSlowSpeedHelp, onTogglePasswordVisibility = { viewModel.handleTogglePasswordVisibility() }, + onToggleShowOptions = { viewModel.handleToggleOptions(it) }, ) } diff --git a/info/src/main/java/com/pyamsoft/tetherfi/info/InfoScreen.kt b/info/src/main/java/com/pyamsoft/tetherfi/info/InfoScreen.kt index 14d82925..546a26b1 100644 --- a/info/src/main/java/com/pyamsoft/tetherfi/info/InfoScreen.kt +++ b/info/src/main/java/com/pyamsoft/tetherfi/info/InfoScreen.kt @@ -53,6 +53,7 @@ fun InfoScreen( onTogglePasswordVisibility: () -> Unit, onShowQRCode: () -> Unit, onShowSlowSpeedHelp: () -> Unit, + onToggleShowOptions: (InfoViewOptionsType) -> Unit, ) { LazyColumn( modifier = modifier, @@ -87,6 +88,7 @@ fun InfoScreen( onTogglePasswordVisibility = onTogglePasswordVisibility, onShowQRCode = onShowQRCode, onShowSlowSpeedHelp = onShowSlowSpeedHelp, + onToggleShowOptions = onToggleShowOptions, ) item( @@ -110,5 +112,6 @@ private fun PreviewInfoScreen() { onTogglePasswordVisibility = {}, onShowQRCode = {}, onShowSlowSpeedHelp = {}, + onToggleShowOptions = {}, ) } diff --git a/info/src/main/java/com/pyamsoft/tetherfi/info/InfoViewModeler.kt b/info/src/main/java/com/pyamsoft/tetherfi/info/InfoViewModeler.kt index a3a2bc46..151dda42 100644 --- a/info/src/main/java/com/pyamsoft/tetherfi/info/InfoViewModeler.kt +++ b/info/src/main/java/com/pyamsoft/tetherfi/info/InfoViewModeler.kt @@ -16,7 +16,9 @@ package com.pyamsoft.tetherfi.info +import androidx.compose.runtime.saveable.SaveableStateRegistry import com.pyamsoft.pydroid.arch.AbstractViewModeler +import com.pyamsoft.pydroid.core.cast import javax.inject.Inject import kotlinx.coroutines.flow.update @@ -26,7 +28,46 @@ internal constructor( override val state: MutableInfoViewState, ) : InfoViewState by state, AbstractViewModeler(state) { + override fun registerSaveState( + registry: SaveableStateRegistry + ): List = + mutableListOf().apply { + registry + .registerProvider(KEY_SHOW_HTTP_OPTIONS) { state.showHttpOptions.value } + .also { add(it) } + + registry + .registerProvider(KEY_SHOW_SOCKS_OPTIONS) { state.showSocksOptions.value } + .also { add(it) } + } + + override fun consumeRestoredState(registry: SaveableStateRegistry) { + registry.consumeRestored(KEY_SHOW_HTTP_OPTIONS)?.cast()?.also { + state.showHttpOptions.value = it + } + + registry.consumeRestored(KEY_SHOW_SOCKS_OPTIONS)?.cast()?.also { + state.showSocksOptions.value = it + } + } + fun handleTogglePasswordVisibility() { state.isPasswordVisible.update { !it } } + + fun handleToggleOptions(type: InfoViewOptionsType) = + when (type) { + InfoViewOptionsType.HTTP -> { + state.showHttpOptions.update { !it } + } + InfoViewOptionsType.SOCKS -> { + state.showSocksOptions.update { !it } + } + } + + companion object { + + private const val KEY_SHOW_HTTP_OPTIONS = "show_http_options" + private const val KEY_SHOW_SOCKS_OPTIONS = "show_socks_options" + } } diff --git a/info/src/main/java/com/pyamsoft/tetherfi/info/InfoViewState.kt b/info/src/main/java/com/pyamsoft/tetherfi/info/InfoViewState.kt index 63912bbd..f0ee7c15 100644 --- a/info/src/main/java/com/pyamsoft/tetherfi/info/InfoViewState.kt +++ b/info/src/main/java/com/pyamsoft/tetherfi/info/InfoViewState.kt @@ -22,12 +22,21 @@ import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +enum class InfoViewOptionsType { + HTTP, + SOCKS, +} + @Stable interface InfoViewState : UiViewState { val isPasswordVisible: StateFlow + val showHttpOptions: StateFlow + val showSocksOptions: StateFlow } @Stable class MutableInfoViewState @Inject internal constructor() : InfoViewState { override val isPasswordVisible = MutableStateFlow(false) + override val showHttpOptions = MutableStateFlow(false) + override val showSocksOptions = MutableStateFlow(false) } diff --git a/info/src/main/java/com/pyamsoft/tetherfi/info/RenderConnectionInstructions.kt b/info/src/main/java/com/pyamsoft/tetherfi/info/RenderConnectionInstructions.kt index 940c7652..5e7c1c8a 100644 --- a/info/src/main/java/com/pyamsoft/tetherfi/info/RenderConnectionInstructions.kt +++ b/info/src/main/java/com/pyamsoft/tetherfi/info/RenderConnectionInstructions.kt @@ -49,6 +49,7 @@ internal fun LazyListScope.renderConnectionInstructions( onShowQRCode: () -> Unit, onTogglePasswordVisibility: () -> Unit, onShowSlowSpeedHelp: () -> Unit, + onToggleShowOptions: (InfoViewOptionsType) -> Unit, ) { item( contentType = ConnectionInstructionContentTypes.SPACER, @@ -92,6 +93,7 @@ internal fun LazyListScope.renderConnectionInstructions( serverViewState = serverViewState, onTogglePasswordVisibility = onTogglePasswordVisibility, onShowQRCode = onShowQRCode, + onToggleShowOptions = onToggleShowOptions, ) item( @@ -129,6 +131,7 @@ private fun PreviewConnectionInstructions(state: InfoViewState, server: TestServ onTogglePasswordVisibility = {}, onShowQRCode = {}, onShowSlowSpeedHelp = {}, + onToggleShowOptions = {}, ) } } diff --git a/info/src/main/java/com/pyamsoft/tetherfi/info/sections/RenderDeviceSetup.kt b/info/src/main/java/com/pyamsoft/tetherfi/info/sections/RenderDeviceSetup.kt index ddb115a7..93a8aadb 100644 --- a/info/src/main/java/com/pyamsoft/tetherfi/info/sections/RenderDeviceSetup.kt +++ b/info/src/main/java/com/pyamsoft/tetherfi/info/sections/RenderDeviceSetup.kt @@ -33,7 +33,6 @@ import androidx.compose.material3.MaterialTheme 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.ui.Alignment import androidx.compose.ui.Modifier @@ -46,6 +45,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.pyamsoft.pydroid.theme.keylines import com.pyamsoft.pydroid.ui.haptics.LocalHapticManager import com.pyamsoft.tetherfi.core.FeatureFlags +import com.pyamsoft.tetherfi.info.InfoViewOptionsType import com.pyamsoft.tetherfi.info.InfoViewState import com.pyamsoft.tetherfi.info.MutableInfoViewState import com.pyamsoft.tetherfi.info.R @@ -77,6 +77,7 @@ internal fun LazyListScope.renderDeviceSetup( serverViewState: ServerViewState, onShowQRCode: () -> Unit, onTogglePasswordVisibility: () -> Unit, + onToggleShowOptions: (InfoViewOptionsType) -> Unit, ) { item( contentType = DeviceSetupContentTypes.SETTINGS, @@ -238,21 +239,22 @@ internal fun LazyListScope.renderDeviceSetup( style = MaterialTheme.typography.bodyLarge, ) - // TODO(Peter): Move into VM scope - val (showHttpOptions, setShowHttpOptions) = remember { mutableStateOf(false) } - val (showSocksOptions, setShowSocksOptions) = remember { mutableStateOf(false) } - Column( modifier = Modifier.padding(top = MaterialTheme.keylines.baseline), ) { + val showHttpOptions by state.showHttpOptions.collectAsStateWithLifecycle() + if (featureFlags.isSocksProxyEnabled) { Row( - modifier = Modifier.clickable { setShowHttpOptions(!showHttpOptions) }, + modifier = Modifier.clickable { onToggleShowOptions(InfoViewOptionsType.HTTP) }, verticalAlignment = Alignment.CenterVertically, ) { Text( text = stringResource(R.string.view_http_options), - style = MaterialTheme.typography.labelLarge, + style = + MaterialTheme.typography.labelLarge.copy( + color = MaterialTheme.colorScheme.onSurfaceVariant, + ), ) Icon( @@ -261,6 +263,7 @@ internal fun LazyListScope.renderDeviceSetup( if (showHttpOptions) Icons.AutoMirrored.Filled.KeyboardArrowRight else Icons.Filled.KeyboardArrowDown, contentDescription = stringResource(R.string.view_http_options), + tint = MaterialTheme.colorScheme.onSurfaceVariant, ) } } @@ -338,13 +341,18 @@ internal fun LazyListScope.renderDeviceSetup( Column( modifier = Modifier.padding(top = MaterialTheme.keylines.baseline), ) { + val showSocksOptions by state.showSocksOptions.collectAsStateWithLifecycle() + Row( - modifier = Modifier.clickable { setShowSocksOptions(!showSocksOptions) }, + modifier = Modifier.clickable { onToggleShowOptions(InfoViewOptionsType.SOCKS) }, verticalAlignment = Alignment.CenterVertically, ) { Text( text = stringResource(R.string.view_socks_options), - style = MaterialTheme.typography.labelLarge, + style = + MaterialTheme.typography.labelLarge.copy( + color = MaterialTheme.colorScheme.onSurfaceVariant, + ), ) Icon( @@ -353,6 +361,7 @@ internal fun LazyListScope.renderDeviceSetup( if (showSocksOptions) Icons.AutoMirrored.Filled.KeyboardArrowRight else Icons.Filled.KeyboardArrowDown, contentDescription = stringResource(R.string.view_http_options), + tint = MaterialTheme.colorScheme.onSurfaceVariant, ) } @@ -441,6 +450,7 @@ private fun PreviewDeviceSetup(state: InfoViewState, server: TestServerState) { state = state, onTogglePasswordVisibility = {}, onShowQRCode = {}, + onToggleShowOptions = {}, ) } }