Add account name validation to CreateAccountFragment using Kotlin extension functions and RxJava debounce operator.
This commit is contained in:
parent
9236e374bc
commit
e530dc531e
3 changed files with 56 additions and 2 deletions
|
@ -9,6 +9,8 @@ import androidx.navigation.fragment.findNavController
|
||||||
import com.jakewharton.rxbinding3.widget.textChanges
|
import com.jakewharton.rxbinding3.widget.textChanges
|
||||||
import cy.agorise.bitsybitshareswallet.R
|
import cy.agorise.bitsybitshareswallet.R
|
||||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||||
|
import cy.agorise.bitsybitshareswallet.utils.containsDigits
|
||||||
|
import cy.agorise.bitsybitshareswallet.utils.containsVowels
|
||||||
import cy.agorise.bitsybitshareswallet.utils.toast
|
import cy.agorise.bitsybitshareswallet.utils.toast
|
||||||
import cy.agorise.graphenej.Address
|
import cy.agorise.graphenej.Address
|
||||||
import cy.agorise.graphenej.BrainKey
|
import cy.agorise.graphenej.BrainKey
|
||||||
|
@ -28,6 +30,7 @@ class CreateAccountFragment : ConnectedFragment() {
|
||||||
private const val TAG = "CreateAccountFragment"
|
private const val TAG = "CreateAccountFragment"
|
||||||
|
|
||||||
private const val BRAINKEY_FILE = "brainkeydict.txt"
|
private const val BRAINKEY_FILE = "brainkeydict.txt"
|
||||||
|
private const val MIN_ACCOUNT_NAME_LENGTH = 8
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var mBrainKey: BrainKey
|
private lateinit var mBrainKey: BrainKey
|
||||||
|
@ -36,7 +39,7 @@ class CreateAccountFragment : ConnectedFragment() {
|
||||||
/** Variables used to store the validation status of the form fields */
|
/** Variables used to store the validation status of the form fields */
|
||||||
private var isPINValid = false
|
private var isPINValid = false
|
||||||
private var isPINConfirmationValid = false
|
private var isPINConfirmationValid = false
|
||||||
private var isAccountValid = true // TODO make false
|
private var isAccountValidAndAvailable = false
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
|
@ -47,6 +50,15 @@ class CreateAccountFragment : ConnectedFragment() {
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
// Use RxJava Debounce to check the validity and availability of the user's proposed account name
|
||||||
|
mDisposables.add(
|
||||||
|
tietAccountName.textChanges()
|
||||||
|
.skipInitialValue()
|
||||||
|
.debounce(500, TimeUnit.MILLISECONDS)
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe { validateAccountName(it.toString()) }
|
||||||
|
)
|
||||||
|
|
||||||
// Use RxJava Debounce to update the PIN error only after the user stops writing for > 500 ms
|
// Use RxJava Debounce to update the PIN error only after the user stops writing for > 500 ms
|
||||||
mDisposables.add(
|
mDisposables.add(
|
||||||
tietPin.textChanges()
|
tietPin.textChanges()
|
||||||
|
@ -73,6 +85,31 @@ class CreateAccountFragment : ConnectedFragment() {
|
||||||
generateKeys()
|
generateKeys()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun validateAccountName(accountName: String) {
|
||||||
|
isAccountValidAndAvailable = false
|
||||||
|
|
||||||
|
if ( !isAccountNameValid(accountName) ) {
|
||||||
|
tilAccountName.error = getString(R.string.error__invalid_account_name)
|
||||||
|
tilAccountName.helperText = ""
|
||||||
|
} else {
|
||||||
|
tilAccountName.isErrorEnabled = false
|
||||||
|
tilAccountName.helperText = getString(R.string.text__verifying_account_availability)
|
||||||
|
}
|
||||||
|
|
||||||
|
enableDisableCreateButton()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method used to determine if the account name entered by the user is valid
|
||||||
|
* @param accountName The proposed account name
|
||||||
|
* @return True if the name is valid, false otherwise
|
||||||
|
*/
|
||||||
|
private fun isAccountNameValid(accountName: String): Boolean {
|
||||||
|
return accountName.length >= MIN_ACCOUNT_NAME_LENGTH &&
|
||||||
|
(accountName.containsDigits() || !accountName.containsVowels()) &&
|
||||||
|
!accountName.contains("_")
|
||||||
|
}
|
||||||
|
|
||||||
private fun validatePIN() {
|
private fun validatePIN() {
|
||||||
val pin = tietPin.text.toString()
|
val pin = tietPin.text.toString()
|
||||||
|
|
||||||
|
@ -102,7 +139,7 @@ class CreateAccountFragment : ConnectedFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun enableDisableCreateButton() {
|
private fun enableDisableCreateButton() {
|
||||||
btnCreate.isEnabled = (isPINValid && isPINConfirmationValid && isAccountValid)
|
btnCreate.isEnabled = (isPINValid && isPINConfirmationValid && isAccountValidAndAvailable)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handleJsonRpcResponse(response: JsonRpcResponse<*>) {
|
override fun handleJsonRpcResponse(response: JsonRpcResponse<*>) {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import android.content.res.ColorStateList
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an enabled state, by enabling the button and using the given [colorResource] to color it.
|
* Creates an enabled state, by enabling the button and using the given [colorResource] to color it.
|
||||||
|
@ -27,4 +28,18 @@ fun FloatingActionButton.disable(colorResource: Int) {
|
||||||
*/
|
*/
|
||||||
fun Context.toast(message: CharSequence, duration: Int = Toast.LENGTH_LONG) {
|
fun Context.toast(message: CharSequence, duration: Int = Toast.LENGTH_LONG) {
|
||||||
Toast.makeText(this, message, duration).show()
|
Toast.makeText(this, message, duration).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that the current string contains at least one digit
|
||||||
|
*/
|
||||||
|
fun String.containsDigits(): Boolean {
|
||||||
|
return Pattern.matches("\\d", this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that the current string contains at least one vowel
|
||||||
|
*/
|
||||||
|
fun String.containsVowels(): Boolean {
|
||||||
|
return Pattern.matches("[aeiou]", this)
|
||||||
}
|
}
|
|
@ -28,6 +28,8 @@
|
||||||
<!-- Create Account -->
|
<!-- Create Account -->
|
||||||
<string name="text__bitshares_account_name">BitShares account name</string>
|
<string name="text__bitshares_account_name">BitShares account name</string>
|
||||||
<string name="error__read_dict_file">Error reading dictionary file</string>
|
<string name="error__read_dict_file">Error reading dictionary file</string>
|
||||||
|
<string name="error__invalid_account_name">The account name has to either have more than 8 characters, contain a number or have no vowels. The underscore character is also not allowed. </string>
|
||||||
|
<string name="text__verifying_account_availability">Verifying account availability…</string>
|
||||||
|
|
||||||
<!-- Home -->
|
<!-- Home -->
|
||||||
<string name="title_transactions">Transactions</string>
|
<string name="title_transactions">Transactions</string>
|
||||||
|
|
Loading…
Reference in a new issue