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 c32aa44..6099f3c 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/BaseSecurityLockDialog.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/BaseSecurityLockDialog.kt @@ -16,15 +16,19 @@ abstract class BaseSecurityLockDialog : DialogFragment() { companion object { /** Used to denote that the user is in the step of creating the preferred security lock option */ - const val SECURITY_LOG_STEP_CREATE = 1 + 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 SECURITY_LOG_STEP_CONFIRM = 2 + private 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 * current security lock option */ - const val SECURITY_LOG_STEP_VERIFY = 3 + const val STEP_SECURITY_LOCK_VERIFY = 3 + + /** Used to let the dialog know if the user wants to create a new PIN/Pattern or just verify its correctness + * to get access to security constrained actions */ + const val KEY_STEP_SECURITY_LOCK = "key_step_security_lock" /** The calling fragment can be calling this dialog to unlock many different things, this variable helps to * keep track of what action the calling fragment wants to achieve */ @@ -40,6 +44,10 @@ abstract class BaseSecurityLockDialog : DialogFragment() { /** Callback used to notify the parent that a PIN/Pattern has been entered successfully */ protected var mCallback: OnPINPatternEnteredListener? = null + /** Keeps track of the current step, can be create, confirm of verify */ + 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 /** Keeps track of all RxJava disposables, to make sure they are all disposed when the fragment is destroyed */ @@ -56,6 +64,8 @@ abstract class BaseSecurityLockDialog : DialogFragment() { currentEncryptedPINPattern = PreferenceManager.getDefaultSharedPreferences(context) .getString(Constants.KEY_ENCRYPTED_PIN, "")?.trim() + currentStep = arguments?.getInt(KEY_STEP_SECURITY_LOCK) ?: 0 + actionIdentifier = arguments?.getInt(KEY_ACTION_IDENTIFIER) ?: 0 } 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 f3e1dac..df9e30a 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/PINSecurityLockDialog.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/PINSecurityLockDialog.kt @@ -33,12 +33,16 @@ class PINSecurityLockDialog : BaseSecurityLockDialog() { var handled = false if (actionId == EditorInfo.IME_ACTION_GO) { 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" + + if (currentStep == STEP_SECURITY_LOCK_VERIFY) { + // The user just wants to verify the current encrypted PIN/Pattern + if (encryptedPIN == currentEncryptedPINPattern) { + // PIN is correct, proceed + dismiss() + mCallback?.onPINPatternEntered(actionIdentifier) + } else { + tilPIN.error = "Wrong PIN" + } } handled = true 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 9473d43..f6b13d4 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/SettingsFragment.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/SettingsFragment.kt @@ -43,6 +43,7 @@ class SettingsFragment : Fragment(), ServiceConnection, BaseSecurityLockDialog.O private const val TAG = "SettingsFragment" private const val ACTION_CHANGE_SECURITY_LOCK = 1 + private const val ACTION_SHOW_BRAINKEY = 2 } private var mDisposables = CompositeDisposable() @@ -74,8 +75,6 @@ class SettingsFragment : Fragment(), ServiceConnection, BaseSecurityLockDialog.O initNightModeSwitch() - btnViewBrainKey.setOnClickListener { getBrainkey(it) } - tvNetworkStatus.setOnClickListener { v -> if (mNetworkService != null) { // PublishSubject used to announce full node latencies updates @@ -109,6 +108,11 @@ 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) } @@ -213,26 +217,46 @@ class SettingsFragment : Fragment(), ServiceConnection, BaseSecurityLockDialog.O } private fun onSecurityLockTextSelected(securityLockSelected: Int) { - when (securityLockSelected) { + if (!verifySecurityLock(securityLockSelected, ACTION_CHANGE_SECURITY_LOCK)) + showChooseSecurityLockDialog() + } + + /** + * Encapsulated the logic required to do actions possibly locked by the Security Lock. If PIN/Pattern is selected + * then it prompts for it. + * + * @param securityLockSelected Current Security Lock option selected + * @param actionIdentifier Identifier used to know why a verify security lock was launched + * @return true if the action was handled, false otherwise + */ + private fun verifySecurityLock(securityLockSelected: Int, actionIdentifier: Int): Boolean { + return when (securityLockSelected) { 0 /* PIN */ -> { val pinFrag = PINSecurityLockDialog() val args = Bundle() - args.putInt(BaseSecurityLockDialog.KEY_ACTION_IDENTIFIER, ACTION_CHANGE_SECURITY_LOCK) + args.putInt(BaseSecurityLockDialog.KEY_STEP_SECURITY_LOCK, + BaseSecurityLockDialog.STEP_SECURITY_LOCK_VERIFY) + args.putInt(BaseSecurityLockDialog.KEY_ACTION_IDENTIFIER, actionIdentifier) pinFrag.arguments = args pinFrag.show(childFragmentManager, "pin_security_lock_tag") + true } 1 /* Pattern */ -> { + true } else -> { /* None */ - + false } } } override fun onPINPatternEntered(actionIdentifier: Int) { - if (actionIdentifier == ACTION_CHANGE_SECURITY_LOCK) + if (actionIdentifier == ACTION_CHANGE_SECURITY_LOCK) { showChooseSecurityLockDialog() + } else if (actionIdentifier == ACTION_SHOW_BRAINKEY) { + getBrainkey() + } } override fun onPINPatternChanged() { @@ -249,9 +273,6 @@ class SettingsFragment : Fragment(), ServiceConnection, BaseSecurityLockDialog.O listItems(R.array.security_lock_options) {dialog, index, text -> dialog.context.toast("$text selected!") } - cancelable(false) - cancelOnTouchOutside(false) - negativeButton(android.R.string.cancel) } } } @@ -260,37 +281,41 @@ class SettingsFragment : Fragment(), ServiceConnection, BaseSecurityLockDialog.O * 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 */ - private fun getBrainkey(view: View) { - val userId = PreferenceManager.getDefaultSharedPreferences(view.context) - .getString(Constants.KEY_CURRENT_ACCOUNT_ID, "") ?: "" + private fun getBrainkey() { + context?.let { + val userId = PreferenceManager.getDefaultSharedPreferences(it) + .getString(Constants.KEY_CURRENT_ACCOUNT_ID, "") ?: "" - val authorityRepository = AuthorityRepository(view.context) + val authorityRepository = AuthorityRepository(it) - mDisposables.add(authorityRepository.get(userId) - .subscribeOn(Schedulers.computation()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { authority -> - if (authority != null) { - val plainBrainKey = CryptoUtils.decrypt(view.context, authority.encryptedBrainKey) - val plainSequenceNumber = CryptoUtils.decrypt(view.context, authority.encryptedSequenceNumber) - val sequenceNumber = Integer.parseInt(plainSequenceNumber) - val brainKey = BrainKey(plainBrainKey, sequenceNumber) - showBrainKeyDialog(view, brainKey) + mDisposables.add(authorityRepository.get(userId) + .subscribeOn(Schedulers.computation()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { authority -> + if (authority != null) { + val plainBrainKey = CryptoUtils.decrypt(it, authority.encryptedBrainKey) + val plainSequenceNumber = CryptoUtils.decrypt(it, authority.encryptedSequenceNumber) + val sequenceNumber = Integer.parseInt(plainSequenceNumber) + val brainKey = BrainKey(plainBrainKey, sequenceNumber) + showBrainKeyDialog(brainKey) + } } - } - ) + ) + } } /** * Shows the plain brainkey in a dialog so that the user can view and Copy it. */ - private fun showBrainKeyDialog(view: View, brainKey: BrainKey) { - MaterialDialog(view.context).show { - title(text = "BrainKey") - message(text = brainKey.brainKey) - customView(R.layout.dialog_copy_brainkey) - cancelable(false) - positiveButton(R.string.button__copied) { it.dismiss() } + private fun showBrainKeyDialog(brainKey: BrainKey) { + context?.let { context -> + MaterialDialog(context).show { + title(text = "BrainKey") + message(text = brainKey.brainKey) + customView(R.layout.dialog_copy_brainkey) + cancelable(false) + positiveButton(R.string.button__copied) { it.dismiss() } + } } }