Skip to content

Commit 706e029

Browse files
committed
Implemented password generator.
1 parent af4b7f8 commit 706e029

File tree

13 files changed

+266
-16
lines changed

13 files changed

+266
-16
lines changed

app/src/main/java/com/ladsers/passtable/android/activities/EditActivity.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,10 @@ class EditActivity : AppCompatActivity() {
5858
}
5959

6060
passwordGeneratorProcessor = PasswordGeneratorProcessor(activityResultRegistry, this)
61-
{ s -> s?.let { binding.etPassword.setText(s) } }
61+
{ s -> s?.let {
62+
binding.etPassword.setText(s) }
63+
binding.etPassword.setSelection(binding.etPassword.text.length)
64+
}
6265

6366
originalTag = intent.getStringExtra("dataTag") ?: "0"
6467
originalNote = intent.getStringExtra("dataNote") ?: ""

app/src/main/java/com/ladsers/passtable/android/activities/PasswordGeneratorActivity.kt

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,17 @@ import android.content.Intent
44
import android.os.Bundle
55
import android.view.KeyEvent
66
import android.view.View
7+
import android.view.animation.Animation
8+
import android.view.animation.AnimationUtils
79
import android.view.inputmethod.InputMethodManager
810
import android.widget.EditText
11+
import android.widget.ScrollView
912
import android.widget.TextView
1013
import android.widget.Toast
1114
import androidx.appcompat.app.AppCompatActivity
1215
import androidx.core.content.ContextCompat
1316
import androidx.core.widget.doAfterTextChanged
17+
import com.google.android.material.button.MaterialButton
1418
import com.ladsers.passtable.android.R
1519
import com.ladsers.passtable.android.components.PasswordGeneratorProcessor
1620
import com.ladsers.passtable.android.containers.Param
@@ -23,12 +27,15 @@ import com.ladsers.passtable.lib.PasswordGenerator
2327
class PasswordGeneratorActivity : AppCompatActivity() {
2428
private lateinit var binding: ActivityPasswordGeneratorBinding
2529
private lateinit var generator: PasswordGenerator
30+
private lateinit var errorWindowAnim: Animation
2631

2732
override fun onCreate(savedInstanceState: Bundle?) {
2833
super.onCreate(savedInstanceState)
2934
binding = ActivityPasswordGeneratorBinding.inflate(layoutInflater)
3035
setContentView(binding.root)
3136

37+
errorWindowAnim = AnimationUtils.loadAnimation(this, R.anim.error_window_anim)
38+
3239
binding.toolbar.root.title = getString(R.string.ui_ct_passwordGenerator)
3340
binding.toolbar.root.navigationIcon =
3441
ContextCompat.getDrawable(this, R.drawable.ic_back_arrow)
@@ -39,6 +46,7 @@ class PasswordGeneratorActivity : AppCompatActivity() {
3946
if (y > oldY || y < oldY) binding.toolbar.root.elevation = 7f
4047
if (y == 0) binding.toolbar.root.elevation = 0f
4148
}
49+
binding.svLayout.postDelayed({ binding.svLayout.fullScroll(ScrollView.FOCUS_DOWN) }, 500)
4250

4351
generator = PasswordGenerator()
4452

@@ -47,6 +55,10 @@ class PasswordGeneratorActivity : AppCompatActivity() {
4755
binding.passwordLength.textView,
4856
Param.GENERATOR_PASSWORD_LENGTH
4957
)
58+
// special behavior
59+
binding.passwordLength.editText.doAfterTextChanged { x ->
60+
if (x.toString().startsWith('0')) binding.passwordLength.editText.setText("")
61+
}
5062
configureEditParamExclude()
5163

5264
configureCollection(
@@ -74,11 +86,26 @@ class PasswordGeneratorActivity : AppCompatActivity() {
7486
Param.GENERATOR_SYMBOLS_MINIMUM
7587
)
7688

77-
binding.btOk.setOnClickListener {
78-
val intent = Intent().putExtra(PasswordGeneratorProcessor.ResultKey, "some test string")
89+
val isCopyMode = intent.getBooleanExtra(PasswordGeneratorProcessor.CopyModeKey, false)
90+
if (isCopyMode) {
91+
(binding.result.btOk as MaterialButton).icon =
92+
ContextCompat.getDrawable(this, R.drawable.ic_copy)
93+
}
94+
95+
binding.result.btRefresh.setOnClickListener {
96+
generate()
97+
}
98+
99+
binding.result.btOk.setOnClickListener {
100+
val intent = Intent().putExtra(
101+
PasswordGeneratorProcessor.ResultKey,
102+
binding.result.etPassword.text.toString()
103+
)
79104
setResult(RESULT_OK, intent)
80105
finish()
81106
}
107+
108+
generate() // auto generation at startup
82109
}
83110

84111
override fun onResume() {
@@ -219,6 +246,75 @@ class PasswordGeneratorActivity : AppCompatActivity() {
219246
}
220247

221248
private fun generate() {
249+
generator.isLowercaseLettersAllowed = binding.lowercaseLetters.swCollectionAllow.isChecked
250+
generator.isCapitalLettersAllowed = binding.capitalLetters.swCollectionAllow.isChecked
251+
generator.isNumbersAllowed = binding.numbers.swCollectionAllow.isChecked
252+
generator.isSymbolsAllowed = binding.symbols.swCollectionAllow.isChecked
253+
generator.isEasySymbolsMode = binding.symbols.rbBasicSet.isChecked
254+
generator.blockChars(binding.exclude.editText.text.toString())
255+
256+
val length = binding.passwordLength.editText.text.toString().toInt()
257+
val minLowercaseLetters =
258+
if (generator.isLowercaseLettersAllowed) binding.lowercaseLetters.etMinimumNumber.text.toString()
259+
.toInt() else 0
260+
val minSymbolsChars =
261+
if (generator.isSymbolsAllowed) binding.symbols.etMinimumNumber.text.toString()
262+
.toInt() else 0
263+
val minCapitalLetters =
264+
if (generator.isCapitalLettersAllowed) binding.capitalLetters.etMinimumNumber.text.toString()
265+
.toInt() else 0
266+
val minNumbers =
267+
if (generator.isNumbersAllowed) binding.numbers.etMinimumNumber.text.toString()
268+
.toInt() else 0
269+
270+
if (!generator.isLowercaseLettersAllowed && !generator.isCapitalLettersAllowed
271+
&& !generator.isNumbersAllowed && !generator.isSymbolsAllowed
272+
) {
273+
badResult(getString(R.string.ui_ct_noCharSetsGeneration))
274+
return
275+
}
276+
277+
if (!generator.checkGenParams(
278+
length,
279+
minLowercaseLetters,
280+
minSymbolsChars,
281+
minCapitalLetters,
282+
minNumbers
283+
)
284+
) {
285+
badResult(getString(R.string.ui_ct_sumMinNumberExceedsPassLength))
286+
return
287+
}
288+
289+
try {
290+
binding.result.etPassword.text = generator.generate(
291+
length,
292+
minLowercaseLetters,
293+
minSymbolsChars,
294+
minCapitalLetters,
295+
minNumbers
296+
)
297+
goodResult()
298+
} catch (e: IllegalStateException) {
299+
badResult(getString(R.string.ui_ct_tooManyCharsExcluded))
300+
}
301+
}
302+
303+
private fun goodResult() {
304+
binding.result.btOk.isEnabled = true
305+
binding.result.btRefresh.isEnabled = true
306+
binding.result.clErr.clearAnimation()
307+
binding.result.clErr.visibility = View.GONE
308+
}
222309

310+
private fun badResult(errMsg: String) {
311+
binding.result.btOk.isEnabled = false
312+
binding.result.btRefresh.isEnabled = false
313+
binding.result.etPassword.text = ""
314+
binding.result.clErr.visibility = View.VISIBLE
315+
binding.result.clErr.clearAnimation()
316+
binding.result.clErr.startAnimation(errorWindowAnim)
317+
binding.result.tvErrMsg.text = errMsg
318+
binding.svLayout.post { binding.svLayout.fullScroll(ScrollView.FOCUS_DOWN) }
223319
}
224320
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<set xmlns:android="http://schemas.android.com/apk/res/android">
3+
<translate
4+
android:duration="350"
5+
android:fillAfter="true"
6+
android:fillBefore="false"
7+
android:fillEnabled="true"
8+
android:fromYDelta="30%p"
9+
android:interpolator="@android:anim/decelerate_interpolator"
10+
android:toYDelta="0%p" />
11+
</set>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<selector xmlns:android="http://schemas.android.com/apk/res/android">
3+
<item android:color="?toolbarBackground" android:state_enabled="true" />
4+
<item android:color="?attr/additionalActionDisabled" android:state_enabled="false" />
5+
</selector>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:tint="?iconTint"
5+
android:viewportWidth="24"
6+
android:viewportHeight="24">
7+
<path
8+
android:fillColor="@android:color/white"
9+
android:pathData="M17.65,6.35c-1.63,-1.63 -3.94,-2.57 -6.48,-2.31 -3.67,0.37 -6.69,3.35 -7.1,7.02C3.52,15.91 7.27,20 12,20c3.19,0 5.93,-1.87 7.21,-4.56 0.32,-0.67 -0.16,-1.44 -0.9,-1.44 -0.37,0 -0.72,0.2 -0.88,0.53 -1.13,2.43 -3.84,3.97 -6.8,3.31 -2.22,-0.49 -4.01,-2.3 -4.48,-4.52C5.31,9.44 8.26,6 12,6c1.66,0 3.14,0.69 4.22,1.78l-1.51,1.51c-0.63,0.63 -0.19,1.71 0.7,1.71H19c0.55,0 1,-0.45 1,-1V6.41c0,-0.89 -1.08,-1.34 -1.71,-0.71l-0.64,0.65z" />
10+
</vector>

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

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -91,19 +91,15 @@
9191
app:layout_constraintStart_toStartOf="parent"
9292
app:layout_constraintTop_toBottomOf="@+id/symbols" />
9393

94-
95-
<Button
96-
android:id="@+id/btOk"
97-
style="@style/MainButton"
98-
android:layout_width="wrap_content"
99-
android:layout_marginBottom="24dp"
100-
android:elevation="2dp"
101-
android:text="@string/app_bt_save"
102-
android:visibility="gone"
103-
app:icon="@drawable/ic_save"
104-
app:layout_constraintBottom_toBottomOf="parent"
94+
<include
95+
android:id="@+id/result"
96+
layout="@layout/password_generator_result"
97+
android:layout_width="0dp"
98+
android:layout_height="wrap_content"
99+
android:layout_marginTop="24dp"
105100
app:layout_constraintEnd_toEndOf="parent"
106-
app:layout_constraintStart_toStartOf="parent" />
101+
app:layout_constraintStart_toStartOf="parent"
102+
app:layout_constraintTop_toBottomOf="@+id/exclude" />
107103

108104
</androidx.constraintlayout.widget.ConstraintLayout>
109105
</ScrollView>
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:app="http://schemas.android.com/apk/res-auto"
4+
xmlns:tools="http://schemas.android.com/tools"
5+
android:layout_width="match_parent"
6+
android:layout_height="wrap_content">
7+
8+
<androidx.constraintlayout.widget.ConstraintLayout
9+
android:id="@+id/clErr"
10+
android:layout_width="0dp"
11+
android:layout_height="wrap_content"
12+
android:layout_marginStart="16dp"
13+
android:layout_marginEnd="16dp"
14+
android:background="@drawable/background_info_error"
15+
android:visibility="gone"
16+
app:layout_constraintEnd_toEndOf="parent"
17+
app:layout_constraintStart_toStartOf="parent"
18+
app:layout_constraintTop_toTopOf="parent"
19+
tools:visibility="visible">
20+
21+
<ImageView
22+
android:id="@+id/ivErrIcon"
23+
android:layout_width="wrap_content"
24+
android:layout_height="wrap_content"
25+
android:layout_marginStart="10dp"
26+
android:layout_marginTop="12dp"
27+
android:layout_marginBottom="12dp"
28+
app:layout_constraintBottom_toBottomOf="parent"
29+
app:layout_constraintStart_toStartOf="parent"
30+
app:layout_constraintTop_toTopOf="parent"
31+
app:srcCompat="@drawable/ic_error"
32+
app:tint="?infoErrorForeground" />
33+
34+
<TextView
35+
android:id="@+id/tvErrMsg"
36+
android:layout_width="0dp"
37+
android:layout_height="wrap_content"
38+
android:layout_marginStart="12dp"
39+
android:layout_marginTop="12dp"
40+
android:layout_marginEnd="12dp"
41+
android:layout_marginBottom="12dp"
42+
android:text="Error message"
43+
android:textColor="?infoErrorForeground"
44+
app:layout_constraintBottom_toBottomOf="parent"
45+
app:layout_constraintEnd_toEndOf="parent"
46+
app:layout_constraintStart_toEndOf="@+id/ivErrIcon"
47+
app:layout_constraintTop_toTopOf="parent" />
48+
</androidx.constraintlayout.widget.ConstraintLayout>
49+
50+
<androidx.constraintlayout.widget.ConstraintLayout
51+
android:id="@+id/constraintLayout"
52+
android:layout_width="0dp"
53+
android:layout_height="wrap_content"
54+
android:layout_marginStart="16dp"
55+
android:layout_marginTop="12dp"
56+
android:layout_marginEnd="16dp"
57+
android:background="@drawable/background_settings_item"
58+
android:paddingTop="10dp"
59+
android:paddingBottom="10dp"
60+
app:layout_constraintEnd_toEndOf="parent"
61+
app:layout_constraintStart_toStartOf="parent"
62+
app:layout_constraintTop_toBottomOf="@+id/clErr"
63+
app:layout_goneMarginTop="0dp">
64+
65+
<TextView
66+
android:id="@+id/etPassword"
67+
style="@style/EditData"
68+
android:layout_width="0dp"
69+
android:layout_height="wrap_content"
70+
android:layout_marginStart="16dp"
71+
android:layout_marginEnd="16dp"
72+
android:background="@null"
73+
android:fontFamily="@font/overpassmono_semibold"
74+
android:hint="@string/ui_ct_error"
75+
android:padding="0dp"
76+
app:layout_constraintBottom_toBottomOf="parent"
77+
app:layout_constraintEnd_toStartOf="@+id/btRefresh"
78+
app:layout_constraintStart_toStartOf="parent"
79+
app:layout_constraintTop_toTopOf="parent"
80+
tools:text="@string/app_com_password" />
81+
82+
<Button
83+
android:id="@+id/btRefresh"
84+
style="@style/AdditionalButton"
85+
android:layout_width="46dp"
86+
android:layout_marginTop="8dp"
87+
android:layout_marginEnd="12dp"
88+
android:layout_marginBottom="8dp"
89+
android:elevation="2dp"
90+
app:icon="@drawable/ic_refresh"
91+
app:iconGravity="textEnd"
92+
app:iconPadding="0dp"
93+
app:layout_constraintBottom_toBottomOf="parent"
94+
app:layout_constraintEnd_toStartOf="@+id/btOk"
95+
app:layout_constraintTop_toTopOf="parent" />
96+
97+
<Button
98+
android:id="@+id/btOk"
99+
style="@style/MainButton"
100+
android:layout_width="46dp"
101+
android:layout_marginTop="8dp"
102+
android:layout_marginEnd="16dp"
103+
android:layout_marginBottom="8dp"
104+
android:elevation="2dp"
105+
app:icon="@drawable/ic_enter"
106+
app:iconGravity="textEnd"
107+
app:iconPadding="0dp"
108+
app:layout_constraintBottom_toBottomOf="parent"
109+
app:layout_constraintEnd_toEndOf="parent"
110+
app:layout_constraintTop_toTopOf="parent" />
111+
112+
</androidx.constraintlayout.widget.ConstraintLayout>
113+
114+
115+
</androidx.constraintlayout.widget.ConstraintLayout>

app/src/main/res/values-night/themes.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
<item name="editBackground">@color/editBackgroundDark</item>
2121
<item name="settingsItemBackground">@color/grayOverlay</item>
2222
<item name="actionDisabled">@color/actionDisabledDark</item>
23+
<item name="additionalActionDisabled">@color/additionalActionDisabledDark</item>
2324
<item name="infoErrorForeground">@color/infoRedPrimaryDark</item>
2425
<item name="infoErrorBackground">@color/infoRedSecondaryDark</item>
2526
<item name="notificationTint">@color/notificationTintDark</item>

app/src/main/res/values-ru/strings.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@
132132
<string name="ui_ct_symbols">Символы</string>
133133
<string name="ui_ct_passwordLength">Длина пароля</string>
134134
<string name="ui_ct_exclude">Исключить</string>
135+
<string name="ui_ct_error">Ошибка</string>
136+
<string name="ui_ct_noCharSetsGeneration">Нет доступных наборов символов для генерации</string>
137+
<string name="ui_ct_sumMinNumberExceedsPassLength">Сумма минимальных количеств символов превышает длину пароля</string>
138+
<string name="ui_ct_tooManyCharsExcluded">Невозможно сгенерировать пароль с указанными параметрами: слишком много символов было исключено</string>
135139
<!-- end! -->
136140

137141
<!-- msg (message) -->

app/src/main/res/values/attrs.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<attr name="toolbarBackground" format="color" />
44
<attr name="overlayBackground" format="color" />
55
<attr name="panelTableBackground" format="color" />
6+
<attr name="additionalActionDisabled" format="color" />
67
<attr name="iconTint" format="color" />
78
<attr name="tagNoneStroke" format="color" />
89
<attr name="whiteOrBlack" format="color" />

0 commit comments

Comments
 (0)