Added the functionality to PINSecurityLockDialog to check if the entered PIN corresponds with the current PIN and if that is the case let the calling fragmet about it, so that the fragment can respond accordingly

This commit is contained in:
Severiano Jaramillo 2019-02-14 12:53:35 -06:00
parent 123482e996
commit f3c85e8875
6 changed files with 111 additions and 17 deletions

View file

@ -32,7 +32,7 @@ abstract class BaseAccountFragment : ConnectedFragment() {
* @param accountProperties Account properties object * @param accountProperties Account properties object
*/ */
protected fun onAccountSelected(accountProperties: AccountProperties, pin: String) { protected fun onAccountSelected(accountProperties: AccountProperties, pin: String) {
val encryptedPIN = CryptoUtils.encrypt(context!!, pin) val encryptedPIN = CryptoUtils.encrypt(context!!, pin).trim()
// Stores the user selected PIN encrypted // Stores the user selected PIN encrypted
PreferenceManager.getDefaultSharedPreferences(context!!) PreferenceManager.getDefaultSharedPreferences(context!!)

View file

@ -1,8 +1,13 @@
package cy.agorise.bitsybitshareswallet.fragments package cy.agorise.bitsybitshareswallet.fragments
import android.app.Dialog
import android.os.Bundle import android.os.Bundle
import android.preference.PreferenceManager
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import cy.agorise.bitsybitshareswallet.utils.Constants
import io.reactivex.disposables.CompositeDisposable
/** /**
* Encapsulates the shared logic required for the PIN and Pattern Security Lock Fragments. * Encapsulates the shared logic required for the PIN and Pattern Security Lock Fragments.
@ -11,18 +16,72 @@ abstract class BaseSecurityLockDialog : DialogFragment() {
companion object { companion object {
/** Used to denote that the user is in the step of creating the preferred security lock option */ /** Used to denote that the user is in the step of creating the preferred security lock option */
const val SECURITY_LOG_STEP_CREATE = 0x01 const val SECURITY_LOG_STEP_CREATE = 1
/** Used to denote that the user is in the step of confirming the just created security lock option */ /** Used to denote that the user is in the step of confirming the newly created security lock option */
const val SECURITY_LOG_STEP_CONFIRM = 0x02 private const val SECURITY_LOG_STEP_CONFIRM = 2
/** Used to denote that the user is in the step of verifying the current security lock option, to give /** 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 * permission to do a security constrained action like sending a transaction or trying to change the
* current security lock option */ * current security lock option */
const val SECURITY_LOG_STEP_VERIFY = 0x04 const val SECURITY_LOG_STEP_VERIFY = 3
/** 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 */
const val KEY_ACTION_IDENTIFIER = "key_action_identifier"
} }
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { // Container Fragment must implement this interface
return super.onCreateDialog(savedInstanceState) interface OnPINPatternEnteredListener {
fun onPINPatternEntered(actionIdentifier: Int)
fun onPINPatternChanged()
}
/** Callback used to notify the parent that a PIN/Pattern has been entered successfully */
protected var mCallback: OnPINPatternEnteredListener? = null
protected var actionIdentifier: Int = 0
/** Keeps track of all RxJava disposables, to make sure they are all disposed when the fragment is destroyed */
protected var mDisposables = CompositeDisposable()
/** Current encrypted version of the PIN/Pattern */
protected var currentEncryptedPINPattern: String? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
onAttachToParentFragment(parentFragment)
currentEncryptedPINPattern = PreferenceManager.getDefaultSharedPreferences(context)
.getString(Constants.KEY_ENCRYPTED_PIN, "")?.trim()
actionIdentifier = arguments?.getInt(KEY_ACTION_IDENTIFIER) ?: 0
}
/**
* Attaches the current [DialogFragment] to its [Fragment] parent, to initialize the
* [OnPINPatternEnteredListener] interface
*/
private fun onAttachToParentFragment(fragment: Fragment?) {
try {
mCallback = fragment as OnPINPatternEnteredListener
} catch (e: ClassCastException) {
throw ClassCastException("$fragment must implement OnFilterOptionsSelectedListener")
}
}
override fun onResume() {
super.onResume()
// Force dialog fragment to use the full width of the screen
val dialogWindow = dialog.window
dialogWindow?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
}
override fun onDestroy() {
super.onDestroy()
if (!mDisposables.isDisposed) mDisposables.dispose()
} }
} }

View file

@ -149,7 +149,7 @@ class FilterOptionsDialog : DialogFragment() {
tvEndDate.text = dateFormat.format(date) tvEndDate.text = dateFormat.format(date)
} }
// Container Activity must implement this interface // Container Fragment must implement this interface
interface OnFilterOptionsSelectedListener { interface OnFilterOptionsSelectedListener {
fun onFilterOptionsSelected(filterTransactionsDirection: Int, fun onFilterOptionsSelected(filterTransactionsDirection: Int,
filterDateRangeAll: Boolean, filterDateRangeAll: Boolean,

View file

@ -4,7 +4,12 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import com.jakewharton.rxbinding3.widget.textChanges
import cy.agorise.bitsybitshareswallet.R import cy.agorise.bitsybitshareswallet.R
import cy.agorise.bitsybitshareswallet.utils.CryptoUtils
import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.android.synthetic.main.dialog_pin_security_lock.*
/** /**
* Contains all the specific logic to create and confirm a new PIN or verifying the validity of the current one. * Contains all the specific logic to create and confirm a new PIN or verifying the validity of the current one.
@ -23,14 +28,29 @@ class PINSecurityLockDialog : BaseSecurityLockDialog() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
// 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 (encryptedPIN == currentEncryptedPINPattern) {
// PIN is correct, proceed
dismiss()
mCallback?.onPINPatternEntered(actionIdentifier)
} else {
tilPIN.error = "Wrong PIN"
} }
override fun onResume() { handled = true
super.onResume() }
handled
}
// Force dialog fragment to use the full width of the screen // Use RxBindings to clear the error when the user edits the PIN
val dialogWindow = dialog.window mDisposables.add(
dialogWindow?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) tietPIN.textChanges()
.observeOn(AndroidSchedulers.mainThread())
.subscribe { tilPIN.isErrorEnabled = false }
)
} }
} }

View file

@ -37,10 +37,12 @@ import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.fragment_settings.* import kotlinx.android.synthetic.main.fragment_settings.*
import java.text.NumberFormat import java.text.NumberFormat
class SettingsFragment : Fragment(), ServiceConnection { class SettingsFragment : Fragment(), ServiceConnection, BaseSecurityLockDialog.OnPINPatternEnteredListener {
companion object { companion object {
private const val TAG = "SettingsFragment" private const val TAG = "SettingsFragment"
private const val ACTION_CHANGE_SECURITY_LOCK = 1
} }
private var mDisposables = CompositeDisposable() private var mDisposables = CompositeDisposable()
@ -214,6 +216,9 @@ class SettingsFragment : Fragment(), ServiceConnection {
when (securityLockSelected) { when (securityLockSelected) {
0 /* PIN */ -> { 0 /* PIN */ -> {
val pinFrag = PINSecurityLockDialog() val pinFrag = PINSecurityLockDialog()
val args = Bundle()
args.putInt(BaseSecurityLockDialog.KEY_ACTION_IDENTIFIER, ACTION_CHANGE_SECURITY_LOCK)
pinFrag.arguments = args
pinFrag.show(childFragmentManager, "pin_security_lock_tag") pinFrag.show(childFragmentManager, "pin_security_lock_tag")
} }
1 /* Pattern */ -> { 1 /* Pattern */ -> {
@ -225,6 +230,15 @@ class SettingsFragment : Fragment(), ServiceConnection {
} }
} }
override fun onPINPatternEntered(actionIdentifier: Int) {
if (actionIdentifier == ACTION_CHANGE_SECURITY_LOCK)
showChooseSecurityLockDialog()
}
override fun onPINPatternChanged() {
}
/** /**
* Shows a dialog so the user can select its desired Security Lock option. * Shows a dialog so the user can select its desired Security Lock option.
*/ */

View file

@ -40,6 +40,7 @@
app:layout_constraintTop_toBottomOf="@id/tvTitle"/> app:layout_constraintTop_toBottomOf="@id/tvTitle"/>
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/tilPIN"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_different_section" android:layout_marginStart="@dimen/spacing_different_section"