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:
parent
53479f8837
commit
00df51e9f9
2 changed files with 91 additions and 25 deletions
|
@ -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,29 +186,33 @@ 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) }
|
||||||
if (message is JsonRpcResponse<*>) {
|
|
||||||
if (responseMap.containsKey(message.id)) {
|
|
||||||
val responseType = responseMap[message.id]
|
|
||||||
when (responseType) {
|
|
||||||
RESPONSE_GET_ACCOUNT_BY_NAME -> handleAccountName(message.result)
|
|
||||||
}
|
|
||||||
responseMap.remove(message.id)
|
|
||||||
}
|
|
||||||
} else if (message is ConnectionStatusUpdate) {
|
|
||||||
if (message.updateCode == ConnectionStatusUpdate.DISCONNECTED) {
|
|
||||||
// If we got a disconnection notification, we should clear our response map, since
|
|
||||||
// all its stored request ids will now be reset
|
|
||||||
responseMap.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleIncomingMessage(message: Any?) {
|
||||||
|
if (message is JsonRpcResponse<*>) {
|
||||||
|
if (responseMap.containsKey(message.id)) {
|
||||||
|
val responseType = responseMap[message.id]
|
||||||
|
when (responseType) {
|
||||||
|
RESPONSE_GET_ACCOUNT_BY_NAME -> handleAccountName(message.result)
|
||||||
|
RESPOSE_GET_DYNAMIC_GLOBAL_PARAMETERS -> handleDynamicGlobalProperties(message.result)
|
||||||
|
}
|
||||||
|
responseMap.remove(message.id)
|
||||||
|
}
|
||||||
|
} else if (message is ConnectionStatusUpdate) {
|
||||||
|
if (message.updateCode == ConnectionStatusUpdate.DISCONNECTED) {
|
||||||
|
// If we got a disconnection notification, we should clear our response map, since
|
||||||
|
// all its stored request ids will now be reset
|
||||||
|
responseMap.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleAccountName(result: Any?) {
|
private fun handleAccountName(result: Any?) {
|
||||||
if (result is AccountProperties) {
|
if (result is AccountProperties) {
|
||||||
mSelectedUserAccount = UserAccount(result.id, result.name)
|
mSelectedUserAccount = UserAccount(result.id, result.name)
|
||||||
|
@ -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() {
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
Loading…
Reference in a new issue