diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..320e54ff --- /dev/null +++ b/.editorconfig @@ -0,0 +1,3 @@ +[*.{kt,kts}] +max_line_length=210 +ktlint_standard_property-naming=disabled diff --git a/.github/workflows/ktlint.yml b/.github/workflows/ktlint.yml new file mode 100644 index 00000000..a4d4b347 --- /dev/null +++ b/.github/workflows/ktlint.yml @@ -0,0 +1,17 @@ +name: ktlint +on: [pull_request] +jobs: + ktlint: + name: Check Code Quality + runs-on: ubuntu-latest + + steps: + - name: Clone repo + uses: actions/checkout@master + with: + fetch-depth: 1 + - name: ktlint + uses: ScaCap/action-ktlint@master + with: + github_token: ${{ secrets.github_token }} + reporter: github-pr-review # Change reporter diff --git a/example/android/app/src/main/kotlin/com/signify/hue/reactivebleexample/MainActivity.kt b/example/android/app/src/main/kotlin/com/signify/hue/reactivebleexample/MainActivity.kt index 6a6f0e2f..36dfe989 100644 --- a/example/android/app/src/main/kotlin/com/signify/hue/reactivebleexample/MainActivity.kt +++ b/example/android/app/src/main/kotlin/com/signify/hue/reactivebleexample/MainActivity.kt @@ -1,14 +1,14 @@ package com.signify.hue.reactivebleexample - import androidx.annotation.NonNull import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugins.GeneratedPluginRegistrant - -class MainActivity: FlutterActivity(){ - override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { - GeneratedPluginRegistrant.registerWith(flutterEngine) - } +class MainActivity : FlutterActivity() { + override fun configureFlutterEngine( + @NonNull flutterEngine: FlutterEngine, + ) { + GeneratedPluginRegistrant.registerWith(flutterEngine) + } } diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/PluginController.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/PluginController.kt index 1de34c09..22468cc2 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/PluginController.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/PluginController.kt @@ -22,7 +22,8 @@ import com.signify.hue.flutterreactiveble.ProtobufModel as pb @Suppress("TooManyFunctions") class PluginController { - private val pluginMethods = mapOf Unit>( + private val pluginMethods = + mapOf Unit>( "initialize" to this::initializeClient, "deinitialize" to this::deinitializeClient, "scanForDevices" to this::scanForDevices, @@ -39,7 +40,7 @@ class PluginController { "discoverServices" to this::discoverServices, "getDiscoveredServices" to this::discoverServices, "readRssi" to this::readRssi, - ) + ) private lateinit var bleClient: com.signify.hue.flutterreactiveble.ble.BleClient @@ -54,7 +55,10 @@ class PluginController { private val uuidConverter = UuidConverter() private val protoConverter = ProtobufMessageConverter() - internal fun initialize(messenger: BinaryMessenger, context: Context) { + internal fun initialize( + messenger: BinaryMessenger, + context: Context, + ) { bleClient = com.signify.hue.flutterreactiveble.ble.ReactiveBleClient(context) scanchannel = EventChannel(messenger, "flutter_reactive_ble_scan") @@ -78,58 +82,83 @@ class PluginController { deviceConnectionHandler.disconnectAll() } - internal fun execute(call: MethodCall, result: Result) { + internal fun execute( + call: MethodCall, + result: Result, + ) { pluginMethods[call.method]?.invoke(call, result) ?: result.notImplemented() } - private fun initializeClient(call: MethodCall, result: Result) { + private fun initializeClient( + call: MethodCall, + result: Result, + ) { bleClient.initializeClient() result.success(null) } - private fun deinitializeClient(call: MethodCall, result: Result) { + private fun deinitializeClient( + call: MethodCall, + result: Result, + ) { deinitialize() result.success(null) } - private fun scanForDevices(call: MethodCall, result: Result) { + private fun scanForDevices( + call: MethodCall, + result: Result, + ) { scanDevicesHandler.prepareScan(pb.ScanForDevicesRequest.parseFrom(call.arguments as ByteArray)) result.success(null) } - private fun connectToDevice(call: MethodCall, result: Result) { + private fun connectToDevice( + call: MethodCall, + result: Result, + ) { result.success(null) val connectDeviceMessage = pb.ConnectToDeviceRequest.parseFrom(call.arguments as ByteArray) deviceConnectionHandler.connectToDevice(connectDeviceMessage) } - private fun clearGattCache(call: MethodCall, result: Result) { + private fun clearGattCache( + call: MethodCall, + result: Result, + ) { val args = pb.ClearGattCacheRequest.parseFrom(call.arguments as ByteArray) bleClient.clearGattCache(args.deviceId) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - { - val info = pb.ClearGattCacheInfo.getDefaultInstance() - result.success(info.toByteArray()) - }, - { - val info = protoConverter.convertClearGattCacheError( - ClearGattCacheErrorType.UNKNOWN, - it.message - ) - result.success(info.toByteArray()) - } - ) - .discard() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + val info = pb.ClearGattCacheInfo.getDefaultInstance() + result.success(info.toByteArray()) + }, + { + val info = + protoConverter.convertClearGattCacheError( + ClearGattCacheErrorType.UNKNOWN, + it.message, + ) + result.success(info.toByteArray()) + }, + ) + .discard() } - private fun disconnectFromDevice(call: MethodCall, result: Result) { + private fun disconnectFromDevice( + call: MethodCall, + result: Result, + ) { result.success(null) val connectDeviceMessage = pb.DisconnectFromDeviceRequest.parseFrom(call.arguments as ByteArray) deviceConnectionHandler.disconnectDevice(connectDeviceMessage.deviceId) } - private fun readCharacteristic(call: MethodCall, result: Result) { + private fun readCharacteristic( + call: MethodCall, + result: Result, + ) { result.success(null) val readCharMessage = pb.ReadCharacteristicRequest.parseFrom(call.arguments as ByteArray) @@ -138,154 +167,225 @@ class PluginController { val characteristicInstance = readCharMessage.characteristic.characteristicInstanceId.toInt() bleClient.readCharacteristic( - deviceId, - characteristic, - characteristicInstance + deviceId, + characteristic, + characteristicInstance, ) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - { charResult -> - when (charResult) { - is com.signify.hue.flutterreactiveble.ble.CharOperationSuccessful -> { - val charInfo = protoConverter.convertCharacteristicInfo( - readCharMessage.characteristic, - charResult.value.toByteArray() - ) - charNotificationHandler.addSingleReadToStream(charInfo) - } - is com.signify.hue.flutterreactiveble.ble.CharOperationFailed -> { - protoConverter.convertCharacteristicError(readCharMessage.characteristic, - "Failed to connect") - charNotificationHandler.addSingleErrorToStream( - readCharMessage.characteristic, - charResult.errorMessage - ) - } - } - }, - { throwable -> - protoConverter.convertCharacteristicError( + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { charResult -> + when (charResult) { + is com.signify.hue.flutterreactiveble.ble.CharOperationSuccessful -> { + val charInfo = + protoConverter.convertCharacteristicInfo( readCharMessage.characteristic, - throwable.message) + charResult.value.toByteArray(), + ) + charNotificationHandler.addSingleReadToStream(charInfo) + } + is com.signify.hue.flutterreactiveble.ble.CharOperationFailed -> { + protoConverter.convertCharacteristicError( + readCharMessage.characteristic, + "Failed to connect", + ) charNotificationHandler.addSingleErrorToStream( - readCharMessage.characteristic, - throwable?.message ?: "Failure") + readCharMessage.characteristic, + charResult.errorMessage, + ) } - ) - .discard() + } + }, + { throwable -> + protoConverter.convertCharacteristicError( + readCharMessage.characteristic, + throwable.message, + ) + charNotificationHandler.addSingleErrorToStream( + readCharMessage.characteristic, + throwable?.message ?: "Failure", + ) + }, + ) + .discard() } - private fun writeCharacteristicWithResponse(call: MethodCall, result: Result) { - executeWriteAndPropagateResultToChannel(call, result, com.signify.hue.flutterreactiveble.ble.BleClient::writeCharacteristicWithResponse) + private fun writeCharacteristicWithResponse( + call: MethodCall, + result: Result, + ) { + executeWriteAndPropagateResultToChannel( + call, + result, + com.signify.hue.flutterreactiveble.ble.BleClient::writeCharacteristicWithResponse, + ) } - private fun writeCharacteristicWithoutResponse(call: MethodCall, result: Result) { - executeWriteAndPropagateResultToChannel(call, result, com.signify.hue.flutterreactiveble.ble.BleClient::writeCharacteristicWithoutResponse) + private fun writeCharacteristicWithoutResponse( + call: MethodCall, + result: Result, + ) { + executeWriteAndPropagateResultToChannel( + call, + result, + com.signify.hue.flutterreactiveble.ble.BleClient::writeCharacteristicWithoutResponse, + ) } private fun executeWriteAndPropagateResultToChannel( - call: MethodCall, - result: Result, - writeOperation: com.signify.hue.flutterreactiveble.ble.BleClient.( - deviceId: String, - characteristic: UUID, - characteristicInstanceId: Int, - value: ByteArray - ) -> Single + call: MethodCall, + result: Result, + writeOperation: com.signify.hue.flutterreactiveble.ble.BleClient.( + deviceId: String, + characteristic: UUID, + characteristicInstanceId: Int, + value: ByteArray, + ) -> Single, ) { val writeCharMessage = pb.WriteCharacteristicRequest.parseFrom(call.arguments as ByteArray) - bleClient.writeOperation(writeCharMessage.characteristic.deviceId, - uuidConverter.uuidFromByteArray(writeCharMessage.characteristic.characteristicUuid.data.toByteArray()), - writeCharMessage.characteristic.characteristicInstanceId.toInt(), - writeCharMessage.value.toByteArray()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ operationResult -> + bleClient.writeOperation( + writeCharMessage.characteristic.deviceId, + uuidConverter.uuidFromByteArray(writeCharMessage.characteristic.characteristicUuid.data.toByteArray()), + writeCharMessage.characteristic.characteristicInstanceId.toInt(), + writeCharMessage.value.toByteArray(), + ) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { operationResult -> when (operationResult) { is com.signify.hue.flutterreactiveble.ble.CharOperationSuccessful -> { - result.success(protoConverter.convertWriteCharacteristicInfo(writeCharMessage, - null).toByteArray()) + result.success( + protoConverter.convertWriteCharacteristicInfo( + writeCharMessage, + null, + ).toByteArray(), + ) } is com.signify.hue.flutterreactiveble.ble.CharOperationFailed -> { - result.success(protoConverter.convertWriteCharacteristicInfo(writeCharMessage, - operationResult.errorMessage).toByteArray()) + result.success( + protoConverter.convertWriteCharacteristicInfo( + writeCharMessage, + operationResult.errorMessage, + ).toByteArray(), + ) } } }, - { throwable -> - result.success(protoConverter.convertWriteCharacteristicInfo(writeCharMessage, - throwable.message).toByteArray()) - } - ) - .discard() + { throwable -> + result.success( + protoConverter.convertWriteCharacteristicInfo( + writeCharMessage, + throwable.message, + ).toByteArray(), + ) + }, + ) + .discard() } - private fun readNotifications(call: MethodCall, result: Result) { + private fun readNotifications( + call: MethodCall, + result: Result, + ) { val request = pb.NotifyCharacteristicRequest.parseFrom(call.arguments as ByteArray) charNotificationHandler.subscribeToNotifications(request) result.success(null) } - private fun stopNotifications(call: MethodCall, result: Result) { + private fun stopNotifications( + call: MethodCall, + result: Result, + ) { val request = pb.NotifyNoMoreCharacteristicRequest.parseFrom(call.arguments as ByteArray) charNotificationHandler.unsubscribeFromNotifications(request) result.success(null) } - private fun negotiateMtuSize(call: MethodCall, result: Result) { + private fun negotiateMtuSize( + call: MethodCall, + result: Result, + ) { val request = pb.NegotiateMtuRequest.parseFrom(call.arguments as ByteArray) bleClient.negotiateMtuSize(request.deviceId, request.mtuSize) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ mtuResult -> + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { mtuResult -> result.success(protoConverter.convertNegotiateMtuInfo(mtuResult).toByteArray()) - }, { throwable -> - result.success(protoConverter.convertNegotiateMtuInfo(com.signify.hue.flutterreactiveble.ble.MtuNegotiateFailed(request.deviceId, - throwable.message ?: "")).toByteArray()) - } - ) - .discard() + }, + { throwable -> + result.success( + protoConverter.convertNegotiateMtuInfo( + com.signify.hue.flutterreactiveble.ble.MtuNegotiateFailed( + request.deviceId, + throwable.message ?: "", + ), + ).toByteArray(), + ) + }, + ) + .discard() } - private fun requestConnectionPriority(call: MethodCall, result: Result) { + private fun requestConnectionPriority( + call: MethodCall, + result: Result, + ) { val request = pb.ChangeConnectionPriorityRequest.parseFrom(call.arguments as ByteArray) bleClient.requestConnectionPriority(request.deviceId, request.priority.toConnectionPriority()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ requestResult -> - result.success(protoConverter - .convertRequestConnectionPriorityInfo(requestResult).toByteArray()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { requestResult -> + result.success( + protoConverter + .convertRequestConnectionPriorityInfo(requestResult).toByteArray(), + ) + }, + { throwable -> + result.success( + protoConverter.convertRequestConnectionPriorityInfo( + RequestConnectionPriorityFailed( + request.deviceId, + throwable?.message + ?: "Unknown error", + ), + ).toByteArray(), + ) }, - { throwable -> - result.success(protoConverter.convertRequestConnectionPriorityInfo( - RequestConnectionPriorityFailed(request.deviceId, throwable?.message - ?: "Unknown error")).toByteArray()) - }) - .discard() + ) + .discard() } - private fun discoverServices(call: MethodCall, result: Result) { + private fun discoverServices( + call: MethodCall, + result: Result, + ) { val request = pb.DiscoverServicesRequest.parseFrom(call.arguments as ByteArray) bleClient.discoverServices(request.deviceId) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ discoverResult -> - result.success(protoConverter.convertDiscoverServicesInfo(request.deviceId, discoverResult).toByteArray()) - }, { - throwable -> result.error("service_discovery_failure", throwable.toString(), throwable.stackTrace.toList().toString()) - }) - .discard() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ discoverResult -> + result.success(protoConverter.convertDiscoverServicesInfo(request.deviceId, discoverResult).toByteArray()) + }, { + throwable -> + result.error("service_discovery_failure", throwable.toString(), throwable.stackTrace.toList().toString()) + }) + .discard() } - private fun readRssi(call: MethodCall, result: Result) { + private fun readRssi( + call: MethodCall, + result: Result, + ) { val args = pb.ReadRssiRequest.parseFrom(call.arguments as ByteArray) bleClient.readRssi(args.deviceId) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ rssi -> - val info = protoConverter.convertReadRssiResult(rssi) - result.success(info.toByteArray()) - }, { error -> - result.error("read_rssi_error", error.message, null) - }) - .discard() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ rssi -> + val info = protoConverter.convertReadRssiResult(rssi) + result.success(info.toByteArray()) + }, { error -> + result.error("read_rssi_error", error.message, null) + }) + .discard() } } diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ReactiveBlePlugin.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ReactiveBlePlugin.kt index 9c55357b..f4202c38 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ReactiveBlePlugin.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ReactiveBlePlugin.kt @@ -23,7 +23,7 @@ class ReactiveBlePlugin : FlutterPlugin, MethodChannel.MethodCallHandler { private fun initializePlugin( messenger: BinaryMessenger, context: Context, - plugin: ReactiveBlePlugin + plugin: ReactiveBlePlugin, ) { val channel = MethodChannel(messenger, "flutter_reactive_ble_method") channel.setMethodCallHandler(plugin) @@ -37,7 +37,10 @@ class ReactiveBlePlugin : FlutterPlugin, MethodChannel.MethodCallHandler { } } - override fun onMethodCall(call: MethodCall, result: Result) { + override fun onMethodCall( + call: MethodCall, + result: Result, + ) { pluginController.execute(call, result) } } diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BleClient.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BleClient.kt index 35b3a365..75870941 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BleClient.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BleClient.kt @@ -12,41 +12,66 @@ import java.util.UUID @Suppress("TooManyFunctions") interface BleClient { - val connectionUpdateSubject: BehaviorSubject fun initializeClient() - fun scanForDevices(services: List, scanMode: ScanMode, requireLocationServicesEnabled: Boolean): Observable - fun connectToDevice(deviceId: String, timeout: Duration) + + fun scanForDevices( + services: List, + scanMode: ScanMode, + requireLocationServicesEnabled: Boolean, + ): Observable + + fun connectToDevice( + deviceId: String, + timeout: Duration, + ) + fun disconnectDevice(deviceId: String) + fun disconnectAllDevices() + fun discoverServices(deviceId: String): Single + fun clearGattCache(deviceId: String): Completable + fun readCharacteristic( deviceId: String, characteristicId: UUID, - characteristicInstanceId: Int + characteristicInstanceId: Int, ): Single + fun setupNotification( deviceId: String, characteristicId: UUID, - characteristicInstanceId: Int + characteristicInstanceId: Int, ): Observable + fun writeCharacteristicWithResponse( deviceId: String, characteristicId: UUID, characteristicInstanceId: Int, - value: ByteArray + value: ByteArray, ): Single + fun writeCharacteristicWithoutResponse( deviceId: String, characteristicId: UUID, characteristicInstanceId: Int, - value: ByteArray + value: ByteArray, ): Single - fun negotiateMtuSize(deviceId: String, size: Int): Single + + fun negotiateMtuSize( + deviceId: String, + size: Int, + ): Single + fun observeBleStatus(): Observable - fun requestConnectionPriority(deviceId: String, priority: ConnectionPriority): - Single + + fun requestConnectionPriority( + deviceId: String, + priority: ConnectionPriority, + ): Single + fun readRssi(deviceId: String): Single } diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BleWrapper.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BleWrapper.kt index b1e9f004..d15f2eb4 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BleWrapper.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/BleWrapper.kt @@ -34,23 +34,33 @@ data class ScanInfo(val deviceId: String, val name: String, val rssi: Int, val c } sealed class ConnectionUpdate + data class ConnectionUpdateSuccess(val deviceId: String, val connectionState: Int) : ConnectionUpdate() + data class ConnectionUpdateError(val deviceId: String, val errorMessage: String) : ConnectionUpdate() sealed class EstablishConnectionResult + data class EstablishedConnection(val deviceId: String, val rxConnection: RxBleConnection) : EstablishConnectionResult() + data class EstablishConnectionFailure(val deviceId: String, val errorMessage: String) : EstablishConnectionResult() sealed class MtuNegotiateResult + data class MtuNegotiateSuccessful(val deviceId: String, val size: Int) : MtuNegotiateResult() + data class MtuNegotiateFailed(val deviceId: String, val errorMessage: String) : MtuNegotiateResult() sealed class CharOperationResult + data class CharOperationSuccessful(val deviceId: String, val value: List) : CharOperationResult() + data class CharOperationFailed(val deviceId: String, val errorMessage: String) : CharOperationResult() sealed class RequestConnectionPriorityResult + data class RequestConnectionPrioritySuccess(val deviceId: String) : RequestConnectionPriorityResult() + data class RequestConnectionPriorityFailed(val deviceId: String, val errorMessage: String) : RequestConnectionPriorityResult() enum class BleStatus(val code: Int) { @@ -59,17 +69,17 @@ enum class BleStatus(val code: Int) { UNAUTHORIZED(code = 2), POWERED_OFF(code = 3), LOCATION_SERVICES_DISABLED(code = 4), - READY(code = 5) + READY(code = 5), } enum class ConnectionPriority(val code: Int) { BALANCED(code = 0), HIGH_PERFORMACE(code = 1), - LOW_POWER(code = 2) + LOW_POWER(code = 2), } enum class Connectable(val code: Int) { UNKNOWN(code = 0), NOT_CONNECTABLE(code = 1), - CONNECTABLE(code = 2) + CONNECTABLE(code = 2), } diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/ConnectionQueue.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/ConnectionQueue.kt index 80287378..6c0343fe 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/ConnectionQueue.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/ConnectionQueue.kt @@ -4,7 +4,6 @@ import androidx.annotation.VisibleForTesting import io.reactivex.subjects.BehaviorSubject internal class ConnectionQueue { - private val queueSubject = BehaviorSubject.createDefault(listOf()) fun observeQueue() = queueSubject diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/DeviceConnector.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/DeviceConnector.kt index 2037a9f5..8a53cf7b 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/DeviceConnector.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/DeviceConnector.kt @@ -16,12 +16,11 @@ import io.reactivex.subjects.BehaviorSubject import java.util.concurrent.TimeUnit internal class DeviceConnector( - private val device: RxBleDevice, - private val connectionTimeout: Duration, - private val updateListeners: (update: ConnectionUpdate) -> Unit, - private val connectionQueue: ConnectionQueue + private val device: RxBleDevice, + private val connectionTimeout: Duration, + private val updateListeners: (update: ConnectionUpdate) -> Unit, + private val connectionQueue: ConnectionQueue, ) { - companion object { private const val minTimeMsBeforeDisconnectingIsAllowed = 200L private const val delayMsAfterClearingCache = 300L @@ -34,10 +33,11 @@ internal class DeviceConnector( @VisibleForTesting internal var connectionDisposable: Disposable? = null - private val lazyConnection = lazy { - connectionDisposable = establishConnection(device) - connectDeviceSubject - } + private val lazyConnection = + lazy { + connectionDisposable = establishConnection(device) + connectDeviceSubject + } private val currentConnection: EstablishConnectionResult? get() = if (lazyConnection.isInitialized()) connection.value else null @@ -46,15 +46,18 @@ internal class DeviceConnector( private val connectionStatusUpdates by lazy { device.observeConnectionStateChanges() - .startWith(device.connectionState) - .map { ConnectionUpdateSuccess(device.macAddress, it.toConnectionState().code) } - .onErrorReturn { - ConnectionUpdateError(device.macAddress, it.message - ?: "Unknown error") - } - .subscribe { - updateListeners.invoke(it) - } + .startWith(device.connectionState) + .map { ConnectionUpdateSuccess(device.macAddress, it.toConnectionState().code) } + .onErrorReturn { + ConnectionUpdateError( + device.macAddress, + it.message + ?: "Unknown error", + ) + } + .subscribe { + updateListeners.invoke(it) + } } internal fun disconnectDevice(deviceId: String) { @@ -66,10 +69,10 @@ internal class DeviceConnector( */ if (diff < DeviceConnector.Companion.minTimeMsBeforeDisconnectingIsAllowed) { Single.timer(DeviceConnector.Companion.minTimeMsBeforeDisconnectingIsAllowed - diff, TimeUnit.MILLISECONDS) - .doFinally { - sendDisconnectedUpdate(deviceId) - disposeSubscriptions() - }.subscribe() + .doFinally { + sendDisconnectedUpdate(deviceId) + disposeSubscriptions() + }.subscribe() } else { sendDisconnectedUpdate(deviceId) disposeSubscriptions() @@ -94,59 +97,76 @@ internal class DeviceConnector( updateListeners(ConnectionUpdateSuccess(deviceId, ConnectionState.CONNECTING.code)) return waitUntilFirstOfQueue(deviceId) - .switchMap { queue -> - if (!queue.contains(deviceId)) { - Observable.just(EstablishConnectionFailure(deviceId, - "Device is not in queue")) - } else { - connectDevice(rxBleDevice, shouldNotTimeout) - .map { EstablishedConnection(rxBleDevice.macAddress, it) } - } - } - .onErrorReturn { error -> - EstablishConnectionFailure(rxBleDevice.macAddress, - error.message ?: "Unknown error") - } - .doOnNext { - // Trigger side effect by calling the lazy initialization of this property so - // listening to changes starts. - connectionStatusUpdates - timestampEstablishConnection = System.currentTimeMillis() - connectionQueue.removeFromQueue(deviceId) - if (it is EstablishConnectionFailure) { - updateListeners.invoke(ConnectionUpdateError(deviceId, it.errorMessage)) - } + .switchMap { queue -> + if (!queue.contains(deviceId)) { + Observable.just( + EstablishConnectionFailure( + deviceId, + "Device is not in queue", + ), + ) + } else { + connectDevice(rxBleDevice, shouldNotTimeout) + .map { EstablishedConnection(rxBleDevice.macAddress, it) } } - .doOnError { - connectionQueue.removeFromQueue(deviceId) - updateListeners.invoke(ConnectionUpdateError(deviceId, it.message - ?: "Unknown error")) + } + .onErrorReturn { error -> + EstablishConnectionFailure( + rxBleDevice.macAddress, + error.message ?: "Unknown error", + ) + } + .doOnNext { + // Trigger side effect by calling the lazy initialization of this property so + // listening to changes starts. + connectionStatusUpdates + timestampEstablishConnection = System.currentTimeMillis() + connectionQueue.removeFromQueue(deviceId) + if (it is EstablishConnectionFailure) { + updateListeners.invoke(ConnectionUpdateError(deviceId, it.errorMessage)) } - .subscribe({ connectDeviceSubject.onNext(it) }, - { throwable -> connectDeviceSubject.onError(throwable) }) + } + .doOnError { + connectionQueue.removeFromQueue(deviceId) + updateListeners.invoke( + ConnectionUpdateError( + deviceId, + it.message + ?: "Unknown error", + ), + ) + } + .subscribe( + { connectDeviceSubject.onNext(it) }, + { throwable -> connectDeviceSubject.onError(throwable) }, + ) } - private fun connectDevice(rxBleDevice: RxBleDevice, shouldNotTimeout: Boolean): Observable = - rxBleDevice.establishConnection(shouldNotTimeout) - .compose { - if (shouldNotTimeout) { - it - } else { - it.timeout( - Observable.timer(connectionTimeout.value, connectionTimeout.unit), - Function> { - Observable.never() - } - ) - } - } + private fun connectDevice( + rxBleDevice: RxBleDevice, + shouldNotTimeout: Boolean, + ): Observable = + rxBleDevice.establishConnection(shouldNotTimeout) + .compose { + if (shouldNotTimeout) { + it + } else { + it.timeout( + Observable.timer(connectionTimeout.value, connectionTimeout.unit), + Function> { + Observable.never() + }, + ) + } + } - internal fun clearGattCache(): Completable = currentConnection?.let { connection -> - when (connection) { - is EstablishedConnection -> clearGattCache(connection.rxConnection) - is EstablishConnectionFailure -> Completable.error(Throwable(connection.errorMessage)) - } - } ?: Completable.error(IllegalStateException("Connection is not established")) + internal fun clearGattCache(): Completable = + currentConnection?.let { connection -> + when (connection) { + is EstablishedConnection -> clearGattCache(connection.rxConnection) + is EstablishConnectionFailure -> Completable.error(Throwable(connection.errorMessage)) + } + } ?: Completable.error(IllegalStateException("Connection is not established")) /** * Clear GATT attribute cache using an undocumented method `BluetoothGatt.refresh()`. @@ -160,38 +180,40 @@ internal class DeviceConnector( * Known to work up to Android Q beta 2. */ private fun clearGattCache(connection: RxBleConnection): Completable { - val operation = RxBleCustomOperation { bluetoothGatt, _, _ -> - try { - val refreshMethod = bluetoothGatt.javaClass.getMethod("refresh") - val success = refreshMethod.invoke(bluetoothGatt) as Boolean - if (success) { - Observable.empty() + val operation = + RxBleCustomOperation { bluetoothGatt, _, _ -> + try { + val refreshMethod = bluetoothGatt.javaClass.getMethod("refresh") + val success = refreshMethod.invoke(bluetoothGatt) as Boolean + if (success) { + Observable.empty() .delay(DeviceConnector.Companion.delayMsAfterClearingCache, TimeUnit.MILLISECONDS) - } else { - val reason = "BluetoothGatt.refresh() returned false" - Observable.error(RuntimeException(reason)) + } else { + val reason = "BluetoothGatt.refresh() returned false" + Observable.error(RuntimeException(reason)) + } + } catch (e: ReflectiveOperationException) { + Observable.error(e) } - } catch (e: ReflectiveOperationException) { - Observable.error(e) } - } return connection.queue(operation).ignoreElements() } private fun waitUntilFirstOfQueue(deviceId: String) = - connectionQueue.observeQueue() - .filter { queue -> - queue.firstOrNull() == deviceId || !queue.contains(deviceId) - } - .takeUntil { it.isEmpty() || it.first() == deviceId } + connectionQueue.observeQueue() + .filter { queue -> + queue.firstOrNull() == deviceId || !queue.contains(deviceId) + } + .takeUntil { it.isEmpty() || it.first() == deviceId } /** * Reads the current RSSI value of the device */ - internal fun readRssi(): Single = currentConnection?.let { connection -> - when (connection) { - is EstablishedConnection -> connection.rxConnection.readRssi() - is EstablishConnectionFailure -> Single.error(Throwable(connection.errorMessage)) - } - } ?: Single.error(IllegalStateException("Connection is not established")) + internal fun readRssi(): Single = + currentConnection?.let { connection -> + when (connection) { + is EstablishedConnection -> connection.rxConnection.readRssi() + is EstablishConnectionFailure -> Single.error(Throwable(connection.errorMessage)) + } + } ?: Single.error(IllegalStateException("Connection is not established")) } diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/ReactiveBleClient.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/ReactiveBleClient.kt index cfd5093c..9fa076c1 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/ReactiveBleClient.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/ReactiveBleClient.kt @@ -60,18 +60,19 @@ open class ReactiveBleClient(private val context: Context) : BleClient { /*yes spread operator is not performant but after kotlin v1.60 it is less bad and it is also the recommended way to call varargs in java https://kotlinlang.org/docs/reference/java-interop.html#java-varargs - */ + */ @Suppress("SpreadOperator") override fun scanForDevices( services: List, scanMode: ScanMode, - requireLocationServicesEnabled: Boolean + requireLocationServicesEnabled: Boolean, ): Observable { - val filters = services.map { service -> - ScanFilter.Builder() - .setServiceUuid(service) - .build() - }.toTypedArray() + val filters = + services.map { service -> + ScanFilter.Builder() + .setServiceUuid(service) + .build() + }.toTypedArray() return rxBleClient.scanBleDevices( ScanSettings.Builder() @@ -80,11 +81,13 @@ open class ReactiveBleClient(private val context: Context) : BleClient { .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) .setShouldCheckLocationServicesState(requireLocationServicesEnabled) .build(), - *filters + *filters, ) .map { result -> - ScanInfo(result.bleDevice.macAddress, result.scanRecord.deviceName - ?: result.bleDevice.name ?: "", + ScanInfo( + result.bleDevice.macAddress, + result.scanRecord.deviceName + ?: result.bleDevice.name ?: "", result.rssi, when (result.isConnectable) { null -> Connectable.UNKNOWN @@ -94,33 +97,39 @@ open class ReactiveBleClient(private val context: Context) : BleClient { }, result.scanRecord.serviceData?.mapKeys { it.key.uuid } ?: emptyMap(), result.scanRecord.serviceUuids?.map { it.uuid } ?: emptyList(), - extractManufacturerData(result.scanRecord.manufacturerSpecificData)) + extractManufacturerData(result.scanRecord.manufacturerSpecificData), + ) } } - override fun connectToDevice(deviceId: String, timeout: Duration) { - allConnections.add(getConnection(deviceId, timeout) - .subscribe({ result -> - when (result) { - is EstablishedConnection -> { - } - is EstablishConnectionFailure -> { - connectionUpdateBehaviorSubject.onNext( - ConnectionUpdateError( - deviceId, - result.errorMessage + override fun connectToDevice( + deviceId: String, + timeout: Duration, + ) { + allConnections.add( + getConnection(deviceId, timeout) + .subscribe({ result -> + when (result) { + is EstablishedConnection -> { + } + is EstablishConnectionFailure -> { + connectionUpdateBehaviorSubject.onNext( + ConnectionUpdateError( + deviceId, + result.errorMessage, + ), ) - ) + } } - } - }, { error -> - connectionUpdateBehaviorSubject.onNext( - ConnectionUpdateError( - deviceId, error?.message - ?: "unknown error" + }, { error -> + connectionUpdateBehaviorSubject.onNext( + ConnectionUpdateError( + deviceId, + error?.message + ?: "unknown error", + ), ) - ) - }) + }), ) } @@ -143,7 +152,11 @@ open class ReactiveBleClient(private val context: Context) : BleClient { when (connectionResult) { is EstablishedConnection -> if (rxBleClient.getBleDevice(connectionResult.deviceId).bluetoothDevice.bondState == BOND_BONDING) { - Single.error(Exception("Bonding is in progress wait for bonding to be finished before executing more operations on the device")) + Single.error( + Exception( + "Bonding is in progress wait for bonding to be finished before executing more operations on the device", + ), + ) } else { connectionResult.rxConnection.discoverServices() } @@ -155,13 +168,14 @@ open class ReactiveBleClient(private val context: Context) : BleClient { override fun readCharacteristic( deviceId: String, characteristicId: UUID, - characteristicInstanceId: Int + characteristicInstanceId: Int, ): Single = getConnection(deviceId).flatMapSingle { connectionResult -> when (connectionResult) { is EstablishedConnection -> { connectionResult.rxConnection.resolveCharacteristic( - characteristicId, characteristicInstanceId + characteristicId, + characteristicInstanceId, ).flatMap { c: BluetoothGattCharacteristic -> connectionResult.rxConnection.readCharacteristic(c) /* @@ -169,19 +183,19 @@ open class ReactiveBleClient(private val context: Context) : BleClient { the error GAT_AUTH_FAIL(137) when reading char that will establish the bonding with the peripheral. By retrying the operation once we deviate between this flaky one time error and real auth failed cases - */ - .retry(1) { Build.VERSION.SDK_INT < Build.VERSION_CODES.O } - .map { value -> - CharOperationSuccessful(deviceId, value.asList()) - } + */ + .retry(1) { Build.VERSION.SDK_INT < Build.VERSION_CODES.O } + .map { value -> + CharOperationSuccessful(deviceId, value.asList()) + } } } is EstablishConnectionFailure -> Single.just( CharOperationFailed( deviceId, - "failed to connect ${connectionResult.errorMessage}" - ) + "failed to connect ${connectionResult.errorMessage}", + ), ) } }.first(CharOperationFailed(deviceId, "read char failed")) @@ -190,28 +204,28 @@ open class ReactiveBleClient(private val context: Context) : BleClient { deviceId: String, characteristicId: UUID, characteristicInstanceId: Int, - value: ByteArray + value: ByteArray, ): Single = executeWriteOperation( deviceId, characteristicId, characteristicInstanceId, value, - RxBleConnection::writeCharWithResponse + RxBleConnection::writeCharWithResponse, ) override fun writeCharacteristicWithoutResponse( deviceId: String, characteristicId: UUID, characteristicInstanceId: Int, - value: ByteArray + value: ByteArray, ): Single = executeWriteOperation( deviceId, characteristicId, characteristicInstanceId, value, - RxBleConnection::writeCharWithoutResponse + RxBleConnection::writeCharWithoutResponse, ) override fun setupNotification( @@ -224,7 +238,7 @@ open class ReactiveBleClient(private val context: Context) : BleClient { setupNotificationOrIndication( deviceConnection, characteristicId, - characteristicInstanceId + characteristicInstanceId, ) } // now we have setup the subscription and we want the actual value @@ -233,33 +247,40 @@ open class ReactiveBleClient(private val context: Context) : BleClient { } } - override fun negotiateMtuSize(deviceId: String, size: Int): Single = + override fun negotiateMtuSize( + deviceId: String, + size: Int, + ): Single = getConnection(deviceId).flatMapSingle { connectionResult -> when (connectionResult) { - is EstablishedConnection -> connectionResult.rxConnection.requestMtu(size) - .map { value -> MtuNegotiateSuccessful(deviceId, value) } + is EstablishedConnection -> + connectionResult.rxConnection.requestMtu(size) + .map { value -> MtuNegotiateSuccessful(deviceId, value) } is EstablishConnectionFailure -> Single.just( MtuNegotiateFailed( deviceId, - "failed to connect ${connectionResult.errorMessage}" - ) + "failed to connect ${connectionResult.errorMessage}", + ), ) } }.first(MtuNegotiateFailed(deviceId, "negotiate mtu timed out")) - override fun observeBleStatus(): Observable = rxBleClient.observeStateChanges() - .startWith(rxBleClient.state) - .map { it.toBleState() } + override fun observeBleStatus(): Observable = + rxBleClient.observeStateChanges() + .startWith(rxBleClient.state) + .map { it.toBleState() } @VisibleForTesting - internal open fun createDeviceConnector(device: RxBleDevice, timeout: Duration) = - DeviceConnector(device, timeout, connectionUpdateBehaviorSubject::onNext, connectionQueue) + internal open fun createDeviceConnector( + device: RxBleDevice, + timeout: Duration, + ) = DeviceConnector(device, timeout, connectionUpdateBehaviorSubject::onNext, connectionQueue) private fun getConnection( deviceId: String, - timeout: Duration = Duration(0, TimeUnit.MILLISECONDS) + timeout: Duration = Duration(0, TimeUnit.MILLISECONDS), ): Observable { val device = rxBleClient.getBleDevice(deviceId) val connector = @@ -273,24 +294,24 @@ open class ReactiveBleClient(private val context: Context) : BleClient { characteristicId: UUID, characteristicInstanceId: Int, value: ByteArray, - bleOperation: RxBleConnection.(characteristic: BluetoothGattCharacteristic, value: ByteArray) -> Single + bleOperation: RxBleConnection.(characteristic: BluetoothGattCharacteristic, value: ByteArray) -> Single, ): Single { return getConnection(deviceId) .flatMapSingle { connectionResult -> when (connectionResult) { is EstablishedConnection -> { connectionResult.rxConnection.resolveCharacteristic(characteristicId, characteristicInstanceId) - .flatMap { characteristic -> - connectionResult.rxConnection.bleOperation(characteristic, value) - .map { value -> CharOperationSuccessful(deviceId, value.asList()) } - } + .flatMap { characteristic -> + connectionResult.rxConnection.bleOperation(characteristic, value) + .map { value -> CharOperationSuccessful(deviceId, value.asList()) } + } } is EstablishConnectionFailure -> { Single.just( CharOperationFailed( deviceId, - "failed to connect ${connectionResult.errorMessage}" - ) + "failed to connect ${connectionResult.errorMessage}", + ), ) } } @@ -300,26 +321,30 @@ open class ReactiveBleClient(private val context: Context) : BleClient { private fun setupNotificationOrIndication( deviceConnection: EstablishConnectionResult, characteristicId: UUID, - characteristicInstanceId: Int + characteristicInstanceId: Int, ): Observable> = when (deviceConnection) { is EstablishedConnection -> { if (rxBleClient.getBleDevice(deviceConnection.deviceId).bluetoothDevice.bondState == BOND_BONDING) { - Observable.error(Exception("Bonding is in progress wait for bonding to be finished before executing more operations on the device")) + Observable.error( + Exception("Bonding is in progress wait for bonding to be finished before executing more operations on the device"), + ) } else { deviceConnection.rxConnection.resolveCharacteristic( - characteristicId, characteristicInstanceId + characteristicId, + characteristicInstanceId, ).flatMapObservable { characteristic -> - val mode = if (characteristic.descriptors.isEmpty()) { - NotificationSetupMode.COMPAT - } else { - NotificationSetupMode.DEFAULT - } + val mode = + if (characteristic.descriptors.isEmpty()) { + NotificationSetupMode.COMPAT + } else { + NotificationSetupMode.DEFAULT + } if ((characteristic.properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) { deviceConnection.rxConnection.setupNotification( characteristic, - mode + mode, ) } else { deviceConnection.rxConnection.setupIndication(characteristic, mode) @@ -334,7 +359,7 @@ open class ReactiveBleClient(private val context: Context) : BleClient { override fun requestConnectionPriority( deviceId: String, - priority: ConnectionPriority + priority: ConnectionPriority, ): Single = getConnection(deviceId).switchMapSingle { connectionResult -> when (connectionResult) { @@ -342,39 +367,41 @@ open class ReactiveBleClient(private val context: Context) : BleClient { connectionResult.rxConnection.requestConnectionPriority( priority.code, 2, - TimeUnit.SECONDS + TimeUnit.SECONDS, ) .toSingle { RequestConnectionPrioritySuccess(deviceId) } - is EstablishConnectionFailure -> Single.fromCallable { - RequestConnectionPriorityFailed(deviceId, connectionResult.errorMessage) - } + is EstablishConnectionFailure -> + Single.fromCallable { + RequestConnectionPriorityFailed(deviceId, connectionResult.errorMessage) + } } }.first(RequestConnectionPriorityFailed(deviceId, "Unknown failure")) override fun readRssi(deviceId: String): Single = - getConnection(deviceId).flatMapSingle { connectionResult -> - when (connectionResult) { - is EstablishedConnection -> { - connectionResult.rxConnection.readRssi() - } - is EstablishConnectionFailure -> - Single.error( - java.lang.IllegalStateException( - "Reading RSSI failed. Device is not connected" - ) - ) + getConnection(deviceId).flatMapSingle { connectionResult -> + when (connectionResult) { + is EstablishedConnection -> { + connectionResult.rxConnection.readRssi() } - }.firstOrError() + is EstablishConnectionFailure -> + Single.error( + java.lang.IllegalStateException( + "Reading RSSI failed. Device is not connected", + ), + ) + } + }.firstOrError() // enable this for extra debug output on the android stack - private fun enableDebugLogging() = RxBleClient - .updateLogOptions( - LogOptions.Builder().setLogLevel(LogConstants.VERBOSE) - .setMacAddressLogSetting(LogConstants.MAC_ADDRESS_FULL) - .setUuidsLogSetting(LogConstants.UUIDS_FULL) - .setShouldLogAttributeValues(true) - .build() - ) + private fun enableDebugLogging() = + RxBleClient + .updateLogOptions( + LogOptions.Builder().setLogLevel(LogConstants.VERBOSE) + .setMacAddressLogSetting(LogConstants.MAC_ADDRESS_FULL) + .setUuidsLogSetting(LogConstants.UUIDS_FULL) + .setShouldLogAttributeValues(true) + .build(), + ) } diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/extensions/RxBleConnectionExtension.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/extensions/RxBleConnectionExtension.kt index bf4c3279..ac094f66 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/extensions/RxBleConnectionExtension.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/ble/extensions/RxBleConnectionExtension.kt @@ -5,21 +5,32 @@ import com.polidea.rxandroidble2.RxBleConnection import io.reactivex.Single import java.util.UUID -fun RxBleConnection.resolveCharacteristic(uuid: UUID, instanceId: Int): Single = - discoverServices().flatMap { services -> - Single.just(services.bluetoothGattServices.flatMap { service -> +fun RxBleConnection.resolveCharacteristic( + uuid: UUID, + instanceId: Int, +): Single = + discoverServices().flatMap { services -> + Single.just( + services.bluetoothGattServices.flatMap { service -> service.characteristics.filter { it.uuid == uuid && it.instanceId == instanceId } - }.single()) - } + }.single(), + ) + } -fun RxBleConnection.writeCharWithResponse(characteristic: BluetoothGattCharacteristic, value: ByteArray): Single { +fun RxBleConnection.writeCharWithResponse( + characteristic: BluetoothGattCharacteristic, + value: ByteArray, +): Single { characteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT return writeCharacteristic(characteristic, value) } -fun RxBleConnection.writeCharWithoutResponse(characteristic: BluetoothGattCharacteristic, value: ByteArray): Single { +fun RxBleConnection.writeCharWithoutResponse( + characteristic: BluetoothGattCharacteristic, + value: ByteArray, +): Single { characteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE return writeCharacteristic(characteristic, value) } diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/BleStatusHandler.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/BleStatusHandler.kt index 2b942097..035071eb 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/BleStatusHandler.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/BleStatusHandler.kt @@ -10,14 +10,16 @@ import java.util.concurrent.TimeUnit import com.signify.hue.flutterreactiveble.ProtobufModel as pb class BleStatusHandler(private val bleClient: BleClient) : EventChannel.StreamHandler { - companion object { private const val delayListenBleStatus = 500L } private val subscriptionDisposable = SerialDisposable() - override fun onListen(arg: Any?, eventSink: EventChannel.EventSink?) { + override fun onListen( + arg: Any?, + eventSink: EventChannel.EventSink?, + ) { subscriptionDisposable.set(eventSink?.let(::listenToBleStatus)) } @@ -26,15 +28,16 @@ class BleStatusHandler(private val bleClient: BleClient) : EventChannel.StreamHa } private fun listenToBleStatus(eventSink: EventChannel.EventSink): Disposable = - Observable.timer(delayListenBleStatus, TimeUnit.MILLISECONDS) - .switchMap { bleClient.observeBleStatus() } - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ bleStatus -> - val message = pb.BleStatusInfo.newBuilder() - .setStatus(bleStatus.code) - .build() - eventSink.success(message.toByteArray()) - }, { throwable -> - eventSink.error("ObserveBleStatusFailure", throwable.message, null) - }) + Observable.timer(delayListenBleStatus, TimeUnit.MILLISECONDS) + .switchMap { bleClient.observeBleStatus() } + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ bleStatus -> + val message = + pb.BleStatusInfo.newBuilder() + .setStatus(bleStatus.code) + .build() + eventSink.success(message.toByteArray()) + }, { throwable -> + eventSink.error("ObserveBleStatusFailure", throwable.message, null) + }) } diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/CharNotificationHandler.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/CharNotificationHandler.kt index 7bccb222..a699eb58 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/CharNotificationHandler.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/CharNotificationHandler.kt @@ -1,12 +1,12 @@ package com.signify.hue.flutterreactiveble.channelhandlers import com.polidea.rxandroidble2.exceptions.BleDisconnectedException -import com.signify.hue.flutterreactiveble.ProtobufModel as pb import com.signify.hue.flutterreactiveble.converters.ProtobufMessageConverter import com.signify.hue.flutterreactiveble.converters.UuidConverter import io.flutter.plugin.common.EventChannel import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable +import com.signify.hue.flutterreactiveble.ProtobufModel as pb class CharNotificationHandler(private val bleClient: com.signify.hue.flutterreactiveble.ble.BleClient) : EventChannel.StreamHandler { @@ -19,7 +19,10 @@ class CharNotificationHandler(private val bleClient: com.signify.hue.flutterreac private val subscriptionMap = mutableMapOf() } - override fun onListen(objectSink: Any?, eventSink: EventChannel.EventSink?) { + override fun onListen( + objectSink: Any?, + eventSink: EventChannel.EventSink?, + ) { eventSink?.let { charNotificationSink = eventSink } @@ -30,26 +33,28 @@ class CharNotificationHandler(private val bleClient: com.signify.hue.flutterreac } fun subscribeToNotifications(request: pb.NotifyCharacteristicRequest) { - val charUuid = uuidConverter - .uuidFromByteArray(request.characteristic.characteristicUuid.data.toByteArray()) - val subscription = bleClient.setupNotification( + val charUuid = + uuidConverter + .uuidFromByteArray(request.characteristic.characteristicUuid.data.toByteArray()) + val subscription = + bleClient.setupNotification( request.characteristic.deviceId, charUuid, - request.characteristic.characteristicInstanceId.toInt() - ) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ value -> - handleNotificationValue(request.characteristic, value) - }, { - when (it) { - is BleDisconnectedException -> { - subscriptionMap.remove(request.characteristic)?.dispose() + request.characteristic.characteristicInstanceId.toInt(), + ) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ value -> + handleNotificationValue(request.characteristic, value) + }, { + when (it) { + is BleDisconnectedException -> { + subscriptionMap.remove(request.characteristic)?.dispose() + } + else -> { + handleNotificationError(request.characteristic, it) + } } - else -> { - handleNotificationError(request.characteristic, it) - } - } - }) + }) subscriptionMap[request.characteristic] = subscription } @@ -61,7 +66,10 @@ class CharNotificationHandler(private val bleClient: com.signify.hue.flutterreac handleNotificationValue(charInfo.characteristic, charInfo.value.toByteArray()) } - fun addSingleErrorToStream(subscriptionRequest: pb.CharacteristicAddress, error: String) { + fun addSingleErrorToStream( + subscriptionRequest: pb.CharacteristicAddress, + error: String, + ) { val convertedMsg = protobufConverter.convertCharacteristicError(subscriptionRequest, error) charNotificationSink?.success(convertedMsg.toByteArray()) } @@ -73,7 +81,7 @@ class CharNotificationHandler(private val bleClient: com.signify.hue.flutterreac private fun handleNotificationValue( subscriptionRequest: pb.CharacteristicAddress, - value: ByteArray + value: ByteArray, ) { val convertedMsg = protobufConverter.convertCharacteristicInfo(subscriptionRequest, value) charNotificationSink?.success(convertedMsg.toByteArray()) @@ -81,7 +89,7 @@ class CharNotificationHandler(private val bleClient: com.signify.hue.flutterreac private fun handleNotificationError( subscriptionRequest: pb.CharacteristicAddress, - error: Throwable + error: Throwable, ) { val convertedMsg = protobufConverter.convertCharacteristicError(subscriptionRequest, error.message ?: "") diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/DeviceConnectionHandler.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/DeviceConnectionHandler.kt index 54ab690c..0ad6a25b 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/DeviceConnectionHandler.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/DeviceConnectionHandler.kt @@ -1,12 +1,12 @@ package com.signify.hue.flutterreactiveble.channelhandlers -import com.signify.hue.flutterreactiveble.ProtobufModel as pb import com.signify.hue.flutterreactiveble.converters.ProtobufMessageConverter import com.signify.hue.flutterreactiveble.utils.Duration import io.flutter.plugin.common.EventChannel import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable import java.util.concurrent.TimeUnit +import com.signify.hue.flutterreactiveble.ProtobufModel as pb class DeviceConnectionHandler(private val bleClient: com.signify.hue.flutterreactiveble.ble.BleClient) : EventChannel.StreamHandler { private var connectDeviceSink: EventChannel.EventSink? = null @@ -14,7 +14,10 @@ class DeviceConnectionHandler(private val bleClient: com.signify.hue.flutterreac private lateinit var connectionUpdatesDisposable: Disposable - override fun onListen(objectSink: Any?, eventSink: EventChannel.EventSink?) { + override fun onListen( + objectSink: Any?, + eventSink: EventChannel.EventSink?, + ) { eventSink?.let { connectDeviceSink = eventSink connectionUpdatesDisposable = listenToConnectionChanges() @@ -28,8 +31,8 @@ class DeviceConnectionHandler(private val bleClient: com.signify.hue.flutterreac fun connectToDevice(connectToDeviceMessage: pb.ConnectToDeviceRequest) { bleClient.connectToDevice( - connectToDeviceMessage.deviceId, - Duration(connectToDeviceMessage.timeoutInMs.toLong(), TimeUnit.MILLISECONDS) + connectToDeviceMessage.deviceId, + Duration(connectToDeviceMessage.timeoutInMs.toLong(), TimeUnit.MILLISECONDS), ) } @@ -42,7 +45,8 @@ class DeviceConnectionHandler(private val bleClient: com.signify.hue.flutterreac bleClient.disconnectAllDevices() } - private fun listenToConnectionChanges() = bleClient.connectionUpdateSubject + private fun listenToConnectionChanges() = + bleClient.connectionUpdateSubject .observeOn(AndroidSchedulers.mainThread()) .subscribe { update -> when (update) { @@ -51,7 +55,7 @@ class DeviceConnectionHandler(private val bleClient: com.signify.hue.flutterreac } is com.signify.hue.flutterreactiveble.ble.ConnectionUpdateError -> { handleDeviceConnectionUpdateResult( - converter.convertConnectionErrorToDeviceInfo(update.deviceId, update.errorMessage) + converter.convertConnectionErrorToDeviceInfo(update.deviceId, update.errorMessage), ) } } diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/ScanDevicesHandler.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/ScanDevicesHandler.kt index cc820422..270ab0e8 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/ScanDevicesHandler.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/ScanDevicesHandler.kt @@ -1,7 +1,6 @@ package com.signify.hue.flutterreactiveble.channelhandlers import android.os.ParcelUuid -import com.signify.hue.flutterreactiveble.ProtobufModel as pb import com.signify.hue.flutterreactiveble.converters.ProtobufMessageConverter import com.signify.hue.flutterreactiveble.converters.UuidConverter import com.signify.hue.flutterreactiveble.model.ScanMode @@ -9,9 +8,9 @@ import com.signify.hue.flutterreactiveble.model.createScanMode import io.flutter.plugin.common.EventChannel import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable +import com.signify.hue.flutterreactiveble.ProtobufModel as pb class ScanDevicesHandler(private val bleClient: com.signify.hue.flutterreactiveble.ble.BleClient) : EventChannel.StreamHandler { - private var scanDevicesSink: EventChannel.EventSink? = null private lateinit var scanForDevicesDisposable: Disposable private val converter = ProtobufMessageConverter() @@ -20,7 +19,10 @@ class ScanDevicesHandler(private val bleClient: com.signify.hue.flutterreactiveb private var scanParameters: ScanParameters? = null } - override fun onListen(objectSink: Any?, eventSink: EventChannel.EventSink?) { + override fun onListen( + objectSink: Any?, + eventSink: EventChannel.EventSink?, + ) { eventSink?.let { scanDevicesSink = eventSink startDeviceScan() @@ -34,32 +36,36 @@ class ScanDevicesHandler(private val bleClient: com.signify.hue.flutterreactiveb private fun startDeviceScan() { scanParameters?.let { params -> - scanForDevicesDisposable = bleClient.scanForDevices(params.filter, params.mode, params.locationServiceIsMandatory) + scanForDevicesDisposable = + bleClient.scanForDevices(params.filter, params.mode, params.locationServiceIsMandatory) .observeOn(AndroidSchedulers.mainThread()) .subscribe( - { scanResult -> - handleDeviceScanResult(converter.convertScanInfo(scanResult)) - }, - { throwable -> - handleDeviceScanResult(converter.convertScanErrorInfo(throwable.message)) - } + { scanResult -> + handleDeviceScanResult(converter.convertScanInfo(scanResult)) + }, + { throwable -> + handleDeviceScanResult(converter.convertScanErrorInfo(throwable.message)) + }, ) } - ?: handleDeviceScanResult(converter.convertScanErrorInfo("Scanning parameters are not set")) + ?: handleDeviceScanResult(converter.convertScanErrorInfo("Scanning parameters are not set")) } fun stopDeviceScan() { - if (this::scanForDevicesDisposable.isInitialized) scanForDevicesDisposable.let { - if (!it.isDisposed) { - it.dispose() - scanParameters = null + if (this::scanForDevicesDisposable.isInitialized) { + scanForDevicesDisposable.let { + if (!it.isDisposed) { + it.dispose() + scanParameters = null + } } } } fun prepareScan(scanMessage: pb.ScanForDevicesRequest) { stopDeviceScan() - val filter = scanMessage.serviceUuidsList + val filter = + scanMessage.serviceUuidsList .map { ParcelUuid(UuidConverter().uuidFromByteArray(it.data.toByteArray())) } val scanMode = createScanMode(scanMessage.scanMode) scanParameters = ScanParameters(filter, scanMode, scanMessage.requireLocationServicesEnabled) diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverter.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverter.kt index 9ccf1d3e..7c8077f0 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverter.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverter.kt @@ -23,7 +23,6 @@ import com.signify.hue.flutterreactiveble.ProtobufModel as pb @Suppress("TooManyFunctions") class ProtobufMessageConverter { - companion object { private const val positionMostSignificantBit = 2 private const val positionLeastSignificantBit = 3 @@ -32,90 +31,100 @@ class ProtobufMessageConverter { private val uuidConverter = UuidConverter() fun convertScanInfo(scanInfo: ScanInfo): pb.DeviceScanInfo = - pb.DeviceScanInfo.newBuilder() - .setId(scanInfo.deviceId) - .setName(scanInfo.name) - .setRssi(scanInfo.rssi) - .setIsConnectable(pb.IsConnectable.newBuilder() - .setCode(scanInfo.connectable.code) - .build()) - .addAllServiceData(createServiceDataEntry(scanInfo.serviceData)) - .addAllServiceUuids(createServiceUuids(scanInfo.serviceUuids)) - .setManufacturerData(ByteString.copyFrom(scanInfo.manufacturerData)) - .build() + pb.DeviceScanInfo.newBuilder() + .setId(scanInfo.deviceId) + .setName(scanInfo.name) + .setRssi(scanInfo.rssi) + .setIsConnectable( + pb.IsConnectable.newBuilder() + .setCode(scanInfo.connectable.code) + .build(), + ) + .addAllServiceData(createServiceDataEntry(scanInfo.serviceData)) + .addAllServiceUuids(createServiceUuids(scanInfo.serviceUuids)) + .setManufacturerData(ByteString.copyFrom(scanInfo.manufacturerData)) + .build() fun convertScanErrorInfo(errorMessage: String?): pb.DeviceScanInfo = - pb.DeviceScanInfo.newBuilder() - .setFailure(pb.GenericFailure.newBuilder() - .setCode(ScanErrorType.UNKNOWN.code) - .setMessage(errorMessage ?: "") - .build()) - .build() + pb.DeviceScanInfo.newBuilder() + .setFailure( + pb.GenericFailure.newBuilder() + .setCode(ScanErrorType.UNKNOWN.code) + .setMessage(errorMessage ?: "") + .build(), + ) + .build() fun convertToDeviceInfo(connection: ConnectionUpdateSuccess): pb.DeviceInfo = - pb.DeviceInfo.newBuilder() - .setId(connection.deviceId) - .setConnectionState(connection.connectionState) - .build() + pb.DeviceInfo.newBuilder() + .setId(connection.deviceId) + .setConnectionState(connection.connectionState) + .build() fun convertConnectionErrorToDeviceInfo( - deviceId: String, - errorMessage: String? + deviceId: String, + errorMessage: String?, ): pb.DeviceInfo { - return pb.DeviceInfo.newBuilder() - .setId(deviceId) - .setConnectionState(ConnectionState.DISCONNECTED.code) - .setFailure(pb.GenericFailure.newBuilder() - .setCode(ConnectionErrorType.FAILEDTOCONNECT.code) - .setMessage(errorMessage ?: "") - .build()) - .build() + .setId(deviceId) + .setConnectionState(ConnectionState.DISCONNECTED.code) + .setFailure( + pb.GenericFailure.newBuilder() + .setCode(ConnectionErrorType.FAILEDTOCONNECT.code) + .setMessage(errorMessage ?: "") + .build(), + ) + .build() } - fun convertClearGattCacheError(code: ClearGattCacheErrorType, message: String?): pb.ClearGattCacheInfo { + fun convertClearGattCacheError( + code: ClearGattCacheErrorType, + message: String?, + ): pb.ClearGattCacheInfo { val failure = pb.GenericFailure.newBuilder().setCode(code.code) message?.let(failure::setMessage) return pb.ClearGattCacheInfo.newBuilder().setFailure(failure).build() } fun convertCharacteristicInfo( - request: pb.CharacteristicAddress, - value: ByteArray + request: pb.CharacteristicAddress, + value: ByteArray, ): pb.CharacteristicValueInfo { - val characteristicAddress = createCharacteristicAddress(request) return pb.CharacteristicValueInfo.newBuilder() - .setCharacteristic(characteristicAddress) - .setValue(ByteString.copyFrom(value)) - .build() + .setCharacteristic(characteristicAddress) + .setValue(ByteString.copyFrom(value)) + .build() } fun convertCharacteristicError( - request: pb.CharacteristicAddress, - error: String? + request: pb.CharacteristicAddress, + error: String?, ): pb.CharacteristicValueInfo { val characteristicAddress = createCharacteristicAddress(request) - val failure = pb.GenericFailure.newBuilder() + val failure = + pb.GenericFailure.newBuilder() .setCode(CharacteristicErrorType.UNKNOWN.code) .setMessage(error ?: "Unknown error") return pb.CharacteristicValueInfo.newBuilder() - .setCharacteristic(characteristicAddress) - .setFailure(failure) - .build() + .setCharacteristic(characteristicAddress) + .setFailure(failure) + .build() } fun convertWriteCharacteristicInfo( - request: pb.WriteCharacteristicRequest, - error: String? + request: pb.WriteCharacteristicRequest, + error: String?, ): pb.WriteCharacteristicInfo { - val builder = pb.WriteCharacteristicInfo.newBuilder() + val builder = + pb.WriteCharacteristicInfo.newBuilder() .setCharacteristic(request.characteristic) error?.let { - val failure = pb.GenericFailure.newBuilder() + val failure = + pb.GenericFailure.newBuilder() .setCode(CharacteristicErrorType.UNKNOWN.code) .setMessage(error) @@ -126,68 +135,70 @@ class ProtobufMessageConverter { } fun convertNegotiateMtuInfo(result: MtuNegotiateResult): pb.NegotiateMtuInfo = - when (result) { - is MtuNegotiateSuccessful -> pb.NegotiateMtuInfo.newBuilder() - .setDeviceId(result.deviceId) - .setMtuSize(result.size) + when (result) { + is MtuNegotiateSuccessful -> + pb.NegotiateMtuInfo.newBuilder() + .setDeviceId(result.deviceId) + .setMtuSize(result.size) + .build() + is MtuNegotiateFailed -> { + val failure = + pb.GenericFailure.newBuilder() + .setCode(NegotiateMtuErrorType.UNKNOWN.code) + .setMessage(result.errorMessage) .build() - is MtuNegotiateFailed -> { - - val failure = pb.GenericFailure.newBuilder() - .setCode(NegotiateMtuErrorType.UNKNOWN.code) - .setMessage(result.errorMessage) - .build() - - pb.NegotiateMtuInfo.newBuilder() - .setDeviceId(result.deviceId) - .setFailure(failure) - .build() - } + + pb.NegotiateMtuInfo.newBuilder() + .setDeviceId(result.deviceId) + .setFailure(failure) + .build() } + } - fun convertRequestConnectionPriorityInfo( - result: RequestConnectionPriorityResult - ): pb.ChangeConnectionPriorityInfo { + fun convertRequestConnectionPriorityInfo(result: RequestConnectionPriorityResult): pb.ChangeConnectionPriorityInfo { return when (result) { - is RequestConnectionPrioritySuccess -> pb.ChangeConnectionPriorityInfo.newBuilder() + is RequestConnectionPrioritySuccess -> + pb.ChangeConnectionPriorityInfo.newBuilder() .setDeviceId(result.deviceId) .build() is RequestConnectionPriorityFailed -> { - val failure = pb.GenericFailure.newBuilder() + val failure = + pb.GenericFailure.newBuilder() .setCode(0) .setMessage(result.errorMessage) .build() pb.ChangeConnectionPriorityInfo.newBuilder() - .setDeviceId(result.deviceId) - .setFailure(failure) - .build() + .setDeviceId(result.deviceId) + .setFailure(failure) + .build() } } } fun convertDiscoverServicesInfo( - deviceId: String, - services: RxBleDeviceServices + deviceId: String, + services: RxBleDeviceServices, ): pb.DiscoverServicesInfo { return pb.DiscoverServicesInfo.newBuilder() - .setDeviceId(deviceId) - .addAllServices(services.bluetoothGattServices.map { fromBluetoothGattService(it) }) - .build() + .setDeviceId(deviceId) + .addAllServices(services.bluetoothGattServices.map { fromBluetoothGattService(it) }) + .build() } fun convertReadRssiResult(rssi: Int): pb.ReadRssiResult { return pb.ReadRssiResult.newBuilder() - .setRssi(rssi) - .build() + .setRssi(rssi) + .build() } private fun fromBluetoothGattService(gattService: BluetoothGattService): pb.DiscoveredService { return pb.DiscoveredService.newBuilder() - .setServiceUuid(createUuidFromParcelUuid(gattService.uuid)) - .setServiceInstanceId(gattService.instanceId.toString()) - .addAllCharacteristicUuids(gattService.characteristics.map { createUuidFromParcelUuid(it.uuid) }) - .addAllCharacteristics(gattService.characteristics.map { + .setServiceUuid(createUuidFromParcelUuid(gattService.uuid)) + .setServiceInstanceId(gattService.instanceId.toString()) + .addAllCharacteristicUuids(gattService.characteristics.map { createUuidFromParcelUuid(it.uuid) }) + .addAllCharacteristics( + gattService.characteristics.map { val prop = it.properties val readable = (prop and BluetoothGattCharacteristic.PROPERTY_READ) > 0 val write = (prop and BluetoothGattCharacteristic.PROPERTY_WRITE) > 0 @@ -196,39 +207,41 @@ class ProtobufMessageConverter { val indicate = (prop and BluetoothGattCharacteristic.PROPERTY_INDICATE) > 0 pb.DiscoveredCharacteristic.newBuilder() - .setCharacteristicId(createUuidFromParcelUuid(it.uuid)) - .setCharacteristicInstanceId(it.instanceId.toString()) - .setServiceId(createUuidFromParcelUuid(it.service.uuid)) - .setIsReadable(readable) - .setIsWritableWithResponse(write) - .setIsWritableWithoutResponse(writeNoResp) - .setIsNotifiable(notify) - .setIsIndicatable(indicate) - .build() - }) - .addAllIncludedServices(gattService.includedServices.map { convertInternalService(it) }) - .build() + .setCharacteristicId(createUuidFromParcelUuid(it.uuid)) + .setCharacteristicInstanceId(it.instanceId.toString()) + .setServiceId(createUuidFromParcelUuid(it.service.uuid)) + .setIsReadable(readable) + .setIsWritableWithResponse(write) + .setIsWritableWithoutResponse(writeNoResp) + .setIsNotifiable(notify) + .setIsIndicatable(indicate) + .build() + }, + ) + .addAllIncludedServices(gattService.includedServices.map { convertInternalService(it) }) + .build() } private fun convertInternalService(gattService: BluetoothGattService): pb.DiscoveredService { - val root = pb.DiscoveredService.newBuilder() + val root = + pb.DiscoveredService.newBuilder() .setServiceUuid(createUuidFromParcelUuid(gattService.uuid)) .addAllCharacteristicUuids(gattService.characteristics.map { createUuidFromParcelUuid(it.uuid) }) - val children = gattService.includedServices.map { - convertInternalService(it) - } + val children = + gattService.includedServices.map { + convertInternalService(it) + } return root.addAllIncludedServices(children).build() } - private fun createCharacteristicAddress(request: pb.CharacteristicAddress): - pb.CharacteristicAddress.Builder? { + private fun createCharacteristicAddress(request: pb.CharacteristicAddress): pb.CharacteristicAddress.Builder? { return pb.CharacteristicAddress.newBuilder() - .setDeviceId(request.deviceId) - .setServiceUuid(request.serviceUuid) - .setServiceInstanceId(request.serviceInstanceId) - .setCharacteristicInstanceId(request.characteristicInstanceId) - .setCharacteristicUuid(request.characteristicUuid) + .setDeviceId(request.deviceId) + .setServiceUuid(request.serviceUuid) + .setServiceInstanceId(request.serviceInstanceId) + .setCharacteristicInstanceId(request.characteristicInstanceId) + .setCharacteristicUuid(request.characteristicUuid) } private fun createServiceDataEntry(serviceData: Map): List { @@ -236,10 +249,12 @@ class ProtobufMessageConverter { // Needed ugly for-loop because we support API23 that does not support kotlin foreach for (entry: Map.Entry in serviceData) { - serviceDataEntries.add(pb.ServiceDataEntry.newBuilder() + serviceDataEntries.add( + pb.ServiceDataEntry.newBuilder() .setServiceUuid(createUuidFromParcelUuid(entry.key)) .setData(ByteString.copyFrom(entry.value)) - .build()) + .build(), + ) } return serviceDataEntries diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/converters/UuidConverter.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/converters/UuidConverter.kt index 89a74570..a0f7cf59 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/converters/UuidConverter.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/converters/UuidConverter.kt @@ -11,7 +11,6 @@ class UuidConverter { } fun uuidFromByteArray(bytes: ByteArray): UUID { - return when (bytes.size) { byteSize16Bit -> convert16BitToUuid(bytes) byteSize32Bit -> convert32BitToUuid(bytes) @@ -22,16 +21,50 @@ class UuidConverter { @Suppress("Detekt.MagicNumber") private fun convert16BitToUuid(bytes: ByteArray): UUID { // UUID construction is retrieved from BLE corespec v5.0 page 1917 - val uuidConstruct = byteArrayOf(0x00, 0x00, bytes[0], bytes[1], 0x00, 0x00, 0x10, 0x00, - 0x80.toByte(), 0x00, 0x00, 0x80.toByte(), 0x5F, 0x9B.toByte(), 0x34, 0xFB.toByte()) + val uuidConstruct = + byteArrayOf( + 0x00, + 0x00, + bytes[0], + bytes[1], + 0x00, + 0x00, + 0x10, + 0x00, + 0x80.toByte(), + 0x00, + 0x00, + 0x80.toByte(), + 0x5F, + 0x9B.toByte(), + 0x34, + 0xFB.toByte(), + ) return convert128BitNotationToUuid(uuidConstruct) } @Suppress("Detekt.MagicNumber") private fun convert32BitToUuid(bytes: ByteArray): UUID { - val uuidConstruct = byteArrayOf(bytes[0], bytes[1], bytes[2], bytes[3], 0x00, 0x00, 0x10, 0x00, - 0x80.toByte(), 0x00, 0x00, 0x80.toByte(), 0x5F, 0x9B.toByte(), 0x34, 0xFB.toByte()) + val uuidConstruct = + byteArrayOf( + bytes[0], + bytes[1], + bytes[2], + bytes[3], + 0x00, + 0x00, + 0x10, + 0x00, + 0x80.toByte(), + 0x00, + 0x00, + 0x80.toByte(), + 0x5F, + 0x9B.toByte(), + 0x34, + 0xFB.toByte(), + ) return convert128BitNotationToUuid(uuidConstruct) } diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/debugutils/PerformanceAnalyzer.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/debugutils/PerformanceAnalyzer.kt index 14bf87ae..8c105593 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/debugutils/PerformanceAnalyzer.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/debugutils/PerformanceAnalyzer.kt @@ -1,7 +1,6 @@ package com.signify.hue.flutterreactiveble.debugutils object PerformanceAnalyzer { - var timer = Pair(0, 0) fun start(startTime: Long) { diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/model/ConnectionState.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/model/ConnectionState.kt index b6a88a45..9be3be13 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/model/ConnectionState.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/model/ConnectionState.kt @@ -8,7 +8,7 @@ enum class ConnectionState(val code: Int) { CONNECTED(1), DISCONNECTING(2), DISCONNECTED(3), - UNKNOWN(4) + UNKNOWN(4), } fun RxBleConnection.RxBleConnectionState.toConnectionState(): ConnectionState = @@ -18,4 +18,4 @@ fun RxBleConnection.RxBleConnectionState.toConnectionState(): ConnectionState = "CONNECTED" -> ConnectionState.CONNECTED "DISCONNECTING" -> ConnectionState.DISCONNECTING else -> ConnectionState.UNKNOWN -} + } diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/model/ErrorType.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/model/ErrorType.kt index 46e71118..456db908 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/model/ErrorType.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/model/ErrorType.kt @@ -2,21 +2,21 @@ package com.signify.hue.flutterreactiveble.model enum class ConnectionErrorType(val code: Int) { UNKNOWN(0), - FAILEDTOCONNECT(1) + FAILEDTOCONNECT(1), } enum class ClearGattCacheErrorType(val code: Int) { - UNKNOWN(0) + UNKNOWN(0), } enum class CharacteristicErrorType(val code: Int) { - UNKNOWN(0) + UNKNOWN(0), } enum class NegotiateMtuErrorType(val code: Int) { - UNKNOWN(0) + UNKNOWN(0), } enum class ScanErrorType(val code: Int) { - UNKNOWN(0) + UNKNOWN(0), } diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/model/ScanMode.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/model/ScanMode.kt index 31ec3198..9614612f 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/model/ScanMode.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/model/ScanMode.kt @@ -6,7 +6,7 @@ enum class ScanMode(val code: Int) { OPPORTUNISTIC(-1), LOW_POWER(0), BALANCED(1), - LOW_LATENCY(2) + LOW_LATENCY(2), } internal fun ScanMode.toScanSettings(): Int = @@ -18,10 +18,10 @@ internal fun ScanMode.toScanSettings(): Int = } internal fun createScanMode(mode: Int): ScanMode = - when (mode) { - -1 -> ScanMode.OPPORTUNISTIC - 0 -> ScanMode.LOW_POWER - 1 -> ScanMode.BALANCED - 2 -> ScanMode.LOW_LATENCY - else -> ScanMode.LOW_POWER - } + when (mode) { + -1 -> ScanMode.OPPORTUNISTIC + 0 -> ScanMode.LOW_POWER + 1 -> ScanMode.BALANCED + 2 -> ScanMode.LOW_LATENCY + else -> ScanMode.LOW_POWER + } diff --git a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/utils/BleWrapperExtensions.kt b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/utils/BleWrapperExtensions.kt index c4a120dd..13b0b9dd 100644 --- a/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/utils/BleWrapperExtensions.kt +++ b/packages/reactive_ble_mobile/android/src/main/kotlin/com/signify/hue/flutterreactiveble/utils/BleWrapperExtensions.kt @@ -10,18 +10,18 @@ import com.signify.hue.flutterreactiveble.ble.BleStatus import com.signify.hue.flutterreactiveble.ble.ConnectionPriority fun RxBleClient.State.toBleState(): BleStatus = - when (this) { - BLUETOOTH_NOT_AVAILABLE -> BleStatus.UNSUPPORTED - LOCATION_PERMISSION_NOT_GRANTED -> BleStatus.UNAUTHORIZED - BLUETOOTH_NOT_ENABLED -> BleStatus.POWERED_OFF - LOCATION_SERVICES_NOT_ENABLED -> BleStatus.LOCATION_SERVICES_DISABLED - READY -> BleStatus.READY - } + when (this) { + BLUETOOTH_NOT_AVAILABLE -> BleStatus.UNSUPPORTED + LOCATION_PERMISSION_NOT_GRANTED -> BleStatus.UNAUTHORIZED + BLUETOOTH_NOT_ENABLED -> BleStatus.POWERED_OFF + LOCATION_SERVICES_NOT_ENABLED -> BleStatus.LOCATION_SERVICES_DISABLED + READY -> BleStatus.READY + } fun Int.toConnectionPriority() = - when (this) { - 0 -> ConnectionPriority.BALANCED - 1 -> ConnectionPriority.HIGH_PERFORMACE - 2 -> ConnectionPriority.LOW_POWER - else -> ConnectionPriority.BALANCED - } + when (this) { + 0 -> ConnectionPriority.BALANCED + 1 -> ConnectionPriority.HIGH_PERFORMACE + 2 -> ConnectionPriority.LOW_POWER + else -> ConnectionPriority.BALANCED + } diff --git a/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/ble/DeviceConnectorTest.kt b/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/ble/DeviceConnectorTest.kt index 76f2b35c..31b0e83f 100644 --- a/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/ble/DeviceConnectorTest.kt +++ b/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/ble/DeviceConnectorTest.kt @@ -9,7 +9,6 @@ import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.verify -import io.mockk.verifySequence import io.reactivex.Observable import io.reactivex.subjects.BehaviorSubject import org.junit.jupiter.api.AfterEach @@ -21,10 +20,7 @@ import java.lang.Exception import java.util.concurrent.TimeUnit @DisplayName("DeviceConnector unit tests") - - class DeviceConnectorTest { - @MockK private lateinit var connection: RxBleConnection @@ -35,7 +31,7 @@ class DeviceConnectorTest { private lateinit var connectionQueue: ConnectionQueue @MockK - private lateinit var updateListener: (update: ConnectionUpdate) -> Unit + private lateinit var updateListener: (update: ConnectionUpdate) -> Unit private lateinit var sut: DeviceConnector private lateinit var subject: BehaviorSubject> @@ -46,28 +42,26 @@ class DeviceConnectorTest { MockKAnnotations.init(this) subject = BehaviorSubject.create() every { device.connectionState }.returns(RxBleConnection.RxBleConnectionState.DISCONNECTED) - every { device.observeConnectionStateChanges()}.returns(Observable.just(RxBleConnection.RxBleConnectionState.CONNECTED)) + every { device.observeConnectionStateChanges() }.returns(Observable.just(RxBleConnection.RxBleConnectionState.CONNECTED)) every { device.macAddress }.returns(deviceId) every { updateListener.invoke(allAny()) }.returns(Unit) - every {connectionQueue.addToQueue(any())}.returns(Unit) - every {connectionQueue.observeQueue()}.returns(subject) - every {connectionQueue.removeFromQueue(any())}.returns(Unit) + every { connectionQueue.addToQueue(any()) }.returns(Unit) + every { connectionQueue.observeQueue() }.returns(subject) + every { connectionQueue.removeFromQueue(any()) }.returns(Unit) subject.onNext(listOf(device.macAddress)) sut = DeviceConnector(device, Duration(0L, TimeUnit.MILLISECONDS), updateListener, connectionQueue) - } @AfterEach - fun teardown(){ + fun teardown() { sut.disconnectDevice(deviceId) } @Nested - @DisplayName ("Successfull connection") + @DisplayName("Successfull connection") inner class SuccesfullConnectionTest { - @BeforeEach fun setup() { every { device.establishConnection(any()) }.returns(Observable.just(connection)) @@ -75,12 +69,11 @@ class DeviceConnectorTest { @Test @DisplayName("Add device to queue") - fun addDeviceToQueue(){ + fun addDeviceToQueue() { sut.connection.test() verify(exactly = 1) { connectionQueue.addToQueue(deviceId) } } - @Test @DisplayName("Connects to device only once") fun connectOnlyOnce() { @@ -104,7 +97,6 @@ class DeviceConnectorTest { sut.connection.test() verify(exactly = 1) { updateListener.invoke(ConnectionUpdateSuccess(deviceId, ConnectionState.CONNECTED.code)) } - } @Test @@ -120,15 +112,14 @@ class DeviceConnectorTest { @Test @DisplayName("Remove device from queue") - fun removeDeviceFromQueue(){ + fun removeDeviceFromQueue() { sut.connection.test() verify(exactly = 1) { connectionQueue.removeFromQueue(deviceId) } } } - @Nested - @DisplayName ("Failed connection") + @DisplayName("Failed connection") inner class NotSuccesfullConnectionTest { private val errorMessage = "aaa" @@ -155,7 +146,7 @@ class DeviceConnectorTest { @Test @DisplayName("Remove device from queue") - fun removeDeviceFromQueue(){ + fun removeDeviceFromQueue() { sut.connection.test() verify(exactly = 1) { connectionQueue.removeFromQueue(deviceId) } } @@ -175,4 +166,3 @@ class DeviceConnectorTest { verify(exactly = 1) { updateListener.invoke(ConnectionUpdateSuccess(deviceId, ConnectionState.DISCONNECTED.code)) } } } - diff --git a/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/ble/ReactiveBleClientTest.kt b/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/ble/ReactiveBleClientTest.kt index 31ace0b8..a3fee977 100644 --- a/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/ble/ReactiveBleClientTest.kt +++ b/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/ble/ReactiveBleClientTest.kt @@ -34,19 +34,22 @@ import org.junit.jupiter.api.Test import java.util.UUID import java.util.concurrent.TimeUnit -private class BleClientForTesting(val bleClient: RxBleClient, appContext: Context, val deviceConnector: DeviceConnector) : ReactiveBleClient(appContext) { - +private class BleClientForTesting(val bleClient: RxBleClient, appContext: Context, val deviceConnector: DeviceConnector) : ReactiveBleClient( + appContext, +) { override fun initializeClient() { rxBleClient = bleClient activeConnections = mutableMapOf() } - override fun createDeviceConnector(device: RxBleDevice, timeout: Duration): DeviceConnector = deviceConnector + override fun createDeviceConnector( + device: RxBleDevice, + timeout: Duration, + ): DeviceConnector = deviceConnector } @DisplayName("BleClient unit tests") class ReactiveBleClientTest { - @MockK private lateinit var context: Context @@ -106,11 +109,9 @@ class ReactiveBleClientTest { subject.onComplete() } - @DisplayName("Establishing a connection") @Nested inner class EstablishConnectionTest { - @Test fun `should use deviceconnector when connecting to a device`() { sut.connectToDevice("test", testTimeout) @@ -124,7 +125,12 @@ class ReactiveBleClientTest { inner class BleOperationsTest { @Test fun `should call readcharacteristic in case the connection is established`() { - every { rxConnection.resolveCharacteristic(any(), any()) }.returns(Single.just(BluetoothGattCharacteristic(UUID.randomUUID(), 0, 0))) + every { + rxConnection.resolveCharacteristic( + any(), + any(), + ) + }.returns(Single.just(BluetoothGattCharacteristic(UUID.randomUUID(), 0, 0))) sut.readCharacteristic("test", UUID.randomUUID(), 11).test() @@ -155,9 +161,19 @@ class ReactiveBleClientTest { val byteMin = Byte.MIN_VALUE val byteMax = Byte.MAX_VALUE - every { rxConnection.readCharacteristic(any()) }.returns(Single.just(byteArrayOf(byteMin, byteMax))) - every { rxConnection.resolveCharacteristic(any(), any()) }.returns(Single.just(BluetoothGattCharacteristic(UUID.randomUUID(), 0, 0))) - val observable = sut.readCharacteristic("test", UUID.randomUUID(), 11) + every { + rxConnection.readCharacteristic( + any(), + ) + }.returns(Single.just(byteArrayOf(byteMin, byteMax))) + every { + rxConnection.resolveCharacteristic( + any(), + any(), + ) + }.returns(Single.just(BluetoothGattCharacteristic(UUID.randomUUID(), 0, 0))) + val observable = + sut.readCharacteristic("test", UUID.randomUUID(), 11) .map { result -> result as CharOperationSuccessful }.test() assertThat(observable.values().first().value).isEqualTo(listOf(byteMin, byteMax)) @@ -169,7 +185,12 @@ class ReactiveBleClientTest { val byteMax = Byte.MAX_VALUE val bytes = byteArrayOf(byteMin, byteMax) - every { rxConnection.resolveCharacteristic(any(), any()) }.returns(Single.just(BluetoothGattCharacteristic(UUID.randomUUID(), 0, 0))) + every { + rxConnection.resolveCharacteristic( + any(), + any(), + ) + }.returns(Single.just(BluetoothGattCharacteristic(UUID.randomUUID(), 0, 0))) sut.writeCharacteristicWithResponse("test", UUID.randomUUID(), 11, bytes).test() verify(exactly = 1) { rxConnection.writeCharWithResponse(any(), any()) } @@ -205,7 +226,12 @@ class ReactiveBleClientTest { val byteMax = Byte.MAX_VALUE val bytes = byteArrayOf(byteMin, byteMax) - every { rxConnection.resolveCharacteristic(any(), any()) }.returns(Single.just(BluetoothGattCharacteristic(UUID.randomUUID(), 0, 0))) + every { + rxConnection.resolveCharacteristic( + any(), + any(), + ) + }.returns(Single.just(BluetoothGattCharacteristic(UUID.randomUUID(), 0, 0))) sut.writeCharacteristicWithoutResponse("test", UUID.randomUUID(), 11, bytes).test() verify(exactly = 1) { rxConnection.writeCharWithoutResponse(any(), any()) } @@ -244,8 +270,14 @@ class ReactiveBleClientTest { val bytes = byteArrayOf(byteMin, byteMax) every { rxConnection.writeCharWithResponse(any(), any()) }.returns(Single.just(byteArrayOf(byteMin, byteMax))) - every { rxConnection.resolveCharacteristic(any(), any()) }.returns(Single.just(BluetoothGattCharacteristic(UUID.randomUUID(), 0, 0))) - val observable = sut.writeCharacteristicWithResponse("test", UUID.randomUUID(), 11, bytes) + every { + rxConnection.resolveCharacteristic( + any(), + any(), + ) + }.returns(Single.just(BluetoothGattCharacteristic(UUID.randomUUID(), 0, 0))) + val observable = + sut.writeCharacteristicWithResponse("test", UUID.randomUUID(), 11, bytes) .map { result -> result as CharOperationSuccessful }.test() assertThat(observable.values().first().value).isEqualTo(bytes.toList()) @@ -255,7 +287,6 @@ class ReactiveBleClientTest { @Nested @DisplayName("Negotiate mtu") inner class NegotiateMtuTest { - @Test fun `should return mtunegotiatesuccesful in case it succeeds`() { val mtuSize = 19 @@ -280,7 +311,6 @@ class ReactiveBleClientTest { @Nested @DisplayName("Read RSSI") inner class ReadRssiTest { - @Test fun `should return RSSI in case it succeeds`() { val rssi = -42 @@ -323,7 +353,6 @@ class ReactiveBleClientTest { @Test fun `starts with current state`() { - val result = sut.observeBleStatus().test() assertThat(result.values().count()).isEqualTo(2) assertThat(result.values().first()).isEqualTo(BleStatus.POWERED_OFF) @@ -333,7 +362,6 @@ class ReactiveBleClientTest { @Nested @DisplayName("Change priority") inner class ChangePriorityTest { - @Test fun `returns prioritysuccess when completed`() { val completer = Completable.fromCallable { true } @@ -354,11 +382,10 @@ class ReactiveBleClientTest { @Nested @DisplayName("Discover services") inner class DiscoverServicesTest { - @BeforeEach fun setup() { every { bleDevice.bluetoothDevice }.returns(bluetoothDevice) - every {bluetoothDevice.bondState}.returns(BOND_BONDED) + every { bluetoothDevice.bondState }.returns(BOND_BONDED) } @Test diff --git a/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/ConnectionQueueTest.kt b/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/ConnectionQueueTest.kt index 1ee09253..f0c8fe75 100644 --- a/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/ConnectionQueueTest.kt +++ b/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/channelhandlers/ConnectionQueueTest.kt @@ -1,11 +1,10 @@ package com.signify.hue.flutterreactiveble.channelhandlers import com.google.common.truth.Truth.assertThat import io.reactivex.subjects.BehaviorSubject -import org.junit.jupiter.api.Test import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test class ConnectionQueueTest { - private lateinit var sut: com.signify.hue.flutterreactiveble.ble.ConnectionQueue @BeforeEach diff --git a/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverterTest.kt b/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverterTest.kt index c111661f..ce69d725 100644 --- a/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverterTest.kt +++ b/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/converters/ProtobufMessageConverterTest.kt @@ -20,7 +20,6 @@ class ProtobufMessageConverterTest { @Nested @DisplayName("Convert to scaninfo") inner class ScanInfoTest { - @Test fun `converts scan result to DeviceDiscoveryMessage`() { val scanInfo = createScanInfo() @@ -107,7 +106,6 @@ class ProtobufMessageConverterTest { @Nested @DisplayName("Convert to deviceinfo") inner class DeviceInfoTest { - @Test fun `converts device id as parameter in device connection message`() { val deviceId = "2" @@ -126,13 +124,12 @@ class ProtobufMessageConverterTest { @Nested @DisplayName("Convert to charinfo") inner class ConvertCharInfoTest { - @Test fun `converts to a characteristicvalueInfo object `() { val request = createCharacteristicRequest("a", UUID.randomUUID()) assertThat(protobufConverter.convertCharacteristicInfo(request.characteristic, byteArrayOf(1))) - .isInstanceOf(pb.CharacteristicValueInfo::class.java) + .isInstanceOf(pb.CharacteristicValueInfo::class.java) } @Test @@ -148,7 +145,6 @@ class ProtobufMessageConverterTest { @Nested @DisplayName("Convert to negotiatemtuinfo") inner class NegotiateMtuInfoTest { - @Test fun `converts to negotiatemtuinfo object`() { val result = MtuNegotiateSuccessful("", 3) @@ -179,14 +175,14 @@ class ProtobufMessageConverterTest { val errorMessage = "whoops" val result = MtuNegotiateFailed("id", errorMessage) assertThat(protobufConverter.convertNegotiateMtuInfo(result).failure.message) - .isEqualTo(errorMessage) + .isEqualTo(errorMessage) } @Test fun `converts error code`() { val result = MtuNegotiateFailed("id", "") assertThat(protobufConverter.convertNegotiateMtuInfo(result).failure.code) - .isEqualTo(NegotiateMtuErrorType.UNKNOWN.code) + .isEqualTo(NegotiateMtuErrorType.UNKNOWN.code) } } @@ -204,29 +200,33 @@ class ProtobufMessageConverterTest { val manufacturerData = "123".toByteArray() return ScanInfo( - deviceId = macAddress, - name = deviceName, - rssi = rssi, - connectable = Connectable.UNKNOWN, - serviceData = serviceData, - manufacturerData = manufacturerData, - serviceUuids = listOf(serviceUuid), + deviceId = macAddress, + name = deviceName, + rssi = rssi, + connectable = Connectable.UNKNOWN, + serviceData = serviceData, + manufacturerData = manufacturerData, + serviceUuids = listOf(serviceUuid), ) } - private fun createCharacteristicRequest(deviceId: String, serviceUuid: UUID): pb.ReadCharacteristicRequest { + private fun createCharacteristicRequest( + deviceId: String, + serviceUuid: UUID, + ): pb.ReadCharacteristicRequest { val uuidConverter = UuidConverter() - val uuid = pb.Uuid.newBuilder() + val uuid = + pb.Uuid.newBuilder() .setData(ByteString.copyFrom(uuidConverter.byteArrayFromUuid(serviceUuid))) - val characteristicAddress = pb.CharacteristicAddress.newBuilder() + val characteristicAddress = + pb.CharacteristicAddress.newBuilder() .setDeviceId(deviceId) .setServiceUuid(uuid) .setCharacteristicUuid(uuid) return pb.ReadCharacteristicRequest.newBuilder() - .setCharacteristic(characteristicAddress) - .build() + .setCharacteristic(characteristicAddress) + .build() } } - diff --git a/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/converters/ServicesWithCharacteristicsConverterTest.kt b/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/converters/ServicesWithCharacteristicsConverterTest.kt index 2d509a7e..34ee6f59 100644 --- a/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/converters/ServicesWithCharacteristicsConverterTest.kt +++ b/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/converters/ServicesWithCharacteristicsConverterTest.kt @@ -12,9 +12,7 @@ import org.junit.Test import java.util.UUID import com.signify.hue.flutterreactiveble.ProtobufModel as pb - class ServicesWithCharacteristicsConverterTest { - private val serviceUuid = UUID.randomUUID() private val characteristicUuid = UUID.randomUUID() private val internalCharacteristicUuid = UUID.randomUUID() @@ -42,7 +40,6 @@ class ServicesWithCharacteristicsConverterTest { @MockK lateinit var internalCharacteristicLevel2: BluetoothGattCharacteristic - @Before fun setUp() { MockKAnnotations.init(this) @@ -61,8 +58,11 @@ class ServicesWithCharacteristicsConverterTest { every { internalServiceLevel2.includedServices }.returns(listOf()) every { internalServiceLevel2.characteristics }.returns(listOf(internalCharacteristicLevel2)) - conversionResult = sut.convertDiscoverServicesInfo("test", - RxBleDeviceServices(listOf(service))) + conversionResult = + sut.convertDiscoverServicesInfo( + "test", + RxBleDeviceServices(listOf(service)), + ) } @Test @@ -75,11 +75,11 @@ class ServicesWithCharacteristicsConverterTest { assertThat(conversionResult.getServices(0).characteristicUuidsCount).isEqualTo(1) } - @Test fun `It converts nested internal services correctly`() { - assertThat(conversionResult.getServices(0).getIncludedServices(0) - .includedServicesCount).isEqualTo(2) + assertThat( + conversionResult.getServices(0).getIncludedServices(0) + .includedServicesCount, + ).isEqualTo(2) } } - diff --git a/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/converters/UuidConverterTest.kt b/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/converters/UuidConverterTest.kt index a3cf42ec..52e47d2e 100644 --- a/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/converters/UuidConverterTest.kt +++ b/packages/reactive_ble_mobile/android/src/test/kotlin/com/signify/hue/flutterreactiveble/converters/UuidConverterTest.kt @@ -2,11 +2,9 @@ package com.signify.hue.flutterreactiveble.converters import com.google.common.truth.Truth.assertThat import org.junit.jupiter.api.Test -import java.util.* - +import java.util.UUID class UuidConverterTest { - val converter = UuidConverter() @Test @@ -20,7 +18,6 @@ class UuidConverterTest { @Test fun `should be able to convert 16bit uuid`() { - val array = byteArrayOf(0xFE.toByte(), 0x0F.toByte()) val uuid = converter.uuidFromByteArray(array) assertThat(uuid.toString().toUpperCase()).isEqualTo("0000FE0F-0000-1000-8000-00805F9B34FB") @@ -28,7 +25,6 @@ class UuidConverterTest { @Test fun `should be able to convert 32bit uuid`() { - val array = byteArrayOf(0xFE.toByte(), 0x0F.toByte(), 0x0F.toByte(), 0xFE.toByte()) val uuid = converter.uuidFromByteArray(array) assertThat(uuid.toString().toUpperCase()).isEqualTo("FE0F0FFE-0000-1000-8000-00805F9B34FB")