From 892f65fc10bb8619b1eae8b74c2b83c05fa8af61 Mon Sep 17 00:00:00 2001 From: Yogesh Choudhary Paliyal Date: Sun, 2 Jun 2024 16:27:31 +0530 Subject: [PATCH] Add password config in account detail (#861) * feat: WIP password configs from AccountDetail * feat: spotless fixes --- .../ChangeDefaultPasswordLengthViewModel.kt | 3 +- .../keypass/ui/detail/AccountDetailPage.kt | 23 ++++++++------- .../keypass/ui/detail/DetailViewModel.kt | 20 +++++++++---- .../keypass/ui/detail/components/BottomBar.kt | 13 +++++++++ .../keypass/ui/detail/components/Fields.kt | 5 +++- .../ui/generate/GeneratePasswordActivity.kt | 1 - .../ui/generate/GeneratePasswordViewModel.kt | 21 ++++++-------- .../ui/generate/GeneratePasswordViewState.kt | 2 ++ .../ui/generate/ui/GeneratePasswordContent.kt | 8 +++--- .../ui/generate/ui/GeneratePasswordScreen.kt | 12 ++++---- .../ui/nav/DashboardComposeActivity.kt | 3 ++ .../ui/redux/states/PasswordGeneratorState.kt | 3 ++ .../keypass/ui/settings/MySettingsFragment.kt | 2 +- .../keypass/SharedPreferenceUtilsTest.kt | 4 +-- .../common/data/PasswordConfig.kt | 28 +++++++++++++++++++ .../yogeshpaliyal/common/data/UserSettings.kt | 4 ++- .../common/utils/PasswordGenerator.kt | 26 ++++++++--------- .../common/utils/SharedPreferenceUtils.kt | 13 ++++++++- 18 files changed, 126 insertions(+), 65 deletions(-) create mode 100644 app/src/main/java/com/yogeshpaliyal/keypass/ui/redux/states/PasswordGeneratorState.kt create mode 100644 common/src/main/java/com/yogeshpaliyal/common/data/PasswordConfig.kt diff --git a/app/src/main/java/com/yogeshpaliyal/keypass/ui/changeDefaultPasswordLength/ChangeDefaultPasswordLengthViewModel.kt b/app/src/main/java/com/yogeshpaliyal/keypass/ui/changeDefaultPasswordLength/ChangeDefaultPasswordLengthViewModel.kt index 6553e10b..b4a48d21 100644 --- a/app/src/main/java/com/yogeshpaliyal/keypass/ui/changeDefaultPasswordLength/ChangeDefaultPasswordLengthViewModel.kt +++ b/app/src/main/java/com/yogeshpaliyal/keypass/ui/changeDefaultPasswordLength/ChangeDefaultPasswordLengthViewModel.kt @@ -5,7 +5,6 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.yogeshpaliyal.common.utils.getUserSettings import com.yogeshpaliyal.common.utils.setDefaultPasswordLength -import com.yogeshpaliyal.keypass.ui.generate.ui.components.DEFAULT_PASSWORD_LENGTH import com.yogeshpaliyal.keypass.ui.redux.states.ChangeDefaultPasswordLengthState import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -24,7 +23,7 @@ class ChangeDefaultPasswordLengthViewModel : ViewModel() { viewModelScope.launch { _viewState.update { val oldPasswordLength = - context.getUserSettings().defaultPasswordLength ?: DEFAULT_PASSWORD_LENGTH + context.getUserSettings().passwordConfig.length ChangeDefaultPasswordLengthState(length = oldPasswordLength) } } diff --git a/app/src/main/java/com/yogeshpaliyal/keypass/ui/detail/AccountDetailPage.kt b/app/src/main/java/com/yogeshpaliyal/keypass/ui/detail/AccountDetailPage.kt index 2dd8173b..ffeced1a 100644 --- a/app/src/main/java/com/yogeshpaliyal/keypass/ui/detail/AccountDetailPage.kt +++ b/app/src/main/java/com/yogeshpaliyal/keypass/ui/detail/AccountDetailPage.kt @@ -17,6 +17,7 @@ import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment @@ -29,12 +30,13 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.hilt.navigation.compose.hiltViewModel import com.yogeshpaliyal.common.constants.ScannerType -import com.yogeshpaliyal.common.data.AccountModel import com.yogeshpaliyal.common.utils.TOTPHelper import com.yogeshpaliyal.keypass.ui.detail.components.BottomBar import com.yogeshpaliyal.keypass.ui.detail.components.Fields import com.yogeshpaliyal.keypass.ui.redux.actions.CopyToClipboard import com.yogeshpaliyal.keypass.ui.redux.actions.GoBackAction +import com.yogeshpaliyal.keypass.ui.redux.actions.NavigationAction +import com.yogeshpaliyal.keypass.ui.redux.states.PasswordGeneratorState import org.reduxkotlin.compose.rememberDispatcher import java.io.File import java.io.FileOutputStream @@ -60,17 +62,11 @@ fun AccountDetailPage( val showDialog = remember { mutableStateOf(false) } // task value state - val (accountModel, setAccountModel) = remember { - mutableStateOf( - AccountModel() - ) - } + val accountModel = viewModel.accountModel.collectAsState().value // Set initial object LaunchedEffect(key1 = id) { - viewModel.loadAccount(id) { - setAccountModel(it.copy()) - } + viewModel.loadAccount(id) } val goBack: () -> Unit = { @@ -80,7 +76,7 @@ fun AccountDetailPage( val launcher = rememberLauncherForActivityResult(QRScanner()) { when (it.type) { ScannerType.Password -> { - setAccountModel(accountModel.copy(password = it.scannedText)) + viewModel.setAccountModel(accountModel.copy(password = it.scannedText)) } ScannerType.Secret -> { it.scannedText ?: return@rememberLauncherForActivityResult @@ -94,7 +90,7 @@ fun AccountDetailPage( if (newAccountModel.username.isNullOrEmpty()) { newAccountModel = newAccountModel.copy(username = totp.issuer) } - setAccountModel(newAccountModel) + viewModel.setAccountModel(newAccountModel) } } } @@ -111,6 +107,9 @@ fun AccountDetailPage( val qrCodeBitmap = viewModel.generateQrCode(accountModel) generatedQrCodeBitmap.value = qrCodeBitmap + }, + openPasswordConfiguration = { + dispatchAction(NavigationAction(PasswordGeneratorState())) } ) { viewModel.insertOrUpdate(accountModel, goBack) @@ -121,7 +120,7 @@ fun AccountDetailPage( Fields( accountModel = accountModel, updateAccountModel = { newAccountModel -> - setAccountModel(newAccountModel) + viewModel.setAccountModel(newAccountModel) }, copyToClipboardClicked = { value -> dispatchAction(CopyToClipboard(value)) diff --git a/app/src/main/java/com/yogeshpaliyal/keypass/ui/detail/DetailViewModel.kt b/app/src/main/java/com/yogeshpaliyal/keypass/ui/detail/DetailViewModel.kt index 2d0e8edf..1de84a9c 100644 --- a/app/src/main/java/com/yogeshpaliyal/keypass/ui/detail/DetailViewModel.kt +++ b/app/src/main/java/com/yogeshpaliyal/keypass/ui/detail/DetailViewModel.kt @@ -3,8 +3,6 @@ package com.yogeshpaliyal.keypass.ui.detail import android.app.Application import android.graphics.Bitmap import androidx.lifecycle.AndroidViewModel -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import com.google.gson.Gson import com.google.zxing.BarcodeFormat @@ -16,6 +14,8 @@ import com.yogeshpaliyal.common.data.AccountModel import com.yogeshpaliyal.common.worker.executeAutoBackup import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.util.* @@ -33,15 +33,23 @@ class DetailViewModel @Inject constructor( val appDb: com.yogeshpaliyal.common.AppDatabase ) : AndroidViewModel(app) { - private val _accountModel by lazy { MutableLiveData() } - val accountModel: LiveData = _accountModel + private val _accountModel by lazy { MutableStateFlow(AccountModel()) } + val accountModel: StateFlow = _accountModel - fun loadAccount(id: Long?, getAccount: (AccountModel) -> Unit) { + fun loadAccount(id: Long?) { viewModelScope.launch(Dispatchers.IO) { - getAccount(appDb.getDao().getAccount(id) ?: AccountModel()) + if (id == null) { + _accountModel.emit(AccountModel()) + } else { + _accountModel.emit(appDb.getDao().getAccount(id) ?: AccountModel()) + } } } + fun setAccountModel(accountModel: AccountModel) { + _accountModel.value = accountModel + } + fun deleteAccount(accountModel: AccountModel, onExecCompleted: () -> Unit) { viewModelScope.launch { accountModel.let { diff --git a/app/src/main/java/com/yogeshpaliyal/keypass/ui/detail/components/BottomBar.kt b/app/src/main/java/com/yogeshpaliyal/keypass/ui/detail/components/BottomBar.kt index 0dcda2d3..09ce4d6a 100644 --- a/app/src/main/java/com/yogeshpaliyal/keypass/ui/detail/components/BottomBar.kt +++ b/app/src/main/java/com/yogeshpaliyal/keypass/ui/detail/components/BottomBar.kt @@ -1,6 +1,7 @@ package com.yogeshpaliyal.keypass.ui.detail.components import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Password import androidx.compose.material.icons.filled.QrCode import androidx.compose.material.icons.rounded.ArrowBackIosNew import androidx.compose.material.icons.rounded.Delete @@ -24,6 +25,7 @@ fun BottomBar( backPressed: () -> Unit, onDeleteAccount: () -> Unit, generateQrCodeClicked: () -> Unit, + openPasswordConfiguration: () -> Unit, onSaveClicked: () -> Unit ) { val openDialog = remember { mutableStateOf(false) } @@ -38,6 +40,17 @@ fun BottomBar( ) } + IconButton( + modifier = Modifier.testTag("action_configure_password"), + onClick = { openPasswordConfiguration() } + ) { + Icon( + painter = rememberVectorPainter(image = Icons.Default.Password), + contentDescription = "Open Password Configuration", + tint = MaterialTheme.colorScheme.onSurface + ) + } + if (accountModel.id != null) { IconButton( modifier = Modifier.testTag("action_delete"), diff --git a/app/src/main/java/com/yogeshpaliyal/keypass/ui/detail/components/Fields.kt b/app/src/main/java/com/yogeshpaliyal/keypass/ui/detail/components/Fields.kt index 2e301557..fdce96d1 100644 --- a/app/src/main/java/com/yogeshpaliyal/keypass/ui/detail/components/Fields.kt +++ b/app/src/main/java/com/yogeshpaliyal/keypass/ui/detail/components/Fields.kt @@ -30,6 +30,7 @@ import com.yogeshpaliyal.common.utils.PasswordGenerator import com.yogeshpaliyal.keypass.R import com.yogeshpaliyal.keypass.ui.commonComponents.KeyPassInputField import com.yogeshpaliyal.keypass.ui.commonComponents.PasswordTrailingIcon +import com.yogeshpaliyal.keypass.ui.nav.LocalUserSettings @Composable fun Fields( @@ -39,6 +40,8 @@ fun Fields( copyToClipboardClicked: (String) -> Unit, scanClicked: (scannerType: Int) -> Unit ) { + val passwordConfig = LocalUserSettings.current.passwordConfig + Column( modifier = modifier .fillMaxSize() @@ -90,7 +93,7 @@ fun Fields( { IconButton( onClick = { - updateAccountModel(accountModel.copy(password = PasswordGenerator().generatePassword())) + updateAccountModel(accountModel.copy(password = PasswordGenerator(passwordConfig).generatePassword())) } ) { Icon( diff --git a/app/src/main/java/com/yogeshpaliyal/keypass/ui/generate/GeneratePasswordActivity.kt b/app/src/main/java/com/yogeshpaliyal/keypass/ui/generate/GeneratePasswordActivity.kt index c8506bd2..5b15a798 100644 --- a/app/src/main/java/com/yogeshpaliyal/keypass/ui/generate/GeneratePasswordActivity.kt +++ b/app/src/main/java/com/yogeshpaliyal/keypass/ui/generate/GeneratePasswordActivity.kt @@ -14,7 +14,6 @@ class GeneratePasswordActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - viewModel.retrieveSavedPasswordLength(baseContext) setContent { Mdc3Theme { GeneratePasswordScreen(viewModel) diff --git a/app/src/main/java/com/yogeshpaliyal/keypass/ui/generate/GeneratePasswordViewModel.kt b/app/src/main/java/com/yogeshpaliyal/keypass/ui/generate/GeneratePasswordViewModel.kt index 3dfac22b..642a8963 100644 --- a/app/src/main/java/com/yogeshpaliyal/keypass/ui/generate/GeneratePasswordViewModel.kt +++ b/app/src/main/java/com/yogeshpaliyal/keypass/ui/generate/GeneratePasswordViewModel.kt @@ -3,10 +3,10 @@ package com.yogeshpaliyal.keypass.ui.generate import android.content.Context import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.yogeshpaliyal.common.data.PasswordConfig import com.yogeshpaliyal.common.utils.PasswordGenerator import com.yogeshpaliyal.common.utils.getUserSettings -import com.yogeshpaliyal.common.utils.setDefaultPasswordLength -import com.yogeshpaliyal.keypass.ui.generate.ui.components.DEFAULT_PASSWORD_LENGTH +import com.yogeshpaliyal.common.utils.setPasswordConfig import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.FlowPreview @@ -23,18 +23,18 @@ class GeneratePasswordViewModel @Inject constructor( @ApplicationContext context: Context ) : ViewModel() { - private val _viewState = MutableStateFlow(GeneratePasswordViewState.Initial) + private val _viewState = MutableStateFlow(PasswordConfig.Initial) val viewState = _viewState.asStateFlow() init { observeState(context) } - fun retrieveSavedPasswordLength(context: Context) { + fun retrieveSavedPasswordConfig(context: Context) { viewModelScope.launch { - val savedPasswordLength = context.getUserSettings().defaultPasswordLength ?: DEFAULT_PASSWORD_LENGTH + val passwordConfig = context.getUserSettings().passwordConfig _viewState.update { - _viewState.value.copy(length = savedPasswordLength) + passwordConfig } } } @@ -43,12 +43,7 @@ class GeneratePasswordViewModel @Inject constructor( val currentViewState = _viewState.value val passwordGenerator = PasswordGenerator( - length = currentViewState.length.toInt(), - includeUpperCaseLetters = currentViewState.includeUppercaseLetters, - includeLowerCaseLetters = currentViewState.includeLowercaseLetters, - includeSymbols = currentViewState.includeSymbols, - includeNumbers = currentViewState.includeNumbers, - includeBlankSpaces = currentViewState.includeBlankSpaces + currentViewState ) _viewState.update { @@ -100,7 +95,7 @@ class GeneratePasswordViewModel @Inject constructor( _viewState .debounce(400) .collectLatest { state -> - context.setDefaultPasswordLength(state.length) + context.setPasswordConfig(state) } } } diff --git a/app/src/main/java/com/yogeshpaliyal/keypass/ui/generate/GeneratePasswordViewState.kt b/app/src/main/java/com/yogeshpaliyal/keypass/ui/generate/GeneratePasswordViewState.kt index 29f3e175..7d7c60de 100644 --- a/app/src/main/java/com/yogeshpaliyal/keypass/ui/generate/GeneratePasswordViewState.kt +++ b/app/src/main/java/com/yogeshpaliyal/keypass/ui/generate/GeneratePasswordViewState.kt @@ -1,7 +1,9 @@ package com.yogeshpaliyal.keypass.ui.generate +import androidx.annotation.Keep import com.yogeshpaliyal.keypass.ui.generate.ui.components.DEFAULT_PASSWORD_LENGTH +@Keep data class GeneratePasswordViewState( val length: Float, val includeUppercaseLetters: Boolean, diff --git a/app/src/main/java/com/yogeshpaliyal/keypass/ui/generate/ui/GeneratePasswordContent.kt b/app/src/main/java/com/yogeshpaliyal/keypass/ui/generate/ui/GeneratePasswordContent.kt index e2af9c03..d9e11af3 100644 --- a/app/src/main/java/com/yogeshpaliyal/keypass/ui/generate/ui/GeneratePasswordContent.kt +++ b/app/src/main/java/com/yogeshpaliyal/keypass/ui/generate/ui/GeneratePasswordContent.kt @@ -23,13 +23,13 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.google.accompanist.themeadapter.material3.Mdc3Theme -import com.yogeshpaliyal.keypass.ui.generate.GeneratePasswordViewState +import com.yogeshpaliyal.common.data.PasswordConfig import com.yogeshpaliyal.keypass.ui.generate.ui.components.CheckboxWithLabel import com.yogeshpaliyal.keypass.ui.generate.ui.components.PasswordLengthInput @Composable fun GeneratePasswordContent( - viewState: GeneratePasswordViewState, + viewState: PasswordConfig, onCopyPasswordClick: () -> Unit, onGeneratePasswordClick: () -> Unit, onPasswordLengthChange: (Float) -> Unit, @@ -77,7 +77,7 @@ private fun GeneratePasswordFab(onGeneratePasswordClick: () -> Unit) { @Composable private fun FormInputCard( - viewState: GeneratePasswordViewState, + viewState: PasswordConfig, onCopyPasswordClick: () -> Unit, onPasswordLengthChange: (Float) -> Unit, onUppercaseCheckedChange: (Boolean) -> Unit, @@ -211,7 +211,7 @@ private fun BlankSpaceInput( @Composable @Suppress("UnusedPrivateMember", "MagicNumber") private fun GeneratePasswordContentPreview() { - val viewState = GeneratePasswordViewState.Initial + val viewState = PasswordConfig.Initial Mdc3Theme { GeneratePasswordContent( diff --git a/app/src/main/java/com/yogeshpaliyal/keypass/ui/generate/ui/GeneratePasswordScreen.kt b/app/src/main/java/com/yogeshpaliyal/keypass/ui/generate/ui/GeneratePasswordScreen.kt index dc6bf10e..58e04f99 100644 --- a/app/src/main/java/com/yogeshpaliyal/keypass/ui/generate/ui/GeneratePasswordScreen.kt +++ b/app/src/main/java/com/yogeshpaliyal/keypass/ui/generate/ui/GeneratePasswordScreen.kt @@ -4,22 +4,22 @@ import android.content.Context import android.widget.Toast import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.platform.LocalContext +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.yogeshpaliyal.keypass.R import com.yogeshpaliyal.keypass.ui.generate.GeneratePasswordViewModel import com.yogeshpaliyal.keypass.ui.generate.ui.utils.copyTextToClipboard @Composable -fun GeneratePasswordScreen(viewModel: GeneratePasswordViewModel) { +fun GeneratePasswordScreen(viewModel: GeneratePasswordViewModel = hiltViewModel()) { val context = LocalContext.current - // replace collectAsState() with collectAsStateWithLifecycle() when compose version and kotlin version are bumped up. - val viewState by viewModel.viewState.collectAsState() + val viewState by viewModel.viewState.collectAsStateWithLifecycle() - LaunchedEffect(key1 = Unit) { - viewModel.generatePassword() + LaunchedEffect(Unit) { + viewModel.retrieveSavedPasswordConfig(context) } GeneratePasswordContent( diff --git a/app/src/main/java/com/yogeshpaliyal/keypass/ui/nav/DashboardComposeActivity.kt b/app/src/main/java/com/yogeshpaliyal/keypass/ui/nav/DashboardComposeActivity.kt index 24acbaec..e821b06e 100644 --- a/app/src/main/java/com/yogeshpaliyal/keypass/ui/nav/DashboardComposeActivity.kt +++ b/app/src/main/java/com/yogeshpaliyal/keypass/ui/nav/DashboardComposeActivity.kt @@ -31,6 +31,7 @@ import com.yogeshpaliyal.keypass.ui.backupsImport.BackupImporter import com.yogeshpaliyal.keypass.ui.changeDefaultPasswordLength.ChangeDefaultPasswordLengthScreen import com.yogeshpaliyal.keypass.ui.changePassword.ChangePassword import com.yogeshpaliyal.keypass.ui.detail.AccountDetailPage +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 @@ -46,6 +47,7 @@ import com.yogeshpaliyal.keypass.ui.redux.states.ChangeAppPasswordState import com.yogeshpaliyal.keypass.ui.redux.states.ChangeDefaultPasswordLengthState import com.yogeshpaliyal.keypass.ui.redux.states.HomeState import com.yogeshpaliyal.keypass.ui.redux.states.KeyPassState +import com.yogeshpaliyal.keypass.ui.redux.states.PasswordGeneratorState import com.yogeshpaliyal.keypass.ui.redux.states.ScreenState import com.yogeshpaliyal.keypass.ui.redux.states.SettingsState import com.yogeshpaliyal.keypass.ui.settings.MySettingCompose @@ -169,6 +171,7 @@ fun CurrentPage() { is BackupImporterState -> BackupImporter(state = it) is AboutState -> AboutScreen() + is PasswordGeneratorState -> GeneratePasswordScreen() } } } diff --git a/app/src/main/java/com/yogeshpaliyal/keypass/ui/redux/states/PasswordGeneratorState.kt b/app/src/main/java/com/yogeshpaliyal/keypass/ui/redux/states/PasswordGeneratorState.kt new file mode 100644 index 00000000..90eb7957 --- /dev/null +++ b/app/src/main/java/com/yogeshpaliyal/keypass/ui/redux/states/PasswordGeneratorState.kt @@ -0,0 +1,3 @@ +package com.yogeshpaliyal.keypass.ui.redux.states + +class PasswordGeneratorState : ScreenState(false) diff --git a/app/src/main/java/com/yogeshpaliyal/keypass/ui/settings/MySettingsFragment.kt b/app/src/main/java/com/yogeshpaliyal/keypass/ui/settings/MySettingsFragment.kt index aefe76ea..1110ce0f 100644 --- a/app/src/main/java/com/yogeshpaliyal/keypass/ui/settings/MySettingsFragment.kt +++ b/app/src/main/java/com/yogeshpaliyal/keypass/ui/settings/MySettingsFragment.kt @@ -64,7 +64,7 @@ fun MySettingCompose() { // Retrieving saved password length var savedPasswordLength by remember { mutableStateOf(DEFAULT_PASSWORD_LENGTH) } LaunchedEffect(key1 = Unit) { - userSettings.defaultPasswordLength.let { value -> savedPasswordLength = value } + userSettings.passwordConfig.length.let { value -> savedPasswordLength = value } } Column(modifier = Modifier.fillMaxSize(1f).verticalScroll(rememberScrollState())) { diff --git a/common/src/androidTest/java/com/yogeshpaliyal/keypass/SharedPreferenceUtilsTest.kt b/common/src/androidTest/java/com/yogeshpaliyal/keypass/SharedPreferenceUtilsTest.kt index 2f22dc8f..7eb0b6f5 100644 --- a/common/src/androidTest/java/com/yogeshpaliyal/keypass/SharedPreferenceUtilsTest.kt +++ b/common/src/androidTest/java/com/yogeshpaliyal/keypass/SharedPreferenceUtilsTest.kt @@ -28,7 +28,7 @@ class SharedPreferenceUtilsTest { @Test fun getKeyPassPasswordLength_test() = runBlocking { - val result = context.getUserSettings().defaultPasswordLength + val result = context.getUserSettings().passwordConfig.length assertEquals(DEFAULT_PASSWORD_LENGTH, result) } @@ -36,7 +36,7 @@ class SharedPreferenceUtilsTest { fun setKeyPassPasswordLength_test() = runBlocking { val expectedLength = 8f context.setDefaultPasswordLength(expectedLength) - val result = context.getUserSettings().defaultPasswordLength + val result = context.getUserSettings().passwordConfig.length assertEquals(expectedLength, result) } diff --git a/common/src/main/java/com/yogeshpaliyal/common/data/PasswordConfig.kt b/common/src/main/java/com/yogeshpaliyal/common/data/PasswordConfig.kt new file mode 100644 index 00000000..60c71056 --- /dev/null +++ b/common/src/main/java/com/yogeshpaliyal/common/data/PasswordConfig.kt @@ -0,0 +1,28 @@ +package com.yogeshpaliyal.common.data + +import androidx.annotation.Keep +import kotlinx.serialization.Serializable + +@Keep +@Serializable +data class PasswordConfig( + val length: Float, + val includeUppercaseLetters: Boolean, + val includeLowercaseLetters: Boolean, + val includeSymbols: Boolean, + val includeNumbers: Boolean, + val includeBlankSpaces: Boolean, + val password: String +) { + companion object { + val Initial = PasswordConfig( + length = DEFAULT_PASSWORD_LENGTH, + includeUppercaseLetters = true, + includeLowercaseLetters = true, + includeSymbols = true, + includeNumbers = true, + includeBlankSpaces = true, + password = "" + ) + } +} diff --git a/common/src/main/java/com/yogeshpaliyal/common/data/UserSettings.kt b/common/src/main/java/com/yogeshpaliyal/common/data/UserSettings.kt index 12eba6a9..b20b06db 100644 --- a/common/src/main/java/com/yogeshpaliyal/common/data/UserSettings.kt +++ b/common/src/main/java/com/yogeshpaliyal/common/data/UserSettings.kt @@ -10,6 +10,7 @@ const val DEFAULT_PASSWORD_LENGTH = 10f data class UserSettings( val keyPassPassword: String? = null, val dbPassword: String? = null, + @Deprecated("Use passwordConfig instead") val defaultPasswordLength: Float = DEFAULT_PASSWORD_LENGTH, val backupKey: String? = null, val isBiometricEnable: Boolean = false, @@ -18,7 +19,8 @@ data class UserSettings( val autoBackupEnable: Boolean = false, val overrideAutoBackup: Boolean = false, val lastAppVersion: Int? = null, - val currentAppVersion: Int? = null + val currentAppVersion: Int? = null, + val passwordConfig: PasswordConfig = PasswordConfig.Initial ) { fun isKeyPresent() = backupKey != null } diff --git a/common/src/main/java/com/yogeshpaliyal/common/utils/PasswordGenerator.kt b/common/src/main/java/com/yogeshpaliyal/common/utils/PasswordGenerator.kt index 210ec014..a2115390 100644 --- a/common/src/main/java/com/yogeshpaliyal/common/utils/PasswordGenerator.kt +++ b/common/src/main/java/com/yogeshpaliyal/common/utils/PasswordGenerator.kt @@ -1,15 +1,11 @@ package com.yogeshpaliyal.common.utils -class PasswordGenerator( - private var length: Int, - private var includeUpperCaseLetters: Boolean, - private var includeLowerCaseLetters: Boolean, - private var includeSymbols: Boolean, - private var includeNumbers: Boolean, - private var includeBlankSpaces: Boolean -) { +import com.yogeshpaliyal.common.data.PasswordConfig - constructor() : this(10, true, true, true, true, true) private val UPPER_CASE = 0 +class PasswordGenerator( + val passwordConfig: PasswordConfig +) { + private val UPPER_CASE = 0 private val LOWER_CASE = 1 private val NUMBERS = 2 private val SYMBOLS = 3 @@ -18,24 +14,24 @@ class PasswordGenerator( fun generatePassword(): String { var password = "" val list = ArrayList() - if (includeUpperCaseLetters) { + if (passwordConfig.includeUppercaseLetters) { list.add(UPPER_CASE) } - if (includeLowerCaseLetters) { + if (passwordConfig.includeLowercaseLetters) { list.add(LOWER_CASE) } - if (includeNumbers) { + if (passwordConfig.includeNumbers) { list.add(NUMBERS) } - if (includeSymbols) { + if (passwordConfig.includeSymbols) { list.add(SYMBOLS) } - if (includeBlankSpaces) { + if (passwordConfig.includeBlankSpaces) { list.add(BLANKSPACES) } - for (i in 1..length) { + for (i in 1..passwordConfig.length.toInt()) { if (list.isNotEmpty()) { when (list.random()) { UPPER_CASE -> password += ('A'..'Z').random().toString() diff --git a/common/src/main/java/com/yogeshpaliyal/common/utils/SharedPreferenceUtils.kt b/common/src/main/java/com/yogeshpaliyal/common/utils/SharedPreferenceUtils.kt index 3a7a3a99..0ce6f8dc 100644 --- a/common/src/main/java/com/yogeshpaliyal/common/utils/SharedPreferenceUtils.kt +++ b/common/src/main/java/com/yogeshpaliyal/common/utils/SharedPreferenceUtils.kt @@ -9,6 +9,7 @@ import androidx.datastore.preferences.core.longPreferencesKey import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore import com.yogeshpaliyal.common.data.DEFAULT_PASSWORD_LENGTH +import com.yogeshpaliyal.common.data.PasswordConfig import com.yogeshpaliyal.common.data.UserSettings import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first @@ -32,6 +33,12 @@ private fun Context.getUserSettingsDataStore(): DataStore { return res } +suspend fun Context.setPasswordConfig(passwordConfig: PasswordConfig) { + getUserSettingsDataStore().updateData { + it.copy(passwordConfig = passwordConfig) + } +} + suspend fun Context.getUserSettings(): UserSettings { return getUserSettingsDataStore().data.firstOrNull() ?: UserSettings() } @@ -52,7 +59,7 @@ suspend fun Context.setKeyPassPassword(password: String?) { suspend fun Context.setDefaultPasswordLength(password: Float) { getUserSettingsDataStore().updateData { - it.copy(defaultPasswordLength = password) + it.copy(passwordConfig = it.passwordConfig.copy(length = password)) } } @@ -159,6 +166,10 @@ suspend fun Context.migrateOldDataToNewerDataStore() { userSettings = userSettings.copy(defaultPasswordLength = olderData[KEYPASS_PASSWORD_LENGTH] ?: DEFAULT_PASSWORD_LENGTH) } + if (userSettings.defaultPasswordLength != DEFAULT_PASSWORD_LENGTH) { + userSettings = userSettings.copy(passwordConfig = userSettings.passwordConfig.copy(length = userSettings.defaultPasswordLength)) + } + if (olderData.contains(BACKUP_DIRECTORY)) { userSettings = userSettings.copy(backupDirectory = olderData[BACKUP_DIRECTORY]) }