Skip to content

Commit 6bb5d68

Browse files
author
Vincent Masselis
committed
Refactored the disconnection business logic
1 parent f35e8c5 commit 6bb5d68

File tree

11 files changed

+451
-159
lines changed

11 files changed

+451
-159
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,5 @@ task clean(type: Delete) {
3636
ext {
3737
versionCode = "$System.env.BITRISE_BUILD_NUMBER"
3838
groupId = 'com.vincentmasselis.rxbluetoothkotlin'
39-
libVersion = "1.1.9"
39+
libVersion = "1.2.0"
4040
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.vincentmasselis.devapp
2+
3+
import java.util.*
4+
5+
6+
val BATTERY_CHARACTERISTIC = UUID.fromString("00002A19-0000-1000-8000-00805F9B34FB")
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
package com.vincentmasselis.devapp
2+
3+
import android.bluetooth.BluetoothManager
4+
import android.content.Context
5+
import androidx.test.ext.junit.runners.AndroidJUnit4
6+
import androidx.test.rule.ActivityTestRule
7+
import androidx.test.rule.GrantPermissionRule
8+
import com.vincentmasselis.rxbluetoothkotlin.*
9+
import com.vincentmasselis.rxuikotlin.postForUI
10+
import org.junit.Rule
11+
import org.junit.Test
12+
import org.junit.runner.RunWith
13+
import java.util.concurrent.TimeUnit
14+
15+
@RunWith(AndroidJUnit4::class)
16+
class DisconnectionTests {
17+
18+
@Rule
19+
@JvmField
20+
val permissionRule: GrantPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.BLUETOOTH_ADMIN)
21+
22+
@Rule
23+
@JvmField
24+
val mainActivityRule = ActivityTestRule(TestActivity::class.java, true, false)
25+
26+
/** Disconnects right after a connection is done */
27+
@Test
28+
fun disconnectionImmediatelyTest() {
29+
val activity = mainActivityRule.launchActivity(null)
30+
val gatt = (activity.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager)
31+
.rxScan()
32+
.doOnSubscribe { activity.setMessage("Please wakeup your device") }
33+
.filter { it.device.address == "E9:98:86:03:D5:9F" } // Write the mac address for your own device here
34+
.firstElement()
35+
.doOnSuccess { activity.setMessage("Connecting") }
36+
.flatMapSingleElement { it.device.connectRxGatt(logger = Logger) }
37+
.flatMap { gatt ->
38+
gatt.disconnect()
39+
gatt.whenConnectionIsReady().map { gatt }
40+
}
41+
.doOnSuccess { activity.setMessage("Discovering services") }
42+
.flatMap { gatt -> gatt.discoverServices().doOnSubscribe { Logger.v(TAG, "Subscribing to fetch services") }.map { gatt } }
43+
.timeout(20, TimeUnit.SECONDS)
44+
.doOnError { Logger.e(TAG, "Failed, reason :$it") }
45+
.blockingGet()
46+
check(gatt == null)
47+
48+
Thread.sleep(5000)
49+
50+
mainActivityRule.finishActivity()
51+
}
52+
53+
/** Disconnects 10 millis after a connection */
54+
@Test
55+
fun disconnection10msTest() {
56+
val activity = mainActivityRule.launchActivity(null)
57+
val gatt = (activity.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager)
58+
.rxScan()
59+
.doOnSubscribe { activity.setMessage("Please wakeup your device") }
60+
.filter { it.device.address == "E9:98:86:03:D5:9F" } // Write the mac address for your own device here
61+
.firstElement()
62+
.doOnSuccess { activity.setMessage("Connecting") }
63+
.flatMapSingleElement { it.device.connectRxGatt(logger = Logger) }
64+
.flatMap { gatt ->
65+
activity.postForUI(10L to TimeUnit.MILLISECONDS) { gatt.disconnect() }
66+
gatt.whenConnectionIsReady().map { gatt }
67+
}
68+
.doOnSuccess { activity.setMessage("Discovering services") }
69+
.flatMap { gatt -> gatt.discoverServices().doOnSubscribe { Logger.v(TAG, "Subscribing to fetch services") }.map { gatt } }
70+
.timeout(20, TimeUnit.SECONDS)
71+
.doOnError { Logger.e(TAG, "Failed, reason :$it") }
72+
.blockingGet()
73+
check(gatt == null)
74+
75+
Thread.sleep(5000)
76+
77+
mainActivityRule.finishActivity()
78+
}
79+
80+
/** Disconnects 100 millis after a connection */
81+
@Test
82+
fun disconnection100msTest() {
83+
val activity = mainActivityRule.launchActivity(null)
84+
val gatt = (activity.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager)
85+
.rxScan()
86+
.doOnSubscribe { activity.setMessage("Please wakeup your device") }
87+
.filter { it.device.address == "E9:98:86:03:D5:9F" } // Write the mac address for your own device here
88+
.firstElement()
89+
.doOnSuccess { activity.setMessage("Connecting") }
90+
.flatMapSingleElement { it.device.connectRxGatt(logger = Logger) }
91+
.flatMap { gatt ->
92+
activity.postForUI(100L to TimeUnit.MILLISECONDS) { gatt.disconnect() }
93+
gatt.whenConnectionIsReady().map { gatt }
94+
}
95+
.doOnSuccess { activity.setMessage("Discovering services") }
96+
.flatMap { gatt -> gatt.discoverServices().doOnSubscribe { Logger.v(TAG, "Subscribing to fetch services") }.map { gatt } }
97+
.flatMap { it.listenDisconnection().toMaybe<RxBluetoothGatt>() }
98+
.timeout(20, TimeUnit.SECONDS)
99+
.doOnError { Logger.e(TAG, "Failed, reason :$it") }
100+
.blockingGet()
101+
check(gatt == null)
102+
103+
Thread.sleep(5000)
104+
105+
mainActivityRule.finishActivity()
106+
}
107+
108+
/** Disconnects 5 second after a connection */
109+
@Test
110+
fun disconnection5sTest() {
111+
val activity = mainActivityRule.launchActivity(null)
112+
val gatt = (activity.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager)
113+
.rxScan()
114+
.doOnSubscribe { activity.setMessage("Please wakeup your device") }
115+
.filter { it.device.address == "E9:98:86:03:D5:9F" } // Write the mac address for your own device here
116+
.firstElement()
117+
.doOnSuccess { activity.setMessage("Connecting") }
118+
.flatMapSingleElement { it.device.connectRxGatt(logger = Logger) }
119+
.flatMap { gatt ->
120+
activity.postForUI(5L to TimeUnit.SECONDS) { gatt.disconnect() }
121+
gatt.whenConnectionIsReady().map { gatt }
122+
}
123+
.doOnSuccess { activity.setMessage("Discovering services") }
124+
.flatMap { gatt -> gatt.discoverServices().doOnSubscribe { Logger.v(TAG, "Subscribing to fetch services") }.map { gatt } }
125+
.flatMapCompletable { it.listenDisconnection().doOnSubscribe { Logger.v(TAG, "Listening for disconnection") } }
126+
.timeout(20, TimeUnit.SECONDS)
127+
.doOnError { Logger.e(TAG, "Failed, reason :$it") }
128+
.blockingGet()
129+
130+
check(gatt == null)
131+
132+
Thread.sleep(5000)
133+
134+
mainActivityRule.finishActivity()
135+
}
136+
137+
/** Disconnects when reading the services */
138+
@Test
139+
fun disconnectionDiscoverServicesTest() {
140+
val activity = mainActivityRule.launchActivity(null)
141+
val gatt = (activity.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager)
142+
.rxScan()
143+
.doOnSubscribe { activity.setMessage("Please wakeup your device") }
144+
.filter { it.device.address == "E9:98:86:03:D5:9F" } // Write the mac address for your own device here
145+
.firstElement()
146+
.doOnSuccess { activity.setMessage("Connecting") }
147+
.flatMapSingleElement { it.device.connectRxGatt(logger = Logger) }
148+
.flatMap { gatt -> gatt.whenConnectionIsReady().map { gatt } }
149+
.doOnSuccess { activity.setMessage("Discovering services") }
150+
.flatMap { gatt -> gatt.discoverServices().doOnSubscribe { Logger.v(TAG, "Subscribing to fetch services"); gatt.disconnect() }.map { gatt } }
151+
.timeout(20, TimeUnit.SECONDS)
152+
.doOnError { Logger.e(TAG, "Failed, reason :$it") }
153+
.blockingGet()
154+
check(gatt == null)
155+
156+
Thread.sleep(5000)
157+
158+
mainActivityRule.finishActivity()
159+
}
160+
161+
companion object {
162+
const val TAG = "DisconnectionTests"
163+
}
164+
}

dev-app/src/androidTest/java/com/vincentmasselis/devapp/EnqueueUnitTest.kt

Lines changed: 55 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package com.vincentmasselis.devapp
22

33
import android.bluetooth.BluetoothManager
44
import android.content.Context
5-
import android.util.Log
65
import androidx.test.ext.junit.runners.AndroidJUnit4
76
import androidx.test.rule.ActivityTestRule
87
import androidx.test.rule.GrantPermissionRule
@@ -12,7 +11,6 @@ import io.reactivex.rxkotlin.Maybes
1211
import org.junit.Rule
1312
import org.junit.Test
1413
import org.junit.runner.RunWith
15-
import java.util.*
1614
import java.util.concurrent.TimeUnit
1715

1816

@@ -27,33 +25,6 @@ class EnqueueUnitTest {
2725
@JvmField
2826
val mainActivityRule = ActivityTestRule(TestActivity::class.java, true, false)
2927

30-
private object Logger : com.vincentmasselis.rxbluetoothkotlin.Logger {
31-
override fun v(tag: String, message: String, throwable: Throwable?) {
32-
Log.v(TAG, message, throwable)
33-
}
34-
35-
override fun d(tag: String, message: String, throwable: Throwable?) {
36-
Log.v(TAG, message, throwable)
37-
}
38-
39-
override fun i(tag: String, message: String, throwable: Throwable?) {
40-
Log.v(TAG, message, throwable)
41-
}
42-
43-
override fun w(tag: String, message: String, throwable: Throwable?) {
44-
Log.v(TAG, message, throwable)
45-
}
46-
47-
override fun e(tag: String, message: String, throwable: Throwable?) {
48-
Log.v(TAG, message, throwable)
49-
}
50-
51-
override fun wtf(tag: String, message: String, throwable: Throwable?) {
52-
Log.v(TAG, message, throwable)
53-
}
54-
55-
}
56-
5728
/** Check the queue is correctly working, if not, [CannotInitialize] exceptions are fired */
5829
@Test
5930
fun enqueueingTest() {
@@ -84,8 +55,10 @@ class EnqueueUnitTest {
8455
.doOnComplete { throw IllegalStateException("Should not complete here") }
8556
.doOnError { Logger.e(TAG, "Failed, reason :$it") }
8657
.blockingGet()
87-
gatt.disconnect().subscribe()
58+
gatt.disconnect()
8859
mainActivityRule.finishActivity()
60+
61+
Thread.sleep(5000)
8962
}
9063

9164
/** Disconnects while there is an I/O in the queue */
@@ -108,7 +81,7 @@ class EnqueueUnitTest {
10881
Maybes
10982
.zip(
11083
gatt.read(gatt.source.findCharacteristic(BATTERY_CHARACTERISTIC)!!)
111-
.doOnSubscribe { activity.postForUI(50L to TimeUnit.MILLISECONDS) { gatt.disconnect().subscribe() } } // Manual disconnection while reading
84+
.doOnSubscribe { activity.postForUI(50L to TimeUnit.MILLISECONDS) { gatt.disconnect() } } // Manual disconnection while reading
11285
.doOnSubscribe { Logger.v(TAG, "battery1 subscription") }
11386
.doOnComplete { Logger.v(TAG, "battery1 completed") },
11487
gatt.enableNotification(gatt.source.findCharacteristic(BATTERY_CHARACTERISTIC)!!)
@@ -124,10 +97,60 @@ class EnqueueUnitTest {
12497
.blockingGet()
12598
check(gatt == null)
12699
mainActivityRule.finishActivity()
100+
101+
Thread.sleep(5000)
102+
}
103+
104+
@Test
105+
fun queueCallRightAfterDisconnectionTest() {
106+
val activity = mainActivityRule.launchActivity(null)
107+
val gatt = (activity.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager)
108+
.rxScan()
109+
.doOnSubscribe { activity.setMessage("Please wakeup your device") }
110+
.filter { it.device.address == "E9:98:86:03:D5:9F" } // Write the mac address for your own device here
111+
.firstElement()
112+
.doOnSuccess { activity.setMessage("Connecting") }
113+
.flatMapSingleElement { it.device.connectRxGatt(logger = Logger) }
114+
.flatMap { gatt -> gatt.whenConnectionIsReady().map { gatt } }
115+
.doOnSuccess { it.disconnect() }
116+
.doOnSuccess { activity.setMessage("Discovering services") }
117+
.flatMap { gatt -> gatt.discoverServices().map { gatt } }
118+
.doOnSuccess { throw IllegalStateException("Should not succeed here, It should complete with because of the gatt.disconnect() call") }
119+
.doOnError { Logger.e(TAG, "Failed, reason :$it") }
120+
.timeout(20L, TimeUnit.SECONDS)
121+
.blockingGet()
122+
check(gatt == null)
123+
mainActivityRule.finishActivity()
124+
125+
Thread.sleep(5000)
126+
}
127+
128+
@Test
129+
fun queueCallDelayAfterDisconnectionTest() {
130+
val activity = mainActivityRule.launchActivity(null)
131+
val gatt = (activity.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager)
132+
.rxScan()
133+
.doOnSubscribe { activity.setMessage("Please wakeup your device") }
134+
.filter { it.device.address == "E9:98:86:03:D5:9F" } // Write the mac address for your own device here
135+
.firstElement()
136+
.doOnSuccess { activity.setMessage("Connecting") }
137+
.flatMapSingleElement { it.device.connectRxGatt(logger = Logger) }
138+
.flatMap { gatt -> gatt.whenConnectionIsReady().map { gatt } }
139+
.doOnSuccess { it.disconnect() }
140+
.doOnSuccess { activity.setMessage("Discovering services") }
141+
.delay(600, TimeUnit.MILLISECONDS)
142+
.flatMap { gatt -> gatt.discoverServices().map { gatt } }
143+
.doOnSuccess { throw IllegalStateException("Should not succeed here, It should complete with because of the gatt.disconnect() call") }
144+
.doOnError { Logger.e(TAG, "Failed, reason :$it") }
145+
.timeout(20L, TimeUnit.SECONDS)
146+
.blockingGet()
147+
check(gatt == null)
148+
mainActivityRule.finishActivity()
149+
150+
Thread.sleep(5000)
127151
}
128152

129153
companion object {
130-
private val BATTERY_CHARACTERISTIC = UUID.fromString("00002A19-0000-1000-8000-00805F9B34FB")
131154
const val TAG = "EnqueueUnitTest"
132155
}
133156
}

dev-app/src/androidTest/java/com/vincentmasselis/devapp/ListenChangeUnitTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ class ListenChangeUnitTest {
7676
.flatMap { gatt -> gatt.whenConnectionIsReady().map { gatt } }
7777
.doOnSuccess { gatt ->
7878
android.os.Handler(Looper.getMainLooper()).postDelayed({
79-
gatt.disconnect().subscribe()
79+
gatt.disconnect()
8080
}, 1000)
8181
}
8282
.flatMap { gatt -> gatt.discoverServices().map { gatt } }
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.vincentmasselis.devapp
2+
3+
import android.util.Log
4+
5+
object Logger : com.vincentmasselis.rxbluetoothkotlin.Logger {
6+
override fun v(tag: String, message: String, throwable: Throwable?) {
7+
Log.v(tag, message, throwable)
8+
}
9+
10+
override fun d(tag: String, message: String, throwable: Throwable?) {
11+
Log.v(tag, message, throwable)
12+
}
13+
14+
override fun i(tag: String, message: String, throwable: Throwable?) {
15+
Log.v(tag, message, throwable)
16+
}
17+
18+
override fun w(tag: String, message: String, throwable: Throwable?) {
19+
Log.v(tag, message, throwable)
20+
}
21+
22+
override fun e(tag: String, message: String, throwable: Throwable?) {
23+
Log.v(tag, message, throwable)
24+
}
25+
26+
override fun wtf(tag: String, message: String, throwable: Throwable?) {
27+
Log.v(tag, message, throwable)
28+
}
29+
30+
}

0 commit comments

Comments
 (0)