- Added the RxBindings library to be able to use RxJava on Android Widgets.

- Connected the SendTransactionFragment to Graphenej's NetworkService so that it can directly send requests to the nodes.
- Added the RxBus to SendTransactionsFragment so that it can listen to responses from the NetworkService and take the useful ones.
- Added account validation to SendTransactionFragment, if the account typed exists then the Send button is enabled, if not the button is disabled an a error is shown below the typed account.
This commit is contained in:
Severiano Jaramillo 2018-12-05 18:38:34 -06:00
parent 5388e90331
commit 4739e41fce
3 changed files with 119 additions and 2 deletions

View file

@ -47,6 +47,7 @@ dependencies {
kapt "androidx.room:room-compiler:$room_version" kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-rxjava2:$room_version" // RxJava support for Room implementation "androidx.room:room-rxjava2:$room_version" // RxJava support for Room
implementation 'com.jakewharton.rxbinding2:rxbinding:2.1.1'
implementation 'org.bitcoinj:bitcoinj-core:0.14.3' implementation 'org.bitcoinj:bitcoinj-core:0.14.3'
implementation 'com.moldedbits.r2d2:r2d2:1.0.1' implementation 'com.moldedbits.r2d2:r2d2:1.0.1'

View file

@ -6,7 +6,6 @@ import android.preference.PreferenceManager
import android.util.Log import android.util.Log
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.widget.Toast
import androidx.collection.LongSparseArray import androidx.collection.LongSparseArray
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomnavigation.BottomNavigationView

View file

@ -1,8 +1,14 @@
package cy.agorise.bitsybitshareswallet.fragments package cy.agorise.bitsybitshareswallet.fragments
import android.Manifest import android.Manifest
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Bundle import android.os.Bundle
import android.os.IBinder
import android.preference.PreferenceManager
import android.util.Log import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@ -15,24 +21,39 @@ import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders import androidx.lifecycle.ViewModelProviders
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 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.utils.Constants
import cy.agorise.bitsybitshareswallet.viewmodels.BalanceDetailViewModel import cy.agorise.bitsybitshareswallet.viewmodels.BalanceDetailViewModel
import cy.agorise.graphenej.Invoice import cy.agorise.graphenej.Invoice
import cy.agorise.graphenej.UserAccount
import cy.agorise.graphenej.api.ConnectionStatusUpdate
import cy.agorise.graphenej.api.android.NetworkService
import cy.agorise.graphenej.api.android.RxBus
import cy.agorise.graphenej.api.calls.GetAccountByName
import cy.agorise.graphenej.models.AccountProperties
import cy.agorise.graphenej.models.JsonRpcResponse
import cy.agorise.graphenej.operations.TransferOperationBuilder
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
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 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.Locale import java.util.Locale
import java.util.concurrent.TimeUnit
class SendTransactionFragment : Fragment(), ZXingScannerView.ResultHandler { class SendTransactionFragment : Fragment(), ZXingScannerView.ResultHandler, ServiceConnection {
private val TAG = this.javaClass.simpleName private val TAG = this.javaClass.simpleName
// Camera Permission // Camera Permission
private val REQUEST_CAMERA_PERMISSION = 1 private val REQUEST_CAMERA_PERMISSION = 1
private val RESPONSE_GET_ACCOUNT_BY_NAME = 1
private var isCameraPreviewVisible = false private var isCameraPreviewVisible = false
private var mBalancesDetails: List<BalanceDetail>? = null private var mBalancesDetails: List<BalanceDetail>? = null
@ -43,6 +64,22 @@ class SendTransactionFragment : Fragment(), ZXingScannerView.ResultHandler {
private var selectedAssetSymbol = "" private var selectedAssetSymbol = ""
/** Current user account */
private var mUserAccount: UserAccount? = null
/** User account to which send the funds */
private var mSelectedUserAccount: UserAccount? = null
private var mDisposables = CompositeDisposable()
/* Network service connection */
private var mNetworkService: NetworkService? = null
/** Flag used to keep track of the NetworkService binding state */
private var mShouldUnbindNetwork: Boolean = false
// Map used to keep track of request and response id pairs
private val responseMap = HashMap<Long, Int>()
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)
@ -51,6 +88,12 @@ class SendTransactionFragment : Fragment(), ZXingScannerView.ResultHandler {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
val userId = PreferenceManager.getDefaultSharedPreferences(context)
.getString(Constants.KEY_CURRENT_ACCOUNT_ID, "")
if (userId != "")
mUserAccount = UserAccount(userId)
verifyCameraPermission() verifyCameraPermission()
fabOpenCamera.setOnClickListener { if (isCameraPreviewVisible) stopCameraPreview() else verifyCameraPermission() } fabOpenCamera.setOnClickListener { if (isCameraPreviewVisible) stopCameraPreview() else verifyCameraPermission() }
@ -81,6 +124,52 @@ class SendTransactionFragment : Fragment(), ZXingScannerView.ResultHandler {
} }
fabSendTransaction.setOnClickListener { validateFields() } fabSendTransaction.setOnClickListener { validateFields() }
fabSendTransaction.hide()
// Use RxJava Debounce to avoid making calls to the NetworkService on every text change event
mDisposables.add(RxTextView.textChanges(tietTo)
.map { it.toString().trim() }
.filter { it.length > 1 }
.debounce(500, TimeUnit.MILLISECONDS)
.subscribe {
val id = mNetworkService!!.sendMessage(GetAccountByName(it!!), GetAccountByName.REQUIRED_API)
responseMap[id] = RESPONSE_GET_ACCOUNT_BY_NAME
}
)
mDisposables.add(RxBus.getBusInstance()
.asFlowable()
.observeOn(AndroidSchedulers.mainThread())
.subscribe { message ->
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 handleAccountName(result: Any?) {
if (result is AccountProperties) {
mSelectedUserAccount = UserAccount(result.id, result.name)
tilTo.isErrorEnabled = false
fabSendTransaction.show()
} else {
mSelectedUserAccount = null
tilTo.error = "Invalid account"
fabSendTransaction.hide()
}
} }
private fun verifyCameraPermission() { private fun verifyCameraPermission() {
@ -162,13 +251,27 @@ class SendTransactionFragment : Fragment(), ZXingScannerView.ResultHandler {
} }
private fun validateFields() { private fun validateFields() {
tilAmount.isErrorEnabled = false
// val selectedAsset = mAssetsAdapter!!.getItem(spAsset.selectedItemPosition)
// val selectedAmount = tietAmount.getTex
// Create TransferOperation
val builder = TransferOperationBuilder()
builder.setSource(mUserAccount)
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
if (isCameraPreviewVisible) if (isCameraPreviewVisible)
startCameraPreview() startCameraPreview()
val intent = Intent(context, NetworkService::class.java)
if (context!!.bindService(intent, this, Context.BIND_AUTO_CREATE)) {
mShouldUnbindNetwork = true
} else {
Log.e(TAG, "Binding to the network service failed.")
}
} }
override fun onPause() { override fun onPause() {
@ -176,4 +279,18 @@ class SendTransactionFragment : Fragment(), ZXingScannerView.ResultHandler {
if (!isCameraPreviewVisible) if (!isCameraPreviewVisible)
stopCameraPreview() stopCameraPreview()
} }
override fun onDestroy() {
super.onDestroy()
if (!mDisposables.isDisposed) mDisposables.dispose()
}
override fun onServiceDisconnected(name: ComponentName?) { }
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
val binder = service as NetworkService.LocalBinder
mNetworkService = binder.service
}
} }