From 5da33841058463f052a1489ddae6a7b23c072119 Mon Sep 17 00:00:00 2001 From: Travis Wyatt Date: Thu, 21 May 2020 09:55:41 -0700 Subject: [PATCH] Have onConnectionStateChange backed by StateFlow (#66) --- core/src/main/java/gatt/CoroutinesGatt.kt | 2 +- core/src/main/java/gatt/GattCallback.kt | 15 ++++++++++++--- core/src/main/java/gatt/GattConnection.kt | 16 ++++------------ gradle/dependencies.gradle | 2 +- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/core/src/main/java/gatt/CoroutinesGatt.kt b/core/src/main/java/gatt/CoroutinesGatt.kt index 3136ec7..2bd6bb1 100644 --- a/core/src/main/java/gatt/CoroutinesGatt.kt +++ b/core/src/main/java/gatt/CoroutinesGatt.kt @@ -34,7 +34,7 @@ class CoroutinesGatt internal constructor( ) : Gatt { @FlowPreview - override val onConnectionStateChange = callback.onConnectionStateChange.asFlow() + override val onConnectionStateChange = callback.onConnectionStateChange @FlowPreview override val onCharacteristicChanged = callback.onCharacteristicChanged.asFlow() diff --git a/core/src/main/java/gatt/GattCallback.kt b/core/src/main/java/gatt/GattCallback.kt index 5808081..76333c1 100644 --- a/core/src/main/java/gatt/GattCallback.kt +++ b/core/src/main/java/gatt/GattCallback.kt @@ -5,6 +5,7 @@ package com.juul.able.gatt import android.bluetooth.BluetoothGatt +import android.bluetooth.BluetoothGatt.GATT_SUCCESS import android.bluetooth.BluetoothGattCallback import android.bluetooth.BluetoothGattCharacteristic import android.bluetooth.BluetoothGattDescriptor @@ -17,13 +18,19 @@ import kotlinx.coroutines.channels.BroadcastChannel import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel.Factory.BUFFERED import kotlinx.coroutines.channels.Channel.Factory.CONFLATED +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.runBlocking internal class GattCallback( private val dispatcher: ExecutorCoroutineDispatcher ) : BluetoothGattCallback() { - val onConnectionStateChange = BroadcastChannel(CONFLATED) + private val _onConnectionStateChange = MutableStateFlow(null) + val onConnectionStateChange: Flow = + _onConnectionStateChange.filterNotNull() + val onCharacteristicChanged = BroadcastChannel(BUFFERED) val onResponse = Channel(CONFLATED) @@ -38,9 +45,11 @@ internal class GattCallback( if (isClosed.compareAndSet(false, true)) { Able.verbose { "Closing GattCallback belonging to device ${gatt.device}" } onDisconnecting() // Duplicate call in case Android skips STATE_DISCONNECTING. - onConnectionStateChange.close() gatt.close() + _onConnectionStateChange.value = + OnConnectionStateChange(GATT_SUCCESS, STATE_DISCONNECTED) + // todo: Remove when https://github.com/Kotlin/kotlinx.coroutines/issues/261 is fixed. dispatcher.close() } @@ -53,7 +62,7 @@ internal class GattCallback( ) { val event = OnConnectionStateChange(status, newState) Able.debug { "← $event" } - onConnectionStateChange.offer(event) + _onConnectionStateChange.value = event when (newState) { STATE_DISCONNECTING -> onDisconnecting() diff --git a/core/src/main/java/gatt/GattConnection.kt b/core/src/main/java/gatt/GattConnection.kt index 169be91..e488a3a 100644 --- a/core/src/main/java/gatt/GattConnection.kt +++ b/core/src/main/java/gatt/GattConnection.kt @@ -12,7 +12,7 @@ import android.bluetooth.BluetoothProfile.STATE_DISCONNECTED import com.juul.able.Able import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.onEach /** @@ -60,18 +60,10 @@ internal suspend fun GattConnection.suspendUntilConnectionState(state: GattConne .onEach { event -> Able.verbose { "← Received $event while waiting for ${state.asGattConnectionStateString()}" } if (event.status != GATT_SUCCESS) throw GattStatusFailure(event) + if (event.newState == STATE_DISCONNECTED && state != STATE_DISCONNECTED) throw ConnectionLost() } - .firstOrNull { (_, newState) -> newState == state } - .also { - if (it == null) { // Upstream Channel closed due to STATE_DISCONNECTED. - if (state == STATE_DISCONNECTED) { - Able.info { "Reached (implicit) STATE_DISCONNECTED" } - } else { - throw ConnectionLost() - } - } - } - ?.also { (_, newState) -> + .first { (_, newState) -> newState == state } + .also { (_, newState) -> Able.info { "Reached ${newState.asGattConnectionStateString()}" } } } diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index f6993e1..ee4e444 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -7,7 +7,7 @@ ext.versions = [ ext.deps = [ kotlin: [ stdlib: "org.jetbrains.kotlin:kotlin-stdlib", - coroutines: "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.5", + coroutines: "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7", junit: "org.jetbrains.kotlin:kotlin-test-junit", ],