From 33b42d8cc305191ed154fef266c1164f1b6fe5dd Mon Sep 17 00:00:00 2001 From: "Jack Boswell (boswelja)" Date: Thu, 10 Jun 2021 14:53:08 +1200 Subject: [PATCH 01/12] Update Gradle and fix build --- build.gradle.kts | 2 +- buildSrc/src/main/kotlin/Publishing.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index b6113123..71d87c23 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ buildscript { mavenCentral() } dependencies { - classpath("com.android.tools.build:gradle:7.1.0-alpha01") + classpath("com.android.tools.build:gradle:7.1.0-alpha02") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") } } diff --git a/buildSrc/src/main/kotlin/Publishing.kt b/buildSrc/src/main/kotlin/Publishing.kt index 3061b7e2..285dccf3 100644 --- a/buildSrc/src/main/kotlin/Publishing.kt +++ b/buildSrc/src/main/kotlin/Publishing.kt @@ -12,9 +12,9 @@ object Publishing { val version: String? get() = System.getenv("VERSION") - private val ossrhUsername: String? + val ossrhUsername: String? get() = System.getenv("OSSRH_USERNAME") - private val ossrhPassword: String? + val ossrhPassword: String? get() = System.getenv("OSSRH_PASSWORD") const val groupId = "io.github.boswelja.watchconnection" From 2075217af76e08b5dfd598acdf85de11d540ac2a Mon Sep 17 00:00:00 2001 From: "Jack Boswell (boswelja)" Date: Fri, 11 Jun 2021 17:00:57 +1200 Subject: [PATCH 02/12] Create Message.kt --- .../boswelja/watchconnection/core/Message.kt | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 core/src/main/kotlin/com/boswelja/watchconnection/core/Message.kt diff --git a/core/src/main/kotlin/com/boswelja/watchconnection/core/Message.kt b/core/src/main/kotlin/com/boswelja/watchconnection/core/Message.kt new file mode 100644 index 00000000..f9235008 --- /dev/null +++ b/core/src/main/kotlin/com/boswelja/watchconnection/core/Message.kt @@ -0,0 +1,38 @@ +package com.boswelja.watchconnection.core + +import java.util.UUID + +/** + * A data class containing information about a received message. + * @param sourceWatchId The [Watch.id] of the watch that sent the message. + * @param message The message itself. + * @param data Any data that may have been included with the message, or null if there is none. + */ +data class Message internal constructor( + val sourceWatchId: UUID, + val message: String, + val data: ByteArray? +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Message + + if (sourceWatchId != other.sourceWatchId) return false + if (message != other.message) return false + if (data != null) { + if (other.data == null) return false + if (!data.contentEquals(other.data)) return false + } else if (other.data != null) return false + + return true + } + + override fun hashCode(): Int { + var result = sourceWatchId.hashCode() + result = 31 * result + message.hashCode() + result = 31 * result + (data?.contentHashCode() ?: 0) + return result + } +} From b869aa0c123b8665ea500d26bf8429e5debc014c Mon Sep 17 00:00:00 2001 From: "Jack Boswell (boswelja)" Date: Fri, 11 Jun 2021 17:02:14 +1200 Subject: [PATCH 03/12] Update Gradle wrapper --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b5b34164..0d4acee0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,4 @@ -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-rc-2-bin.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStorePath=wrapper/dists From 6cf0bb343158d7cafafa358c9c32ba6aaba033a5 Mon Sep 17 00:00:00 2001 From: "Jack Boswell (boswelja)" Date: Fri, 11 Jun 2021 17:03:26 +1200 Subject: [PATCH 04/12] Deprecate existing message listener functions --- .../kotlin/com/boswelja/watchconnection/core/MessageListener.kt | 1 + .../kotlin/com/boswelja/watchconnection/core/WatchPlatform.kt | 2 ++ .../com/boswelja/watchconnection/core/WatchPlatformManager.kt | 2 ++ 3 files changed, 5 insertions(+) diff --git a/core/src/main/kotlin/com/boswelja/watchconnection/core/MessageListener.kt b/core/src/main/kotlin/com/boswelja/watchconnection/core/MessageListener.kt index fb74d63a..2210fadd 100644 --- a/core/src/main/kotlin/com/boswelja/watchconnection/core/MessageListener.kt +++ b/core/src/main/kotlin/com/boswelja/watchconnection/core/MessageListener.kt @@ -6,6 +6,7 @@ import java.util.UUID * An interface for message listeners. Add listener with [WatchPlatformManager.addMessageListener], * and remove with [WatchPlatformManager.removeMessageListener] when no longer needed. */ +@Deprecated("Collect inbound messages with inboundMessages flow instead") interface MessageListener { /** * Called when a message is received by this callback. diff --git a/core/src/main/kotlin/com/boswelja/watchconnection/core/WatchPlatform.kt b/core/src/main/kotlin/com/boswelja/watchconnection/core/WatchPlatform.kt index 9ae281f0..8d454e0c 100644 --- a/core/src/main/kotlin/com/boswelja/watchconnection/core/WatchPlatform.kt +++ b/core/src/main/kotlin/com/boswelja/watchconnection/core/WatchPlatform.kt @@ -55,6 +55,7 @@ interface WatchPlatform { * Adds a new [MessageListener]. * @param listener The [MessageListener] to register. */ + @Deprecated("Use incomingMessages Flow instead") fun addMessageListener(listener: MessageListener) /** @@ -62,5 +63,6 @@ interface WatchPlatform { * registered. * @param listener The [MessageListener] to unregister. */ + @Deprecated("Use incomingMessages Flow instead") fun removeMessageListener(listener: MessageListener) } diff --git a/core/src/main/kotlin/com/boswelja/watchconnection/core/WatchPlatformManager.kt b/core/src/main/kotlin/com/boswelja/watchconnection/core/WatchPlatformManager.kt index 9c5e6911..3110650a 100644 --- a/core/src/main/kotlin/com/boswelja/watchconnection/core/WatchPlatformManager.kt +++ b/core/src/main/kotlin/com/boswelja/watchconnection/core/WatchPlatformManager.kt @@ -81,6 +81,7 @@ class WatchPlatformManager( /** * Adds a [MessageListener] to all platforms. */ + @Deprecated("Use incomingMessages flow instead") fun addMessageListener(messageListener: MessageListener) { connectionHandlers.values.forEach { it.addMessageListener(messageListener) @@ -90,6 +91,7 @@ class WatchPlatformManager( /** * Removes a [MessageListener] from all platforms. */ + @Deprecated("Use incomingMessages flow instead") fun removeMessageListener(messageListener: MessageListener) { connectionHandlers.values.forEach { it.removeMessageListener(messageListener) From 9872ea6851614c0cfe22e31c0065f740f9f7f0c7 Mon Sep 17 00:00:00 2001 From: "Jack Boswell (boswelja)" Date: Fri, 11 Jun 2021 17:06:06 +1200 Subject: [PATCH 05/12] breaking: Add incomingMessages flow to WatchPlatform.kt definition --- .../com/boswelja/watchconnection/core/WatchPlatform.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/src/main/kotlin/com/boswelja/watchconnection/core/WatchPlatform.kt b/core/src/main/kotlin/com/boswelja/watchconnection/core/WatchPlatform.kt index 8d454e0c..60727510 100644 --- a/core/src/main/kotlin/com/boswelja/watchconnection/core/WatchPlatform.kt +++ b/core/src/main/kotlin/com/boswelja/watchconnection/core/WatchPlatform.kt @@ -13,6 +13,12 @@ interface WatchPlatform { */ val platformIdentifier: String + /** + * The [Flow] of [Message] received by this platform. This should not emit anything unless there + * are collectors attached. + */ + val incomingMessages: Flow + /** * A flow of all available watches for this platform. */ From 166c3c588f97cd3168d5b7304dfbd2fe3f27b058 Mon Sep 17 00:00:00 2001 From: "Jack Boswell (boswelja)" Date: Fri, 11 Jun 2021 17:29:25 +1200 Subject: [PATCH 06/12] First revision of incomingMessages flow on Wear OS --- .../boswelja/watchconnection/core/Message.kt | 2 +- .../watchconnection/wearos/WearOSPlatform.kt | 35 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/core/src/main/kotlin/com/boswelja/watchconnection/core/Message.kt b/core/src/main/kotlin/com/boswelja/watchconnection/core/Message.kt index f9235008..1fc380d5 100644 --- a/core/src/main/kotlin/com/boswelja/watchconnection/core/Message.kt +++ b/core/src/main/kotlin/com/boswelja/watchconnection/core/Message.kt @@ -8,7 +8,7 @@ import java.util.UUID * @param message The message itself. * @param data Any data that may have been included with the message, or null if there is none. */ -data class Message internal constructor( +data class Message( val sourceWatchId: UUID, val message: String, val data: ByteArray? diff --git a/wearos/src/main/kotlin/com/boswelja/watchconnection/wearos/WearOSPlatform.kt b/wearos/src/main/kotlin/com/boswelja/watchconnection/wearos/WearOSPlatform.kt index 58ccbd9d..12425b5a 100644 --- a/wearos/src/main/kotlin/com/boswelja/watchconnection/wearos/WearOSPlatform.kt +++ b/wearos/src/main/kotlin/com/boswelja/watchconnection/wearos/WearOSPlatform.kt @@ -1,6 +1,7 @@ package com.boswelja.watchconnection.wearos import android.content.Context +import com.boswelja.watchconnection.core.Message import com.boswelja.watchconnection.core.MessageListener import com.boswelja.watchconnection.core.Status import com.boswelja.watchconnection.core.Watch @@ -13,11 +14,16 @@ import com.google.android.gms.wearable.NodeClient import com.google.android.gms.wearable.Wearable import kotlinx.coroutines.CancellationException import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.ProducerScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.tasks.await @@ -48,11 +54,40 @@ class WearOSPlatform constructor( Wearable.getCapabilityClient(context) ) + private val coroutineScope = MainScope() + private val messageListeners = mutableMapOf() + private val wearableMessageReceiver = MessageClient.OnMessageReceivedListener { messageEvent -> + val message = Message( + Watch.createUUID(PLATFORM, messageEvent.sourceNodeId), + messageEvent.path, + messageEvent.data + ) + incomingMessageFlow.tryEmit(message) + } + + private val incomingMessageFlow = MutableSharedFlow( + replay = 0, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) + override val platformIdentifier = PLATFORM + override val incomingMessages: Flow = incomingMessageFlow + + init { + coroutineScope.launch { + incomingMessageFlow.subscriptionCount.collect { subscriberCount -> + if (subscriberCount > 0) { + messageClient.addListener(wearableMessageReceiver) + } else { + messageClient.removeListener(wearableMessageReceiver) + } + } + } + } override fun allWatches(): Flow> = flow { emit( nodeClient.connectedNodes.await().map { node -> From 10a4d39182e77057b96a216f96d8c4d26e6876de Mon Sep 17 00:00:00 2001 From: "Jack Boswell (boswelja)" Date: Fri, 11 Jun 2021 18:12:08 +1200 Subject: [PATCH 07/12] Initial implementation of incomingMessages flow for tizen --- .../watchconnection/tizen/MessageReceiver.kt | 7 ++++ .../tizen/TizenAccessoryAgent.kt | 13 +++++-- .../watchconnection/tizen/TizenPlatform.kt | 39 +++++++++++++++++++ 3 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/MessageReceiver.kt diff --git a/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/MessageReceiver.kt b/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/MessageReceiver.kt new file mode 100644 index 00000000..8eb15a55 --- /dev/null +++ b/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/MessageReceiver.kt @@ -0,0 +1,7 @@ +package com.boswelja.watchconnection.tizen + +import java.util.UUID + +abstract class MessageReceiver() { + abstract fun onMessageReceived(watchId: UUID, message: String, data: ByteArray?) +} diff --git a/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenAccessoryAgent.kt b/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenAccessoryAgent.kt index 12756c1a..b3b9eab8 100644 --- a/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenAccessoryAgent.kt +++ b/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenAccessoryAgent.kt @@ -32,6 +32,7 @@ class TizenAccessoryAgent internal constructor( private val peerMap = HashMap() private val messageListeners = ArrayList() + private var messageListener: MessageReceiver? = null // Keep a map of channels to message IDs to private val messageChannelMap = HashMap>() @@ -48,6 +49,10 @@ class TizenAccessoryAgent internal constructor( // Capability message, assume we have data handleCapabilityMessage(peerAgent, messageData!!) } else { + val watchId = Watch.createUUID( + TizenPlatform.PLATFORM, peerAgent.accessory.accessoryId + ) + messageListener?.onMessageReceived(watchId, message, messageData) handleMessage(peerAgent, message, messageData) } } @@ -117,12 +122,12 @@ class TizenAccessoryAgent internal constructor( return withTimeoutOrNull(OPERATION_TIMEOUT) { channel.receive() } == true } - fun registerMessageListener(listener: MessageListener) { - messageListeners.add(listener) + fun registerMessageListener(listener: MessageReceiver) { + messageListener = listener } - fun unregisterMessageListener(listener: MessageListener) { - messageListeners.remove(listener) + fun unregisterMessageListener(listener: MessageReceiver) { + messageListener = listener } override fun onFindPeerAgentsResponse(peers: Array?, result: Int) { diff --git a/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenPlatform.kt b/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenPlatform.kt index 6deb8262..69110809 100644 --- a/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenPlatform.kt +++ b/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenPlatform.kt @@ -1,14 +1,21 @@ package com.boswelja.watchconnection.tizen import android.content.Context +import com.boswelja.watchconnection.core.Message import com.boswelja.watchconnection.core.MessageListener import com.boswelja.watchconnection.core.Status import com.boswelja.watchconnection.core.Watch import com.boswelja.watchconnection.core.WatchPlatform import com.samsung.android.sdk.accessory.SAAgentV2 +import java.util.UUID import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch /** * A [WatchPlatform] with support for Tizen via Samsung's Accessory SDK. @@ -18,6 +25,24 @@ class TizenPlatform( context: Context ) : WatchPlatform { + private val coroutineScope = MainScope() + + private val incomingMessagesFlow = MutableSharedFlow( + replay = 0, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) + + private val messageReceiver = object : MessageReceiver() { + override fun onMessageReceived(watchId: UUID, message: String, data: ByteArray?) { + val message = Message( + watchId, + message, + data + ) + incomingMessagesFlow.tryEmit(message) + } + } + private lateinit var accessoryAgent: TizenAccessoryAgent var isReady: Boolean = false private set @@ -45,6 +70,20 @@ class TizenPlatform( override val platformIdentifier = PLATFORM + override val incomingMessages: Flow = incomingMessagesFlow + + init { + coroutineScope.launch { + incomingMessagesFlow.subscriptionCount.collect { subscriberCount -> + if (subscriberCount > 0) { + accessoryAgent.registerMessageListener(messageReceiver) + } else { + accessoryAgent.registerMessageListener(messageReceiver) + } + } + } + } + @ExperimentalCoroutinesApi override fun allWatches(): Flow> = accessoryAgent.allWatches() From cfec4bf8717d1da3b90fcc8f650ad90cb406dd03 Mon Sep 17 00:00:00 2001 From: "Jack Boswell (boswelja)" Date: Fri, 11 Jun 2021 18:15:28 +1200 Subject: [PATCH 08/12] breaking: Remove deprecated code Breaking changes for a reason --- .../watchconnection/core/MessageListener.kt | 18 ---------------- .../watchconnection/core/WatchPlatform.kt | 15 ------------- .../core/WatchPlatformManager.kt | 20 ------------------ .../tizen/TizenPlatformTest.kt | 1 - .../wearos/WearOSPlatformTest.kt | 1 - .../tizen/TizenAccessoryAgent.kt | 9 -------- .../watchconnection/tizen/TizenPlatform.kt | 7 ------- .../watchconnection/wearos/WearOSPlatform.kt | 21 ------------------- 8 files changed, 92 deletions(-) delete mode 100644 core/src/main/kotlin/com/boswelja/watchconnection/core/MessageListener.kt diff --git a/core/src/main/kotlin/com/boswelja/watchconnection/core/MessageListener.kt b/core/src/main/kotlin/com/boswelja/watchconnection/core/MessageListener.kt deleted file mode 100644 index 2210fadd..00000000 --- a/core/src/main/kotlin/com/boswelja/watchconnection/core/MessageListener.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.boswelja.watchconnection.core - -import java.util.UUID - -/** - * An interface for message listeners. Add listener with [WatchPlatformManager.addMessageListener], - * and remove with [WatchPlatformManager.removeMessageListener] when no longer needed. - */ -@Deprecated("Collect inbound messages with inboundMessages flow instead") -interface MessageListener { - /** - * Called when a message is received by this callback. - * @param sourceWatchId The [UUID] of the source watch. - * @param message The message that was received. - * @param data The data sent with the message, or null if there was none. - */ - fun onMessageReceived(sourceWatchId: UUID, message: String, data: ByteArray?) -} diff --git a/core/src/main/kotlin/com/boswelja/watchconnection/core/WatchPlatform.kt b/core/src/main/kotlin/com/boswelja/watchconnection/core/WatchPlatform.kt index 60727510..a953fe50 100644 --- a/core/src/main/kotlin/com/boswelja/watchconnection/core/WatchPlatform.kt +++ b/core/src/main/kotlin/com/boswelja/watchconnection/core/WatchPlatform.kt @@ -56,19 +56,4 @@ interface WatchPlatform { message: String, data: ByteArray? = null ): Boolean - - /** - * Adds a new [MessageListener]. - * @param listener The [MessageListener] to register. - */ - @Deprecated("Use incomingMessages Flow instead") - fun addMessageListener(listener: MessageListener) - - /** - * Removes a [MessageListener]. This will do nothing if the provided listener is not - * registered. - * @param listener The [MessageListener] to unregister. - */ - @Deprecated("Use incomingMessages Flow instead") - fun removeMessageListener(listener: MessageListener) } diff --git a/core/src/main/kotlin/com/boswelja/watchconnection/core/WatchPlatformManager.kt b/core/src/main/kotlin/com/boswelja/watchconnection/core/WatchPlatformManager.kt index 3110650a..5d6e64e0 100644 --- a/core/src/main/kotlin/com/boswelja/watchconnection/core/WatchPlatformManager.kt +++ b/core/src/main/kotlin/com/boswelja/watchconnection/core/WatchPlatformManager.kt @@ -77,24 +77,4 @@ class WatchPlatformManager( fun getStatusFor(watch: Watch): Flow? { return connectionHandlers[watch.platform]?.getStatusFor(watch.platformId) } - - /** - * Adds a [MessageListener] to all platforms. - */ - @Deprecated("Use incomingMessages flow instead") - fun addMessageListener(messageListener: MessageListener) { - connectionHandlers.values.forEach { - it.addMessageListener(messageListener) - } - } - - /** - * Removes a [MessageListener] from all platforms. - */ - @Deprecated("Use incomingMessages flow instead") - fun removeMessageListener(messageListener: MessageListener) { - connectionHandlers.values.forEach { - it.removeMessageListener(messageListener) - } - } } diff --git a/test/src/test/kotlin/com/boswelja/watchconnection/tizen/TizenPlatformTest.kt b/test/src/test/kotlin/com/boswelja/watchconnection/tizen/TizenPlatformTest.kt index eee9b3b3..d7264b71 100644 --- a/test/src/test/kotlin/com/boswelja/watchconnection/tizen/TizenPlatformTest.kt +++ b/test/src/test/kotlin/com/boswelja/watchconnection/tizen/TizenPlatformTest.kt @@ -4,7 +4,6 @@ import android.os.Build import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.boswelja.watchconnection.core.MessageListener import com.samsung.android.sdk.accessory.SAAgentV2 import io.mockk.MockKAnnotations import io.mockk.coVerify diff --git a/test/src/test/kotlin/com/boswelja/watchconnection/wearos/WearOSPlatformTest.kt b/test/src/test/kotlin/com/boswelja/watchconnection/wearos/WearOSPlatformTest.kt index 8e0289dd..9122628f 100644 --- a/test/src/test/kotlin/com/boswelja/watchconnection/wearos/WearOSPlatformTest.kt +++ b/test/src/test/kotlin/com/boswelja/watchconnection/wearos/WearOSPlatformTest.kt @@ -2,7 +2,6 @@ package com.boswelja.watchconnection.wearos import android.os.Build import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.boswelja.watchconnection.core.MessageListener import com.boswelja.watchconnection.core.Status import com.boswelja.watchconnection.wearos.CapabilityHelpers.createCapabilities import com.boswelja.watchconnection.wearos.NodeHelpers.createDummyNodes diff --git a/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenAccessoryAgent.kt b/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenAccessoryAgent.kt index b3b9eab8..8ca6dda4 100644 --- a/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenAccessoryAgent.kt +++ b/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenAccessoryAgent.kt @@ -1,7 +1,6 @@ package com.boswelja.watchconnection.tizen import android.content.Context -import com.boswelja.watchconnection.core.MessageListener import com.boswelja.watchconnection.core.Messages.sendBroadcast import com.boswelja.watchconnection.core.Watch import com.samsung.android.sdk.SsdkUnsupportedException @@ -31,7 +30,6 @@ class TizenAccessoryAgent internal constructor( private val allWatches = MutableStateFlow>(emptyList()) private val peerMap = HashMap() - private val messageListeners = ArrayList() private var messageListener: MessageReceiver? = null // Keep a map of channels to message IDs to @@ -170,13 +168,6 @@ class TizenAccessoryAgent internal constructor( TizenPlatform.PLATFORM, peer.accessory.accessoryId ) - messageListeners.forEach { listener -> - listener.onMessageReceived( - id, - message, - data - ) - } // Send message broadcast sendBroadcast(applicationContext, id, message, data) diff --git a/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenPlatform.kt b/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenPlatform.kt index 69110809..ff8db2b8 100644 --- a/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenPlatform.kt +++ b/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenPlatform.kt @@ -2,7 +2,6 @@ package com.boswelja.watchconnection.tizen import android.content.Context import com.boswelja.watchconnection.core.Message -import com.boswelja.watchconnection.core.MessageListener import com.boswelja.watchconnection.core.Status import com.boswelja.watchconnection.core.Watch import com.boswelja.watchconnection.core.WatchPlatform @@ -105,12 +104,6 @@ class TizenPlatform( override suspend fun sendMessage(watchId: String, message: String, data: ByteArray?): Boolean = accessoryAgent.sendMessage(watchId, message, data) - override fun addMessageListener(listener: MessageListener) = - accessoryAgent.registerMessageListener(listener) - - override fun removeMessageListener(listener: MessageListener) = - accessoryAgent.unregisterMessageListener(listener) - companion object { const val CAPABILITY_MESSAGE = "/request_capabilities" const val PLATFORM = "TIZEN" diff --git a/wearos/src/main/kotlin/com/boswelja/watchconnection/wearos/WearOSPlatform.kt b/wearos/src/main/kotlin/com/boswelja/watchconnection/wearos/WearOSPlatform.kt index 12425b5a..3011f8f6 100644 --- a/wearos/src/main/kotlin/com/boswelja/watchconnection/wearos/WearOSPlatform.kt +++ b/wearos/src/main/kotlin/com/boswelja/watchconnection/wearos/WearOSPlatform.kt @@ -2,7 +2,6 @@ package com.boswelja.watchconnection.wearos import android.content.Context import com.boswelja.watchconnection.core.Message -import com.boswelja.watchconnection.core.MessageListener import com.boswelja.watchconnection.core.Status import com.boswelja.watchconnection.core.Watch import com.boswelja.watchconnection.core.WatchPlatform @@ -56,9 +55,6 @@ class WearOSPlatform constructor( private val coroutineScope = MainScope() - private val messageListeners = - mutableMapOf() - private val wearableMessageReceiver = MessageClient.OnMessageReceivedListener { messageEvent -> val message = Message( Watch.createUUID(PLATFORM, messageEvent.sourceNodeId), @@ -184,23 +180,6 @@ class WearOSPlatform constructor( } } - override fun addMessageListener(listener: MessageListener) { - val onMessageReceiveListener = MessageClient.OnMessageReceivedListener { - val id = Watch.createUUID(PLATFORM, it.sourceNodeId) - listener.onMessageReceived(id, it.path, it.data) - } - messageClient.addListener(onMessageReceiveListener) - // Store this in a map, so we can look it up to unregister later - messageListeners[listener] = onMessageReceiveListener - } - - override fun removeMessageListener(listener: MessageListener) { - // Look up listener and remove it from both the map and messageClient - messageListeners.remove(listener)?.let { - messageClient.removeListener(it) - } - } - @ExperimentalCoroutinesApi private fun ProducerScope.getStatusFromCapabilityInfo( watchId: String, From 64aaf909faa14f4eccc81be73727ff0ec39d42aa Mon Sep 17 00:00:00 2001 From: "Jack Boswell (boswelja)" Date: Fri, 11 Jun 2021 18:17:48 +1200 Subject: [PATCH 09/12] Clean up code --- .../watchconnection/tizen/MessageReceiver.kt | 2 +- .../tizen/TizenAccessoryAgent.kt | 18 +----------------- .../watchconnection/tizen/TizenPlatform.kt | 11 ++++++----- 3 files changed, 8 insertions(+), 23 deletions(-) diff --git a/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/MessageReceiver.kt b/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/MessageReceiver.kt index 8eb15a55..4151b072 100644 --- a/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/MessageReceiver.kt +++ b/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/MessageReceiver.kt @@ -2,6 +2,6 @@ package com.boswelja.watchconnection.tizen import java.util.UUID -abstract class MessageReceiver() { +abstract class MessageReceiver { abstract fun onMessageReceived(watchId: UUID, message: String, data: ByteArray?) } diff --git a/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenAccessoryAgent.kt b/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenAccessoryAgent.kt index 8ca6dda4..4af14ab9 100644 --- a/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenAccessoryAgent.kt +++ b/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenAccessoryAgent.kt @@ -51,7 +51,7 @@ class TizenAccessoryAgent internal constructor( TizenPlatform.PLATFORM, peerAgent.accessory.accessoryId ) messageListener?.onMessageReceived(watchId, message, messageData) - handleMessage(peerAgent, message, messageData) + sendBroadcast(applicationContext, watchId, message, data) } } } @@ -157,22 +157,6 @@ class TizenAccessoryAgent internal constructor( } } - /** - * Pass a received message on to message listeners and broadcast receivers. - * @param peer The [SAPeerAgent] that sent the message. - * @param message The message received. - * @param data The data received with the message, or null if there was none. - */ - private fun handleMessage(peer: SAPeerAgent, message: String, data: ByteArray?) { - val id = Watch.createUUID( - TizenPlatform.PLATFORM, - peer.accessory.accessoryId - ) - - // Send message broadcast - sendBroadcast(applicationContext, id, message, data) - } - companion object { private const val TAG = "TizenConnectionHandler" private const val OPERATION_TIMEOUT = 1000L diff --git a/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenPlatform.kt b/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenPlatform.kt index ff8db2b8..8d510c16 100644 --- a/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenPlatform.kt +++ b/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenPlatform.kt @@ -33,12 +33,13 @@ class TizenPlatform( private val messageReceiver = object : MessageReceiver() { override fun onMessageReceived(watchId: UUID, message: String, data: ByteArray?) { - val message = Message( - watchId, - message, - data + incomingMessagesFlow.tryEmit( + Message( + watchId, + message, + data + ) ) - incomingMessagesFlow.tryEmit(message) } } From f8279b8dd6884d5089827f6501df6e1935ce4c9c Mon Sep 17 00:00:00 2001 From: "Jack Boswell (boswelja)" Date: Fri, 11 Jun 2021 18:19:39 +1200 Subject: [PATCH 10/12] Switch MessageReceiver.kt to our new Message data class --- .../watchconnection/core/MessageReceiver.kt | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/core/src/main/kotlin/com/boswelja/watchconnection/core/MessageReceiver.kt b/core/src/main/kotlin/com/boswelja/watchconnection/core/MessageReceiver.kt index 7effb739..abcc7854 100644 --- a/core/src/main/kotlin/com/boswelja/watchconnection/core/MessageReceiver.kt +++ b/core/src/main/kotlin/com/boswelja/watchconnection/core/MessageReceiver.kt @@ -18,15 +18,11 @@ abstract class MessageReceiver : BroadcastReceiver() { /** * Called when a message has been received. While this is a suspendable function, the limits * of [BroadcastReceiver.goAsync] still apply. - * @param sourceWatchId The [Watch.id] of the watch that sent the message. - * @param message The message that was received. - * @param data The data included with the message, or null if there was no data. + * @param message The [Message] that was received. */ abstract suspend fun onMessageReceived( context: Context, - sourceWatchId: UUID, - message: String, - data: ByteArray? + message: Message ) final override fun onReceive(context: Context?, intent: Intent?) { @@ -43,7 +39,10 @@ abstract class MessageReceiver : BroadcastReceiver() { val data = intent.getByteArrayExtra(EXTRA_DATA) // Pass it on to user code - onMessageReceived(context, watchId, message, data) + onMessageReceived( + context, + Message(watchId, message, data) + ) // Let the BroadcastReceiver know we're done pendingResult.finish() From e7e39846792b50326267849ecb403a6b87292772 Mon Sep 17 00:00:00 2001 From: "Jack Boswell (boswelja)" Date: Fri, 11 Jun 2021 19:49:42 +1200 Subject: [PATCH 11/12] Update tests --- .../core/ConcreteMessageReceiver.kt | 5 +- .../watchconnection/core/DummyPlatform.kt | 20 +++---- .../core/MessageReceiverTest.kt | 22 ++----- .../core/WatchPlatformManagerTest.kt | 35 +---------- .../tizen/TizenPlatformTest.kt | 27 --------- .../wearos/WearOSPlatformTest.kt | 59 ------------------- .../watchconnection/tizen/TizenPlatform.kt | 2 +- .../watchconnection/wearos/WearOSPlatform.kt | 2 +- 8 files changed, 16 insertions(+), 156 deletions(-) diff --git a/test/src/test/kotlin/com/boswelja/watchconnection/core/ConcreteMessageReceiver.kt b/test/src/test/kotlin/com/boswelja/watchconnection/core/ConcreteMessageReceiver.kt index 93b0fcef..55f14dae 100644 --- a/test/src/test/kotlin/com/boswelja/watchconnection/core/ConcreteMessageReceiver.kt +++ b/test/src/test/kotlin/com/boswelja/watchconnection/core/ConcreteMessageReceiver.kt @@ -1,14 +1,11 @@ package com.boswelja.watchconnection.core import android.content.Context -import java.util.UUID class ConcreteMessageReceiver : MessageReceiver() { override suspend fun onMessageReceived( context: Context, - sourceWatchId: UUID, - message: String, - data: ByteArray? + message: Message ) { } } diff --git a/test/src/test/kotlin/com/boswelja/watchconnection/core/DummyPlatform.kt b/test/src/test/kotlin/com/boswelja/watchconnection/core/DummyPlatform.kt index cb4c6739..515bc81a 100644 --- a/test/src/test/kotlin/com/boswelja/watchconnection/core/DummyPlatform.kt +++ b/test/src/test/kotlin/com/boswelja/watchconnection/core/DummyPlatform.kt @@ -6,15 +6,17 @@ import kotlinx.coroutines.flow.flowOf class DummyPlatform( override val platformIdentifier: String, - val allWatches: Array, - val watchesWithApp: Array + val allWatches: List, + val watchesWithApp: List ) : WatchPlatform { - override fun allWatches(): Flow> = flowOf(allWatches) + override val incomingMessages: Flow = flow { } - override fun watchesWithApp(): Flow> = flowOf(watchesWithApp) + override fun allWatches(): Flow> = flowOf(allWatches) - override fun getCapabilitiesFor(watchId: String): Flow> = flow { } + override fun watchesWithApp(): Flow> = flowOf(watchesWithApp) + + override fun getCapabilitiesFor(watchId: String): Flow> = flow { } override fun getStatusFor(watchId: String): Flow = flow { } @@ -22,12 +24,4 @@ class DummyPlatform( // Do nothing return false } - - override fun addMessageListener(listener: MessageListener) { - // Do nothing - } - - override fun removeMessageListener(listener: MessageListener) { - // Do nothing - } } diff --git a/test/src/test/kotlin/com/boswelja/watchconnection/core/MessageReceiverTest.kt b/test/src/test/kotlin/com/boswelja/watchconnection/core/MessageReceiverTest.kt index 356f4a12..19b7d7c1 100644 --- a/test/src/test/kotlin/com/boswelja/watchconnection/core/MessageReceiverTest.kt +++ b/test/src/test/kotlin/com/boswelja/watchconnection/core/MessageReceiverTest.kt @@ -43,27 +43,12 @@ class MessageReceiverTest { messageReceiver.onReceive(context, intent) verify(inverse = true, timeout = 50) { messageReceiver.goAsync() } coVerify(inverse = true, timeout = 50) { - messageReceiver.onMessageReceived(any(), any(), any(), any()) + messageReceiver.onMessageReceived(any(), any()) } } @Test - fun `onReceive passes variables to onMessageReceived if message has no data`() { - val id = UUID.randomUUID() - val message = "message" - - Intent(ACTION_MESSAGE_RECEIVED).apply { - putExtra(EXTRA_WATCH_ID, id.toString()) - putExtra(EXTRA_MESSAGE, message) - }.also { intent -> - messageReceiver.onReceive(context, intent) - } - - coVerify(timeout = 50) { messageReceiver.onMessageReceived(context, id, message, null) } - } - - @Test - fun `onReceive passes variables to onMessageReceived if message has data`() { + fun `onReceive passes variables to onMessageReceived`() { val id = UUID.randomUUID() val message = "message" val data = Random.nextBytes(10) @@ -76,6 +61,7 @@ class MessageReceiverTest { messageReceiver.onReceive(context, intent) } - coVerify(timeout = 50) { messageReceiver.onMessageReceived(context, id, message, data) } + val expectedMessage = Message(id, message, data) + coVerify(timeout = 50) { messageReceiver.onMessageReceived(context, expectedMessage) } } } diff --git a/test/src/test/kotlin/com/boswelja/watchconnection/core/WatchPlatformManagerTest.kt b/test/src/test/kotlin/com/boswelja/watchconnection/core/WatchPlatformManagerTest.kt index ab2cf446..57c7183e 100644 --- a/test/src/test/kotlin/com/boswelja/watchconnection/core/WatchPlatformManagerTest.kt +++ b/test/src/test/kotlin/com/boswelja/watchconnection/core/WatchPlatformManagerTest.kt @@ -5,7 +5,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import io.mockk.coVerify import io.mockk.spyk import io.mockk.verify -import java.util.UUID import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import org.junit.Before @@ -37,7 +36,7 @@ class WatchPlatformManagerTest { dummyPlatformIds.forEach { platform -> // Generate dummy watches - var allWatches = emptyArray() + val allWatches = mutableListOf() (1..dummyWatchCountPerPlatform + 1).forEach { count -> allWatches += Watch( name = "Watch $count", @@ -47,7 +46,7 @@ class WatchPlatformManagerTest { } // Generate dummy watches with app - var watchesWithApp = emptyArray() + val watchesWithApp = mutableListOf() (1..dummyWatchWithAppCountPerPlatform + 1).forEach { count -> watchesWithApp += Watch( name = "Watch $count", @@ -102,34 +101,4 @@ class WatchPlatformManagerTest { coVerify { platform.getCapabilitiesFor(watch.platformId) } } } - - @Test - fun `registerMessageListener adds the message listener to all platforms`() { - val messageListener = object : MessageListener { - override fun onMessageReceived( - sourceWatchId: UUID, - message: String, - data: ByteArray? - ) { } - } - platformManager.addMessageListener(messageListener) - dummyPlatforms.forEach { platform -> - verify { platform.addMessageListener(messageListener) } - } - } - - @Test - fun `unregisterMessageListener removes the message listener from all platforms`() { - val messageListener = object : MessageListener { - override fun onMessageReceived( - sourceWatchId: UUID, - message: String, - data: ByteArray? - ) { } - } - platformManager.removeMessageListener(messageListener) - dummyPlatforms.forEach { platform -> - verify { platform.removeMessageListener(messageListener) } - } - } } diff --git a/test/src/test/kotlin/com/boswelja/watchconnection/tizen/TizenPlatformTest.kt b/test/src/test/kotlin/com/boswelja/watchconnection/tizen/TizenPlatformTest.kt index d7264b71..2515326b 100644 --- a/test/src/test/kotlin/com/boswelja/watchconnection/tizen/TizenPlatformTest.kt +++ b/test/src/test/kotlin/com/boswelja/watchconnection/tizen/TizenPlatformTest.kt @@ -11,7 +11,6 @@ import io.mockk.every import io.mockk.impl.annotations.RelaxedMockK import io.mockk.mockkStatic import io.mockk.verify -import java.util.UUID import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.collect import kotlinx.coroutines.runBlocking @@ -125,30 +124,4 @@ class TizenPlatformTest { connectionHandler.sendMessage(id, message, data) coVerify { accessoryAgent.sendMessage(id, message, data) } } - - @Test - fun `registerMessageListener calls agent`() { - val listener = object : MessageListener { - override fun onMessageReceived( - sourceWatchId: UUID, - message: String, - data: ByteArray? - ) { } - } - connectionHandler.addMessageListener(listener) - verify { accessoryAgent.registerMessageListener(listener) } - } - - @Test - fun `unregisterMessageListener calls agent`() { - val listener = object : MessageListener { - override fun onMessageReceived( - sourceWatchId: UUID, - message: String, - data: ByteArray? - ) { } - } - connectionHandler.removeMessageListener(listener) - verify { accessoryAgent.unregisterMessageListener(listener) } - } } diff --git a/test/src/test/kotlin/com/boswelja/watchconnection/wearos/WearOSPlatformTest.kt b/test/src/test/kotlin/com/boswelja/watchconnection/wearos/WearOSPlatformTest.kt index 9122628f..e09d649c 100644 --- a/test/src/test/kotlin/com/boswelja/watchconnection/wearos/WearOSPlatformTest.kt +++ b/test/src/test/kotlin/com/boswelja/watchconnection/wearos/WearOSPlatformTest.kt @@ -14,8 +14,6 @@ import com.google.android.gms.wearable.NodeClient import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.RelaxedMockK -import io.mockk.verify -import java.util.UUID import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.filterNot import kotlinx.coroutines.flow.first @@ -241,61 +239,4 @@ class WearOSPlatformTest { val result = connectionHandler.sendMessage(id, message, data) expectThat(result).isFalse() } - - @Test - fun `registerMessageListener registers a listener with messageClient`() { - // Create dummy listener - val listener = object : MessageListener { - override fun onMessageReceived( - sourceWatchId: UUID, - message: String, - data: ByteArray? - ) { } - } - - // Register listener - connectionHandler.addMessageListener(listener) - - // Check messageClient was called - verify { messageClient.addListener(any()) } - } - - @Test - fun `unregisterMessageListener removes a listener with messageClient`() { - // Create dummy listener - val listener = object : MessageListener { - override fun onMessageReceived( - sourceWatchId: UUID, - message: String, - data: ByteArray? - ) { } - } - - // We need to add a listener first - connectionHandler.addMessageListener(listener) - - // Register listener - connectionHandler.removeMessageListener(listener) - - // Check messageClient was called - verify { messageClient.removeListener(any()) } - } - - @Test - fun `unregisterMessageListener does nothing with previously unregistered listener`() { - // Create dummy listener - val listener = object : MessageListener { - override fun onMessageReceived( - sourceWatchId: UUID, - message: String, - data: ByteArray? - ) { } - } - - // Register listener - connectionHandler.removeMessageListener(listener) - - // Check messageClient was called - verify(inverse = true) { messageClient.removeListener(any()) } - } } diff --git a/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenPlatform.kt b/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenPlatform.kt index 8d510c16..84203eef 100644 --- a/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenPlatform.kt +++ b/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenPlatform.kt @@ -27,7 +27,7 @@ class TizenPlatform( private val coroutineScope = MainScope() private val incomingMessagesFlow = MutableSharedFlow( - replay = 0, + replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST ) diff --git a/wearos/src/main/kotlin/com/boswelja/watchconnection/wearos/WearOSPlatform.kt b/wearos/src/main/kotlin/com/boswelja/watchconnection/wearos/WearOSPlatform.kt index 3011f8f6..5ff80dc9 100644 --- a/wearos/src/main/kotlin/com/boswelja/watchconnection/wearos/WearOSPlatform.kt +++ b/wearos/src/main/kotlin/com/boswelja/watchconnection/wearos/WearOSPlatform.kt @@ -65,7 +65,7 @@ class WearOSPlatform constructor( } private val incomingMessageFlow = MutableSharedFlow( - replay = 0, + replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST ) From c3539ca726642417787d5f1623b89e61bd85b615 Mon Sep 17 00:00:00 2001 From: "Jack Boswell (boswelja)" Date: Fri, 11 Jun 2021 19:56:45 +1200 Subject: [PATCH 12/12] Incoming messages flow version 2 --- .../watchconnection/core/WatchPlatform.kt | 4 +- .../core/WatchPlatformManager.kt | 8 +++ .../watchconnection/core/DummyPlatform.kt | 2 +- .../tizen/TizenAccessoryAgent.kt | 10 ++-- .../watchconnection/tizen/TizenPlatform.kt | 52 +++++++------------ .../watchconnection/wearos/WearOSPlatform.kt | 48 ++++++----------- 6 files changed, 52 insertions(+), 72 deletions(-) diff --git a/core/src/main/kotlin/com/boswelja/watchconnection/core/WatchPlatform.kt b/core/src/main/kotlin/com/boswelja/watchconnection/core/WatchPlatform.kt index a953fe50..52348e23 100644 --- a/core/src/main/kotlin/com/boswelja/watchconnection/core/WatchPlatform.kt +++ b/core/src/main/kotlin/com/boswelja/watchconnection/core/WatchPlatform.kt @@ -14,10 +14,10 @@ interface WatchPlatform { val platformIdentifier: String /** - * The [Flow] of [Message] received by this platform. This should not emit anything unless there + * A [Flow] of [Message]s received by this platform. This should not emit anything unless there * are collectors attached. */ - val incomingMessages: Flow + fun incomingMessages(): Flow /** * A flow of all available watches for this platform. diff --git a/core/src/main/kotlin/com/boswelja/watchconnection/core/WatchPlatformManager.kt b/core/src/main/kotlin/com/boswelja/watchconnection/core/WatchPlatformManager.kt index 5d6e64e0..bb34c672 100644 --- a/core/src/main/kotlin/com/boswelja/watchconnection/core/WatchPlatformManager.kt +++ b/core/src/main/kotlin/com/boswelja/watchconnection/core/WatchPlatformManager.kt @@ -3,6 +3,7 @@ package com.boswelja.watchconnection.core import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.merge /** * A class to simplify handling multiple [WatchPlatform]. @@ -49,6 +50,13 @@ class WatchPlatformManager( list } + /** + * A [Flow] of [Message]s received by all [WatchPlatform]s. + */ + @ExperimentalCoroutinesApi + fun incomingMessages(): Flow = + connectionHandlers.values.map { it.incomingMessages() }.merge() + /** * Send a message to a [Watch]. * @param to The [Watch] to send the message to. diff --git a/test/src/test/kotlin/com/boswelja/watchconnection/core/DummyPlatform.kt b/test/src/test/kotlin/com/boswelja/watchconnection/core/DummyPlatform.kt index 515bc81a..a84f9fca 100644 --- a/test/src/test/kotlin/com/boswelja/watchconnection/core/DummyPlatform.kt +++ b/test/src/test/kotlin/com/boswelja/watchconnection/core/DummyPlatform.kt @@ -10,7 +10,7 @@ class DummyPlatform( val watchesWithApp: List ) : WatchPlatform { - override val incomingMessages: Flow = flow { } + override fun incomingMessages(): Flow = flow { } override fun allWatches(): Flow> = flowOf(allWatches) diff --git a/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenAccessoryAgent.kt b/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenAccessoryAgent.kt index 4af14ab9..f52d1b7c 100644 --- a/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenAccessoryAgent.kt +++ b/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenAccessoryAgent.kt @@ -30,7 +30,7 @@ class TizenAccessoryAgent internal constructor( private val allWatches = MutableStateFlow>(emptyList()) private val peerMap = HashMap() - private var messageListener: MessageReceiver? = null + private val messageListeners = mutableListOf() // Keep a map of channels to message IDs to private val messageChannelMap = HashMap>() @@ -50,7 +50,9 @@ class TizenAccessoryAgent internal constructor( val watchId = Watch.createUUID( TizenPlatform.PLATFORM, peerAgent.accessory.accessoryId ) - messageListener?.onMessageReceived(watchId, message, messageData) + messageListeners.forEach { + it.onMessageReceived(watchId, message, data) + } sendBroadcast(applicationContext, watchId, message, data) } } @@ -121,11 +123,11 @@ class TizenAccessoryAgent internal constructor( } fun registerMessageListener(listener: MessageReceiver) { - messageListener = listener + messageListeners.add(listener) } fun unregisterMessageListener(listener: MessageReceiver) { - messageListener = listener + messageListeners.remove(listener) } override fun onFindPeerAgentsResponse(peers: Array?, result: Int) { diff --git a/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenPlatform.kt b/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenPlatform.kt index 84203eef..97282f01 100644 --- a/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenPlatform.kt +++ b/tizen/src/main/kotlin/com/boswelja/watchconnection/tizen/TizenPlatform.kt @@ -8,13 +8,10 @@ import com.boswelja.watchconnection.core.WatchPlatform import com.samsung.android.sdk.accessory.SAAgentV2 import java.util.UUID import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.MainScope -import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch /** * A [WatchPlatform] with support for Tizen via Samsung's Accessory SDK. @@ -24,25 +21,6 @@ class TizenPlatform( context: Context ) : WatchPlatform { - private val coroutineScope = MainScope() - - private val incomingMessagesFlow = MutableSharedFlow( - replay = 1, - onBufferOverflow = BufferOverflow.DROP_OLDEST - ) - - private val messageReceiver = object : MessageReceiver() { - override fun onMessageReceived(watchId: UUID, message: String, data: ByteArray?) { - incomingMessagesFlow.tryEmit( - Message( - watchId, - message, - data - ) - ) - } - } - private lateinit var accessoryAgent: TizenAccessoryAgent var isReady: Boolean = false private set @@ -70,18 +48,24 @@ class TizenPlatform( override val platformIdentifier = PLATFORM - override val incomingMessages: Flow = incomingMessagesFlow - - init { - coroutineScope.launch { - incomingMessagesFlow.subscriptionCount.collect { subscriberCount -> - if (subscriberCount > 0) { - accessoryAgent.registerMessageListener(messageReceiver) - } else { - accessoryAgent.registerMessageListener(messageReceiver) - } + @ExperimentalCoroutinesApi + override fun incomingMessages(): Flow = callbackFlow { + val receiver = object : MessageReceiver() { + override fun onMessageReceived(watchId: UUID, message: String, data: ByteArray?) { + val messageData = Message( + watchId, message, data + ) + trySend( + messageData + ) } } + + accessoryAgent.registerMessageListener(receiver) + + awaitClose { + accessoryAgent.unregisterMessageListener(receiver) + } } @ExperimentalCoroutinesApi diff --git a/wearos/src/main/kotlin/com/boswelja/watchconnection/wearos/WearOSPlatform.kt b/wearos/src/main/kotlin/com/boswelja/watchconnection/wearos/WearOSPlatform.kt index 5ff80dc9..008b36c2 100644 --- a/wearos/src/main/kotlin/com/boswelja/watchconnection/wearos/WearOSPlatform.kt +++ b/wearos/src/main/kotlin/com/boswelja/watchconnection/wearos/WearOSPlatform.kt @@ -13,16 +13,12 @@ import com.google.android.gms.wearable.NodeClient import com.google.android.gms.wearable.Wearable import kotlinx.coroutines.CancellationException import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.MainScope -import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.ProducerScope import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.channels.trySendBlocking import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.callbackFlow -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.tasks.await @@ -41,6 +37,7 @@ class WearOSPlatform constructor( * @param capabilities A list of capability strings to use when searching for watch capabilities * companion app installed. */ + @Suppress("unused") constructor( context: Context, appCapability: String, @@ -53,37 +50,26 @@ class WearOSPlatform constructor( Wearable.getCapabilityClient(context) ) - private val coroutineScope = MainScope() - - private val wearableMessageReceiver = MessageClient.OnMessageReceivedListener { messageEvent -> - val message = Message( - Watch.createUUID(PLATFORM, messageEvent.sourceNodeId), - messageEvent.path, - messageEvent.data - ) - incomingMessageFlow.tryEmit(message) - } - - private val incomingMessageFlow = MutableSharedFlow( - replay = 1, - onBufferOverflow = BufferOverflow.DROP_OLDEST - ) - override val platformIdentifier = PLATFORM - override val incomingMessages: Flow = incomingMessageFlow + @ExperimentalCoroutinesApi + override fun incomingMessages(): Flow = callbackFlow { + val listener = MessageClient.OnMessageReceivedListener { messageEvent -> + val message = Message( + Watch.createUUID(PLATFORM, messageEvent.sourceNodeId), + messageEvent.path, + messageEvent.data + ) + trySendBlocking(message) + } + + messageClient.addListener(listener) - init { - coroutineScope.launch { - incomingMessageFlow.subscriptionCount.collect { subscriberCount -> - if (subscriberCount > 0) { - messageClient.addListener(wearableMessageReceiver) - } else { - messageClient.removeListener(wearableMessageReceiver) - } - } + awaitClose { + messageClient.removeListener(listener) } } + override fun allWatches(): Flow> = flow { emit( nodeClient.connectedNodes.await().map { node ->