diff --git a/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/BaseAccountFragment.kt b/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/BaseAccountFragment.kt index 5a10ead..0a3521a 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/BaseAccountFragment.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/BaseAccountFragment.kt @@ -32,7 +32,7 @@ abstract class BaseAccountFragment : ConnectedFragment() { * @param accountProperties Account properties object */ 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 PreferenceManager.getDefaultSharedPreferences(context!!) 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 501549f..c32aa44 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/BaseSecurityLockDialog.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/BaseSecurityLockDialog.kt @@ -1,8 +1,13 @@ package cy.agorise.bitsybitshareswallet.fragments -import android.app.Dialog 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.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. @@ -11,18 +16,72 @@ 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 = 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 */ - const val SECURITY_LOG_STEP_CONFIRM = 0x02 + /** 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 /** 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 = 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 { - return super.onCreateDialog(savedInstanceState) + // Container Fragment must implement this interface + 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() } } \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/FilterOptionsDialog.kt b/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/FilterOptionsDialog.kt index ca7ed55..2029ee0 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/FilterOptionsDialog.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/FilterOptionsDialog.kt @@ -149,7 +149,7 @@ class FilterOptionsDialog : DialogFragment() { tvEndDate.text = dateFormat.format(date) } - // Container Activity must implement this interface + // Container Fragment must implement this interface interface OnFilterOptionsSelectedListener { fun onFilterOptionsSelected(filterTransactionsDirection: Int, filterDateRangeAll: Boolean, 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 acd6da9..f3e1dac 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/PINSecurityLockDialog.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/PINSecurityLockDialog.kt @@ -4,7 +4,12 @@ import android.os.Bundle 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.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. @@ -23,14 +28,29 @@ class PINSecurityLockDialog : BaseSecurityLockDialog() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 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" + } - } + handled = true + } + handled + } - 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) + // Use RxBindings to clear the error when the user edits the PIN + mDisposables.add( + tietPIN.textChanges() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { 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 d2c6d7a..9473d43 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/SettingsFragment.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/SettingsFragment.kt @@ -37,10 +37,12 @@ import io.reactivex.schedulers.Schedulers import kotlinx.android.synthetic.main.fragment_settings.* import java.text.NumberFormat -class SettingsFragment : Fragment(), ServiceConnection { +class SettingsFragment : Fragment(), ServiceConnection, BaseSecurityLockDialog.OnPINPatternEnteredListener { companion object { private const val TAG = "SettingsFragment" + + private const val ACTION_CHANGE_SECURITY_LOCK = 1 } private var mDisposables = CompositeDisposable() @@ -214,6 +216,9 @@ class SettingsFragment : Fragment(), ServiceConnection { when (securityLockSelected) { 0 /* PIN */ -> { 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") } 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. */ 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 e6756a2..c7f96a3 100644 --- a/app/src/main/res/layout/dialog_pin_security_lock.xml +++ b/app/src/main/res/layout/dialog_pin_security_lock.xml @@ -40,6 +40,7 @@ app:layout_constraintTop_toBottomOf="@id/tvTitle"/>