Skip to content

Commit 00047d9

Browse files
authored
Android observe should suspend until Connecting.Observes OR Connected (#154)
1 parent 82e2581 commit 00047d9

5 files changed

Lines changed: 249 additions & 4 deletions

File tree

core/build.gradle.kts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,25 @@ kotlin {
2828
}
2929
}
3030

31+
val commonTest by getting {
32+
dependencies {
33+
implementation(kotlin("test-common"))
34+
implementation(kotlin("test-annotations-common"))
35+
}
36+
}
37+
3138
val jsMain by getting {
3239
dependencies {
3340
implementation(wrappers())
3441
}
3542
}
3643

44+
val jsTest by getting {
45+
dependencies {
46+
implementation(kotlin("test-js"))
47+
}
48+
}
49+
3750
val androidMain by getting {
3851
dependencies {
3952
api(coroutines("android"))
@@ -42,6 +55,12 @@ kotlin {
4255
}
4356
}
4457

58+
val androidTest by getting {
59+
dependencies {
60+
implementation(kotlin("test-junit"))
61+
}
62+
}
63+
4564
val macosX64Main by getting {
4665
dependencies {
4766
implementation(stately("isolate-macosx64"))

core/src/androidMain/kotlin/Observers.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ internal class Observers(
6363
onSubscription: OnSubscriptionAction,
6464
): Flow<ByteArray> = characteristicChanges
6565
.onSubscription {
66-
peripheral.suspendUntil<State.Connecting.Observes>()
66+
peripheral.suspendUntilAtLeast<State.Connecting.Observes>()
6767
if (observations.add(characteristic, onSubscription) == 1) {
6868
peripheral.startObservation(characteristic)
6969
}

core/src/commonMain/kotlin/Peripheral.kt

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,17 @@ public interface Peripheral {
157157
* @see [State] for a description of the potential states.
158158
*/
159159
internal suspend inline fun <reified T : State> Peripheral.suspendUntil() {
160-
state.first {
161-
it is T
162-
}
160+
state.first { it is T }
161+
}
162+
163+
/**
164+
* Suspends until [Peripheral] receiver arrives at the [State] specified or any [State] above it.
165+
*
166+
* @see [State] for a description of the potential states.
167+
* @see [State.isAtLeast] for state ordering.
168+
*/
169+
internal suspend inline fun <reified T : State> Peripheral.suspendUntilAtLeast() {
170+
state.first { it.isAtLeast<T>() }
163171
}
164172

165173
/**

core/src/commonMain/kotlin/State.kt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,33 @@ public sealed class State {
108108
}
109109
}
110110
}
111+
112+
/**
113+
* Returns `true` if `this` is at least in state [T], where [State]s are ordered:
114+
* - [State.Disconnected] (smallest)
115+
* - [State.Disconnecting]
116+
* - [State.Connecting.Bluetooth]
117+
* - [State.Connecting.Services]
118+
* - [State.Connecting.Observes]
119+
* - [State.Connected] (largest)
120+
*/
121+
internal inline fun <reified T : State> State.isAtLeast(): Boolean {
122+
val currentState = when (this) {
123+
is State.Disconnected -> 0
124+
State.Disconnecting -> 1
125+
State.Connecting.Bluetooth -> 2
126+
State.Connecting.Services -> 3
127+
State.Connecting.Observes -> 4
128+
State.Connected -> 5
129+
}
130+
val targetState = when (T::class) {
131+
State.Disconnected::class -> 0
132+
State.Disconnecting::class -> 1
133+
State.Connecting.Bluetooth::class -> 2
134+
State.Connecting.Services::class -> 3
135+
State.Connecting.Observes::class -> 4
136+
State.Connected::class -> 5
137+
else -> error("Unreachable.")
138+
}
139+
return currentState >= targetState
140+
}
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
package com.juul.kable
2+
3+
import kotlin.test.Test
4+
import kotlin.test.assertFalse
5+
import kotlin.test.assertTrue
6+
7+
public class StateTests {
8+
9+
@Test
10+
fun disconnected_isAtLeast_disconnected_is_true() {
11+
assertTrue(State.Disconnected().isAtLeast<State.Disconnected>())
12+
}
13+
14+
@Test
15+
fun disconnected_isAtLeast_disconnecting_is_false() {
16+
assertFalse(State.Disconnected().isAtLeast<State.Disconnecting>())
17+
}
18+
19+
@Test
20+
fun disconnected_isAtLeast_connectingBluetooth_is_false() {
21+
assertFalse(State.Disconnected().isAtLeast<State.Connecting.Bluetooth>())
22+
}
23+
24+
@Test
25+
fun disconnected_isAtLeast_connectingServices_is_false() {
26+
assertFalse(State.Disconnected().isAtLeast<State.Connecting.Services>())
27+
}
28+
29+
@Test
30+
fun disconnected_isAtLeast_connectingObserves_is_false() {
31+
assertFalse(State.Disconnected().isAtLeast<State.Connecting.Observes>())
32+
}
33+
34+
@Test
35+
fun disconnected_isAtLeast_connected_is_false() {
36+
assertFalse(State.Disconnected().isAtLeast<State.Connected>())
37+
}
38+
39+
@Test
40+
fun disconnecting_isAtLeast_disconnected_is_true() {
41+
assertTrue(State.Disconnecting.isAtLeast<State.Disconnected>())
42+
}
43+
44+
@Test
45+
fun disconnecting_isAtLeast_disconnecting_is_true() {
46+
assertTrue(State.Disconnecting.isAtLeast<State.Disconnecting>())
47+
}
48+
49+
@Test
50+
fun disconnecting_isAtLeast_connectingBluetooth_is_false() {
51+
assertFalse(State.Disconnecting.isAtLeast<State.Connecting.Bluetooth>())
52+
}
53+
54+
@Test
55+
fun disconnecting_isAtLeast_connectingServices_is_false() {
56+
assertFalse(State.Disconnecting.isAtLeast<State.Connecting.Services>())
57+
}
58+
59+
@Test
60+
fun disconnecting_isAtLeast_connectingObserves_is_false() {
61+
assertFalse(State.Disconnecting.isAtLeast<State.Connecting.Observes>())
62+
}
63+
64+
@Test
65+
fun disconnecting_isAtLeast_connected_is_false() {
66+
assertFalse(State.Disconnecting.isAtLeast<State.Connected>())
67+
}
68+
69+
@Test
70+
fun connectingBluetooth_isAtLeast_disconnected_is_true() {
71+
assertTrue(State.Connecting.Bluetooth.isAtLeast<State.Disconnected>())
72+
}
73+
74+
@Test
75+
fun connectingBluetooth_isAtLeast_disconnecting_is_true() {
76+
assertTrue(State.Connecting.Bluetooth.isAtLeast<State.Disconnecting>())
77+
}
78+
79+
@Test
80+
fun connectingBluetooth_isAtLeast_connectingBluetooth_is_true() {
81+
assertTrue(State.Connecting.Bluetooth.isAtLeast<State.Connecting.Bluetooth>())
82+
}
83+
84+
@Test
85+
fun connectingBluetooth_isAtLeast_connectingServices_is_false() {
86+
assertFalse(State.Connecting.Bluetooth.isAtLeast<State.Connecting.Services>())
87+
}
88+
89+
@Test
90+
fun connectingBluetooth_isAtLeast_connectingObserves_is_false() {
91+
assertFalse(State.Connecting.Bluetooth.isAtLeast<State.Connecting.Observes>())
92+
}
93+
94+
@Test
95+
fun connectingBluetooth_isAtLeast_connected_is_false() {
96+
assertFalse(State.Connecting.Bluetooth.isAtLeast<State.Connected>())
97+
}
98+
99+
@Test
100+
fun connectingServices_isAtLeast_disconnected_is_true() {
101+
assertTrue(State.Connecting.Services.isAtLeast<State.Disconnected>())
102+
}
103+
104+
@Test
105+
fun connectingServices_isAtLeast_disconnecting_is_true() {
106+
assertTrue(State.Connecting.Services.isAtLeast<State.Disconnecting>())
107+
}
108+
109+
@Test
110+
fun connectingServices_isAtLeast_connectingBluetooth_is_true() {
111+
assertTrue(State.Connecting.Services.isAtLeast<State.Connecting.Bluetooth>())
112+
}
113+
114+
@Test
115+
fun connectingServices_isAtLeast_connectingServices_is_true() {
116+
assertTrue(State.Connecting.Services.isAtLeast<State.Connecting.Services>())
117+
}
118+
119+
@Test
120+
fun connectingServices_isAtLeast_connectingObserves_is_false() {
121+
assertFalse(State.Connecting.Services.isAtLeast<State.Connecting.Observes>())
122+
}
123+
124+
@Test
125+
fun connectingServices_isAtLeast_connected_is_false() {
126+
assertFalse(State.Connecting.Services.isAtLeast<State.Connected>())
127+
}
128+
129+
@Test
130+
fun connectingObserves_isAtLeast_disconnected_is_true() {
131+
assertTrue(State.Connecting.Observes.isAtLeast<State.Disconnected>())
132+
}
133+
134+
@Test
135+
fun connectingObserves_isAtLeast_disconnecting_is_true() {
136+
assertTrue(State.Connecting.Observes.isAtLeast<State.Disconnecting>())
137+
}
138+
139+
@Test
140+
fun connectingObserves_isAtLeast_connectingBluetooth_is_true() {
141+
assertTrue(State.Connecting.Observes.isAtLeast<State.Connecting.Bluetooth>())
142+
}
143+
144+
@Test
145+
fun connectingObserves_isAtLeast_connectingServices_is_true() {
146+
assertTrue(State.Connecting.Observes.isAtLeast<State.Connecting.Services>())
147+
}
148+
149+
@Test
150+
fun connectingObserves_isAtLeast_connectingObserves_is_true() {
151+
assertTrue(State.Connecting.Observes.isAtLeast<State.Connecting.Observes>())
152+
}
153+
154+
@Test
155+
fun connectingObserves_isAtLeast_connected_is_false() {
156+
assertFalse(State.Connecting.Observes.isAtLeast<State.Connected>())
157+
}
158+
159+
@Test
160+
fun connected_isAtLeast_disconnected_is_true() {
161+
assertTrue(State.Connected.isAtLeast<State.Disconnected>())
162+
}
163+
164+
@Test
165+
fun connected_isAtLeast_disconnecting_is_true() {
166+
assertTrue(State.Connected.isAtLeast<State.Disconnecting>())
167+
}
168+
169+
@Test
170+
fun connected_isAtLeast_connectingBluetooth_is_true() {
171+
assertTrue(State.Connected.isAtLeast<State.Connecting.Bluetooth>())
172+
}
173+
174+
@Test
175+
fun connected_isAtLeast_connectingServices_is_true() {
176+
assertTrue(State.Connected.isAtLeast<State.Connecting.Services>())
177+
}
178+
179+
@Test
180+
fun connected_isAtLeast_connectingObserves_is_true() {
181+
assertTrue(State.Connected.isAtLeast<State.Connecting.Observes>())
182+
}
183+
184+
@Test
185+
fun connected_isAtLeast_connected_is_true() {
186+
assertTrue(State.Connected.isAtLeast<State.Connected>())
187+
}
188+
}

0 commit comments

Comments
 (0)