From e8e1259314cc195885800ad72f6cbb04e7ac3330 Mon Sep 17 00:00:00 2001 From: Severiano Jaramillo Date: Thu, 14 Feb 2019 15:17:29 -0600 Subject: [PATCH] Added the options to change the Security Lock option between PIN and None, and force the UI to show the correct one when it is changed. --- .../fragments/BaseSecurityLockDialog.kt | 13 ++-- .../fragments/PINSecurityLockDialog.kt | 75 +++++++++++++++++-- .../fragments/SettingsFragment.kt | 67 ++++++++++++++--- .../res/layout/dialog_pin_security_lock.xml | 7 +- app/src/main/res/values-es/strings.xml | 7 ++ app/src/main/res/values/strings.xml | 7 ++ 6 files changed, 150 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/BaseSecurityLockDialog.kt b/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/BaseSecurityLockDialog.kt index 6099f3c..a911d75 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/BaseSecurityLockDialog.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/BaseSecurityLockDialog.kt @@ -18,8 +18,9 @@ abstract class BaseSecurityLockDialog : DialogFragment() { /** Used to denote that the user is in the step of creating the preferred security lock option */ const val STEP_SECURITY_LOCK_CREATE = 1 - /** Used to denote that the user is in the step of confirming the newly created security lock option */ - private const val STEP_SECURITY_LOCK_CONFIRM = 2 + /** Used to denote that the user is in the step of confirming the newly created security lock option, + * this option should only be used internally */ + const val STEP_SECURITY_LOCK_CONFIRM = 2 /** Used to denote that the user is in the step of verifying the current security lock option, to give * permission to do a security constrained action like sending a transaction or trying to change the @@ -45,10 +46,10 @@ abstract class BaseSecurityLockDialog : DialogFragment() { protected var mCallback: OnPINPatternEnteredListener? = null /** Keeps track of the current step, can be create, confirm of verify */ - protected var currentStep: Int = 1 + protected var currentStep: Int = -1 /** Used so the calling object knows what was the intention to ask for the Security Lock */ - protected var actionIdentifier: Int = 0 + protected var actionIdentifier: Int = -1 /** Keeps track of all RxJava disposables, to make sure they are all disposed when the fragment is destroyed */ protected var mDisposables = CompositeDisposable() @@ -64,9 +65,9 @@ abstract class BaseSecurityLockDialog : DialogFragment() { currentEncryptedPINPattern = PreferenceManager.getDefaultSharedPreferences(context) .getString(Constants.KEY_ENCRYPTED_PIN, "")?.trim() - currentStep = arguments?.getInt(KEY_STEP_SECURITY_LOCK) ?: 0 + currentStep = arguments?.getInt(KEY_STEP_SECURITY_LOCK) ?: -1 - actionIdentifier = arguments?.getInt(KEY_ACTION_IDENTIFIER) ?: 0 + actionIdentifier = arguments?.getInt(KEY_ACTION_IDENTIFIER) ?: -1 } /** diff --git a/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/PINSecurityLockDialog.kt b/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/PINSecurityLockDialog.kt index df9e30a..8d7603b 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/PINSecurityLockDialog.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/PINSecurityLockDialog.kt @@ -1,12 +1,14 @@ package cy.agorise.bitsybitshareswallet.fragments import android.os.Bundle +import android.preference.PreferenceManager import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.inputmethod.EditorInfo import com.jakewharton.rxbinding3.widget.textChanges import cy.agorise.bitsybitshareswallet.R +import cy.agorise.bitsybitshareswallet.utils.Constants import cy.agorise.bitsybitshareswallet.utils.CryptoUtils import io.reactivex.android.schedulers.AndroidSchedulers import kotlinx.android.synthetic.main.dialog_pin_security_lock.* @@ -25,23 +27,50 @@ class PINSecurityLockDialog : BaseSecurityLockDialog() { return inflater.inflate(R.layout.dialog_pin_security_lock, container, false) } + private var newPIN = "" + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + setupScreen() + // Listens to the event when the user clicks the 'Enter' button in the keyboard and acts accordingly tietPIN.setOnEditorActionListener { v, actionId, _ -> var handled = false if (actionId == EditorInfo.IME_ACTION_GO) { - val encryptedPIN = CryptoUtils.encrypt(v.context, v.text.toString()).trim() - if (currentStep == STEP_SECURITY_LOCK_VERIFY) { // The user just wants to verify the current encrypted PIN/Pattern + val encryptedPIN = CryptoUtils.encrypt(v.context, v.text.toString()).trim() + if (encryptedPIN == currentEncryptedPINPattern) { // PIN is correct, proceed dismiss() mCallback?.onPINPatternEntered(actionIdentifier) } else { - tilPIN.error = "Wrong PIN" + tilPIN.error = getString(R.string.error__wrong_pin) + } + } else if (currentStep == STEP_SECURITY_LOCK_CREATE) { + // The user is trying to create a new PIN + if (v.text.toString().trim().length >= Constants.MIN_PIN_LENGTH) { + // Proceed to the next step only if the PIN has the min length + newPIN = v.text.toString().trim() + currentStep = STEP_SECURITY_LOCK_CONFIRM + setupScreen() + } + } else if (currentStep == STEP_SECURITY_LOCK_CONFIRM) { + val pinConfirm = v.text.toString().trim() + if (pinConfirm != newPIN) { + tvTitle.text = getString(R.string.title__pins_dont_match) + } else { + val encryptedPIN = CryptoUtils.encrypt(v.context, v.text.toString()).trim() + + // Stores the newly selected PIN, encrypted + PreferenceManager.getDefaultSharedPreferences(v.context).edit() + .putString(Constants.KEY_ENCRYPTED_PIN, encryptedPIN) + .putInt(Constants.KEY_SECURITY_LOCK_SELECTED, 0).apply() // 0 -> PIN + + dismiss() + mCallback?.onPINPatternChanged() } } @@ -50,11 +79,47 @@ class PINSecurityLockDialog : BaseSecurityLockDialog() { handled } - // Use RxBindings to clear the error when the user edits the PIN mDisposables.add( tietPIN.textChanges() .observeOn(AndroidSchedulers.mainThread()) - .subscribe { tilPIN.isErrorEnabled = false } + .subscribe { + if (currentStep == STEP_SECURITY_LOCK_VERIFY) { + // Make sure the error is removed when the user types again + tilPIN.isErrorEnabled = false + } else if (currentStep == STEP_SECURITY_LOCK_CREATE) { + // Show the min length requirement for the PIN only when it has not been fulfilled + if (it.trim().length >= Constants.MIN_PIN_LENGTH) { + tilPIN.helperText = "" + } else { + tilPIN.helperText = getString(R.string.msg__min_pin_length) + } + } + } ) } + + private fun setupScreen() { + when (currentStep) { + STEP_SECURITY_LOCK_VERIFY -> { + tvTitle.text = getString(R.string.title__re_enter_your_pin) + tvSubTitle.text = getString(R.string.msg__enter_your_pin) + tilPIN.helperText = "" + tilPIN.isErrorEnabled = false + } + STEP_SECURITY_LOCK_CREATE -> { + tvTitle.text = getString(R.string.title__set_bitsy_screen_lock) + tvSubTitle.text = getString(R.string.msg__set_bitsy_pin) + tilPIN.helperText = getString(R.string.msg__min_pin_length) + tilPIN.isErrorEnabled = false + } + STEP_SECURITY_LOCK_CONFIRM -> { + tvTitle.text = getString(R.string.title__re_enter_your_pin) + tvSubTitle.text = "" + tvSubTitle.visibility = View.GONE + tietPIN.setText("") + tilPIN.helperText = "" + tilPIN.isErrorEnabled = false + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/SettingsFragment.kt b/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/SettingsFragment.kt index f6b13d4..c8354bf 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/SettingsFragment.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/SettingsFragment.kt @@ -21,7 +21,6 @@ import cy.agorise.bitsybitshareswallet.adapters.FullNodesAdapter import cy.agorise.bitsybitshareswallet.repositories.AuthorityRepository import cy.agorise.bitsybitshareswallet.utils.Constants import cy.agorise.bitsybitshareswallet.utils.CryptoUtils -import cy.agorise.bitsybitshareswallet.utils.toast import cy.agorise.graphenej.BrainKey import cy.agorise.graphenej.api.android.NetworkService import cy.agorise.graphenej.api.android.RxBus @@ -108,15 +107,12 @@ class SettingsFragment : Fragment(), ServiceConnection, BaseSecurityLockDialog.O // 1 -> Pattern // 2 -> None - btnViewBrainKey.setOnClickListener { - if (!verifySecurityLock(securityLockSelected, ACTION_SHOW_BRAINKEY)) - getBrainkey() - } - tvSecurityLockSelected.text = resources.getStringArray(R.array.security_lock_options)[securityLockSelected] - tvSecurityLock.setOnClickListener { onSecurityLockTextSelected(securityLockSelected) } - tvSecurityLockSelected.setOnClickListener { onSecurityLockTextSelected(securityLockSelected) } + tvSecurityLock.setOnClickListener { onSecurityLockTextSelected() } + tvSecurityLockSelected.setOnClickListener { onSecurityLockTextSelected() } + + btnViewBrainKey.setOnClickListener { onShowBrainKeyButtonSelected() } // Connect to the RxBus, which receives events from the NetworkService mDisposables.add( @@ -216,7 +212,14 @@ class SettingsFragment : Fragment(), ServiceConnection, BaseSecurityLockDialog.O } } - private fun onSecurityLockTextSelected(securityLockSelected: Int) { + private fun onSecurityLockTextSelected() { + val securityLockSelected = PreferenceManager.getDefaultSharedPreferences(context) + .getInt(Constants.KEY_SECURITY_LOCK_SELECTED, 0) + // Security Lock Options + // 0 -> PIN + // 1 -> Pattern + // 2 -> None + if (!verifySecurityLock(securityLockSelected, ACTION_CHANGE_SECURITY_LOCK)) showChooseSecurityLockDialog() } @@ -231,7 +234,7 @@ class SettingsFragment : Fragment(), ServiceConnection, BaseSecurityLockDialog.O */ private fun verifySecurityLock(securityLockSelected: Int, actionIdentifier: Int): Boolean { return when (securityLockSelected) { - 0 /* PIN */ -> { + 0 -> { /* PIN */ val pinFrag = PINSecurityLockDialog() val args = Bundle() args.putInt(BaseSecurityLockDialog.KEY_STEP_SECURITY_LOCK, @@ -241,7 +244,7 @@ class SettingsFragment : Fragment(), ServiceConnection, BaseSecurityLockDialog.O pinFrag.show(childFragmentManager, "pin_security_lock_tag") true } - 1 /* Pattern */ -> { + 1 -> { /* Pattern */ true } @@ -260,7 +263,15 @@ class SettingsFragment : Fragment(), ServiceConnection, BaseSecurityLockDialog.O } override fun onPINPatternChanged() { + // Obtain the new Security Lock Option selected and display it in the screen + val securityLockSelected = PreferenceManager.getDefaultSharedPreferences(context) + .getInt(Constants.KEY_SECURITY_LOCK_SELECTED, 0) + // Security Lock Options + // 0 -> PIN + // 1 -> Pattern + // 2 -> None + tvSecurityLockSelected.text = resources.getStringArray(R.array.security_lock_options)[securityLockSelected] } /** @@ -271,12 +282,44 @@ class SettingsFragment : Fragment(), ServiceConnection, BaseSecurityLockDialog.O MaterialDialog(it).show { title(R.string.title__security_dialog) listItems(R.array.security_lock_options) {dialog, index, text -> - dialog.context.toast("$text selected!") + when (index) { + 0 -> { /* PIN */ + val pinFrag = PINSecurityLockDialog() + val args = Bundle() + args.putInt(BaseSecurityLockDialog.KEY_STEP_SECURITY_LOCK, + BaseSecurityLockDialog.STEP_SECURITY_LOCK_CREATE) + args.putInt(BaseSecurityLockDialog.KEY_ACTION_IDENTIFIER, -1) + pinFrag.arguments = args + pinFrag.show(childFragmentManager, "pin_security_lock_tag") + } + 1 -> { /* Pattern */ + + } + else -> { /* None */ + PreferenceManager.getDefaultSharedPreferences(context).edit() + .putInt(Constants.KEY_SECURITY_LOCK_SELECTED, 2).apply() // 2 -> None + + // Call this function to update the UI + onPINPatternChanged() + } + } } } } } + private fun onShowBrainKeyButtonSelected() { + val securityLockSelected = PreferenceManager.getDefaultSharedPreferences(context) + .getInt(Constants.KEY_SECURITY_LOCK_SELECTED, 0) + // Security Lock Options + // 0 -> PIN + // 1 -> Pattern + // 2 -> None + + if (!verifySecurityLock(securityLockSelected, ACTION_SHOW_BRAINKEY)) + getBrainkey() + } + /** * Obtains the brainKey from the authorities db table for the current user account and if it is not null it passes * the brainKey to a method to show it in a nice MaterialDialog diff --git a/app/src/main/res/layout/dialog_pin_security_lock.xml b/app/src/main/res/layout/dialog_pin_security_lock.xml index c7f96a3..5d75a31 100644 --- a/app/src/main/res/layout/dialog_pin_security_lock.xml +++ b/app/src/main/res/layout/dialog_pin_security_lock.xml @@ -19,14 +19,15 @@ android:layout_marginTop="@dimen/spacing_same_topic" android:src="@drawable/ic_lock" app:layout_constraintTop_toTopOf="parent" - app:layout_constraintStart_toStartOf="parent"/> + app:layout_constraintStart_toStartOf="parent" + android:contentDescription="@string/title__re_enter_your_pin" /> @@ -35,7 +36,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" - android:text="Enter your BiTSy PIN to continue" + tools:text="Enter your BiTSy PIN to continue" android:textAppearance="@style/TextAppearance.Bitsy.Body1" app:layout_constraintTop_toBottomOf="@id/tvTitle"/> diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 117e636..6c2bea3 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -144,5 +144,12 @@ PIN Patrón Ninguno + Reingresa tu PIN + Digita tu PIN de BiTSy para continuar + PIN incorrecto + Crea un bloqueo de seguridad para BiTSy + Por seguridad, crea un PIN para BiTSy + El PIN debe tener al menos 6 dígitos + El PIN no concuerda diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 35f6e73..67f7e56 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -145,5 +145,12 @@ PIN Pattern None + Re-enter your PIN + Enter your BiTSy PIN to continue + Wrong PIN + Set BiTSy screen lock + For security, set BiTSy PIN + PIN must be at least 6 digits + PINs don\'t match