Skip to content

Commit 7617c6a

Browse files
committed
Update passphrase generator UI to support random numbers as separator
1 parent 6edde8f commit 7617c6a

File tree

2 files changed

+113
-12
lines changed

2 files changed

+113
-12
lines changed

app/src/main/java/com/kunzisoft/keepass/activities/fragments/PassphraseGeneratorFragment.kt

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import android.widget.*
2929
import androidx.core.widget.doOnTextChanged
3030
import androidx.fragment.app.activityViewModels
3131
import com.google.android.material.slider.Slider
32+
import com.google.android.material.textfield.TextInputLayout
3233
import com.kunzisoft.keepass.R
3334
import com.kunzisoft.keepass.database.ContextualDatabase
3435
import com.kunzisoft.keepass.password.PassphraseGenerator
@@ -46,10 +47,15 @@ class PassphraseGeneratorFragment : DatabaseFragment() {
4647
private lateinit var charactersCountText: TextView
4748
private lateinit var wordSeparator: EditText
4849
private lateinit var wordCaseSpinner: Spinner
50+
private lateinit var separatorTypeSpinner: Spinner
51+
private lateinit var wordSeparatorLayout: TextInputLayout
52+
private lateinit var randomDigitsLayout: TextInputLayout
53+
private lateinit var randomDigitsCount: EditText
4954

5055
private var minSliderWordCount: Int = 0
5156
private var maxSliderWordCount: Int = 0
5257
private var wordCaseAdapter: ArrayAdapter<String>? = null
58+
private var separatorTypeAdapter: ArrayAdapter<String>? = null
5359

5460
private val mKeyGeneratorViewModel: KeyGeneratorViewModel by activityViewModels()
5561

@@ -69,6 +75,10 @@ class PassphraseGeneratorFragment : DatabaseFragment() {
6975
charactersCountText = view.findViewById(R.id.character_count)
7076
wordSeparator = view.findViewById(R.id.word_separator)
7177
wordCaseSpinner = view.findViewById(R.id.word_case)
78+
separatorTypeSpinner = view.findViewById(R.id.separator_type)
79+
wordSeparatorLayout = view.findViewById(R.id.word_separator_layout)
80+
randomDigitsLayout = view.findViewById(R.id.random_digits_layout)
81+
randomDigitsCount = view.findViewById(R.id.random_digits_count)
7282

7383
minSliderWordCount = resources.getInteger(R.integer.passphrase_generator_word_count_min)
7484
maxSliderWordCount = resources.getInteger(R.integer.passphrase_generator_word_count_max)
@@ -90,6 +100,12 @@ class PassphraseGeneratorFragment : DatabaseFragment() {
90100
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
91101
}
92102
wordCaseSpinner.adapter = wordCaseAdapter
103+
104+
separatorTypeAdapter = ArrayAdapter(context,
105+
android.R.layout.simple_spinner_item, resources.getStringArray(R.array.separator_type_array)).apply {
106+
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
107+
}
108+
separatorTypeSpinner.adapter = separatorTypeAdapter
93109
}
94110

95111
loadSettings()
@@ -142,6 +158,17 @@ class PassphraseGeneratorFragment : DatabaseFragment() {
142158

143159
override fun onNothingSelected(parent: AdapterView<*>?) {}
144160
}
161+
separatorTypeSpinner.onItemSelectedListener = object: AdapterView.OnItemSelectedListener {
162+
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
163+
updateSeparatorTypeVisibility()
164+
generatePassphrase()
165+
}
166+
167+
override fun onNothingSelected(parent: AdapterView<*>?) {}
168+
}
169+
randomDigitsCount.doOnTextChanged { _, _, _, _ ->
170+
generatePassphrase()
171+
}
145172

146173
generatePassphrase()
147174

@@ -209,15 +236,50 @@ class PassphraseGeneratorFragment : DatabaseFragment() {
209236
wordSeparator.setText(separator)
210237
}
211238

239+
private fun getSeparatorType(): PassphraseGenerator.SeparatorType {
240+
return when (separatorTypeSpinner.selectedItemPosition) {
241+
1 -> PassphraseGenerator.SeparatorType.RANDOM_NUMBERS
242+
else -> PassphraseGenerator.SeparatorType.CUSTOM_VALUE
243+
}
244+
}
245+
246+
private fun setSeparatorType(separatorType: PassphraseGenerator.SeparatorType) {
247+
separatorTypeSpinner.setSelection(if (separatorType == PassphraseGenerator.SeparatorType.RANDOM_NUMBERS) 1 else 0)
248+
}
249+
250+
private fun getRandomDigitsCount(): Int {
251+
return try {
252+
val text = randomDigitsCount.text.toString()
253+
if (text.isEmpty()) 1 else Integer.valueOf(text).coerceIn(1, 9)
254+
} catch (numberException: NumberFormatException) {
255+
1
256+
}
257+
}
258+
259+
private fun setRandomDigitsCount(count: Int) {
260+
randomDigitsCount.setText(count.toString())
261+
}
262+
263+
private fun updateSeparatorTypeVisibility() {
264+
val separatorType = getSeparatorType()
265+
if (separatorType == PassphraseGenerator.SeparatorType.RANDOM_NUMBERS) {
266+
randomDigitsLayout.visibility = View.VISIBLE
267+
wordSeparatorLayout.visibility = View.GONE
268+
} else {
269+
randomDigitsLayout.visibility = View.GONE
270+
wordSeparatorLayout.visibility = View.VISIBLE
271+
}
272+
}
273+
212274
private fun generatePassphrase() {
213275
var passphrase = ""
214276
try {
215277
passphrase = PassphraseGenerator().generatePassphrase(
216278
getWordCount(),
217279
getWordSeparator(),
218280
getWordCase(),
219-
PassphraseGenerator.SeparatorType.CUSTOM_VALUE,
220-
1)
281+
getSeparatorType(),
282+
getRandomDigitsCount())
221283
} catch (e: Exception) {
222284
Log.e(TAG, "Unable to generate a passphrase", e)
223285
}
@@ -235,6 +297,8 @@ class PassphraseGeneratorFragment : DatabaseFragment() {
235297
PreferencesUtil.setDefaultPassphraseWordCount(context, getWordCount())
236298
PreferencesUtil.setDefaultPassphraseWordCase(context, getWordCase())
237299
PreferencesUtil.setDefaultPassphraseSeparator(context, getSeparator())
300+
PreferencesUtil.setDefaultPassphraseSeparatorType(context, getSeparatorType().toPreferenceString())
301+
PreferencesUtil.setDefaultPassphraseRandomDigitsCount(context, getRandomDigitsCount())
238302
}
239303
}
240304

@@ -243,6 +307,9 @@ class PassphraseGeneratorFragment : DatabaseFragment() {
243307
setWordCount(PreferencesUtil.getDefaultPassphraseWordCount(context))
244308
setWordCase(PreferencesUtil.getDefaultPassphraseWordCase(context))
245309
setSeparator(PreferencesUtil.getDefaultPassphraseSeparator(context))
310+
setSeparatorType(PassphraseGenerator.SeparatorType.fromString(PreferencesUtil.getDefaultPassphraseSeparatorType(context)))
311+
setRandomDigitsCount(PreferencesUtil.getDefaultPassphraseRandomDigitsCount(context))
312+
updateSeparatorTypeVisibility()
246313
}
247314
}
248315

app/src/main/res/layout/fragment_generate_passphrase.xml

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -132,30 +132,64 @@
132132
android:layout_height="wrap_content"
133133
tools:text="Character count: 51" />
134134

135-
<RelativeLayout
135+
<Spinner
136+
android:id="@+id/word_case"
136137
android:layout_width="wrap_content"
137-
android:layout_height="wrap_content">
138+
android:layout_height="48dp"
139+
android:layout_marginTop="12dp"/>
138140

139-
<Spinner
140-
android:id="@+id/word_case"
141+
<LinearLayout
142+
android:layout_width="match_parent"
143+
android:layout_height="wrap_content"
144+
android:orientation="horizontal"
145+
android:layout_marginTop="12dp"
146+
android:baselineAligned="false">
147+
148+
<TextView
141149
android:layout_width="wrap_content"
150+
android:layout_height="wrap_content"
151+
android:text="Separator"
152+
android:layout_gravity="center_vertical"
153+
android:layout_marginEnd="8dp"
154+
android:layout_marginRight="8dp"/>
155+
156+
<Spinner
157+
android:id="@+id/separator_type"
158+
android:layout_width="0dp"
142159
android:layout_height="48dp"
143-
android:layout_marginTop="12dp"/>
160+
android:layout_weight="1"/>
144161

145162
<com.google.android.material.textfield.TextInputLayout
146163
android:id="@+id/word_separator_layout"
147-
android:layout_width="match_parent"
164+
android:layout_width="80dp"
148165
android:layout_height="wrap_content"
149-
android:layout_toEndOf="@+id/word_case"
150-
android:layout_toRightOf="@+id/word_case">
166+
android:layout_marginStart="8dp"
167+
android:layout_marginLeft="8dp">
151168

152169
<com.google.android.material.textfield.TextInputEditText
153170
android:id="@+id/word_separator"
154171
android:layout_width="match_parent"
155172
android:layout_height="wrap_content"
156-
android:hint="@string/word_separator" />
173+
android:hint="@string/value" />
157174
</com.google.android.material.textfield.TextInputLayout>
158-
</RelativeLayout>
175+
176+
<com.google.android.material.textfield.TextInputLayout
177+
android:id="@+id/random_digits_layout"
178+
android:layout_width="80dp"
179+
android:layout_height="wrap_content"
180+
android:layout_marginStart="8dp"
181+
android:layout_marginLeft="8dp"
182+
android:visibility="gone">
183+
184+
<com.google.android.material.textfield.TextInputEditText
185+
android:id="@+id/random_digits_count"
186+
android:layout_width="match_parent"
187+
android:layout_height="wrap_content"
188+
android:hint="@string/length"
189+
android:inputType="number"
190+
android:maxLength="1" />
191+
</com.google.android.material.textfield.TextInputLayout>
192+
</LinearLayout>
159193

160194
</LinearLayout>
161195
</FrameLayout>

0 commit comments

Comments
 (0)