Skip to content

Commit 8bf3039

Browse files
authored
Merge pull request #245 from horizontalsystems/release
Release 0.2.0
2 parents 56758ce + af90314 commit 8bf3039

39 files changed

Lines changed: 695 additions & 206 deletions

README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,11 @@ class MainViewModel : ViewModel(), BitcoinKit.Listener {
168168
bitcoinKit.listener = this
169169
}
170170

171-
override fun onTransactionsUpdate(bitcoinKit: BitcoinKit, inserted: List<TransactionInfo>, updated: List<TransactionInfo>, deleted: List<Int>) {
171+
override fun onTransactionsUpdate(bitcoinKit: BitcoinKit, inserted: List<TransactionInfo>, updated: List<TransactionInfo>) {
172+
// do something with transactions
173+
}
174+
175+
override fun onTransactionsDelete(hashes: List<String>) {
172176
// do something with transactions
173177
}
174178

@@ -197,6 +201,14 @@ class MainViewModel : ViewModel(), BitcoinKit.Listener {
197201
}
198202
```
199203

204+
Listener events are run in a dedicated background thread. It can be switched to main thread by setting the ```listenerExecutor``` property to ```MainThreadExecutor()```
205+
206+
```kotlin
207+
208+
bitcoinKit.listenerExecutor = MainThreadExecutor()
209+
210+
```
211+
200212
## Prerequisites
201213
* JDK >= 1.8
202214
* Android 6 (minSdkVersion 23) or greater

app/src/main/java/io/horizontalsystems/bitcoinkit/demo/BalanceFragment.kt

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@ import android.view.View
99
import android.view.ViewGroup
1010
import android.widget.Button
1111
import android.widget.TextView
12+
import io.horizontalsystems.bitcoinkit.BitcoinKit
1213

1314
class BalanceFragment : Fragment() {
1415

1516
lateinit var viewModel: MainViewModel
1617
lateinit var networkName: TextView
1718
lateinit var balanceValue: TextView
1819
lateinit var lastBlockValue: TextView
19-
lateinit var progressValue: TextView
20+
lateinit var stateValue: TextView
2021
lateinit var startButton: Button
2122
lateinit var clearButton: Button
2223
lateinit var buttonDebug: Button
@@ -38,9 +39,18 @@ class BalanceFragment : Fragment() {
3839
lastBlockValue.text = (it ?: 0).toString()
3940
})
4041

41-
viewModel.progress.observe(this, Observer {
42-
val percentage = (it ?: 0.0) * 100
43-
progressValue.text = "${percentage}%"
42+
viewModel.state.observe(this, Observer {
43+
when (it) {
44+
is BitcoinKit.KitState.Synced -> {
45+
stateValue.text = "synced"
46+
}
47+
is BitcoinKit.KitState.Syncing -> {
48+
stateValue.text = "syncing ${it.progress}"
49+
}
50+
is BitcoinKit.KitState.NotSynced -> {
51+
stateValue.text = "not synced"
52+
}
53+
}
4454
})
4555

4656
viewModel.status.observe(this, Observer {
@@ -69,7 +79,7 @@ class BalanceFragment : Fragment() {
6979

7080
balanceValue = view.findViewById(R.id.balanceValue)
7181
lastBlockValue = view.findViewById(R.id.lastBlockValue)
72-
progressValue = view.findViewById(R.id.progressValue)
82+
stateValue = view.findViewById(R.id.stateValue)
7383
startButton = view.findViewById(R.id.buttonStart)
7484
clearButton = view.findViewById(R.id.buttonClear)
7585
buttonDebug = view.findViewById(R.id.buttonDebug)

app/src/main/java/io/horizontalsystems/bitcoinkit/demo/MainViewModel.kt

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class MainViewModel : ViewModel(), BitcoinKit.Listener {
1717
val transactions = MutableLiveData<List<TransactionInfo>>()
1818
val balance = MutableLiveData<Long>()
1919
val lastBlockHeight = MutableLiveData<Int>()
20-
val progress = MutableLiveData<Double>()
20+
val state = MutableLiveData<KitState>()
2121
val status = MutableLiveData<State>()
2222
val networkName: String
2323
private val disposables = CompositeDisposable()
@@ -47,7 +47,7 @@ class MainViewModel : ViewModel(), BitcoinKit.Listener {
4747
}
4848

4949
lastBlockHeight.value = bitcoinKit.lastBlockInfo?.height ?: 0
50-
progress.value = 0.0
50+
state.value = KitState.NotSynced
5151

5252
started = false
5353
}
@@ -82,33 +82,26 @@ class MainViewModel : ViewModel(), BitcoinKit.Listener {
8282
//
8383
// BitcoinKit Listener implementations
8484
//
85-
override fun onTransactionsUpdate(bitcoinKit: BitcoinKit, inserted: List<TransactionInfo>, updated: List<TransactionInfo>, deleted: List<Int>) {
85+
override fun onTransactionsUpdate(bitcoinKit: BitcoinKit, inserted: List<TransactionInfo>, updated: List<TransactionInfo>) {
8686
bitcoinKit.transactions().subscribe { txList: List<TransactionInfo> ->
87-
transactions.value = txList.sortedByDescending { it.blockHeight }
87+
transactions.postValue(txList.sortedByDescending { it.blockHeight })
8888
}.let {
8989
disposables.add(it)
9090
}
9191
}
9292

93+
override fun onTransactionsDelete(hashes: List<String>) {
94+
}
95+
9396
override fun onBalanceUpdate(bitcoinKit: BitcoinKit, balance: Long) {
94-
this.balance.value = balance
97+
this.balance.postValue(balance)
9598
}
9699

97100
override fun onLastBlockInfoUpdate(bitcoinKit: BitcoinKit, blockInfo: BlockInfo) {
98-
this.lastBlockHeight.value = blockInfo.height
101+
this.lastBlockHeight.postValue(blockInfo.height)
99102
}
100103

101104
override fun onKitStateUpdate(bitcoinKit: BitcoinKit, state: KitState) {
102-
when (state) {
103-
is KitState.Synced -> {
104-
this.progress.postValue(1.0)
105-
}
106-
is KitState.Syncing -> {
107-
this.progress.postValue(state.progress)
108-
}
109-
is KitState.NotSynced -> {
110-
this.progress.postValue(0.0)
111-
}
112-
}
105+
this.state.postValue(state)
113106
}
114107
}

app/src/main/res/layout/fragment_balance.xml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,34 +46,34 @@
4646
tools:text="100.0" />
4747

4848
<TextView
49-
android:id="@+id/progressTitle"
49+
android:id="@+id/stateTitle"
5050
android:layout_width="wrap_content"
5151
android:layout_height="wrap_content"
5252
android:layout_marginTop="8dp"
53-
android:text="Sync Progress: "
53+
android:text="State:"
5454
android:textAppearance="@style/TextAppearance.AppCompat.Large"
5555
app:layout_constraintStart_toStartOf="parent"
5656
app:layout_constraintTop_toBottomOf="@+id/balanceTitle" />
5757

5858
<TextView
59-
android:id="@+id/progressValue"
59+
android:id="@+id/stateValue"
6060
android:layout_width="wrap_content"
6161
android:layout_height="wrap_content"
6262
android:layout_marginStart="16dp"
6363
android:textAppearance="@style/TextAppearance.AppCompat.Large"
64-
app:layout_constraintStart_toEndOf="@+id/progressTitle"
65-
app:layout_constraintTop_toTopOf="@+id/progressTitle"
66-
tools:text="12%" />
64+
app:layout_constraintStart_toEndOf="@+id/stateTitle"
65+
app:layout_constraintTop_toTopOf="@+id/stateTitle"
66+
tools:text="syncing" />
6767

6868
<TextView
6969
android:id="@+id/lastBlockTitle"
7070
android:layout_width="wrap_content"
7171
android:layout_height="wrap_content"
7272
android:layout_marginTop="8dp"
73-
android:text="Last Block: "
73+
android:text="Last Block:"
7474
android:textAppearance="@style/TextAppearance.AppCompat.Large"
7575
app:layout_constraintStart_toStartOf="parent"
76-
app:layout_constraintTop_toBottomOf="@+id/progressTitle" />
76+
app:layout_constraintTop_toBottomOf="@+id/stateTitle" />
7777

7878
<TextView
7979
android:id="@+id/lastBlockValue"

bitcoinkit/build.gradle

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ android {
1010
minSdkVersion 23
1111
targetSdkVersion 28
1212
versionCode 1
13-
versionName "0.1.2"
13+
versionName "0.2.0"
1414

1515
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
1616
}
@@ -48,7 +48,6 @@ dependencies {
4848
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
4949

5050
implementation 'io.reactivex.rxjava2:rxjava:2.2.0'
51-
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
5251

5352
// For cryptocurrency
5453
implementation 'com.madgag.spongycastle:core:1.58.0.0'

bitcoinkit/src/androidTest/java/io/horizontalsystems/bitcoinkit/blocks/BlockchainTest.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ class BlockchainTest {
1414
private val factories = RealmFactoryMock()
1515
private val realm = factories.realmFactory.realm
1616
private val network = mock(Network::class.java)
17-
private val blockchain = Blockchain(network)
17+
private val blockchainDataListener = mock(IBlockchainDataListener::class.java)
18+
private val blockchain = Blockchain(network, blockchainDataListener)
1819

1920
@Before
2021
fun setup() {

bitcoinkit/src/androidTest/java/io/horizontalsystems/bitcoinkit/managers/InitialSyncerTest.kt

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import com.nhaarman.mockito_kotlin.*
44
import helpers.RxTestRule
55
import io.horizontalsystems.bitcoinkit.RealmFactoryMock
66
import io.horizontalsystems.bitcoinkit.core.ISyncStateListener
7-
import io.horizontalsystems.bitcoinkit.models.Block
87
import io.horizontalsystems.bitcoinkit.models.BlockHash
98
import io.horizontalsystems.bitcoinkit.models.PublicKey
109
import io.horizontalsystems.bitcoinkit.network.peer.PeerGroup
@@ -20,7 +19,7 @@ import java.util.concurrent.TimeUnit
2019
class InitialSyncerTest {
2120

2221
private val factories = RealmFactoryMock()
23-
private val initialSyncerApi = mock(InitialSyncerApi::class.java)
22+
private val initialSyncerApi = mock(BlockDiscoveryBatch::class.java)
2423
private val kitStateListener = mock(ISyncStateListener::class.java)
2524

2625
private val stateManager = mock(StateManager::class.java)
@@ -48,10 +47,10 @@ class InitialSyncerTest {
4847
@Test
4948
fun sync() {
5049
whenever(stateManager.restored).thenReturn(false)
51-
whenever(initialSyncerApi.fetchFromApi(0, true)).thenReturn(apiRespStub)
52-
whenever(initialSyncerApi.fetchFromApi(0, false)).thenReturn(apiRespStub)
53-
whenever(initialSyncerApi.fetchFromApi(1, true)).thenReturn(Single.just(Pair(listOf(), listOf())))
54-
whenever(initialSyncerApi.fetchFromApi(1, false)).thenReturn(Single.just(Pair(listOf(), listOf())))
50+
whenever(initialSyncerApi.discoverBlockHashes(0, true)).thenReturn(apiRespStub)
51+
whenever(initialSyncerApi.discoverBlockHashes(0, false)).thenReturn(apiRespStub)
52+
whenever(initialSyncerApi.discoverBlockHashes(1, true)).thenReturn(Single.just(Pair(listOf(), listOf())))
53+
whenever(initialSyncerApi.discoverBlockHashes(1, false)).thenReturn(Single.just(Pair(listOf(), listOf())))
5554

5655
initialSyncer.sync()
5756

@@ -65,8 +64,8 @@ class InitialSyncerTest {
6564
val responseWithTimeout = apiRespStub.timeout(1, TimeUnit.SECONDS)
6665

6766
whenever(stateManager.restored).thenReturn(false)
68-
whenever(initialSyncerApi.fetchFromApi(0, true)).thenReturn(responseWithTimeout)
69-
whenever(initialSyncerApi.fetchFromApi(0, false)).thenReturn(responseWithTimeout)
67+
whenever(initialSyncerApi.discoverBlockHashes(0, true)).thenReturn(responseWithTimeout)
68+
whenever(initialSyncerApi.discoverBlockHashes(0, false)).thenReturn(responseWithTimeout)
7069

7170
initialSyncer.sync()
7271
initialSyncer.stop()
@@ -77,8 +76,8 @@ class InitialSyncerTest {
7776
// @Test
7877
// fun refresh() {
7978
// whenever(stateManager.restored).thenReturn(false)
80-
// whenever(initialSyncerApi.fetchFromApi(true)).thenReturn(apiRespStub)
81-
// whenever(initialSyncerApi.fetchFromApi(false)).thenReturn(apiRespStub)
79+
// whenever(initialSyncerApi.discoverBlockHashes(true)).thenReturn(apiRespStub)
80+
// whenever(initialSyncerApi.discoverBlockHashes(false)).thenReturn(apiRespStub)
8281
//
8382
// initialSyncer.sync()
8483
// initialSyncer.sync() // refresh
@@ -130,10 +129,10 @@ class InitialSyncerTest {
130129

131130
whenever(stateManager.restored).thenReturn(false)
132131

133-
whenever(initialSyncerApi.fetchFromApi(0, true)).thenReturn(externalObservable)
134-
whenever(initialSyncerApi.fetchFromApi(0, false)).thenReturn(internalObservable)
135-
whenever(initialSyncerApi.fetchFromApi(1, true)).thenReturn(Single.just(Pair(listOf(), listOf())))
136-
whenever(initialSyncerApi.fetchFromApi(1, false)).thenReturn(Single.just(Pair(listOf(), listOf())))
132+
whenever(initialSyncerApi.discoverBlockHashes(0, true)).thenReturn(externalObservable)
133+
whenever(initialSyncerApi.discoverBlockHashes(0, false)).thenReturn(internalObservable)
134+
whenever(initialSyncerApi.discoverBlockHashes(1, true)).thenReturn(Single.just(Pair(listOf(), listOf())))
135+
whenever(initialSyncerApi.discoverBlockHashes(1, false)).thenReturn(Single.just(Pair(listOf(), listOf())))
137136

138137
initialSyncer.sync()
139138

@@ -156,16 +155,16 @@ class InitialSyncerTest {
156155
fun sync_apiNotSynced_blocksDiscoveredFail() {
157156
whenever(stateManager.restored).thenReturn(false)
158157

159-
whenever(initialSyncerApi.fetchFromApi(0, true)).thenReturn(Single.error(Exception()))
160-
whenever(initialSyncerApi.fetchFromApi(0, false)).thenReturn(Single.error(Exception()))
158+
whenever(initialSyncerApi.discoverBlockHashes(0, true)).thenReturn(Single.error(Exception()))
159+
whenever(initialSyncerApi.discoverBlockHashes(0, false)).thenReturn(Single.error(Exception()))
161160

162161
initialSyncer.sync()
163162

164163
verify(stateManager, never()).restored = true
165164
verifyNoMoreInteractions(peerGroup)
166165

167166
assertTrue(realm.where(PublicKey::class.java).findAll().isEmpty())
168-
assertTrue(realm.where(Block::class.java).findAll().isEmpty())
167+
assertTrue(realm.where(BlockHash::class.java).findAll().isEmpty())
169168
}
170169

171170
private fun containsBlock(blocks: List<BlockHash>, block: BlockHash) =

bitcoinkit/src/androidTest/java/io/horizontalsystems/bitcoinkit/transactions/TransactionCreatorTest.kt

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,8 @@ import com.nhaarman.mockito_kotlin.verify
55
import com.nhaarman.mockito_kotlin.whenever
66
import helpers.Fixtures
77
import io.horizontalsystems.bitcoinkit.RealmFactoryMock
8-
import io.horizontalsystems.bitcoinkit.models.Transaction
98
import io.horizontalsystems.bitcoinkit.network.peer.PeerGroup
109
import io.horizontalsystems.bitcoinkit.transactions.builder.TransactionBuilder
11-
import org.junit.Assert.assertTrue
1210
import org.junit.Before
1311
import org.junit.Test
1412
import org.mockito.Mockito.mock
@@ -38,10 +36,7 @@ class TransactionCreatorTest {
3836
fun create_Success() {
3937
transactionCreator.create("address", 10_000_000, 8, true)
4038

41-
val insertedTx = realm.where(Transaction::class.java).equalTo("hashHexReversed", transactionP2PKH.hashHexReversed).findFirst()
42-
43-
assertTrue(insertedTx != null)
44-
verify(transactionProcessor).process(transactionP2PKH, realm)
39+
verify(transactionProcessor).processOutgoing(transactionP2PKH, realm)
4540
verify(peerGroup).sendPendingTransactions()
4641
}
4742

bitcoinkit/src/androidTest/java/io/horizontalsystems/bitcoinkit/transactions/TransactionProcessorTest.kt

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
package io.horizontalsystems.bitcoinkit.transactions
22

33
import com.nhaarman.mockito_kotlin.any
4+
import com.nhaarman.mockito_kotlin.check
5+
import com.nhaarman.mockito_kotlin.eq
46
import com.nhaarman.mockito_kotlin.whenever
57
import helpers.Fixtures
68
import io.horizontalsystems.bitcoinkit.RealmFactoryMock
9+
import io.horizontalsystems.bitcoinkit.blocks.IBlockchainDataListener
710
import io.horizontalsystems.bitcoinkit.managers.AddressManager
811
import io.horizontalsystems.bitcoinkit.models.Transaction
912
import io.horizontalsystems.bitcoinkit.models.TransactionInput
1013
import io.horizontalsystems.bitcoinkit.models.TransactionOutput
1114
import io.realm.Realm
15+
import org.junit.Assert
1216
import org.junit.Assert.assertArrayEquals
1317
import org.junit.Assert.assertEquals
1418
import org.junit.Before
@@ -20,10 +24,11 @@ class TransactionProcessorTest {
2024

2125
private val factory = RealmFactoryMock()
2226
private lateinit var realm: Realm
23-
private val transaction = mock(Transaction::class.java)
27+
private val transaction = Fixtures.transactionP2PKH
2428
private val linker = mock(TransactionLinker::class.java)
2529
private val extractor = mock(TransactionExtractor::class.java)
2630
private val addressManager = mock(AddressManager::class.java)
31+
private val blockchainDataListener = mock(IBlockchainDataListener::class.java)
2732

2833
private lateinit var processor: TransactionProcessor
2934

@@ -33,22 +38,25 @@ class TransactionProcessorTest {
3338
realm.executeTransaction {
3439
it.deleteAll()
3540
}
36-
processor = TransactionProcessor(extractor, linker, addressManager)
41+
processor = TransactionProcessor(extractor, linker, addressManager, blockchainDataListener)
3742
}
3843

3944
@Test
4045
fun process() {
41-
whenever(transaction.isMine).thenReturn(false)
42-
processor.process(transaction, realm)
46+
processor.processOutgoing(transaction, realm)
4347

4448
verify(extractor).extractOutputs(transaction, realm)
4549
verify(linker).handle(transaction, realm)
50+
verify(blockchainDataListener).onTransactionsUpdate(check {
51+
Assert.assertEquals(transaction.hashHexReversed, it.firstOrNull()?.hashHexReversed)
52+
}, eq(listOf()))
4653
}
4754

4855
@Test
4956
fun process_isMine() {
50-
whenever(transaction.isMine).thenReturn(true)
51-
processor.process(transaction, realm)
57+
transaction.isMine = true
58+
59+
processor.processOutgoing(transaction, realm)
5260

5361
verify(extractor).extractOutputs(transaction, realm)
5462
verify(extractor).extractInputs(transaction)

0 commit comments

Comments
 (0)