Added a responseMap to the SettingsFragment to differentiate different requests made to the Node through graphenej's NetworkService. Added a method to obtain the encrypted private keys from the db through the SettingsFragmentViewModel, to create and sign the Upgrade to LTM transaction.
This commit is contained in:
parent
99e378d123
commit
b1a1abf231
6 changed files with 91 additions and 19 deletions
|
@ -19,5 +19,8 @@ interface AuthorityDao {
|
||||||
fun getAll(): LiveData<List<Authority>>
|
fun getAll(): LiveData<List<Authority>>
|
||||||
|
|
||||||
@Query("SELECT encrypted_wif FROM authorities WHERE user_id=:userId AND authority_type=:authorityType")
|
@Query("SELECT encrypted_wif FROM authorities WHERE user_id=:userId AND authority_type=:authorityType")
|
||||||
fun getWIF(userId: String, authorityType: Int): Single<String>
|
fun getWIFOld(userId: String, authorityType: Int): Single<String>
|
||||||
|
|
||||||
|
@Query("SELECT encrypted_wif FROM authorities WHERE user_id=:userId AND authority_type=:authorityType")
|
||||||
|
fun getWIF(userId: String, authorityType: Int): LiveData<String>
|
||||||
}
|
}
|
|
@ -181,7 +181,7 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
|
|
||||||
// Obtain the WifKey from the db, which is used in the Send Transfer procedure
|
// Obtain the WifKey from the db, which is used in the Send Transfer procedure
|
||||||
mDisposables.add(
|
mDisposables.add(
|
||||||
authorityRepository!!.getWIF(userId, AuthorityType.ACTIVE.ordinal)
|
authorityRepository!!.getWIFOld(userId, AuthorityType.ACTIVE.ordinal)
|
||||||
.subscribeOn(Schedulers.computation())
|
.subscribeOn(Schedulers.computation())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe { encryptedWIF ->
|
.subscribe { encryptedWIF ->
|
||||||
|
|
|
@ -16,6 +16,7 @@ import com.afollestad.materialdialogs.customview.customView
|
||||||
import com.afollestad.materialdialogs.list.customListAdapter
|
import com.afollestad.materialdialogs.list.customListAdapter
|
||||||
import com.afollestad.materialdialogs.list.listItemsSingleChoice
|
import com.afollestad.materialdialogs.list.listItemsSingleChoice
|
||||||
import com.crashlytics.android.Crashlytics
|
import com.crashlytics.android.Crashlytics
|
||||||
|
import com.google.common.primitives.UnsignedLong
|
||||||
import cy.agorise.bitsybitshareswallet.BuildConfig
|
import cy.agorise.bitsybitshareswallet.BuildConfig
|
||||||
import cy.agorise.bitsybitshareswallet.R
|
import cy.agorise.bitsybitshareswallet.R
|
||||||
import cy.agorise.bitsybitshareswallet.adapters.FullNodesAdapter
|
import cy.agorise.bitsybitshareswallet.adapters.FullNodesAdapter
|
||||||
|
@ -23,19 +24,23 @@ 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.bitsybitshareswallet.utils.CryptoUtils
|
||||||
import cy.agorise.bitsybitshareswallet.viewmodels.SettingsFragmentViewModel
|
import cy.agorise.bitsybitshareswallet.viewmodels.SettingsFragmentViewModel
|
||||||
import cy.agorise.graphenej.BrainKey
|
import cy.agorise.graphenej.*
|
||||||
import cy.agorise.graphenej.UserAccount
|
|
||||||
import cy.agorise.graphenej.api.ConnectionStatusUpdate
|
import cy.agorise.graphenej.api.ConnectionStatusUpdate
|
||||||
|
import cy.agorise.graphenej.api.calls.BroadcastTransaction
|
||||||
import cy.agorise.graphenej.api.calls.GetDynamicGlobalProperties
|
import cy.agorise.graphenej.api.calls.GetDynamicGlobalProperties
|
||||||
import cy.agorise.graphenej.models.DynamicGlobalProperties
|
import cy.agorise.graphenej.models.DynamicGlobalProperties
|
||||||
import cy.agorise.graphenej.models.JsonRpcResponse
|
import cy.agorise.graphenej.models.JsonRpcResponse
|
||||||
import cy.agorise.graphenej.network.FullNode
|
import cy.agorise.graphenej.network.FullNode
|
||||||
|
import cy.agorise.graphenej.operations.AccountUpgradeOperationBuilder
|
||||||
import io.reactivex.Observer
|
import io.reactivex.Observer
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import io.reactivex.disposables.Disposable
|
import io.reactivex.disposables.Disposable
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.schedulers.Schedulers
|
||||||
import kotlinx.android.synthetic.main.fragment_settings.*
|
import kotlinx.android.synthetic.main.fragment_settings.*
|
||||||
|
import org.bitcoinj.core.DumpedPrivateKey
|
||||||
|
import org.bitcoinj.core.ECKey
|
||||||
import java.text.NumberFormat
|
import java.text.NumberFormat
|
||||||
|
import javax.crypto.AEADBadTagException
|
||||||
|
|
||||||
class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatternEnteredListener {
|
class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatternEnteredListener {
|
||||||
|
|
||||||
|
@ -45,18 +50,27 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
||||||
// Constants used to perform security locked requests
|
// Constants used to perform security locked requests
|
||||||
private const val ACTION_CHANGE_SECURITY_LOCK = 1
|
private const val ACTION_CHANGE_SECURITY_LOCK = 1
|
||||||
private const val ACTION_SHOW_BRAINKEY = 2
|
private const val ACTION_SHOW_BRAINKEY = 2
|
||||||
|
|
||||||
|
// Constants used to organize NetworkService requests
|
||||||
|
private const val RESPONSE_GET_DYNAMIC_GLOBAL_PARAMETERS = 1
|
||||||
|
private const val RESPONSE_BROADCAST_TRANSACTION = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var mViewModel: SettingsFragmentViewModel
|
private lateinit var mViewModel: SettingsFragmentViewModel
|
||||||
|
|
||||||
private var mUserAccount: UserAccount? = null
|
private var mUserAccount: UserAccount? = null
|
||||||
|
|
||||||
|
private var privateKey: String? = null
|
||||||
|
|
||||||
// Dialog displaying the list of nodes and their latencies
|
// Dialog displaying the list of nodes and their latencies
|
||||||
private var mNodesDialog: MaterialDialog? = null
|
private var mNodesDialog: MaterialDialog? = null
|
||||||
|
|
||||||
/** Adapter that holds the FullNode list used in the Bitshares nodes modal */
|
/** Adapter that holds the FullNode list used in the Bitshares nodes modal */
|
||||||
private var nodesAdapter: FullNodesAdapter? = null
|
private var nodesAdapter: FullNodesAdapter? = null
|
||||||
|
|
||||||
|
// Map used to keep track of request and response id pairs
|
||||||
|
private val responseMap = HashMap<Long, Int>()
|
||||||
|
|
||||||
private val mHandler = Handler()
|
private val mHandler = Handler()
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
@ -80,9 +94,21 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
||||||
androidx.lifecycle.Observer<cy.agorise.bitsybitshareswallet.database.entities.UserAccount>{ userAccount ->
|
androidx.lifecycle.Observer<cy.agorise.bitsybitshareswallet.database.entities.UserAccount>{ userAccount ->
|
||||||
if (userAccount != null) {
|
if (userAccount != null) {
|
||||||
mUserAccount = UserAccount(userAccount.id, userAccount.name)
|
mUserAccount = UserAccount(userAccount.id, userAccount.name)
|
||||||
|
btnUpgradeToLTM.isEnabled = !userAccount.isLtm // Disable button if already LTM
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
mViewModel.getWIF(userId, AuthorityType.ACTIVE.ordinal).observe(this,
|
||||||
|
androidx.lifecycle.Observer<String> { encryptedWIF ->
|
||||||
|
context?.let {
|
||||||
|
try {
|
||||||
|
privateKey = CryptoUtils.decrypt(it, encryptedWIF)
|
||||||
|
} catch (e: AEADBadTagException) {
|
||||||
|
Log.e(TAG, "AEADBadTagException. Class: " + e.javaClass + ", Msg: " + e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
initAutoCloseSwitch()
|
initAutoCloseSwitch()
|
||||||
|
|
||||||
initNightModeSwitch()
|
initNightModeSwitch()
|
||||||
|
@ -153,31 +179,50 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handleJsonRpcResponse(response: JsonRpcResponse<*>) {
|
override fun handleJsonRpcResponse(response: JsonRpcResponse<*>) {
|
||||||
if (response.result is DynamicGlobalProperties) {
|
if (responseMap.containsKey(response.id)) {
|
||||||
val dynamicGlobalProperties = response.result as DynamicGlobalProperties
|
val responseType = responseMap[response.id]
|
||||||
|
when (responseType) {
|
||||||
|
RESPONSE_GET_DYNAMIC_GLOBAL_PARAMETERS -> handleDynamicGlobalProperties(response.result)
|
||||||
|
RESPONSE_BROADCAST_TRANSACTION -> handleBroadcastTransaction(response)
|
||||||
|
}
|
||||||
|
responseMap.remove(response.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handleConnectionStatusUpdate(connectionStatusUpdate: ConnectionStatusUpdate) { }
|
||||||
|
|
||||||
|
/** Handles the result of the [GetDynamicGlobalProperties] api call to obtain the current block number and update
|
||||||
|
* it in the Nodes Dialog */
|
||||||
|
private fun handleDynamicGlobalProperties(result: Any?) {
|
||||||
|
if (result is DynamicGlobalProperties) {
|
||||||
if (mNodesDialog != null && mNodesDialog?.isShowing == true) {
|
if (mNodesDialog != null && mNodesDialog?.isShowing == true) {
|
||||||
val blockNumber = NumberFormat.getInstance().format(dynamicGlobalProperties.head_block_number)
|
val blockNumber = NumberFormat.getInstance().format(result.head_block_number)
|
||||||
mNodesDialog?.message(text = getString(R.string.title__bitshares_nodes_dialog, blockNumber))
|
mNodesDialog?.message(text = getString(R.string.title__bitshares_nodes_dialog, blockNumber))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handleConnectionStatusUpdate(connectionStatusUpdate: ConnectionStatusUpdate) { }
|
/** Handles the result of the [BroadcastTransaction] api call to find out if the Transaction was sent successfully
|
||||||
|
* or not and acts accordingly */
|
||||||
|
private fun handleBroadcastTransaction(message: JsonRpcResponse<*>) {
|
||||||
|
if (message.result == null && message.error == null) {
|
||||||
|
// context?.toast(getString(R.string.text__transaction_sent))
|
||||||
|
//
|
||||||
|
// // Return to the main screen
|
||||||
|
// findNavController().navigateUp()
|
||||||
|
} else if (message.error != null) {
|
||||||
|
// context?.toast(message.error.message, Toast.LENGTH_LONG)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Task used to obtain frequent updates on the global dynamic properties object
|
* Task used to obtain frequent updates on the global dynamic properties object
|
||||||
*/
|
*/
|
||||||
private val mRequestDynamicGlobalPropertiesTask = object : Runnable {
|
private val mRequestDynamicGlobalPropertiesTask = object : Runnable {
|
||||||
override fun run() {
|
override fun run() {
|
||||||
if (mNetworkService != null) {
|
val id = mNetworkService?.sendMessage(GetDynamicGlobalProperties(), GetDynamicGlobalProperties.REQUIRED_API)
|
||||||
if (mNetworkService?.isConnected == true) {
|
if (id != null) responseMap[id] = RESPONSE_GET_DYNAMIC_GLOBAL_PARAMETERS
|
||||||
mNetworkService?.sendMessage(GetDynamicGlobalProperties(), GetDynamicGlobalProperties.REQUIRED_API)
|
|
||||||
} else {
|
|
||||||
Log.d(TAG, "NetworkService exists but is not connected")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.d(TAG, "NetworkService reference is null")
|
|
||||||
}
|
|
||||||
mHandler.postDelayed(this, Constants.BLOCK_PERIOD)
|
mHandler.postDelayed(this, Constants.BLOCK_PERIOD)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -384,7 +429,20 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
||||||
message(text = content)
|
message(text = content)
|
||||||
negativeButton(android.R.string.cancel)
|
negativeButton(android.R.string.cancel)
|
||||||
positiveButton(android.R.string.ok) {
|
positiveButton(android.R.string.ok) {
|
||||||
|
val operation = AccountUpgradeOperationBuilder()
|
||||||
|
.setIsUpgrade(true)
|
||||||
|
.setFee(AssetAmount(UnsignedLong.ZERO, Asset("1.3.0"))) // 0 BTS
|
||||||
|
.setAccountToUpgrade(mUserAccount).build()
|
||||||
|
|
||||||
|
val operations = ArrayList<BaseOperation>()
|
||||||
|
operations.add(operation)
|
||||||
|
|
||||||
|
val currentPrivateKey = ECKey.fromPrivate(
|
||||||
|
DumpedPrivateKey.fromBase58(null, privateKey).key.privKeyBytes)
|
||||||
|
val transaction = Transaction(currentPrivateKey, null, operations)
|
||||||
|
|
||||||
|
val id = mNetworkService?.sendMessage(BroadcastTransaction(transaction), BroadcastTransaction.REQUIRED_API)
|
||||||
|
if (id != null) responseMap[id] = RESPONSE_BROADCAST_TRANSACTION
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,7 +103,7 @@ class TransfersLoader(private var mContext: Context?) {
|
||||||
if (userId != "") {
|
if (userId != "") {
|
||||||
mCurrentAccount = UserAccount(userId)
|
mCurrentAccount = UserAccount(userId)
|
||||||
mDisposables.add(
|
mDisposables.add(
|
||||||
authorityRepository!!.getWIF(userId, AuthorityType.MEMO.ordinal)
|
authorityRepository!!.getWIFOld(userId, AuthorityType.MEMO.ordinal)
|
||||||
.subscribeOn(Schedulers.computation())
|
.subscribeOn(Schedulers.computation())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe { encryptedWIF ->
|
.subscribe { encryptedWIF ->
|
||||||
|
|
|
@ -2,6 +2,7 @@ package cy.agorise.bitsybitshareswallet.repositories
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.AsyncTask
|
import android.os.AsyncTask
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
import cy.agorise.bitsybitshareswallet.database.daos.AuthorityDao
|
import cy.agorise.bitsybitshareswallet.database.daos.AuthorityDao
|
||||||
import cy.agorise.bitsybitshareswallet.database.BitsyDatabase
|
import cy.agorise.bitsybitshareswallet.database.BitsyDatabase
|
||||||
import cy.agorise.bitsybitshareswallet.database.entities.Authority
|
import cy.agorise.bitsybitshareswallet.database.entities.Authority
|
||||||
|
@ -24,7 +25,11 @@ class AuthorityRepository internal constructor(context: Context) {
|
||||||
return mAuthorityDao.get(userId)
|
return mAuthorityDao.get(userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getWIF(userId: String, authorityType: Int): Single<String> {
|
fun getWIFOld(userId: String, authorityType: Int): Single<String> {
|
||||||
|
return mAuthorityDao.getWIFOld(userId, authorityType)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getWIF(userId: String, authorityType: Int): LiveData<String> {
|
||||||
return mAuthorityDao.getWIF(userId, authorityType)
|
return mAuthorityDao.getWIF(userId, authorityType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,12 +4,18 @@ import android.app.Application
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import cy.agorise.bitsybitshareswallet.database.entities.UserAccount
|
import cy.agorise.bitsybitshareswallet.database.entities.UserAccount
|
||||||
|
import cy.agorise.bitsybitshareswallet.repositories.AuthorityRepository
|
||||||
import cy.agorise.bitsybitshareswallet.repositories.UserAccountRepository
|
import cy.agorise.bitsybitshareswallet.repositories.UserAccountRepository
|
||||||
|
|
||||||
class SettingsFragmentViewModel(application: Application) : AndroidViewModel(application) {
|
class SettingsFragmentViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
private var mUserAccountRepository = UserAccountRepository(application)
|
private var mUserAccountRepository = UserAccountRepository(application)
|
||||||
|
private var mAuthorityRepository = AuthorityRepository(application)
|
||||||
|
|
||||||
internal fun getUserAccount(id: String): LiveData<UserAccount> {
|
internal fun getUserAccount(id: String): LiveData<UserAccount> {
|
||||||
return mUserAccountRepository.getUserAccount(id)
|
return mUserAccountRepository.getUserAccount(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun getWIF(userId: String, authorityType: Int): LiveData<String> {
|
||||||
|
return mAuthorityRepository.getWIF(userId, authorityType)
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue