From a4f1e6bf90f247cc59bfe65fd1de986e691b069b Mon Sep 17 00:00:00 2001 From: David Taylor Date: Wed, 28 Apr 2021 17:12:41 -0700 Subject: [PATCH 1/2] Expose JavaScript peripheral advertisments --- core/src/jsMain/kotlin/Peripheral.kt | 35 ++++++++++++++-------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/core/src/jsMain/kotlin/Peripheral.kt b/core/src/jsMain/kotlin/Peripheral.kt index 9ef63fc21..e507955b4 100644 --- a/core/src/jsMain/kotlin/Peripheral.kt +++ b/core/src/jsMain/kotlin/Peripheral.kt @@ -15,9 +15,12 @@ import kotlinx.coroutines.Deferred import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.async import kotlinx.coroutines.await +import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.job import kotlinx.coroutines.suspendCancellableCoroutine @@ -59,23 +62,14 @@ public class JsPeripheral internal constructor( private val supportsAdvertisements = js("BluetoothDevice.prototype.watchAdvertisements") != null - public override suspend fun rssi(): Int = suspendCancellableCoroutine { continuation -> + public val advertisements: Flow = callbackFlow { check(supportsAdvertisements) { "watchAdvertisements unavailable" } - lateinit var listener: (JsEvent) -> Unit - val cleanup = { - bluetoothDevice.removeEventListener(ADVERTISEMENT_RECEIVED, listener) - // At the time of writing `unwatchAdvertisements()` remains unimplemented - if (bluetoothDevice.watchingAdvertisements && js("BluetoothDevice.prototype.unwatchAdvertisements") != null) { - bluetoothDevice.unwatchAdvertisements() - } - } - - listener = { - val event = it as BluetoothAdvertisingEvent - cleanup() - if (continuation.isActive && event.rssi != null) { - continuation.resume(event.rssi, onCancellation = null) + val listener: (JsEvent) -> Unit = { + runCatching { + offer(Advertisement(it as BluetoothAdvertisingEvent)) + }.onFailure { + console.warn("Unable to deliver advertisement event due to failure in flow or premature closing.") } } @@ -84,11 +78,18 @@ public class JsPeripheral internal constructor( } bluetoothDevice.addEventListener(ADVERTISEMENT_RECEIVED, listener) - continuation.invokeOnCancellation { - cleanup() + awaitClose { + bluetoothDevice.removeEventListener(ADVERTISEMENT_RECEIVED, listener) + // At the time of writing `unwatchAdvertisements()` remains unimplemented + if (bluetoothDevice.watchingAdvertisements && js("BluetoothDevice.prototype.unwatchAdvertisements") != null) { + bluetoothDevice.unwatchAdvertisements() + } } } + public override suspend fun rssi(): Int = + advertisements.first().rssi + private val gatt: BluetoothRemoteGATTServer get() = bluetoothDevice.gatt!! // fixme: !! From 57d3856c8b3da3cec9f7c618d7877c0c59bf8f70 Mon Sep 17 00:00:00 2001 From: David Taylor Date: Wed, 28 Apr 2021 17:23:05 -0700 Subject: [PATCH 2/2] Linting --- core/src/jsMain/kotlin/Peripheral.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/jsMain/kotlin/Peripheral.kt b/core/src/jsMain/kotlin/Peripheral.kt index e507955b4..ccbacb11a 100644 --- a/core/src/jsMain/kotlin/Peripheral.kt +++ b/core/src/jsMain/kotlin/Peripheral.kt @@ -23,7 +23,6 @@ import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.job -import kotlinx.coroutines.suspendCancellableCoroutine import org.khronos.webgl.DataView import kotlin.coroutines.CoroutineContext import org.w3c.dom.events.Event as JsEvent