sample-apps-for-matter-android
sample-apps-for-matter-android copied to clipboard
[Enhancement] Provide CHIP APIs coroutine extension functions
The CHIP APIs are mostly Java callback based APIs but most developers will use Kotlin (and Kotlin coroutines).
In GHSAFM, we have 3 wrappers classes (ChipClient, ClustersHelper and SubscriptionHelper) which are often only adapting the CHIP Java callback based APIs into Kotlin coroutines. In order to avoid that every developer implement and maintain this code on their own, we could provide Kotlin extension functions inside an additional maven artifact.
For example, in ChipClient
we have
suspend fun getConnectedDevicePointer(nodeId: Long): Long {
return suspendCoroutine { continuation ->
chipDeviceController.getConnectedDevicePointer(
nodeId,
object : GetConnectedDeviceCallback {
override fun onDeviceConnected(devicePointer: Long) {
Timber.d("Got connected device pointer")
continuation.resume(devicePointer)
}
override fun onConnectionFailure(nodeId: Long, error: Exception) {
val errorMessage = "Unable to get connected device with nodeId $nodeId."
Timber.e(errorMessage, error)
continuation.resumeWithException(IllegalStateException(errorMessage))
}
})
}
}
(We also have awaitGetConnectedDevicePointer
which seams to be duplicate code)
So we could provide an extension function on ChipDeviceController
like
suspend fun ChipDeviceController.getConnectedDevicePointer(
nodeId: Long
): Long = suspendCoroutine {
getConnectedDevicePointer(
nodeId,
object : GetConnectedDeviceCallback {
override fun onDeviceConnected(devicePointer: Long) {
it.resume(devicePointer)
}
override fun onConnectionFailure(nodeId: Long, error: Exception?) {
it.resumeWithException(
IllegalStateException(
"Unable to get connected device with nodeId $nodeId.",
error
)
)
}
}
)
}
The same can be done for predefined clusters like for
public static class DescriptorCluster extends BaseChipCluster {
[...]
public void readDeviceTypeListAttribute(DeviceTypeListAttributeCallback callback) {
[...]
}
public void subscribeDeviceTypeListAttribute(DeviceTypeListAttributeCallback callback, int minInterval, int maxInterval) {
[...]
}
[...]
}
We could provide
suspend fun ChipClusters.DescriptorCluster.readDeviceTypeListAttribute(
): List<ChipStructs.DescriptorClusterDeviceTypeStruct> = suspendCoroutine {
readDeviceTypeListAttribute(
object : ChipClusters.DescriptorCluster.DeviceTypeListAttributeCallback {
override fun onSuccess(
values: List<ChipStructs.DescriptorClusterDeviceTypeStruct>
) {
it.resume(values)
}
override fun onError(ex: Exception) {
it.resumeWithException(ex)
}
}
)
}
fun ChipClusters.DescriptorCluster.subscribeDeviceTypeListAttribute(
minInterval: Int,
maxInterval: Int
): Flow<List<ChipStructs.DescriptorClusterDeviceTypeStruct>> = callbackFlow {
subscribeDeviceTypeListAttribute(
object : ChipClusters.DescriptorCluster.DeviceTypeListAttributeCallback {
override fun onSuccess(valueList: MutableList<ChipStructs.DescriptorClusterDeviceTypeStruct>) {
trySend(valueList)
}
override fun onError(ex: java.lang.Exception?) {
close(ex)
}
},
minInterval,
maxInterval
)
}
As both use the same callback type, we could even share the callback creation inside a private function but it's just an implementation detail.
This is an enhancement proposal, the actual CHIP artifacts should be published first. Then it should be implemented in the CHIP repository instead of the GHSAFM repository.
Thanks Paul-Marie. Makes sense. We should get resolution on #80 soon, and then we should tackle this as you propose.