feat: masking api keys

Fixes #901
This commit is contained in:
tapframe 2026-05-05 13:18:41 +05:30
parent 42247c1d57
commit 1af3cfeded
5 changed files with 85 additions and 47 deletions

View file

@ -478,6 +478,7 @@
<string name="settings_homescreen_summary">%1$d of %2$d catalogs visible • %3$d hero sources selected</string>
<string name="settings_homescreen_summary_hint">Open a catalog only when you need to rename or reorder it.</string>
<string name="settings_homescreen_visible">Visible</string>
<string name="settings_hide_secret">Hide value</string>
<string name="settings_playback_subtitle">Player, subtitles, and auto-play</string>
<string name="settings_poster_card_radius">Corner Radius</string>
<string name="settings_poster_card_style">Poster Card Style</string>
@ -502,6 +503,7 @@
<string name="settings_poster_width_dense">Dense</string>
<string name="settings_poster_width_large">Large</string>
<string name="settings_poster_width_standard">Standard</string>
<string name="settings_show_secret">Show value</string>
<string name="settings_continue_watching_resume_prompt_description">Show a popup to continue where you left off when opening the app after leaving from the player.</string>
<string name="settings_continue_watching_resume_prompt_title">Resume prompt on launch</string>
<string name="settings_continue_watching_section_card_style">Poster Card Style</string>

View file

@ -6,11 +6,8 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@ -19,7 +16,6 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import com.nuvio.app.features.mdblist.MdbListMetadataService
import com.nuvio.app.features.mdblist.MdbListSettings
@ -170,22 +166,13 @@ private fun MdbListApiKeyRow(
)
}
OutlinedTextField(
SettingsSecretTextField(
value = draft,
onValueChange = {
draft = it
},
modifier = Modifier.fillMaxWidth(),
singleLine = true,
label = { Text(stringResource(Res.string.settings_mdb_api_key_label)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
colors = OutlinedTextFieldDefaults.colors(
focusedBorderColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.75f),
unfocusedBorderColor = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.42f),
focusedContainerColor = MaterialTheme.colorScheme.surface,
unfocusedContainerColor = MaterialTheme.colorScheme.surface,
disabledContainerColor = MaterialTheme.colorScheme.surface,
),
label = stringResource(Res.string.settings_mdb_api_key_label),
)
Row(modifier = Modifier.fillMaxWidth()) {

View file

@ -1960,27 +1960,16 @@ private fun IntroDbApiKeyDialog(
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
Surface(
shape = RoundedCornerShape(12.dp),
color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f),
border = BorderStroke(1.dp, MaterialTheme.colorScheme.outline.copy(alpha = if (errorMessage != null) 1f else 0.3f)),
) {
BasicTextField(
value = value,
onValueChange = {
value = it
errorMessage = null
},
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 14.dp, vertical = 12.dp),
textStyle = MaterialTheme.typography.bodyLarge.copy(
color = MaterialTheme.colorScheme.onSurface,
),
cursorBrush = SolidColor(MaterialTheme.colorScheme.primary),
singleLine = true,
)
}
SettingsSecretTextField(
value = value,
onValueChange = {
value = it
errorMessage = null
},
label = stringResource(Res.string.settings_playback_introdb_api_key),
modifier = Modifier.fillMaxWidth(),
isError = errorMessage != null,
)
if (errorMessage != null) {
Text(
text = errorMessage!!,
@ -2162,4 +2151,3 @@ private fun libassRenderTypeRes(renderType: String): StringResource = when (rend
@Composable
private fun libassRenderTypeLabel(renderType: String): String = stringResource(libassRenderTypeRes(renderType))

View file

@ -0,0 +1,69 @@
package com.nuvio.app.features.settings
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Visibility
import androidx.compose.material.icons.rounded.VisibilityOff
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import nuvio.composeapp.generated.resources.Res
import nuvio.composeapp.generated.resources.settings_hide_secret
import nuvio.composeapp.generated.resources.settings_show_secret
import org.jetbrains.compose.resources.stringResource
@Composable
internal fun SettingsSecretTextField(
value: String,
onValueChange: (String) -> Unit,
label: String,
modifier: Modifier = Modifier,
isError: Boolean = false,
) {
var visible by rememberSaveable { mutableStateOf(false) }
OutlinedTextField(
value = value,
onValueChange = onValueChange,
modifier = modifier,
isError = isError,
singleLine = true,
label = { Text(label) },
visualTransformation = if (visible) {
VisualTransformation.None
} else {
PasswordVisualTransformation()
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
trailingIcon = {
IconButton(onClick = { visible = !visible }) {
Icon(
imageVector = if (visible) Icons.Rounded.VisibilityOff else Icons.Rounded.Visibility,
contentDescription = stringResource(
if (visible) Res.string.settings_hide_secret else Res.string.settings_show_secret,
),
tint = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
},
colors = OutlinedTextFieldDefaults.colors(
focusedBorderColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.75f),
unfocusedBorderColor = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.42f),
focusedContainerColor = MaterialTheme.colorScheme.surface,
unfocusedContainerColor = MaterialTheme.colorScheme.surface,
disabledContainerColor = MaterialTheme.colorScheme.surface,
),
)
}

View file

@ -265,21 +265,13 @@ private fun TmdbApiKeyRow(
val normalizedDraft = draft.trim()
OutlinedTextField(
SettingsSecretTextField(
value = draft,
onValueChange = {
draft = it
},
modifier = Modifier.fillMaxWidth(),
singleLine = true,
label = { Text(stringResource(Res.string.settings_tmdb_api_key_label)) },
colors = OutlinedTextFieldDefaults.colors(
focusedBorderColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.75f),
unfocusedBorderColor = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.42f),
focusedContainerColor = MaterialTheme.colorScheme.surface,
unfocusedContainerColor = MaterialTheme.colorScheme.surface,
disabledContainerColor = MaterialTheme.colorScheme.surface,
),
label = stringResource(Res.string.settings_tmdb_api_key_label),
)
Row(modifier = Modifier.fillMaxWidth()) {