From 2dc0deb23f9e9f8d63f0038e0f9bc2403b60caed Mon Sep 17 00:00:00 2001 From: Severiano Jaramillo Date: Tue, 11 Dec 2018 13:25:15 -0600 Subject: [PATCH] Improve ImportBrainkeyActivity by automatically showing errors if the PIN is not long enough, the PIN Confirmation does not match or if the brainkey does not have the correct format as the user types but with a small debunce to avoid showing errors while the user is typing. Once the three fields are correct then the Import button is enabled. --- .../activities/ImportBrainkeyActivity.kt | 108 ++++++++++++++---- .../res/layout/activity_import_brainkey.xml | 2 - 2 files changed, 83 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/cy/agorise/bitsybitshareswallet/activities/ImportBrainkeyActivity.kt b/app/src/main/java/cy/agorise/bitsybitshareswallet/activities/ImportBrainkeyActivity.kt index da16885..fe8c617 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/activities/ImportBrainkeyActivity.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/activities/ImportBrainkeyActivity.kt @@ -4,10 +4,10 @@ import android.content.Intent import android.os.Bundle import android.preference.PreferenceManager import android.util.Log -import android.view.inputmethod.EditorInfo import android.widget.Toast import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.list.listItemsSingleChoice +import com.jakewharton.rxbinding2.widget.RxTextView import cy.agorise.bitsybitshareswallet.R import cy.agorise.bitsybitshareswallet.database.entities.Authority import cy.agorise.bitsybitshareswallet.repositories.AuthorityRepository @@ -20,9 +20,12 @@ import cy.agorise.graphenej.api.calls.GetAccounts import cy.agorise.graphenej.api.calls.GetKeyReferences import cy.agorise.graphenej.models.AccountProperties import cy.agorise.graphenej.models.JsonRpcResponse +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable import kotlinx.android.synthetic.main.activity_import_brainkey.* import org.bitcoinj.core.ECKey import java.util.ArrayList +import java.util.concurrent.TimeUnit // TODO Add method to load the 20? most important assets // TODO add progress bar or something while the user waits for the import response from the node @@ -51,41 +54,90 @@ class ImportBrainkeyActivity : ConnectedActivity() { private var keyReferencesRequestId: Long = 0 private var getAccountsRequestId: Long = 0 + private var mDisposables = CompositeDisposable() + + private var isPINValid = false + private var isPINConfirmationValid = false + private var isBrainKeyValid = false + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_import_brainkey) - // Custom event to activate import account from the keyboard - tietBrainKey.setOnEditorActionListener { _, actionId, _ -> - if (actionId == EditorInfo.IME_ACTION_DONE) { - importAccount() - true - } else - false - } + // Use RxJava Debounce to update the PIN error only after the user stops writing for > 500 ms + mDisposables.add( + RxTextView.textChanges(tietPin) + .skipInitialValue() + .debounce(500, TimeUnit.MILLISECONDS) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { validatePIN() } + ) - btnImport.setOnClickListener { importAccount() } + // Use RxJava Debounce to update the PIN Confirmation error only after the user stops writing for > 500 ms + mDisposables.add( + RxTextView.textChanges(tietPinConfirmation) + .skipInitialValue() + .debounce(500, TimeUnit.MILLISECONDS) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { validatePINConfirmation() } + ) + + // Use RxJava Debounce to update the BrainKey error only after the user stops writing for > 500 ms + mDisposables.add( + RxTextView.textChanges(tietBrainKey) + .skipInitialValue() + .debounce(500, TimeUnit.MILLISECONDS) + .map { it.toString().trim() } + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { validateBrainKey(it) } + ) + + btnImport.isEnabled = false + btnImport.setOnClickListener { verifyBrainKey(false) } } - private fun importAccount() { - tilPin.isErrorEnabled = false - tilPinConfirmation.isErrorEnabled = false - tilBrainKey.isErrorEnabled = false + private fun validatePIN() { + val pin = tietPin.text.toString() - if (tietPin.text!!.length < Constants.MIN_PIN_LENGTH) + if (pin.length < Constants.MIN_PIN_LENGTH) { tilPin.error = getString(R.string.error__pin_too_short) - else if (tietPin.text.toString() != tietPinConfirmation.text.toString()) - tilPinConfirmation.error = getString(R.string.error__pin_mismatch) - else if (tietBrainKey.text!!.isEmpty() || !tietBrainKey.text.toString().contains(" ")) - tilBrainKey.error = getString(R.string.error__enter_correct_brainkey) - else { - val brainKey = tietBrainKey.text.toString() - if (brainKey.split(" ").size in 12..16) - verifyBrainKey(false) - else - tilBrainKey.error = getString(R.string.error__enter_correct_brainkey) + isPINValid = false + } else { + tilPin.isErrorEnabled = false + isPINValid = true } + validatePINConfirmation() + } + + private fun validatePINConfirmation() { + val pinConfirmation = tietPinConfirmation.text.toString() + + if (pinConfirmation != tietPin.text.toString()) { + tilPinConfirmation.error = getString(R.string.error__pin_mismatch) + isPINConfirmationValid = false + } else { + tilPinConfirmation.isErrorEnabled = false + isPINConfirmationValid = true + } + + enableDisableImportButton() + } + + private fun validateBrainKey(brainKey: String) { + if (brainKey.isEmpty() || !brainKey.contains(" ") || brainKey.split(" ").size !in 12..16) { + tilBrainKey.error = getString(R.string.error__enter_correct_brainkey) + isBrainKeyValid = false + } else { + tilBrainKey.isErrorEnabled = false + isBrainKeyValid = true + } + + enableDisableImportButton() + } + + private fun enableDisableImportButton() { + btnImport.isEnabled = (isPINValid && isPINConfirmationValid && isBrainKeyValid) } /** @@ -296,4 +348,10 @@ class ImportBrainkeyActivity : ConnectedActivity() { val authorityRepository = AuthorityRepository(this) authorityRepository.insert(authority) } + + override fun onDestroy() { + super.onDestroy() + + if (!mDisposables.isDisposed) mDisposables.dispose() + } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_import_brainkey.xml b/app/src/main/res/layout/activity_import_brainkey.xml index 1a6d3ae..1c47f05 100644 --- a/app/src/main/res/layout/activity_import_brainkey.xml +++ b/app/src/main/res/layout/activity_import_brainkey.xml @@ -5,7 +5,6 @@ android:layout_width="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_height="match_parent" - android:focusableInTouchMode="true" android:orientation="vertical" tools:context=".activities.ImportBrainkeyActivity"> @@ -78,7 +77,6 @@ android:layout_marginTop="@dimen/spacing_different_topic" android:layout_marginStart="@dimen/activity_horizontal_margin" android:layout_marginEnd="@dimen/activity_horizontal_margin" - android:backgroundTint="@color/colorPrimary" android:text="@string/button__import"/>