From e530dc531e8dc904c086eb0161287087ac9b1cef Mon Sep 17 00:00:00 2001 From: Severiano Jaramillo Date: Sat, 5 Jan 2019 20:47:04 -0600 Subject: [PATCH] Add account name validation to CreateAccountFragment using Kotlin extension functions and RxJava debounce operator. --- .../fragments/CreateAccountFragment.kt | 41 ++++++++++++++++++- .../bitsybitshareswallet/utils/Extensions.kt | 15 +++++++ app/src/main/res/values/strings.xml | 2 + 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/CreateAccountFragment.kt b/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/CreateAccountFragment.kt index 80fe6e8..d7993dd 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/CreateAccountFragment.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/CreateAccountFragment.kt @@ -9,6 +9,8 @@ import androidx.navigation.fragment.findNavController import com.jakewharton.rxbinding3.widget.textChanges import cy.agorise.bitsybitshareswallet.R 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.graphenej.Address import cy.agorise.graphenej.BrainKey @@ -28,6 +30,7 @@ class CreateAccountFragment : ConnectedFragment() { private const val TAG = "CreateAccountFragment" private const val BRAINKEY_FILE = "brainkeydict.txt" + private const val MIN_ACCOUNT_NAME_LENGTH = 8 } private lateinit var mBrainKey: BrainKey @@ -36,7 +39,7 @@ class CreateAccountFragment : ConnectedFragment() { /** Variables used to store the validation status of the form fields */ private var isPINValid = 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? { setHasOptionsMenu(true) @@ -47,6 +50,15 @@ class CreateAccountFragment : ConnectedFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 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 mDisposables.add( tietPin.textChanges() @@ -73,6 +85,31 @@ class CreateAccountFragment : ConnectedFragment() { 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() { val pin = tietPin.text.toString() @@ -102,7 +139,7 @@ class CreateAccountFragment : ConnectedFragment() { } private fun enableDisableCreateButton() { - btnCreate.isEnabled = (isPINValid && isPINConfirmationValid && isAccountValid) + btnCreate.isEnabled = (isPINValid && isPINConfirmationValid && isAccountValidAndAvailable) } override fun handleJsonRpcResponse(response: JsonRpcResponse<*>) { diff --git a/app/src/main/java/cy/agorise/bitsybitshareswallet/utils/Extensions.kt b/app/src/main/java/cy/agorise/bitsybitshareswallet/utils/Extensions.kt index bd533c2..d9faa1b 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/utils/Extensions.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/utils/Extensions.kt @@ -5,6 +5,7 @@ import android.content.res.ColorStateList import android.widget.Toast import androidx.core.content.ContextCompat 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. @@ -27,4 +28,18 @@ fun FloatingActionButton.disable(colorResource: Int) { */ fun Context.toast(message: CharSequence, duration: Int = Toast.LENGTH_LONG) { 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) } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 338f7bc..39f97d8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -28,6 +28,8 @@ BitShares account name Error reading dictionary file + 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. + Verifying account availability… Transactions