mirror of
https://github.com/yogeshpaliyal/KeyPass.git
synced 2025-12-31 09:09:52 -06:00
Add password hint field (#970)
* Add password hint field Fixes #957 Add a password hint field to the authentication screen. * **UserSettings.kt**: Add a new property `passwordHint` of type `String?` to the `UserSettings` data class. * **SharedPreferenceUtils.kt**: Add functions `setPasswordHint` and `getPasswordHint` to save and retrieve the password hint in the `UserSettings` data store. * **AuthScreen.kt**: Add a new state variable `passwordHint` to store the password hint. Add an `OutlinedTextField` for the password hint in the `Column` layout. Update the `ButtonBar` call to pass the `passwordHint` state variable. * **ButtonBar.kt**: Add a new parameter `passwordHint` to the `ButtonBar` composable function. Update the `onClick` handler for the `Button` to save the password hint using the `setPasswordHint` function. * **strings.xml**: Add a new string resource `enter_password_hint` with the value "Enter password hint". --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/yogeshpaliyal/KeyPass/issues/957?shareId=XXXX-XXXX-XXXX-XXXX). * feat: add password hint
This commit is contained in:
committed by
GitHub
parent
62ff3d1812
commit
d5e2608159
@@ -48,6 +48,10 @@ fun AuthScreen(state: AuthState) {
|
||||
mutableStateOf<Int?>(null)
|
||||
}
|
||||
|
||||
val (passwordHint, setPasswordHint) = remember(state) {
|
||||
mutableStateOf("")
|
||||
}
|
||||
|
||||
BackHandler(state is AuthState.ConfirmPassword) {
|
||||
dispatchAction(NavigationAction(AuthState.CreatePassword, true))
|
||||
}
|
||||
@@ -99,10 +103,11 @@ fun AuthScreen(state: AuthState) {
|
||||
setPassword = setPassword,
|
||||
passwordVisible = passwordVisible,
|
||||
setPasswordVisible = setPasswordVisible,
|
||||
passwordError = passwordError
|
||||
passwordError = passwordError,
|
||||
hint = if (state is AuthState.Login && userSettings.passwordHint != null) userSettings.passwordHint else null
|
||||
)
|
||||
|
||||
ButtonBar(state, password, setPasswordError) {
|
||||
ButtonBar(state, password, setPasswordError, passwordHint) {
|
||||
dispatchAction(it)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.yogeshpaliyal.common.utils.setKeyPassPassword
|
||||
import com.yogeshpaliyal.common.utils.setPasswordHint
|
||||
import com.yogeshpaliyal.keypass.R
|
||||
import com.yogeshpaliyal.keypass.ui.nav.LocalUserSettings
|
||||
import com.yogeshpaliyal.keypass.ui.redux.actions.NavigationAction
|
||||
@@ -27,6 +28,7 @@ fun ButtonBar(
|
||||
state: AuthState,
|
||||
password: String,
|
||||
setPasswordError: (Int?) -> Unit,
|
||||
passwordHint: String, // New parameter for password hint
|
||||
dispatchAction: (NavigationAction) -> Unit
|
||||
) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
@@ -69,6 +71,7 @@ fun ButtonBar(
|
||||
if (state.password == password) {
|
||||
coroutineScope.launch {
|
||||
context.setKeyPassPassword(password)
|
||||
context.setPasswordHint(passwordHint) // Save the password hint
|
||||
dispatchAction(NavigationAction(HomeState(), true))
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.yogeshpaliyal.keypass.ui.auth.components
|
||||
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Info
|
||||
import androidx.compose.material.icons.rounded.Visibility
|
||||
import androidx.compose.material.icons.rounded.VisibilityOff
|
||||
import androidx.compose.material3.Icon
|
||||
@@ -12,11 +14,15 @@ import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import com.yogeshpaliyal.keypass.R
|
||||
import com.yogeshpaliyal.keypass.ui.redux.actions.Action
|
||||
import com.yogeshpaliyal.keypass.ui.redux.actions.ToastActionStr
|
||||
import org.reduxkotlin.compose.rememberTypedDispatcher
|
||||
|
||||
@Composable
|
||||
fun PasswordInputField(
|
||||
@@ -25,8 +31,12 @@ fun PasswordInputField(
|
||||
setPassword: (String) -> Unit,
|
||||
passwordVisible: Boolean,
|
||||
setPasswordVisible: (Boolean) -> Unit,
|
||||
passwordError: Int?
|
||||
passwordError: Int?,
|
||||
hint: String? = null
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val dispatchAction = rememberTypedDispatcher<Action>()
|
||||
|
||||
OutlinedTextField(
|
||||
modifier = Modifier.fillMaxWidth(1f),
|
||||
value = password,
|
||||
@@ -54,9 +64,17 @@ fun PasswordInputField(
|
||||
|
||||
// Please provide localized description for accessibility services
|
||||
val description = if (passwordVisible) "Hide password" else "Show password"
|
||||
|
||||
IconButton(onClick = { setPasswordVisible(!passwordVisible) }) {
|
||||
Icon(imageVector = image, description)
|
||||
Row {
|
||||
IconButton(onClick = { setPasswordVisible(!passwordVisible) }) {
|
||||
Icon(imageVector = image, description)
|
||||
}
|
||||
hint?.let {
|
||||
IconButton(onClick = {
|
||||
dispatchAction(ToastActionStr(hint))
|
||||
}) {
|
||||
Icon(imageVector = Icons.Outlined.Info, "Hint")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -35,6 +35,7 @@ import com.yogeshpaliyal.keypass.ui.generate.ui.GeneratePasswordScreen
|
||||
import com.yogeshpaliyal.keypass.ui.home.Homepage
|
||||
import com.yogeshpaliyal.keypass.ui.nav.components.DashboardBottomSheet
|
||||
import com.yogeshpaliyal.keypass.ui.nav.components.KeyPassBottomBar
|
||||
import com.yogeshpaliyal.keypass.ui.passwordHint.PasswordHintScreen
|
||||
import com.yogeshpaliyal.keypass.ui.redux.KeyPassRedux
|
||||
import com.yogeshpaliyal.keypass.ui.redux.actions.GoBackAction
|
||||
import com.yogeshpaliyal.keypass.ui.redux.actions.UpdateContextAction
|
||||
@@ -43,6 +44,7 @@ import com.yogeshpaliyal.keypass.ui.redux.states.AccountDetailState
|
||||
import com.yogeshpaliyal.keypass.ui.redux.states.AuthState
|
||||
import com.yogeshpaliyal.keypass.ui.redux.states.BackupImporterState
|
||||
import com.yogeshpaliyal.keypass.ui.redux.states.BackupScreenState
|
||||
import com.yogeshpaliyal.keypass.ui.redux.states.ChangeAppHintState
|
||||
import com.yogeshpaliyal.keypass.ui.redux.states.ChangeAppPasswordState
|
||||
import com.yogeshpaliyal.keypass.ui.redux.states.ChangeDefaultPasswordLengthState
|
||||
import com.yogeshpaliyal.keypass.ui.redux.states.HomeState
|
||||
@@ -172,6 +174,7 @@ fun CurrentPage() {
|
||||
is BackupImporterState -> BackupImporter(state = it)
|
||||
is AboutState -> AboutScreen()
|
||||
is PasswordGeneratorState -> GeneratePasswordScreen()
|
||||
ChangeAppHintState -> PasswordHintScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.yogeshpaliyal.keypass.ui.passwordHint
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.yogeshpaliyal.common.utils.setPasswordHint
|
||||
import com.yogeshpaliyal.keypass.R
|
||||
import com.yogeshpaliyal.keypass.ui.commonComponents.DefaultBottomAppBar
|
||||
import com.yogeshpaliyal.keypass.ui.commonComponents.KeyPassInputField
|
||||
import com.yogeshpaliyal.keypass.ui.nav.LocalUserSettings
|
||||
import com.yogeshpaliyal.keypass.ui.redux.actions.Action
|
||||
import com.yogeshpaliyal.keypass.ui.redux.actions.GoBackAction
|
||||
import com.yogeshpaliyal.keypass.ui.redux.actions.ToastAction
|
||||
import kotlinx.coroutines.launch
|
||||
import org.reduxkotlin.compose.rememberTypedDispatcher
|
||||
|
||||
@Composable
|
||||
fun PasswordHintScreen() {
|
||||
val dispatchAction = rememberTypedDispatcher<Action>()
|
||||
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val context = LocalContext.current
|
||||
val userSettings = LocalUserSettings.current
|
||||
val (passwordHint, setPasswordHint) = rememberSaveable { mutableStateOf(userSettings.passwordHint) }
|
||||
|
||||
Scaffold(bottomBar = { DefaultBottomAppBar() }) { contentPadding ->
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.padding(contentPadding)
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Column(modifier = Modifier.fillMaxSize(1f)) {
|
||||
KeyPassInputField(
|
||||
placeholder = R.string.enter_password_hint,
|
||||
value = passwordHint,
|
||||
setValue = setPasswordHint
|
||||
)
|
||||
Button(modifier = Modifier.fillMaxWidth(1f), onClick = {
|
||||
coroutineScope.launch {
|
||||
context.setPasswordHint(passwordHint)
|
||||
dispatchAction(ToastAction(R.string.hint_change_success))
|
||||
dispatchAction(GoBackAction)
|
||||
}
|
||||
}) {
|
||||
Text(text = stringResource(id = R.string.change_app_hint))
|
||||
}
|
||||
|
||||
OutlinedButton(modifier = Modifier.fillMaxWidth(1f), onClick = {
|
||||
coroutineScope.launch {
|
||||
context.setPasswordHint(null)
|
||||
dispatchAction(ToastAction(R.string.hint_removed_success))
|
||||
dispatchAction(GoBackAction)
|
||||
}
|
||||
}) {
|
||||
Text(text = stringResource(id = R.string.remove_app_hint))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun PPasswordHintScreen() {
|
||||
PasswordHintScreen()
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package com.yogeshpaliyal.keypass.ui.redux.states
|
||||
|
||||
object ChangeAppHintState : ScreenState(false)
|
||||
@@ -19,6 +19,7 @@ import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Info
|
||||
import androidx.compose.material.icons.rounded.Feedback
|
||||
import androidx.compose.material.icons.rounded.Fingerprint
|
||||
import androidx.compose.material.icons.rounded.Info
|
||||
import androidx.compose.material.icons.rounded.Password
|
||||
import androidx.compose.material.icons.rounded.Share
|
||||
import androidx.compose.material3.Divider
|
||||
@@ -50,6 +51,7 @@ import com.yogeshpaliyal.keypass.ui.redux.actions.ToastAction
|
||||
import com.yogeshpaliyal.keypass.ui.redux.states.AboutState
|
||||
import com.yogeshpaliyal.keypass.ui.redux.states.BackupImporterState
|
||||
import com.yogeshpaliyal.keypass.ui.redux.states.BackupScreenState
|
||||
import com.yogeshpaliyal.keypass.ui.redux.states.ChangeAppHintState
|
||||
import com.yogeshpaliyal.keypass.ui.redux.states.ChangeAppPasswordState
|
||||
import com.yogeshpaliyal.keypass.ui.redux.states.ChangeDefaultPasswordLengthState
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -90,6 +92,15 @@ fun MySettingCompose() {
|
||||
) {
|
||||
dispatchAction(NavigationAction(ChangeAppPasswordState()))
|
||||
}
|
||||
|
||||
PreferenceItem(
|
||||
title = R.string.app_password_hint,
|
||||
summary = if (userSettings.passwordHint != null) R.string.change_app_password_hint else R.string.set_app_password_hint,
|
||||
icon = Icons.Outlined.Info
|
||||
) {
|
||||
dispatchAction(NavigationAction(ChangeAppHintState))
|
||||
}
|
||||
|
||||
val changePasswordLengthSummary = context.getString(R.string.default_password_length)
|
||||
PreferenceItem(
|
||||
title = R.string.change_password_length,
|
||||
|
||||
@@ -87,6 +87,11 @@
|
||||
<string name="password_no_match">The password did not match</string>
|
||||
<string name="auto_backup_desc">Your accounts will be backed up whenever account is added or modified</string>
|
||||
<string name="change_app_password">Change App Password</string>
|
||||
<string name="change_app_hint">Change Hint</string>
|
||||
<string name="remove_app_hint">Remove Hint</string>
|
||||
<string name="app_password_hint">App Password Hint</string>
|
||||
<string name="set_app_password_hint">Set App Password Hint</string>
|
||||
<string name="change_app_password_hint">Change App Password Hint</string>
|
||||
<string name="old_password">Old Password</string>
|
||||
<string name="new_password">New Password</string>
|
||||
<string name="blank_old_password">Please enter your old password</string>
|
||||
@@ -94,6 +99,8 @@
|
||||
<string name="blank_new_password">Please enter your new password</string>
|
||||
<string name="blank_confirm_password">Please enter confirm password</string>
|
||||
<string name="password_change_success">Password changed successfully</string>
|
||||
<string name="hint_change_success">Hint changed successfully</string>
|
||||
<string name="hint_removed_success">Hint removed successfully</string>
|
||||
|
||||
<string name="biometric_error_no_hardware">No biometric features available on this device.</string>
|
||||
<string name="biometric_error_hw_unavailable">Biometric features are currently unavailable.</string>
|
||||
@@ -113,5 +120,6 @@
|
||||
<string name="invalid_secret_key">Invalid secret key format</string>
|
||||
<string name="invalid_csv_file">Invalid CSV File</string>
|
||||
<string name="file_not_found">File not found (please try again)</string>
|
||||
<string name="enter_password_hint">Enter password hint</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -20,7 +20,8 @@ data class UserSettings(
|
||||
val overrideAutoBackup: Boolean = false,
|
||||
val lastAppVersion: Int? = null,
|
||||
val currentAppVersion: Int? = null,
|
||||
val passwordConfig: PasswordConfig = PasswordConfig.Initial
|
||||
val passwordConfig: PasswordConfig = PasswordConfig.Initial,
|
||||
val passwordHint: String? = null
|
||||
) {
|
||||
fun isKeyPresent() = backupKey != null
|
||||
}
|
||||
|
||||
@@ -15,13 +15,6 @@ import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
|
||||
/*
|
||||
* @author Yogesh Paliyal
|
||||
* techpaliyal@gmail.com
|
||||
* https://techpaliyal.com
|
||||
* created on 21-02-2021 11:18
|
||||
*/
|
||||
|
||||
val Context.dataStore by preferencesDataStore(
|
||||
name = "settings"
|
||||
)
|
||||
@@ -136,6 +129,16 @@ suspend fun Context.getOrCreateBackupKey(reset: Boolean = false): Pair<Boolean,
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun Context.setPasswordHint(passwordHint: String?) {
|
||||
getUserSettingsDataStore().updateData {
|
||||
it.copy(passwordHint = passwordHint)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun Context.getPasswordHint(): String? {
|
||||
return getUserSettings().passwordHint
|
||||
}
|
||||
|
||||
private val BACKUP_KEY = stringPreferencesKey("backup_key")
|
||||
private val BIOMETRIC_ENABLE = booleanPreferencesKey("biometric_enable")
|
||||
private val KEYPASS_PASSWORD = stringPreferencesKey("keypass_password")
|
||||
|
||||
Reference in New Issue
Block a user