- Update the MaterialDialogs library version.

- Add the functionality to the Settings BrainKey's View & Show button. It first fetches the brainkey from the authorities db table and then shows it in a custom MaterialDialog so that the user can view and copy it.
This commit is contained in:
Severiano Jaramillo 2018-12-13 16:01:26 -06:00
parent f45d9055c3
commit 8f0026c205
8 changed files with 112 additions and 4 deletions

View file

@ -59,7 +59,7 @@ dependencies {
implementation 'com.moldedbits.r2d2:r2d2:1.0.1' implementation 'com.moldedbits.r2d2:r2d2:1.0.1'
implementation 'com.google.zxing:core:3.3.1' implementation 'com.google.zxing:core:3.3.1'
implementation 'me.dm7.barcodescanner:zxing:1.9.8' implementation 'me.dm7.barcodescanner:zxing:1.9.8'
implementation 'com.afollestad.material-dialogs:core:2.0.0-rc1' implementation 'com.afollestad.material-dialogs:core:2.0.0-rc3'
// Android Debug Database // Android Debug Database
debugImplementation 'com.amitshekhar.android:debug-db:1.0.4' debugImplementation 'com.amitshekhar.android:debug-db:1.0.4'

View file

@ -39,14 +39,19 @@ class MainActivity : ConnectedActivity() {
val host: NavHostFragment = supportFragmentManager val host: NavHostFragment = supportFragmentManager
.findFragmentById(R.id.navHostFragment) as NavHostFragment? ?: return .findFragmentById(R.id.navHostFragment) as NavHostFragment? ?: return
// Set up Action Bar // Set up Action Bar with Navigation's controller
val navController = host.navController val navController = host.navController
appBarConfiguration = AppBarConfiguration(navController.graph) appBarConfiguration = AppBarConfiguration(navController.graph)
// Sets up the ActionBar with the navigation controller so that it automatically responds to clicks on toolbar
// menu items and shows the up navigation button on all fragments except home (Balances)
setupActionBarWithNavController(navController, appBarConfiguration) setupActionBarWithNavController(navController, appBarConfiguration)
mHandler = Handler() mHandler = Handler()
// When this runnable finishes it first verifies if the auto close feature is enabled and if it is then it
// closes the app, if not then it just restarts the Handler (timer)
mRunnable = Runnable { mRunnable = Runnable {
if (PreferenceManager.getDefaultSharedPreferences(this) if (PreferenceManager.getDefaultSharedPreferences(this)
.getBoolean(Constants.KEY_AUTO_CLOSE_ACTIVATED, false)) .getBoolean(Constants.KEY_AUTO_CLOSE_ACTIVATED, false))
@ -57,11 +62,17 @@ class MainActivity : ConnectedActivity() {
startHandler() startHandler()
} }
/**
* Restarts the Handler (timer) each time there is user's interaction
*/
override fun onUserInteraction() { override fun onUserInteraction() {
super.onUserInteraction() super.onUserInteraction()
restartHandler() restartHandler()
} }
/**
* Stops and then restarts the Handler
*/
private fun restartHandler() { private fun restartHandler() {
stopHandler() stopHandler()
startHandler() startHandler()

View file

@ -12,6 +12,9 @@ interface AuthorityDao {
@Insert @Insert
fun insert(authority: Authority) fun insert(authority: Authority)
@Query("SELECT * FROM authorities WHERE user_id=:userId LIMIT 1")
fun get(userId: String): Single<Authority>
@Query("SELECT * FROM authorities") @Query("SELECT * FROM authorities")
fun getAll(): LiveData<List<Authority>> fun getAll(): LiveData<List<Authority>>

View file

@ -1,17 +1,31 @@
package cy.agorise.bitsybitshareswallet.fragments package cy.agorise.bitsybitshareswallet.fragments
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context.CLIPBOARD_SERVICE
import android.os.Bundle import android.os.Bundle
import android.preference.PreferenceManager import android.preference.PreferenceManager
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.widget.Toast
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.customview.customView
import cy.agorise.bitsybitshareswallet.R import cy.agorise.bitsybitshareswallet.R
import cy.agorise.bitsybitshareswallet.repositories.AuthorityRepository
import cy.agorise.bitsybitshareswallet.utils.Constants import cy.agorise.bitsybitshareswallet.utils.Constants
import cy.agorise.bitsybitshareswallet.utils.CryptoUtils
import cy.agorise.graphenej.BrainKey
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.fragment_settings.* import kotlinx.android.synthetic.main.fragment_settings.*
class SettingsFragment : Fragment() { class SettingsFragment : Fragment() {
private val TAG = this.javaClass.simpleName
private var mDisposables = CompositeDisposable()
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
@ -28,8 +42,14 @@ class SettingsFragment : Fragment() {
initAutoCloseSwitch() initAutoCloseSwitch()
initNightModeSwitch() initNightModeSwitch()
btnViewBrainKey.setOnClickListener { getBrainkey(it) }
} }
/**
* Fetches the relevant preference from the SharedPreferences and configures the corresponding switch accordingly,
* and adds a listener to the said switch to store the preference in case the user changes it.
*/
private fun initAutoCloseSwitch() { private fun initAutoCloseSwitch() {
val autoCloseOn = PreferenceManager.getDefaultSharedPreferences(context) val autoCloseOn = PreferenceManager.getDefaultSharedPreferences(context)
.getBoolean(Constants.KEY_AUTO_CLOSE_ACTIVATED, false) .getBoolean(Constants.KEY_AUTO_CLOSE_ACTIVATED, false)
@ -42,6 +62,11 @@ class SettingsFragment : Fragment() {
} }
} }
/**
* Fetches the relevant preference from the SharedPreferences and configures the corresponding switch accordingly,
* and adds a listener to the said switch to store the preference in case the user changes it. Also makes a call to
* recreate the activity and apply the selected theme.
*/
private fun initNightModeSwitch() { private fun initNightModeSwitch() {
val nightModeOn = PreferenceManager.getDefaultSharedPreferences(context) val nightModeOn = PreferenceManager.getDefaultSharedPreferences(context)
.getBoolean(Constants.KEY_NIGHT_MODE_ACTIVATED, false) .getBoolean(Constants.KEY_NIGHT_MODE_ACTIVATED, false)
@ -57,5 +82,55 @@ class SettingsFragment : Fragment() {
activity?.recreate() activity?.recreate()
} }
} }
/**
* 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, "") ?: ""
val authorityRepository = AuthorityRepository(view.context)
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)
}
}
)
}
/**
* 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(android.R.string.copy) {
Toast.makeText(it.context, "Copied to clipboard", Toast.LENGTH_SHORT).show()
val clipboard = it.context.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("label", brainKey.brainKey)
clipboard.primaryClip = clip
it.dismiss()
}
}
}
override fun onDestroy() {
super.onDestroy()
if (!mDisposables.isDisposed) mDisposables.dispose()
}
} }

View file

@ -122,7 +122,8 @@ class TransfersLoader(private var mContext: Context?): ServiceConnection {
Log.e(TAG, "AEADBadTagException. Class: " + e.javaClass + ", Msg: " + e.message) Log.e(TAG, "AEADBadTagException. Class: " + e.javaClass + ", Msg: " + e.message)
} }
}) }
)
mDisposables.add(RxBus.getBusInstance() mDisposables.add(RxBus.getBusInstance()
.asFlowable() .asFlowable()
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
@ -145,7 +146,8 @@ class TransfersLoader(private var mContext: Context?): ServiceConnection {
responseMap.clear() responseMap.clear()
} }
} }
})) })
)
} else { } else {
// If there is no current user, we should not do anything // If there is no current user, we should not do anything
mState = State.CANCELLED mState = State.CANCELLED

View file

@ -20,6 +20,10 @@ class AuthorityRepository internal constructor(context: Context) {
insertAsyncTask(mAuthorityDao).execute(authority) insertAsyncTask(mAuthorityDao).execute(authority)
} }
fun get(userId: String): Single<Authority> {
return mAuthorityDao.get(userId)
}
fun getWIF(userId: String, authorityType: Int): Single<String> { fun getWIF(userId: String, authorityType: Int): Single<String> {
return mAuthorityDao.getWIF(userId, authorityType) return mAuthorityDao.getWIF(userId, authorityType)
} }

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/msg__brainkey_info"
android:textSize="13sp"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingStart="@dimen/activity_horizontal_margin"
android:paddingEnd="@dimen/activity_horizontal_margin"/>

View file

@ -66,6 +66,8 @@
<string name="msg__brainkey_description">BrainKey. Account recovery words that can be captured or copied, but not <string name="msg__brainkey_description">BrainKey. Account recovery words that can be captured or copied, but not
edited. edited.
</string> </string>
<string name="msg__brainkey_info">Print this out, or write it down. Anyone with access to your recovery key will
have access to funds within this wallet.</string>
<string name="btn__view_and_copy"><![CDATA[View & Copy]]></string> <string name="btn__view_and_copy"><![CDATA[View & Copy]]></string>
<string name="title__bugs_or_ideas">Bugs or Ideas?</string> <string name="title__bugs_or_ideas">Bugs or Ideas?</string>
<string name="msg__bugs_or_ideas">Telegram chat: http://t.me/Agorise\nEmail: Agorise@protonmail.ch\nOpen Source: <string name="msg__bugs_or_ideas">Telegram chat: http://t.me/Agorise\nEmail: Agorise@protonmail.ch\nOpen Source: