mirror of
https://github.com/yogeshpaliyal/KeyPass.git
synced 2026-01-07 00:49:46 -06:00
Migrate GeneratePasswordActivity to compose. (#314)
* Move password generation logic to ViewModel. * Store GeneratePassword state in GeneratePasswordViewState. * Update GeneratePasswordState when form is updated. * Remove password StateFlow nad use the one in the ViewState. * Fix dependency version incompatibility issue and add missing dependencies. * Add mdc3Theme adapter. * Add GeneratePasswordContent form. * Integrate GeneratePasswordContent. * Fix spotless issues. * Remove xml layout file.
This commit is contained in:
@@ -36,6 +36,10 @@ android {
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
useIR = true
|
||||
|
||||
freeCompilerArgs += [
|
||||
'-Xopt-in=androidx.compose.material3.ExperimentalMaterial3Api'
|
||||
]
|
||||
}
|
||||
buildFeatures {
|
||||
compose true
|
||||
@@ -84,13 +88,18 @@ dependencies {
|
||||
api project(":common")
|
||||
|
||||
implementation "androidx.compose.ui:ui:1.3.3"
|
||||
implementation "androidx.compose.material:material:1.3.1"
|
||||
implementation "androidx.compose.material:material:1.4.0-alpha04"
|
||||
implementation "androidx.compose.ui:ui-tooling-preview:1.3.3"
|
||||
implementation 'androidx.activity:activity-compose:1.5.1'
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1"
|
||||
implementation "androidx.lifecycle:lifecycle-runtime-compose:2.6.0-alpha03"
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1"
|
||||
implementation "androidx.compose.material:material-icons-extended:1.3.1"
|
||||
androidTestImplementation "androidx.compose.ui:ui-test-junit4:1.3.3"
|
||||
debugImplementation "androidx.compose.ui:ui-tooling:1.3.3"
|
||||
|
||||
implementation 'androidx.compose.material3:material3:1.1.0-alpha04'
|
||||
implementation "com.google.accompanist:accompanist-themeadapter-material3:0.28.0"
|
||||
|
||||
|
||||
// XML Libraries
|
||||
|
||||
@@ -1,47 +1,23 @@
|
||||
package com.yogeshpaliyal.keypasscompose.ui.generate
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.yogeshpaliyal.common.utils.PasswordGenerator
|
||||
import com.yogeshpaliyal.keypasscompose.R
|
||||
import com.yogeshpaliyal.keypasscompose.databinding.ActivityGeneratePasswordBinding
|
||||
import com.google.accompanist.themeadapter.material3.Mdc3Theme
|
||||
import com.yogeshpaliyal.keypasscompose.ui.generate.ui.GeneratePasswordScreen
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class GeneratePasswordActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityGeneratePasswordBinding
|
||||
private val viewModel: GeneratePasswordViewModel by viewModels()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityGeneratePasswordBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
generatePassword()
|
||||
|
||||
binding.btnRefresh.setOnClickListener {
|
||||
generatePassword()
|
||||
setContent {
|
||||
Mdc3Theme {
|
||||
GeneratePasswordScreen(viewModel)
|
||||
}
|
||||
}
|
||||
|
||||
binding.tilPassword.setEndIconOnClickListener {
|
||||
val clipboard = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clip = ClipData.newPlainText("random_password", binding.etPassword.text)
|
||||
clipboard.setPrimaryClip(clip)
|
||||
Toast.makeText(this, getString(R.string.copied_to_clipboard), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun generatePassword() {
|
||||
val password = PasswordGenerator(
|
||||
length = binding.sliderPasswordLength.value.toInt(),
|
||||
includeUpperCaseLetters = binding.cbCapAlphabets.isChecked,
|
||||
includeLowerCaseLetters = binding.cbLowerAlphabets.isChecked,
|
||||
includeSymbols = binding.cbSymbols.isChecked,
|
||||
includeNumbers = binding.cbNumbers.isChecked
|
||||
).generatePassword()
|
||||
|
||||
binding.etPassword.setText(password)
|
||||
binding.etPassword.setSelection(password.length)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.yogeshpaliyal.keypasscompose.ui.generate
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.yogeshpaliyal.common.utils.PasswordGenerator
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class GeneratePasswordViewModel @Inject constructor() : ViewModel() {
|
||||
|
||||
private val _viewState = MutableStateFlow(GeneratePasswordViewState.Initial)
|
||||
val viewState = _viewState.asStateFlow()
|
||||
|
||||
fun generatePassword() {
|
||||
val currentViewState = _viewState.value
|
||||
|
||||
val passwordGenerator = PasswordGenerator(
|
||||
length = currentViewState.length,
|
||||
includeUpperCaseLetters = currentViewState.includeUppercaseLetters,
|
||||
includeLowerCaseLetters = currentViewState.includeLowercaseLetters,
|
||||
includeSymbols = currentViewState.includeSymbols,
|
||||
includeNumbers = currentViewState.includeNumbers,
|
||||
)
|
||||
|
||||
_viewState.update {
|
||||
val newPassword = passwordGenerator.generatePassword()
|
||||
it.copy(password = newPassword)
|
||||
}
|
||||
}
|
||||
|
||||
fun onPasswordLengthSliderChange(value: Float) {
|
||||
_viewState.update {
|
||||
it.copy(length = value.toInt())
|
||||
}
|
||||
}
|
||||
|
||||
fun onUppercaseCheckedChange(checked: Boolean) {
|
||||
_viewState.update {
|
||||
it.copy(includeUppercaseLetters = checked)
|
||||
}
|
||||
}
|
||||
|
||||
fun onLowercaseCheckedChange(checked: Boolean) {
|
||||
_viewState.update {
|
||||
it.copy(includeLowercaseLetters = checked)
|
||||
}
|
||||
}
|
||||
|
||||
fun onNumbersCheckedChange(checked: Boolean) {
|
||||
_viewState.update {
|
||||
it.copy(includeNumbers = checked)
|
||||
}
|
||||
}
|
||||
|
||||
fun onSymbolsCheckedChange(checked: Boolean) {
|
||||
_viewState.update {
|
||||
it.copy(includeSymbols = checked)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.yogeshpaliyal.keypasscompose.ui.generate
|
||||
|
||||
data class GeneratePasswordViewState(
|
||||
val length: Int,
|
||||
val includeUppercaseLetters: Boolean,
|
||||
val includeLowercaseLetters: Boolean,
|
||||
val includeSymbols: Boolean,
|
||||
val includeNumbers: Boolean,
|
||||
val password: String,
|
||||
) {
|
||||
companion object {
|
||||
val Initial = GeneratePasswordViewState(
|
||||
length = 10,
|
||||
includeUppercaseLetters = true,
|
||||
includeLowercaseLetters = true,
|
||||
includeSymbols = true,
|
||||
includeNumbers = true,
|
||||
password = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
package com.yogeshpaliyal.keypasscompose.ui.generate.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
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.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ContentCopy
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.OutlinedCard
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Slider
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.google.accompanist.themeadapter.material3.Mdc3Theme
|
||||
import com.yogeshpaliyal.keypasscompose.ui.generate.GeneratePasswordViewState
|
||||
import com.yogeshpaliyal.keypasscompose.ui.generate.ui.components.CheckboxWithLabel
|
||||
|
||||
@Composable
|
||||
fun GeneratePasswordContent(
|
||||
viewState: GeneratePasswordViewState,
|
||||
onCopyPasswordClick: () -> Unit,
|
||||
onGeneratePasswordClick: () -> Unit,
|
||||
onPasswordLengthChange: (Float) -> Unit,
|
||||
onUppercaseCheckedChange: (Boolean) -> Unit,
|
||||
onLowercaseCheckedChange: (Boolean) -> Unit,
|
||||
onNumbersCheckedChange: (Boolean) -> Unit,
|
||||
onSymbolsCheckedChange: (Boolean) -> Unit,
|
||||
) {
|
||||
Scaffold(
|
||||
floatingActionButton = { GeneratePasswordFab(onGeneratePasswordClick) }
|
||||
) { innerPadding ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.fillMaxSize()
|
||||
.background(Color(0xFFEEF0F2))
|
||||
.padding(16.dp)
|
||||
) {
|
||||
FormInputCard(
|
||||
viewState = viewState,
|
||||
onCopyPasswordClick = onCopyPasswordClick,
|
||||
onPasswordLengthChange = onPasswordLengthChange,
|
||||
onUppercaseCheckedChange = onUppercaseCheckedChange,
|
||||
onLowercaseCheckedChange = onLowercaseCheckedChange,
|
||||
onNumbersCheckedChange = onNumbersCheckedChange,
|
||||
onSymbolsCheckedChange = onSymbolsCheckedChange
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun GeneratePasswordFab(onGeneratePasswordClick: () -> Unit) {
|
||||
FloatingActionButton(
|
||||
onClick = onGeneratePasswordClick,
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Refresh,
|
||||
contentDescription = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FormInputCard(
|
||||
viewState: GeneratePasswordViewState,
|
||||
onCopyPasswordClick: () -> Unit,
|
||||
onPasswordLengthChange: (Float) -> Unit,
|
||||
onUppercaseCheckedChange: (Boolean) -> Unit,
|
||||
onLowercaseCheckedChange: (Boolean) -> Unit,
|
||||
onNumbersCheckedChange: (Boolean) -> Unit,
|
||||
onSymbolsCheckedChange: (Boolean) -> Unit
|
||||
) {
|
||||
OutlinedCard(
|
||||
colors = CardDefaults.outlinedCardColors(
|
||||
containerColor = Color.White
|
||||
),
|
||||
shape = RoundedCornerShape(8.dp)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(10.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
PasswordTextField(viewState.password, onCopyPasswordClick)
|
||||
|
||||
// temporary label until we put slider label on the thumb to display current value.
|
||||
PasswordLengthInput(viewState.length, onPasswordLengthChange)
|
||||
|
||||
UppercaseAlphabetInput(viewState.includeUppercaseLetters, onUppercaseCheckedChange)
|
||||
|
||||
LowercaseAlphabetInput(viewState.includeLowercaseLetters, onLowercaseCheckedChange)
|
||||
|
||||
NumberInput(viewState.includeNumbers, onNumbersCheckedChange)
|
||||
|
||||
SymbolInput(viewState.includeSymbols, onSymbolsCheckedChange)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PasswordTextField(
|
||||
password: String,
|
||||
onCopyPasswordClick: () -> Unit
|
||||
) {
|
||||
OutlinedTextField(
|
||||
value = password,
|
||||
onValueChange = {},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
readOnly = true,
|
||||
trailingIcon = {
|
||||
IconButton(
|
||||
onClick = onCopyPasswordClick
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ContentCopy,
|
||||
contentDescription = "Copy password"
|
||||
)
|
||||
}
|
||||
},
|
||||
label = {
|
||||
Text(text = "Password")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PasswordLengthInput(
|
||||
length: Int,
|
||||
onPasswordLengthChange: (Float) -> Unit
|
||||
) {
|
||||
Text(text = "Password Length: $length")
|
||||
|
||||
Slider(
|
||||
value = length.toFloat(),
|
||||
onValueChange = onPasswordLengthChange,
|
||||
valueRange = 7f..50f,
|
||||
steps = 43,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UppercaseAlphabetInput(
|
||||
includeUppercaseLetters: Boolean,
|
||||
onUppercaseCheckedChange: (Boolean) -> Unit
|
||||
) {
|
||||
CheckboxWithLabel(
|
||||
label = "Uppercase Alphabets",
|
||||
checked = includeUppercaseLetters,
|
||||
onCheckedChange = onUppercaseCheckedChange,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LowercaseAlphabetInput(
|
||||
includeLowercaseLetters: Boolean,
|
||||
onLowercaseCheckedChange: (Boolean) -> Unit
|
||||
) {
|
||||
CheckboxWithLabel(
|
||||
label = "Lowercase Alphabets",
|
||||
checked = includeLowercaseLetters,
|
||||
onCheckedChange = onLowercaseCheckedChange,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun NumberInput(
|
||||
includeNumbers: Boolean,
|
||||
onNumbersCheckedChange: (Boolean) -> Unit
|
||||
) {
|
||||
CheckboxWithLabel(
|
||||
label = "Numbers",
|
||||
checked = includeNumbers,
|
||||
onCheckedChange = onNumbersCheckedChange,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SymbolInput(
|
||||
includeSymbols: Boolean,
|
||||
onSymbolsCheckedChange: (Boolean) -> Unit
|
||||
) {
|
||||
CheckboxWithLabel(
|
||||
label = "Symbols",
|
||||
checked = includeSymbols,
|
||||
onCheckedChange = onSymbolsCheckedChange,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(
|
||||
name = "Night Mode",
|
||||
uiMode = Configuration.UI_MODE_NIGHT_YES,
|
||||
)
|
||||
@Preview(
|
||||
name = "Day Mode",
|
||||
uiMode = Configuration.UI_MODE_NIGHT_NO,
|
||||
)
|
||||
@Composable
|
||||
@Suppress("UnusedPrivateMember", "MagicNumber")
|
||||
private fun GeneratePasswordContentPreview() {
|
||||
|
||||
val viewState = GeneratePasswordViewState.Initial
|
||||
|
||||
Mdc3Theme {
|
||||
Surface {
|
||||
GeneratePasswordContent(
|
||||
viewState = viewState,
|
||||
onGeneratePasswordClick = {},
|
||||
onCopyPasswordClick = {},
|
||||
onPasswordLengthChange = {},
|
||||
onUppercaseCheckedChange = {},
|
||||
onLowercaseCheckedChange = {},
|
||||
onNumbersCheckedChange = {},
|
||||
onSymbolsCheckedChange = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.yogeshpaliyal.keypasscompose.ui.generate.ui
|
||||
|
||||
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 com.yogeshpaliyal.keypasscompose.R
|
||||
import com.yogeshpaliyal.keypasscompose.ui.generate.GeneratePasswordViewModel
|
||||
import com.yogeshpaliyal.keypasscompose.ui.generate.ui.utils.copyTextToClipboard
|
||||
|
||||
@Composable
|
||||
fun GeneratePasswordScreen(viewModel: GeneratePasswordViewModel) {
|
||||
|
||||
val context = LocalContext.current
|
||||
|
||||
// replace collectAsState() with collectAsStateWithLifecycle() when compose version and kotlin version are bumped up.
|
||||
val viewState by viewModel.viewState.collectAsState()
|
||||
|
||||
LaunchedEffect(key1 = Unit) {
|
||||
viewModel.generatePassword()
|
||||
}
|
||||
|
||||
GeneratePasswordContent(
|
||||
viewState = viewState,
|
||||
onGeneratePasswordClick = viewModel::generatePassword,
|
||||
onCopyPasswordClick = { onCopyPasswordClick(context, viewState.password) },
|
||||
onPasswordLengthChange = viewModel::onPasswordLengthSliderChange,
|
||||
onUppercaseCheckedChange = viewModel::onUppercaseCheckedChange,
|
||||
onLowercaseCheckedChange = viewModel::onLowercaseCheckedChange,
|
||||
onNumbersCheckedChange = viewModel::onNumbersCheckedChange,
|
||||
onSymbolsCheckedChange = viewModel::onSymbolsCheckedChange
|
||||
)
|
||||
}
|
||||
|
||||
private fun onCopyPasswordClick(context: Context, text: String) {
|
||||
copyTextToClipboard(context = context, text = text, label = "random_password")
|
||||
Toast
|
||||
.makeText(context, context.getString(R.string.copied_to_clipboard), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.yogeshpaliyal.keypasscompose.ui.generate.ui.components
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
||||
@Composable
|
||||
fun CheckboxWithLabel(
|
||||
label: String,
|
||||
checked: Boolean,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { onCheckedChange(!checked) },
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Checkbox(
|
||||
checked = checked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
)
|
||||
|
||||
Text(text = label)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.yogeshpaliyal.keypasscompose.ui.generate.ui.utils
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
|
||||
fun copyTextToClipboard(
|
||||
context: Context,
|
||||
text: String,
|
||||
label: String
|
||||
) {
|
||||
val clipboard =
|
||||
context.getSystemService(AppCompatActivity.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clip = ClipData.newPlainText(label, text)
|
||||
clipboard.setPrimaryClip(clip)
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ui.generate.GeneratePasswordActivity">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="@dimen/grid_0_5"
|
||||
android:paddingVertical="@dimen/grid_0_5">
|
||||
|
||||
<com.yogeshpaliyal.keypasscompose.custom_views.MaskedCardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="@dimen/grid_1"
|
||||
android:paddingVertical="@dimen/grid_1">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/tilPassword"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/password"
|
||||
app:endIconDrawable="@drawable/ic_twotone_content_copy_24"
|
||||
app:endIconMode="custom"
|
||||
app:layout_constraintEnd_toEndOf="@id/endGuideline"
|
||||
app:layout_constraintStart_toStartOf="@id/startGuideline"
|
||||
app:layout_constraintTop_toTopOf="@id/topGuideline">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/etPassword"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_marginTop="@dimen/grid_2"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/password_length"/>
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:valueFrom="7"
|
||||
android:valueTo="50"
|
||||
android:stepSize="1"
|
||||
android:id="@+id/sliderPasswordLength"/>
|
||||
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/cbCapAlphabets"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:text="@string/uppercase_alphabets" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/cbLowerAlphabets"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:text="@string/lowercase_alphabets" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/cbNumbers"
|
||||
android:layout_width="match_parent"
|
||||
android:checked="true"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/numbers" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/cbSymbols"
|
||||
android:layout_width="match_parent"
|
||||
android:checked="true"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/symbols" />
|
||||
|
||||
</LinearLayout>
|
||||
</com.yogeshpaliyal.keypasscompose.custom_views.MaskedCardView>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="@dimen/grid_3"
|
||||
app:fabSize="normal"
|
||||
android:src="@drawable/ic_baseline_refresh_24"
|
||||
android:id="@+id/btnRefresh"/>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
Reference in New Issue
Block a user