Create method to initiate the first step required to send a transaction from the wallet, which is building a Graphenej's transaction object and request the DynamicGlobalProperties.

This commit is contained in:
Severiano Jaramillo 2018-12-06 15:12:38 -06:00
parent 53479f8837
commit 00df51e9f9
2 changed files with 91 additions and 25 deletions

View file

@ -19,32 +19,41 @@ import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders import androidx.lifecycle.ViewModelProviders
import com.google.common.primitives.UnsignedLong
import com.google.zxing.BarcodeFormat import com.google.zxing.BarcodeFormat
import com.google.zxing.Result import com.google.zxing.Result
import com.jakewharton.rxbinding2.widget.RxTextView import com.jakewharton.rxbinding2.widget.RxTextView
import cy.agorise.bitsybitshareswallet.R import cy.agorise.bitsybitshareswallet.R
import cy.agorise.bitsybitshareswallet.adapters.AssetsAdapter import cy.agorise.bitsybitshareswallet.adapters.AssetsAdapter
import cy.agorise.bitsybitshareswallet.database.joins.BalanceDetail import cy.agorise.bitsybitshareswallet.database.joins.BalanceDetail
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.viewmodels.BalanceDetailViewModel import cy.agorise.bitsybitshareswallet.viewmodels.BalanceDetailViewModel
import cy.agorise.graphenej.Invoice 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.android.NetworkService import cy.agorise.graphenej.api.android.NetworkService
import cy.agorise.graphenej.api.android.RxBus import cy.agorise.graphenej.api.android.RxBus
import cy.agorise.graphenej.api.calls.GetAccountByName import cy.agorise.graphenej.api.calls.GetAccountByName
import cy.agorise.graphenej.api.calls.GetDynamicGlobalProperties
import cy.agorise.graphenej.models.AccountProperties import cy.agorise.graphenej.models.AccountProperties
import cy.agorise.graphenej.models.DynamicGlobalProperties
import cy.agorise.graphenej.models.JsonRpcResponse import cy.agorise.graphenej.models.JsonRpcResponse
import cy.agorise.graphenej.operations.TransferOperationBuilder import cy.agorise.graphenej.operations.TransferOperationBuilder
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.fragment_send_transaction.* import kotlinx.android.synthetic.main.fragment_send_transaction.*
import me.dm7.barcodescanner.zxing.ZXingScannerView import me.dm7.barcodescanner.zxing.ZXingScannerView
import org.bitcoinj.core.DumpedPrivateKey
import org.bitcoinj.core.ECKey
import java.math.RoundingMode import java.math.RoundingMode
import java.text.DecimalFormat import java.text.DecimalFormat
import java.text.DecimalFormatSymbols import java.text.DecimalFormatSymbols
import java.util.ArrayList
import java.util.Locale import java.util.Locale
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.crypto.AEADBadTagException
class SendTransactionFragment : Fragment(), ZXingScannerView.ResultHandler, ServiceConnection { class SendTransactionFragment : Fragment(), ZXingScannerView.ResultHandler, ServiceConnection {
private val TAG = this.javaClass.simpleName private val TAG = this.javaClass.simpleName
@ -53,6 +62,7 @@ class SendTransactionFragment : Fragment(), ZXingScannerView.ResultHandler, Serv
private val REQUEST_CAMERA_PERMISSION = 1 private val REQUEST_CAMERA_PERMISSION = 1
private val RESPONSE_GET_ACCOUNT_BY_NAME = 1 private val RESPONSE_GET_ACCOUNT_BY_NAME = 1
private val RESPOSE_GET_DYNAMIC_GLOBAL_PARAMETERS = 2
private var isCameraPreviewVisible = false private var isCameraPreviewVisible = false
private var isToAccountCorrect = false private var isToAccountCorrect = false
@ -83,6 +93,14 @@ class SendTransactionFragment : Fragment(), ZXingScannerView.ResultHandler, Serv
// Map used to keep track of request and response id pairs // Map used to keep track of request and response id pairs
private val responseMap = HashMap<Long, Int>() private val responseMap = HashMap<Long, Int>()
private var transaction: Transaction? = null
/** Variable holding the current user's private key in the WIF format */
private var wifKey: String? = null
/** Repository to access and update Authorities */
private var authorityRepository: AuthorityRepository? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_send_transaction, container, false) return inflater.inflate(R.layout.fragment_send_transaction, container, false)
} }
@ -131,9 +149,23 @@ class SendTransactionFragment : Fragment(), ZXingScannerView.ResultHandler, Serv
} }
} }
fabSendTransaction.setOnClickListener { validateFields() } fabSendTransaction.setOnClickListener { startSendTransferOperation() }
fabSendTransaction.hide() fabSendTransaction.hide()
authorityRepository = AuthorityRepository(context!!)
mDisposables.add(authorityRepository!!.getWIF(userId!!, AuthorityType.ACTIVE.ordinal)
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { encryptedWIF ->
try {
wifKey = CryptoUtils.decrypt(context!!, encryptedWIF)
} catch (e: AEADBadTagException) {
Log.e(TAG, "AEADBadTagException. Class: " + e.javaClass + ", Msg: " + e.message)
}
})
// Use RxJava Debounce to avoid making calls to the NetworkService on every text change event // Use RxJava Debounce to avoid making calls to the NetworkService on every text change event
mDisposables.add(RxTextView.textChanges(tietTo) mDisposables.add(RxTextView.textChanges(tietTo)
.debounce(500, TimeUnit.MILLISECONDS) .debounce(500, TimeUnit.MILLISECONDS)
@ -145,6 +177,7 @@ class SendTransactionFragment : Fragment(), ZXingScannerView.ResultHandler, Serv
} }
) )
// Use RxJava Debounce to update the Amount error only after the user stops writing for > 500 ms
mDisposables.add(RxTextView.textChanges(tietAmount) mDisposables.add(RxTextView.textChanges(tietAmount)
.debounce(500, TimeUnit.MILLISECONDS) .debounce(500, TimeUnit.MILLISECONDS)
.filter { it.isNotEmpty() } .filter { it.isNotEmpty() }
@ -153,15 +186,21 @@ class SendTransactionFragment : Fragment(), ZXingScannerView.ResultHandler, Serv
.subscribe { validateAmount(it!!) } .subscribe { validateAmount(it!!) }
) )
// Connect to the RxBus, which receives events from the NetworkService
mDisposables.add(RxBus.getBusInstance() mDisposables.add(RxBus.getBusInstance()
.asFlowable() .asFlowable()
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe { message -> .subscribe { handleIncomingMessage(it) }
)
}
private fun handleIncomingMessage(message: Any?) {
if (message is JsonRpcResponse<*>) { if (message is JsonRpcResponse<*>) {
if (responseMap.containsKey(message.id)) { if (responseMap.containsKey(message.id)) {
val responseType = responseMap[message.id] val responseType = responseMap[message.id]
when (responseType) { when (responseType) {
RESPONSE_GET_ACCOUNT_BY_NAME -> handleAccountName(message.result) RESPONSE_GET_ACCOUNT_BY_NAME -> handleAccountName(message.result)
RESPOSE_GET_DYNAMIC_GLOBAL_PARAMETERS -> handleDynamicGlobalProperties(message.result)
} }
responseMap.remove(message.id) responseMap.remove(message.id)
} }
@ -173,8 +212,6 @@ class SendTransactionFragment : Fragment(), ZXingScannerView.ResultHandler, Serv
} }
} }
} }
)
}
private fun handleAccountName(result: Any?) { private fun handleAccountName(result: Any?) {
if (result is AccountProperties) { if (result is AccountProperties) {
@ -190,6 +227,15 @@ class SendTransactionFragment : Fragment(), ZXingScannerView.ResultHandler, Serv
enableDisableSendFAB() enableDisableSendFAB()
} }
private fun handleDynamicGlobalProperties(result: Any?) {
if (result is DynamicGlobalProperties) {
Log.d(TAG, "DynamicGlobalProperties: " + result.toString())
} else {
// TODO unableToSendTransactionError()
}
}
private fun verifyCameraPermission() { private fun verifyCameraPermission() {
if (ContextCompat.checkSelfPermission(activity!!, Manifest.permission.CAMERA) if (ContextCompat.checkSelfPermission(activity!!, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) { != PackageManager.PERMISSION_GRANTED) {
@ -290,10 +336,30 @@ class SendTransactionFragment : Fragment(), ZXingScannerView.ResultHandler, Serv
fabSendTransaction.hide() fabSendTransaction.hide()
} }
private fun validateFields() { private fun startSendTransferOperation() {
// Create TransferOperation // Create TransferOperation
val builder = TransferOperationBuilder() if (mNetworkService!!.isConnected) {
builder.setSource(mUserAccount) val balance = mAssetsAdapter!!.getItem(spAsset.selectedItemPosition)!!
val amount = (tietAmount.text.toString().toDouble() * Math.pow(10.0, balance.precision.toDouble())).toLong()
val transferAmount = AssetAmount(UnsignedLong.valueOf(amount), Asset(balance.id))
val operationBuilder = TransferOperationBuilder()
.setSource(mUserAccount)
.setDestination(mSelectedUserAccount)
.setTransferAmount(transferAmount)
val operations = ArrayList<BaseOperation>()
operations.add(operationBuilder.build())
val privateKey = ECKey.fromPrivate(DumpedPrivateKey.fromBase58(null, wifKey).key.privKeyBytes)
transaction = Transaction(privateKey, null, operations)
val id = mNetworkService!!.sendMessage(GetDynamicGlobalProperties(),
GetDynamicGlobalProperties.REQUIRED_API)
responseMap[id] = RESPOSE_GET_DYNAMIC_GLOBAL_PARAMETERS
} else
Log.d(TAG, "Network Service is not connected")
} }
override fun onResume() { override fun onResume() {

View file

@ -72,7 +72,7 @@ class TransfersLoader(private var mContext: Context?, private val mLifeCycle: Li
/** Repository to access and update Transfers */ /** Repository to access and update Transfers */
private var transferRepository: TransferRepository? = null private var transferRepository: TransferRepository? = null
/** Repository to access and update Transfers */ /** Repository to access and update Authorities */
private var authorityRepository: AuthorityRepository? = null private var authorityRepository: AuthorityRepository? = null
/* Network service connection */ /* Network service connection */