Repository: horizontalsystems/unstoppable-wallet-android
Issue: Coin rate is not expiring after 10 minutes on Balance page if it was fresh at setting time 

On Balance page if rate was fresh when it was set, but later on if its not received fresh rate or received outdated rate from IPFS, the rate is not updating. Thus its stays as fresh(not dimmed as supposed to be) but behaves like outdated in Send dialog. 

Changed files:
app/src/test/java/io/horizontalsystems/bankwallet/modules/balance/BalancePresenterTest.kt
app/src/test/java/io/horizontalsystems/bankwallet/modules/send/SendPresenterTest.kt
app/src/main/java/io/horizontalsystems/bankwallet/modules/main/MainActivity.kt
app/src/main/java/io/horizontalsystems/bankwallet/modules/balance/BalancePresenter.kt
app/src/test/java/io/horizontalsystems/bankwallet/modules/receive/ReceivePresenterTest.kt
app/src/main/java/io/horizontalsystems/bankwallet/modules/balance/BalanceViewModel.kt
app/src/main/java/io/horizontalsystems/bankwallet/modules/send/SendInteractor.kt
app/src/main/java/io/horizontalsystems/bankwallet/modules/send/SendPresenter.kt
app/src/test/java/io/horizontalsystems/bankwallet/modules/receive/ReceiveInteractorTest.kt
app/src/test/java/io/horizontalsystems/bankwallet/modules/send/SendInteractorTest.kt
app/src/main/java/io/horizontalsystems/bankwallet/modules/balance/BalanceModule.kt

diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/balance/BalanceModule.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/balance/BalanceModule.kt
index 98ef825ba..f82ceb900 100644
--- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/balance/BalanceModule.kt
+++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/balance/BalanceModule.kt
@@ -54,6 +54,8 @@ object BalanceModule {
     }
 
     class BalanceItemDataSource {
+        private var updatedPositions = mutableListOf<Int>()
+
         val count
             get() = items.count()
 
@@ -64,6 +66,16 @@ object BalanceModule {
 
         var items = listOf<BalanceItem>()
 
+        fun addUpdatedPosition(position: Int) {
+            updatedPositions.add(position)
+        }
+
+        fun clearUpdatedPositions() {
+            updatedPositions.clear()
+        }
+
+        fun getUpdatedPositions(): List<Int> = updatedPositions.distinct()
+
         fun getItem(position: Int): BalanceItem = items[position]
 
         fun reset(items: List<BalanceItem>) {
diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/balance/BalancePresenter.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/balance/BalancePresenter.kt
index 80c0e2c7d..41d8f19fe 100644
--- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/balance/BalancePresenter.kt
+++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/balance/BalancePresenter.kt
@@ -5,7 +5,13 @@ import io.horizontalsystems.bankwallet.core.IAdapter
 import io.horizontalsystems.bankwallet.entities.Currency
 import io.horizontalsystems.bankwallet.entities.Rate
 import io.horizontalsystems.bankwallet.modules.transactions.CoinCode
+import io.reactivex.Flowable
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.disposables.CompositeDisposable
+import io.reactivex.schedulers.Schedulers
+import io.reactivex.subjects.PublishSubject
 import java.math.BigDecimal
+import java.util.concurrent.TimeUnit
 
 class BalancePresenter(
         private var interactor: BalanceModule.IInteractor,
@@ -14,6 +20,8 @@ class BalancePresenter(
         private val factory: BalanceViewItemFactory) : BalanceModule.IViewDelegate, BalanceModule.IInteractorDelegate {
 
     var view: BalanceModule.IView? = null
+    private val disposables = CompositeDisposable()
+    private var flushSubject = PublishSubject.create<Unit>()
 
     //
     // BalanceModule.IViewDelegate
@@ -24,6 +32,24 @@ class BalancePresenter(
 
     override fun viewDidLoad() {
         interactor.initAdapters()
+
+        flushSubject
+                .debounce(1, TimeUnit.SECONDS)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .doOnNext { updateViewItems() }
+                .subscribe()?.let { disposables.add(it) }
+
+        Flowable.interval(1, TimeUnit.MINUTES)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe {
+                    dataSource.items.forEach { item ->
+                        item.rate?.let { rate ->
+                            didUpdateRate(rate)
+                        }
+                    }
+                }?.let { disposables.add(it) }
     }
 
     override fun getViewItem(position: Int) =
@@ -46,6 +72,7 @@ class BalancePresenter(
 
     override fun onClear() {
         interactor.clear()
+        disposables.clear()
     }
 
     //
@@ -70,21 +97,21 @@ class BalancePresenter(
     override fun didUpdateBalance(coinCode: CoinCode, balance: BigDecimal) {
         val position = dataSource.getPosition(coinCode)
         dataSource.setBalance(position, balance)
-        view?.updateItem(position)
+        updateByPosition(position)
         view?.updateHeader()
     }
 
     override fun didUpdateState(coinCode: String, state: AdapterState) {
         val position = dataSource.getPosition(coinCode)
         dataSource.setState(position, state)
-        view?.updateItem(position)
+        updateByPosition(position)
         view?.updateHeader()
     }
 
     override fun didUpdateRate(rate: Rate) {
         val position = dataSource.getPosition(rate.coinCode)
         dataSource.setRate(position, rate)
-        view?.updateItem(position)
+        updateByPosition(position)
         view?.updateHeader()
     }
 
@@ -96,4 +123,16 @@ class BalancePresenter(
         router.openManageCoins()
     }
 
+    private fun updateViewItems() {
+        dataSource.getUpdatedPositions().forEach {
+            view?.updateItem(it)
+        }
+        dataSource.clearUpdatedPositions()
+    }
+
+    private fun updateByPosition(position: Int) {
+        dataSource.addUpdatedPosition(position)
+        flushSubject.onNext(Unit)
+    }
+
 }
diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/balance/BalanceViewModel.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/balance/BalanceViewModel.kt
index 91381d97a..5b7e137ac 100644
--- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/balance/BalanceViewModel.kt
+++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/balance/BalanceViewModel.kt
@@ -29,7 +29,7 @@ class BalanceViewModel : ViewModel(), BalanceModule.IView, BalanceModule.IRouter
     }
 
     override fun updateItem(position: Int) {
-        reloadItemLiveEvent.postValue(position)
+        reloadItemLiveEvent.value = position
     }
 
     override fun updateHeader() {
diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/main/MainActivity.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/main/MainActivity.kt
index ca508ad1b..b749e0db0 100644
--- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/main/MainActivity.kt
+++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/main/MainActivity.kt
@@ -387,6 +387,8 @@ class MainActivity : BaseActivity(), NumPadItemsAdapter.Listener {
         if (amountNumber > BigDecimal.ZERO) {
             editTxtAmount.setText(amountNumber.stripTrailingZeros().toPlainString())
             editTxtAmount.setSelection(editTxtAmount.text.length)
+        } else {
+            editTxtAmount.setText("")
         }
     }
 
diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/send/SendInteractor.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/send/SendInteractor.kt
index 20dff6ee8..694253915 100644
--- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/send/SendInteractor.kt
+++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/send/SendInteractor.kt
@@ -2,11 +2,13 @@ package io.horizontalsystems.bankwallet.modules.send
 
 import io.horizontalsystems.bankwallet.core.*
 import io.horizontalsystems.bankwallet.entities.*
+import io.reactivex.Flowable
 import io.reactivex.android.schedulers.AndroidSchedulers
 import io.reactivex.disposables.CompositeDisposable
 import io.reactivex.schedulers.Schedulers
 import java.math.BigDecimal
 import java.math.RoundingMode
+import java.util.concurrent.TimeUnit
 
 class SendInteractor(private val currencyManager: ICurrencyManager,
                      private val rateStorage: IRateStorage,
@@ -69,6 +71,18 @@ class SendInteractor(private val currencyManager: ICurrencyManager,
                             }
             )
         }
+
+        disposables.add(
+                Flowable.interval(1, TimeUnit.MINUTES)
+                        .subscribeOn(Schedulers.io())
+                        .observeOn(AndroidSchedulers.mainThread())
+                        .subscribe {
+                            if (exchangeRate?.expired == true) {
+                                exchangeRate = null
+                                delegate?.didRateRetrieve(exchangeRate)
+                            }
+                        }
+        )
     }
 
     override fun parsePaymentAddress(address: String): PaymentRequestAddress {
diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/send/SendPresenter.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/send/SendPresenter.kt
index 3bd6a4493..ee348df29 100644
--- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/send/SendPresenter.kt
+++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/send/SendPresenter.kt
@@ -133,12 +133,12 @@ class SendPresenter(
     }
 
     override fun didRateRetrieve(rate: Rate?) {
-        if (userInput.inputType == SendModule.InputType.CURRENCY && rate == null) {
-            view?.dismiss()
-            return
-        }
-
-        if (interactor.defaultInputType == SendModule.InputType.CURRENCY && userInput.amount == BigDecimal.ZERO) {
+        if (rate == null) {
+            if (userInput.inputType == SendModule.InputType.CURRENCY) {
+                userInput.amount = BigDecimal.ZERO
+            }
+            userInput.inputType = SendModule.InputType.COIN
+        } else if (interactor.defaultInputType == SendModule.InputType.CURRENCY && userInput.amount == BigDecimal.ZERO) {
             userInput.inputType = interactor.defaultInputType
         }
 
diff --git a/app/src/test/java/io/horizontalsystems/bankwallet/modules/balance/BalancePresenterTest.kt b/app/src/test/java/io/horizontalsystems/bankwallet/modules/balance/BalancePresenterTest.kt
index 1c59be934..8742a49ab 100644
--- a/app/src/test/java/io/horizontalsystems/bankwallet/modules/balance/BalancePresenterTest.kt
+++ b/app/src/test/java/io/horizontalsystems/bankwallet/modules/balance/BalancePresenterTest.kt
@@ -9,12 +9,16 @@ import io.horizontalsystems.bankwallet.core.IAdapter
 import io.horizontalsystems.bankwallet.entities.Coin
 import io.horizontalsystems.bankwallet.entities.Currency
 import io.horizontalsystems.bankwallet.entities.Rate
+import io.horizontalsystems.bankwallet.modules.RxBaseTest
 import io.horizontalsystems.bankwallet.modules.transactions.CoinCode
+import io.reactivex.plugins.RxJavaPlugins
+import io.reactivex.schedulers.TestScheduler
 import org.junit.Assert
 import org.junit.Before
 import org.junit.Test
 import org.mockito.Mockito.mock
 import java.math.BigDecimal
+import java.util.concurrent.TimeUnit
 
 class BalancePresenterTest {
 
@@ -25,9 +29,13 @@ class BalancePresenterTest {
     private val factory = mock(BalanceViewItemFactory::class.java)
 
     private lateinit var presenter: BalancePresenter
+    private val testScheduler = TestScheduler()
 
     @Before
     fun before() {
+        RxBaseTest.setup()
+        RxJavaPlugins.setComputationSchedulerHandler { testScheduler }
+
         presenter = BalancePresenter(interactor, router, dataSource, factory)
         presenter.view = view
     }
@@ -35,6 +43,7 @@ class BalancePresenterTest {
     @Test
     fun viewDidLoad() {
         presenter.viewDidLoad()
+        testScheduler.advanceTimeBy(1, TimeUnit.MINUTES)
 
         verify(interactor).initAdapters()
     }
@@ -128,7 +137,7 @@ class BalancePresenterTest {
         presenter.didUpdateBalance(coinCode, balance)
 
         verify(dataSource).setBalance(position, balance)
-        verify(view).updateItem(position)
+        verify(dataSource).addUpdatedPosition(position)
         verify(view).updateHeader()
     }
 
@@ -143,7 +152,7 @@ class BalancePresenterTest {
         presenter.didUpdateState(coinCode, state)
 
         verify(dataSource).setState(position, state)
-        verify(view).updateItem(position)
+        verify(dataSource).addUpdatedPosition(position)
         verify(view).updateHeader()
     }
 
@@ -176,7 +185,7 @@ class BalancePresenterTest {
         presenter.didUpdateRate(rate)
 
         verify(dataSource).setRate(position, rate)
-        verify(view).updateItem(position)
+        verify(dataSource).addUpdatedPosition(position)
         verify(view).updateHeader()
     }
 
diff --git a/app/src/test/java/io/horizontalsystems/bankwallet/modules/receive/ReceiveInteractorTest.kt b/app/src/test/java/io/horizontalsystems/bankwallet/modules/receive/ReceiveInteractorTest.kt
index 46485dd16..e9abdf555 100644
--- a/app/src/test/java/io/horizontalsystems/bankwallet/modules/receive/ReceiveInteractorTest.kt
+++ b/app/src/test/java/io/horizontalsystems/bankwallet/modules/receive/ReceiveInteractorTest.kt
@@ -42,7 +42,7 @@ class ReceiveInteractorTest {
 
         interactor.getReceiveAddress()
 
-        verify(delegate).didReceiveAddresses(listOf(AddressItem(coinAddress, coin)))
+        verify(delegate).didReceiveAddress(AddressItem(coinAddress, coin))
     }
 
     @Test
diff --git a/app/src/test/java/io/horizontalsystems/bankwallet/modules/receive/ReceivePresenterTest.kt b/app/src/test/java/io/horizontalsystems/bankwallet/modules/receive/ReceivePresenterTest.kt
index c34fd4db5..7291726e8 100644
--- a/app/src/test/java/io/horizontalsystems/bankwallet/modules/receive/ReceivePresenterTest.kt
+++ b/app/src/test/java/io/horizontalsystems/bankwallet/modules/receive/ReceivePresenterTest.kt
@@ -1,6 +1,5 @@
 package io.horizontalsystems.bankwallet.modules.receive
 
-import com.nhaarman.mockito_kotlin.any
 import com.nhaarman.mockito_kotlin.verify
 import io.horizontalsystems.bankwallet.R
 import io.horizontalsystems.bankwallet.entities.Coin
@@ -18,7 +17,6 @@ class ReceivePresenterTest {
     private var coin = Mockito.mock(Coin::class.java)
     private val coinAddress = "[coin_address]"
     private val addressItem = AddressItem(address = coinAddress, coin = coin)
-    private val addresses = listOf(addressItem)
 
     private lateinit var presenter: ReceivePresenter
 
@@ -38,9 +36,9 @@ class ReceivePresenterTest {
 
     @Test
     fun didReceiveAddress() {
-        presenter.didReceiveAddresses(addresses)
+        presenter.didReceiveAddress(addressItem)
 
-        verify(view).showAddresses(addresses)
+        verify(view).showAddress(addressItem)
     }
 
     @Test
@@ -52,16 +50,16 @@ class ReceivePresenterTest {
 
     @Test
     fun onShareClick() {
-        presenter.didReceiveAddresses(addresses)
-        presenter.onShareClick(any())
+        presenter.didReceiveAddress(addressItem)
+        presenter.onShareClick()
 
         verify(router).shareAddress(addressItem.address)
     }
 
     @Test
     fun onAddressClick() {
-        presenter.didReceiveAddresses(addresses)
-        presenter.onAddressClick(any())
+        presenter.didReceiveAddress(addressItem)
+        presenter.onAddressClick()
 
         verify(interactor).copyToClipboard(addressItem.address)
     }
diff --git a/app/src/test/java/io/horizontalsystems/bankwallet/modules/send/SendInteractorTest.kt b/app/src/test/java/io/horizontalsystems/bankwallet/modules/send/SendInteractorTest.kt
index d01f738f5..b36fbfe14 100644
--- a/app/src/test/java/io/horizontalsystems/bankwallet/modules/send/SendInteractorTest.kt
+++ b/app/src/test/java/io/horizontalsystems/bankwallet/modules/send/SendInteractorTest.kt
@@ -7,12 +7,15 @@ import io.horizontalsystems.bankwallet.modules.RxBaseTest
 import io.horizontalsystems.bankwallet.modules.transactions.CoinCode
 import io.reactivex.Flowable
 import io.reactivex.Single
+import io.reactivex.plugins.RxJavaPlugins
+import io.reactivex.schedulers.TestScheduler
 import org.junit.Assert
 import org.junit.Before
 import org.junit.Test
 import org.mockito.Mockito.mock
 import java.math.BigDecimal
 import java.math.RoundingMode
+import java.util.concurrent.TimeUnit
 
 class SendInteractorTest {
 
@@ -39,12 +42,16 @@ class SendInteractorTest {
     private val maxDecimal = 8
     private val feePriority = FeeRatePriority.MEDIUM
 
+    private val testScheduler = TestScheduler()
+
     private lateinit var interactor: SendInteractor
 
     @Before
     fun setup() {
         RxBaseTest.setup()
 
+        RxJavaPlugins.setComputationSchedulerHandler { testScheduler }
+
         whenever(userInput.inputType).thenReturn(SendModule.InputType.COIN)
         whenever(rate.value).thenReturn(BigDecimal("0.1"))
         whenever(rate.expired).thenReturn(false)
@@ -93,6 +100,8 @@ class SendInteractorTest {
     fun retrieveRate() {
         interactor.retrieveRate()
 
+        testScheduler.advanceTimeBy(1, TimeUnit.MINUTES)
+
         verify(rateStorage).latestRateObservable(coinCode, currency.code)
     }
 
@@ -103,6 +112,8 @@ class SendInteractorTest {
 
         interactor.retrieveRate()
 
+        testScheduler.advanceTimeBy(1, TimeUnit.MINUTES)
+
         verify(delegate).didRateRetrieve(null)
     }
 
@@ -123,6 +134,8 @@ class SendInteractorTest {
 
         interactor.send(userInput)
 
+        testScheduler.advanceTimeBy(1, TimeUnit.MINUTES)
+
         verify(delegate).didSend()
     }
 
@@ -138,6 +151,8 @@ class SendInteractorTest {
 
         interactor.send(userInput)
 
+        testScheduler.advanceTimeBy(1, TimeUnit.MINUTES)
+
         verify(delegate).didSend()
     }
 
@@ -202,6 +217,8 @@ class SendInteractorTest {
 
         val state = interactor.stateForUserInput(input)
 
+        testScheduler.advanceTimeBy(1, TimeUnit.MINUTES)
+
         Assert.assertEquals(CoinValue(coinCode, value = expectedFee), state.feeCoinValue)
         Assert.assertEquals(CurrencyValue(currency, value = expectedCurrencyFee), state.feeCurrencyValue)
     }
@@ -270,6 +287,8 @@ class SendInteractorTest {
         interactor.retrieveRate()
         val state = interactor.stateForUserInput(input)
 
+        testScheduler.advanceTimeBy(1, TimeUnit.MINUTES)
+
         Assert.assertEquals(state.feeCurrencyValue, CurrencyValue(currency, value = fee * rate.value))
     }
 
@@ -292,8 +311,9 @@ class SendInteractorTest {
 
         interactor.retrieveRate()
         val state = interactor.stateForUserInput(input)
+        testScheduler.advanceTimeBy(1, TimeUnit.MINUTES)
 
-        Assert.assertEquals(state.feeCurrencyValue, CurrencyValue(currency, value = fee * fiatFeeRate.value))
+        Assert.assertEquals(CurrencyValue(currency, value = fee * fiatFeeRate.value), state.feeCurrencyValue)
     }
 
     @Test
@@ -318,6 +338,8 @@ class SendInteractorTest {
         interactor.retrieveRate()
         val state = interactor.stateForUserInput(input)
 
+        testScheduler.advanceTimeBy(1, TimeUnit.MINUTES)
+
         Assert.assertEquals(state.feeCurrencyValue, CurrencyValue(currency, value = fee * rate.value))
 
         val balanceMinusFeeInCurrency = balanceMinusFee * rate.value
@@ -364,6 +386,8 @@ class SendInteractorTest {
         val expectedBalanceMinusFee = BigDecimal("12.29999956")
         val balanceMinusFee = interactor.getTotalBalanceMinusFee(input.inputType, input.address, feePriority)
 
+        testScheduler.advanceTimeBy(1, TimeUnit.MINUTES)
+
         Assert.assertEquals(expectedBalanceMinusFee, balanceMinusFee)
     }
 
@@ -478,6 +502,8 @@ class SendInteractorTest {
 
         interactor.retrieveRate()
 
+        testScheduler.advanceTimeBy(1, TimeUnit.MINUTES)
+
         verify(delegate).didFeeRateRetrieve()
     }
 
@@ -489,6 +515,8 @@ class SendInteractorTest {
 
         interactor.retrieveRate()
 
+        testScheduler.advanceTimeBy(1, TimeUnit.MINUTES)
+
         verify(delegate, atLeastOnce()).didRateRetrieve(rate)
     }
 
diff --git a/app/src/test/java/io/horizontalsystems/bankwallet/modules/send/SendPresenterTest.kt b/app/src/test/java/io/horizontalsystems/bankwallet/modules/send/SendPresenterTest.kt
index 9a5b33527..33fa964e6 100644
--- a/app/src/test/java/io/horizontalsystems/bankwallet/modules/send/SendPresenterTest.kt
+++ b/app/src/test/java/io/horizontalsystems/bankwallet/modules/send/SendPresenterTest.kt
@@ -1,7 +1,6 @@
 package io.horizontalsystems.bankwallet.modules.send
 
 import com.nhaarman.mockito_kotlin.any
-import com.nhaarman.mockito_kotlin.never
 import com.nhaarman.mockito_kotlin.verify
 import com.nhaarman.mockito_kotlin.whenever
 import io.horizontalsystems.bankwallet.R
@@ -144,16 +143,8 @@ class SendPresenterTest {
 
         presenter.didRateRetrieve(null)
 
-        verify(view).dismiss()
-    }
-
-    @Test
-    fun didRateRetrieve_withExpiredRate_onFirstOpen() {
-        whenever(userInput.inputType).thenReturn(SendModule.InputType.COIN)
-
-        presenter.didRateRetrieve(null)
-
-        verify(view, never()).dismiss()
+        verify(userInput).amount = BigDecimal.ZERO
+        verify(userInput).inputType = SendModule.InputType.COIN
     }
 
     @Test