- Move the method to updateBalances from the MainActivity to the ConnectedActivity, so that it can be always trying to update the Balances even if we are not in the MainActivity and also to be able to update the Balances right after we notice there are new transactions for the current BitShares account.

- Move the TransfersLoader from MainActivity for the same reason than the updateBalances method described above and fix it so that it no longer needs the activity Lifecycle.
This commit is contained in:
Severiano Jaramillo 2018-12-08 22:55:28 -06:00
parent 0f96c48135
commit 50a266ffb4
3 changed files with 68 additions and 95 deletions

View file

@ -13,13 +13,17 @@ import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders import androidx.lifecycle.ViewModelProviders
import cy.agorise.bitsybitshareswallet.database.entities.Balance
import cy.agorise.bitsybitshareswallet.processors.TransfersLoader import cy.agorise.bitsybitshareswallet.processors.TransfersLoader
import cy.agorise.bitsybitshareswallet.repositories.BalanceRepository
import cy.agorise.bitsybitshareswallet.utils.Constants import cy.agorise.bitsybitshareswallet.utils.Constants
import cy.agorise.bitsybitshareswallet.viewmodels.UserAccountViewModel import cy.agorise.bitsybitshareswallet.viewmodels.UserAccountViewModel
import cy.agorise.graphenej.AssetAmount
import cy.agorise.graphenej.UserAccount 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.GetAccountBalances
import cy.agorise.graphenej.api.calls.GetAccounts import cy.agorise.graphenej.api.calls.GetAccounts
import cy.agorise.graphenej.api.calls.GetFullAccounts import cy.agorise.graphenej.api.calls.GetFullAccounts
import cy.agorise.graphenej.models.AccountProperties import cy.agorise.graphenej.models.AccountProperties
@ -37,6 +41,8 @@ abstract class ConnectedActivity : AppCompatActivity(), ServiceConnection {
private lateinit var mUserAccountViewModel: UserAccountViewModel private lateinit var mUserAccountViewModel: UserAccountViewModel
private var mBalanceRepository: BalanceRepository? = null
/* Current user account */ /* Current user account */
protected var mCurrentAccount: UserAccount? = null protected var mCurrentAccount: UserAccount? = null
@ -65,6 +71,20 @@ abstract class ConnectedActivity : AppCompatActivity(), ServiceConnection {
if (userId != "") if (userId != "")
mCurrentAccount = UserAccount(userId) mCurrentAccount = UserAccount(userId)
mBalanceRepository = BalanceRepository(this)
// Configure UserAccountViewModel to show the current account
mUserAccountViewModel = ViewModelProviders.of(this).get(UserAccountViewModel::class.java)
mUserAccountViewModel.getMissingUserAccountIds().observe(this, Observer<List<String>>{ userAccountIds ->
if (userAccountIds.isNotEmpty()) {
for (userAccountId in userAccountIds)
missingUserAccounts.add(UserAccount(userAccountId))
mHandler.postDelayed(mRequestMissingUserAccountsTask, Constants.NETWORK_SERVICE_RETRY_PERIOD)
}
})
mDisposable = RxBus.getBusInstance() mDisposable = RxBus.getBusInstance()
.asFlowable() .asFlowable()
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
@ -79,6 +99,8 @@ abstract class ConnectedActivity : AppCompatActivity(), ServiceConnection {
handleAccountDetails((message.result as List<*>)[0] as FullAccountDetails) handleAccountDetails((message.result as List<*>)[0] as FullAccountDetails)
} else if ((message.result as List<*>)[0] is AccountProperties) { } else if ((message.result as List<*>)[0] is AccountProperties) {
handleAccountProperties(message.result as List<AccountProperties>) handleAccountProperties(message.result as List<AccountProperties>)
} else if((message.result as List<*>)[0] is AssetAmount) {
handleBalanceUpdate(message.result as List<AssetAmount>)
} }
} }
} else { } else {
@ -92,26 +114,10 @@ abstract class ConnectedActivity : AppCompatActivity(), ServiceConnection {
} }
} else if (message is ConnectionStatusUpdate) { } else if (message is ConnectionStatusUpdate) {
handleConnectionStatusUpdate(message) handleConnectionStatusUpdate(message)
if (message.updateCode == ConnectionStatusUpdate.DISCONNECTED) {
// recurrentAccountUpdateId = -1
// accountOpRequestId = -1
} }
} }
} }
// Configure UserAccountViewModel to show the current account
mUserAccountViewModel = ViewModelProviders.of(this).get(UserAccountViewModel::class.java)
mUserAccountViewModel.getMissingUserAccountIds().observe(this, Observer<List<String>>{ userAccountIds ->
if (userAccountIds.isNotEmpty()) {
for (userAccountId in userAccountIds)
missingUserAccounts.add(UserAccount(userAccountId))
mHandler.postDelayed(mRequestMissingUserAccountsTask, Constants.NETWORK_SERVICE_RETRY_PERIOD)
}
})
}
/** /**
* Method called whenever a response to the 'get_full_accounts' API call has been detected. * Method called whenever a response to the 'get_full_accounts' API call has been detected.
* @param accountDetails De-serialized account details object * @param accountDetails De-serialized account details object
@ -125,13 +131,16 @@ abstract class ConnectedActivity : AppCompatActivity(), ServiceConnection {
"\nAsk the NetworkService to remove the node from the list and connect to another one.") "\nAsk the NetworkService to remove the node from the list and connect to another one.")
mNetworkService!!.removeCurrentNodeAndReconnect() mNetworkService!!.removeCurrentNodeAndReconnect()
} else if (storedOpCount == -1L) { } else if (storedOpCount == -1L) {
// Initial case // Initial case when the app starts
storedOpCount = latestOpCount storedOpCount = latestOpCount
PreferenceManager.getDefaultSharedPreferences(this) PreferenceManager.getDefaultSharedPreferences(this)
.edit().putLong(Constants.KEY_ACCOUNT_OPERATION_COUNT, latestOpCount).apply() .edit().putLong(Constants.KEY_ACCOUNT_OPERATION_COUNT, latestOpCount).apply()
TransfersLoader(this)
updateBalances()
} else if (latestOpCount > storedOpCount) { } else if (latestOpCount > storedOpCount) {
storedOpCount = latestOpCount storedOpCount = latestOpCount
TransfersLoader(this, lifecycle) TransfersLoader(this)
updateBalances()
} }
} }
@ -152,6 +161,31 @@ abstract class ConnectedActivity : AppCompatActivity(), ServiceConnection {
missingUserAccounts.clear() missingUserAccounts.clear()
} }
private fun handleBalanceUpdate(assetAmountList: List<AssetAmount>) {
Log.d(TAG, "handleBalanceUpdate")
val now = System.currentTimeMillis() / 1000
val balances = ArrayList<Balance>()
for (assetAmount in assetAmountList) {
val balance = Balance(
assetAmount.asset.objectId,
assetAmount.amount.toLong(),
now
)
balances.add(balance)
}
mBalanceRepository!!.insertAll(balances)
}
private fun updateBalances() {
if (mNetworkService!!.isConnected) {
mNetworkService!!.sendMessage(
GetAccountBalances(mCurrentAccount, ArrayList()),
GetAccountBalances.REQUIRED_API
)
}
}
/** /**
* Task used to obtain the missing UserAccounts. * Task used to obtain the missing UserAccounts.
*/ */
@ -171,13 +205,14 @@ abstract class ConnectedActivity : AppCompatActivity(), ServiceConnection {
private val mCheckMissingPaymentsTask = object : Runnable { private val mCheckMissingPaymentsTask = object : Runnable {
override fun run() { override fun run() {
if (mNetworkService != null && mNetworkService!!.isConnected) { if (mNetworkService != null && mNetworkService!!.isConnected) {
// Checking that we actually have a user id registered in the shared preferences if (mCurrentAccount != null) {
val userAccounts = ArrayList<String>() val userAccounts = ArrayList<String>()
userAccounts.add(mCurrentAccount!!.objectId) userAccounts.add(mCurrentAccount!!.objectId)
mNetworkService!!.sendMessage( mNetworkService!!.sendMessage(
GetFullAccounts(userAccounts, false), GetFullAccounts(userAccounts, false),
GetFullAccounts.REQUIRED_API GetFullAccounts.REQUIRED_API
) )
}
} else { } else {
Log.w(TAG, "NetworkService is null or is not connected. mNetworkService: $mNetworkService") Log.w(TAG, "NetworkService is null or is not connected. mNetworkService: $mNetworkService")
} }
@ -214,9 +249,6 @@ abstract class ConnectedActivity : AppCompatActivity(), ServiceConnection {
Log.e(TAG, "Binding to the network service failed.") Log.e(TAG, "Binding to the network service failed.")
} }
mHandler.postDelayed(mCheckMissingPaymentsTask, Constants.MISSING_PAYMENT_CHECK_PERIOD) mHandler.postDelayed(mCheckMissingPaymentsTask, Constants.MISSING_PAYMENT_CHECK_PERIOD)
storedOpCount = PreferenceManager.getDefaultSharedPreferences(this)
.getLong(Constants.KEY_ACCOUNT_OPERATION_COUNT, -1)
} }
override fun onDestroy() { override fun onDestroy() {

View file

@ -2,36 +2,22 @@ package cy.agorise.bitsybitshareswallet.activities
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
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
import cy.agorise.bitsybitshareswallet.R import cy.agorise.bitsybitshareswallet.R
import cy.agorise.bitsybitshareswallet.database.entities.Balance
import cy.agorise.bitsybitshareswallet.fragments.BalancesFragment import cy.agorise.bitsybitshareswallet.fragments.BalancesFragment
import cy.agorise.bitsybitshareswallet.fragments.MerchantsFragment import cy.agorise.bitsybitshareswallet.fragments.MerchantsFragment
import cy.agorise.bitsybitshareswallet.fragments.ReceiveTransactionFragment import cy.agorise.bitsybitshareswallet.fragments.ReceiveTransactionFragment
import cy.agorise.bitsybitshareswallet.fragments.SendTransactionFragment import cy.agorise.bitsybitshareswallet.fragments.SendTransactionFragment
import cy.agorise.bitsybitshareswallet.processors.TransfersLoader
import cy.agorise.bitsybitshareswallet.repositories.BalanceRepository
import cy.agorise.graphenej.AssetAmount
import cy.agorise.graphenej.RPC
import cy.agorise.graphenej.api.ApiAccess
import cy.agorise.graphenej.api.ConnectionStatusUpdate import cy.agorise.graphenej.api.ConnectionStatusUpdate
import cy.agorise.graphenej.api.calls.GetAccountBalances
import cy.agorise.graphenej.models.JsonRpcResponse import cy.agorise.graphenej.models.JsonRpcResponse
import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main.*
import java.util.ArrayList
class MainActivity : ConnectedActivity() { class MainActivity : ConnectedActivity() {
private val TAG = this.javaClass.simpleName private val TAG = this.javaClass.simpleName
private val requestMap = LongSparseArray<String>()
private var mBalanceRepository: BalanceRepository? = null
private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item -> private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
when (item.itemId) { when (item.itemId) {
R.id.navigation_receive -> { R.id.navigation_receive -> {
@ -60,8 +46,6 @@ class MainActivity : ConnectedActivity() {
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener) navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
navigation.selectedItemId = R.id.navigation_balances navigation.selectedItemId = R.id.navigation_balances
mBalanceRepository = BalanceRepository(this)
} }
private fun loadFragment(fragment: Fragment) { private fun loadFragment(fragment: Fragment) {
@ -88,9 +72,7 @@ class MainActivity : ConnectedActivity() {
} }
override fun handleJsonRpcResponse(response: JsonRpcResponse<*>) { override fun handleJsonRpcResponse(response: JsonRpcResponse<*>) {
if (requestMap.get(response.id) == RPC.CALL_GET_ACCOUNT_BALANCES) {
handleBalanceUpdate(response as JsonRpcResponse<List<AssetAmount>>)
}
} }
/** /**
@ -102,46 +84,7 @@ class MainActivity : ConnectedActivity() {
ConnectionStatusUpdate.CONNECTED -> { /* Do nothing for now */ } ConnectionStatusUpdate.CONNECTED -> { /* Do nothing for now */ }
ConnectionStatusUpdate.DISCONNECTED -> { /* Do nothing for now */ } ConnectionStatusUpdate.DISCONNECTED -> { /* Do nothing for now */ }
ConnectionStatusUpdate.AUTHENTICATED -> {}//updateBalances() } ConnectionStatusUpdate.AUTHENTICATED -> {}//updateBalances() }
ConnectionStatusUpdate.API_UPDATE -> { ConnectionStatusUpdate.API_UPDATE -> { }
// In certain cases the information about the accounts is not complete, this may not be the best
// solution but at least it works. Feel free to improve it or move it to a better place
//MissingAccountsLoader(this, lifecycle)
if (connectionStatusUpdate.api == ApiAccess.API_NETWORK_BROADCAST) {
Log.d(TAG, "ConnectionStatusUpdate: API_NETWORK_BROADCAST")
// Instantiating this loader is enough to kick-start the transfers loading procedure
TransfersLoader(this, lifecycle)
updateBalances()
} }
} }
} }
}
private fun updateBalances() {
if (mNetworkService!!.isConnected) {
val id = mNetworkService!!.sendMessage(
GetAccountBalances(mCurrentAccount, ArrayList()),
GetAccountBalances.REQUIRED_API
)
requestMap.put(id, RPC.CALL_GET_ACCOUNT_BALANCES)
}
}
private fun handleBalanceUpdate(response: JsonRpcResponse<List<AssetAmount>>) {
Log.d(TAG, "handleBalanceUpdate")
val now = System.currentTimeMillis() / 1000
val assetBalances = response.result
val balances = ArrayList<Balance>()
for (assetBalance in assetBalances) {
val balance = Balance(
assetBalance.asset.objectId,
assetBalance.amount.toLong(),
now
)
balances.add(balance)
}
mBalanceRepository!!.insertAll(balances)
}
}

View file

@ -48,8 +48,7 @@ import javax.crypto.AEADBadTagException
* for every transfer, we must first load all historical transfer operations, and then proceed to * for every transfer, we must first load all historical transfer operations, and then proceed to
* handle those missing columns. * handle those missing columns.
*/ */
class TransfersLoader(private var mContext: Context?, private val mLifeCycle: Lifecycle) : LifecycleObserver, class TransfersLoader(private var mContext: Context?): ServiceConnection {
ServiceConnection {
private val TAG = this.javaClass.simpleName private val TAG = this.javaClass.simpleName
@ -106,7 +105,6 @@ class TransfersLoader(private var mContext: Context?, private val mLifeCycle: Li
} }
init { init {
this.mLifeCycle.addObserver(this)
transferRepository = TransferRepository(mContext!!) transferRepository = TransferRepository(mContext!!)
authorityRepository = AuthorityRepository(mContext!!) authorityRepository = AuthorityRepository(mContext!!)
@ -152,6 +150,8 @@ class TransfersLoader(private var mContext: Context?, private val mLifeCycle: Li
// 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
} }
onStart()
} }
override fun onServiceDisconnected(name: ComponentName?) { override fun onServiceDisconnected(name: ComponentName?) {
@ -167,8 +167,7 @@ class TransfersLoader(private var mContext: Context?, private val mLifeCycle: Li
startTransfersUpdateProcedure() startTransfersUpdateProcedure()
} }
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME) private fun onStart() {
internal fun onStart() {
if (mState != State.CANCELLED) { if (mState != State.CANCELLED) {
val intent = Intent(mContext, NetworkService::class.java) val intent = Intent(mContext, NetworkService::class.java)
if (mContext!!.bindService(intent, this, Context.BIND_AUTO_CREATE)) { if (mContext!!.bindService(intent, this, Context.BIND_AUTO_CREATE)) {
@ -219,7 +218,7 @@ class TransfersLoader(private var mContext: Context?, private val mLifeCycle: Li
// TODO return number of inserted rows // TODO return number of inserted rows
// Log.d(TAG, String.format("Inserted count: %d, list size: %d", insertedCount, operationHistoryList.size)) // Log.d(TAG, String.format("Inserted count: %d, list size: %d", insertedCount, operationHistoryList.size))
if (/* insertedCount == 0 && */ operationHistoryList.isEmpty()) { if (/* insertedCount == 0 && */ operationHistoryList.isEmpty()) {
// TODO Terminate process and obtain MissingTimes and MissingEquivalentValues onDestroy()
} else { } else {
// If we inserted more than one operation, we cannot yet be sure we've reached the // If we inserted more than one operation, we cannot yet be sure we've reached the
@ -320,8 +319,7 @@ class TransfersLoader(private var mContext: Context?, private val mLifeCycle: Li
return transfers return transfers
} }
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) private fun onDestroy() {
internal fun onDestroy() {
Log.d(TAG, "Destroying TransfersLoader") Log.d(TAG, "Destroying TransfersLoader")
if (!mDisposables.isDisposed) mDisposables.dispose() if (!mDisposables.isDisposed) mDisposables.dispose()
if (mShouldUnbindNetwork) { if (mShouldUnbindNetwork) {