Giter Site home page Giter Site logo

vincentmasselis / rxbluetoothkotlin Goto Github PK

View Code? Open in Web Editor NEW
87.0 7.0 12.0 749 KB

Bluetooth low energy reactive framework for Android written in Kotlin

License: Other

Kotlin 100.00%
kotlin bluetooth-low-energy android rxjava3 rxjava rxjava-android bluetooth ble ble-api

rxbluetoothkotlin's Introduction

Build Status Maven Central

RxBluetoothKotlin

Android + BLE + Kotlin + RxJava3

Bluetooth Low Energy on Android made easy with RxJava.

Made with love at the Equisense HQ. This library is used in our Equisense app since years.

Looking for BLE with Coroutines instead of RxJava ? Take a look at the LouisCAD's implementation.

⚠️ Android 12 permissions changes ⚠️

Starting from Android API 31, the fine location permission is not required anymore, instead of this, you have to use the BLUETOOTH_CONNECT and BLUETOOTH_SCAN permissions when dealing the bluetooth low energy framework. Before upgrading targetSdkVersion to 31 in your app, check your requestPermission calls according to this new permission.

Because of this change, RxBluetoothKotlin was updated to fire the NeedBluetoothScanPermission and NeedBluetoothConnectPermission exceptions at the right moment if they're missing at the runtime. Theses exceptions are fired since the release 3.2.0.

Learn more

⚠️ Important notice about Maven Central release ⚠️

RxBluetoothKotlin is released on Maven Central since the version 3.1.0 you don't have to worry about this library when jCenter will shutdown ! Unfortunately, according to the Maven Central policies, I must update my package to match with the host domain I own. I only own masselis.com, so the package name RxBluetothKotlin were renamed from com.vincentmasselis.rxbluetoothkotlin to com.masselis.rxbluetoothkotlin, as consequence, you have to renamed EVERY import from rxbluetoothkotlin to the new package name.

TL/DR

// Check the github release section to find the latest available version
implementation 'com.masselis.rxbluetoothkotlin:rxbluetoothkotlin-core:<<latest_version>>'

Scan

val bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
bluetoothManager.rxScan()
  .subscribe { scanResult ->
  }

Connect

bluetoothDevice.connectRxGatt()
  .flatMapMaybe { gatt -> gatt.whenConnectionIsReady().map { gatt } }
  .subsribe { rxBluetoothGatt ->
  }

Read

rxBluetoothGatt.read(characteristic)
  .subscribe { byteArray ->
  }

Write

rxBluetoothGatt.write(characteristic, byteArray)
  .subscribe { _ ->
  }

Listen

rxBluetoothGatt.enableNotification(characteristic)
  .flatMapPublisher { listenChanges(characteristic) }
  .subscribe { byteArray ->
  }

Disconnect

rxBluetoothGatt.disconnect().subscribe()

Requirements

  • Min target API 18
  • When scanning with RxBluetoothKotlin, you have to grant theses runtime permissions:
    • From Android 6 to 9 inclusive: ACCESS_COARSE_LOCATION
    • From Android 10 to 11 inclusive: ACCESS_FINE_LOCATION
    • From Android 12: BLUETOOTH_SCAN
  • When connecting with RxBluetoothKotlin, you have to grant this runtime permission:
    • From Android 12: BLUETOOTH_CONNECT
  • A turned on bluetooth chip (context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager).adapter.isEnabled
  • You can add to your manifest <uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />

RxBluetoothKotlin declare the BLUETOOTH_SCAN permission into the AndroidManifest with the property usesPermissionFlags="neverForLocation". If you want to remove the usesPermissionFlags property, you have to add tools:remove="usesPermissionFlags" to your uses-permission node into your own AndroidManifest, if you want to update usesPermissionFlags by setting an other value you have to use tools:replace="usesPermissionFlags" instead.

Logging

When scanning with rxScan() or connecting with connectRxGatt(), you can set the logger parameter. By setting it, RxBluetoothKotlin will produce a log for every bluetooth input, output, starting scan, error thrown, etc.. I recommend to set it for debugging purposes and/or if you're not familiar with the Android BLE API. It could helps you a lot to understand what's going on between your app and the Bluetooth Low Energy device.

Error management

Interact with Bluetooth Low Energy devices on Android is hard. The BLE specs uses unfamiliar concepts, the BLE API from Android could fails at any moment and some exceptions are silent. Because of this, a basic method like write(BluetoothGattCharacteristic) could fails for 5 different reasons. It becomes harder if you are chaining this call with other calls, this sum up to a thrown exception when it's impossible to known which call fails and why.

For this reason, every public method from this library is documented with every exceptions which could be fired and most of the exception are unique. For example: write(BluetoothGattCharacteristic) could fire:

  • Unique CharacteristicWriteDeviceDisconnected if the device disconnects while writing
  • Unique CannotInitializeCharacteristicWrite if the Android API returned false when calling writeCharacteristic
  • Unique CharacteristicWriteFailed if the characteristic could not be written
  • BluetoothIsTurnedOff if bluetooth...... is turned off 🤷🏻‍♀️
  • BluetoothTimeout if writing takes more than 1 minute

All the 3 uniques exceptions are containing the required data to understand why it failed. For example CharacteristicWriteDeviceDisconnected has 5 fields, bluetoothDevice, status, service, characteristic and value, all of theses fields should helps you to understand what's going on.

Demo app

If you're not familiar with the Bluetooth Low Energy API or if you want to try this lib, you can build and run the demo app from this repo.

Decorator pattern

⚠ Before reading this part, you must know how a Decorator design pattern works and how to make a new one.

On Android, communicating with a Bluetooth device requires an instance of BluetoothGatt and an instance of BluetoothGattCallback. RxBluetoothKotlin wraps both of theses types into RxBluetoothGatt and RxBluetoothGatt.Callback types to add some reactive touch to the system objects. Both RxBluetoothGatt and RxBluetoothGatt.Callback are interfaces, calling connectRxGatt will return a default implementation for both of them but you are free to wrap the returned implementation by your own implementation to add you own behavior, you only have to follow the Decorator rules.

Decorate RxBluetoothGatt

The following diagram will show you which classes are used to create the decorator pattern:

UML Class diagram

As you can see, to create a decorator, you only have to subclass SimpleRxBluetoothGatt. If you want to decorate RxBluetoothGatt.Callback just subclass SimpleRxBluetoothGattCallback like you do with SimpleRxBluetoothGatt from the previous example.

When your decorators are written you can send them to RxBluetoothKotlin by setting the rxGattBuilder and rxCallbackBuilder parameters when calling connectRxGatt. Defaults implementation of RxBluetoothKotlin uses RxBluetoothGattImpl and RxBluetoothGattCallbackImpl, by using you own decorator you can change the way RxBluetoothKotlin is communicating with the Android SDK in order to match your own requirements.

Links

Report an issue by using github

Follow me on Twitter @VincentMsls

Discover our Equisense sensors

//TODO

  • Getting started

rxbluetoothkotlin's People

Contributors

guillaumehugot avatar vincentmasselis avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

rxbluetoothkotlin's Issues

crash com.vincentmasselis.rxbluetoothkotlin.DescriptorNotFound

##Code:

connectDisposable = connectState.filter { it is RXBleManager.ConnectStates.Connecting }
            .switchMapSingle {
                (connectState.value as? RXBleManager.ConnectStates.Connecting)?.device?.connectRxGatt(true)
            }
            .switchMapMaybe {
                    gatt -> gatt.whenConnectionIsReady().map { gatt }
            }
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(
                { gatt->
                    Toast.makeText(this, "Connected !", Toast.LENGTH_SHORT).show()
                    connectState.onNext(RXBleManager.ConnectStates.Connected(gatt))
                    Maybe
                        .defer {
                            if (gatt.source.services.isEmpty()) gatt.discoverServices()
                            else Maybe.just(gatt.source.services)
                        }.subscribe { serviceList ->
                            for (service in serviceList)
                            {
                                for (characteristic in service.characteristics){

                                    if (containProperty(BluetoothGattCharacteristic.PROPERTY_NOTIFY,characteristic.properties)){
                                        if (characteristic.descriptors.isEmpty()){
                                            BuglyLog.i(tag,"no characteristic.descriptors")
                                            continue
                                        }
                                        gatt.enableNotification(characteristic)
                                            .flatMapPublisher {
                                                gatt.listenChanges(it)
                                            }
                                            .subscribe ({ byteArray ->
                                                //BuglyLog.d(tag,byteArray.toString())
                                            },{//cash here
                                                BuglyLog.e(tag,it.message)
                                                it.printStackTrace()
                                            })
                                    }
                                }
                            }
                        }
                },{
                    val message =
                        when (it) {
                            is BluetoothIsTurnedOff -> "Bluetooth is turned off"
                            is DeviceDisconnected -> "Unable to connect to the device"
                            else -> "Error occurred: $it"
                        }
                    AlertDialog.Builder(this).setMessage(message).show()
//                    bluetoothAdapter.disable()
//                    Handler().postDelayed(Runnable { bluetoothAdapter.enable() },500)
                }
            )

##Log

1 io.reactivex.exceptions.OnErrorNotImplementedException:The exception was not handled due to missing onError handler in the subscribe() method call. Further reading: https://github.com/ReactiveX/RxJava/wiki/Error-Handling | DescriptorNotFound(device=C7:E4:E3:E2:E1:FD, characteristicUUID=00010203-0405-0607-0809-0a0b0c0d2b17, descriptorUUID=00002902-0000-1000-8000-00805f9b34fb)
2 io.reactivex.internal.functions.Functions$OnErrorMissingConsumer.accept(Functions.java:704)
3 ......
4 Caused by:
5 com.vincentmasselis.rxbluetoothkotlin.DescriptorNotFound:
6 com.vincentmasselis.rxbluetoothkotlin.RxBluetoothGattImpl$rxChangeNotification$3.apply(RxBluetoothGattImpl.kt:517)
7 com.vincentmasselis.rxbluetoothkotlin.RxBluetoothGattImpl$rxChangeNotification$3.apply(RxBluetoothGattImpl.kt:28)
8 io.reactivex.internal.operators.maybe.MaybeFlatten$FlatMapMaybeObserver.onSuccess(MaybeFlatten.java:88)
9 io.reactivex.internal.operators.observable.ObservableElementAtMaybe$ElementAtObserver.onNext(ObservableElementAtMaybe.java:82)
10 io.reactivex.internal.operators.observable.ObservableOnErrorNext$OnErrorNextObserver.onNext(ObservableOnErrorNext.java:68)
11 io.reactivex.internal.operators.observable.ObservableFlatMapSingle$FlatMapSingleObserver.innerSuccess(ObservableFlatMapSingle.java:151)
12 io.reactivex.internal.operators.observable.ObservableFlatMapSingle$FlatMapSingleObserver$InnerObserver.onSuccess(ObservableFlatMapSingle.java:284)
13 io.reactivex.internal.operators.single.SingleCreate$Emitter.onSuccess(SingleCreate.java:67)
14 com.vincentmasselis.rxbluetoothkotlin.RxBluetoothGattImpl$enqueue$1$1$singleToEnqueue$1.accept(RxBluetoothGattImpl.kt:203)
15 io.reactivex.internal.operators.single.SingleDoOnSuccess$DoOnSuccess.onSuccess(SingleDoOnSuccess.java:54)
16 io.reactivex.internal.operators.single.SingleTimeout$TimeoutMainObserver.onSuccess(SingleTimeout.java:141)
17 io.reactivex.internal.operators.single.SingleSubscribeOn$SubscribeOnObserver.onSuccess(SingleSubscribeOn.java:68)
18 io.reactivex.internal.operators.single.SingleCreate$Emitter.onSuccess(SingleCreate.java:67)
19 com.vincentmasselis.rxbluetoothkotlin.RxBluetoothGattImpl$rxChangeNotification$1.subscribe(RxBluetoothGattImpl.kt:511)
20 io.reactivex.internal.operators.single.SingleCreate.subscribeActual(SingleCreate.java:39)
21 io.reactivex.Single.subscribe(Single.java:3603)
22 io.reactivex.internal.operators.single.SingleSubscribeOn$SubscribeOnObserver.run(SingleSubscribeOn.java:89)
23 io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:124)
24 android.os.Handler.handleCallback(Handler.java:808)
25 android.os.Handler.dispatchMessage(Handler.java:101)
26 android.os.Looper.loop(Looper.java:166)
27 android.app.ActivityThread.main(ActivityThread.java:7529)
28 java.lang.reflect.Method.invoke(Native Method)
29 com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)
30 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)

图片

Usage of `value class`

Some of the returned data cloud be transformed from data class to value class introduced in Kotlin 1.5, it will increase the global performance of the library when processing input and outputs of the android framework.

We have an exception when running demo-app from this repo.

\RxBluetoothKotlin-maven-central-migration\RxBluetoothKotlin-maven-central-migration\demo-app\app\src\main\java\com\vincentmasselis\demoapp\DeviceActivity.kt: (50, 14): Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public fun Disposable.disposeOnState(exceptedState: ActivityState, activityToMonitor: Activity): Disposable defined in com.vincentmasselis.rxuikotlin
public fun Disposable.disposeOnState(exceptedState: FragmentState, fragmentToMonitor: Fragment): Disposable defined in com.vincentmasselis.rxuikotlin
public fun Disposable.disposeOnState(exceptedState: ViewHolderState, viewHolderToMonitor: LifecycleViewHolder): Disposable defined in com.vincentmasselis.rxuikotlin

Missing LICENSE

I know it is listed as TODO but in order to be used, a library needs a license file / description.
Please add one to your project (in the README or even better, in a specific LICENSE file)

BLE Enable notifications and read characteristic notification (listen change)

Hi

I'm trying to establish BLE communication with an esp32. I succeed to do it in java but I'm trying to do it in Kotlin now. I run the demo app but it seems the Notifications are not used in the example. However I found a file named "ListenChangeTest.kt" I think the solution is in this file but I don't understand it. Can you explain how to use notifications in your example please ?

Type mismatch: inferred type is Application but Boolean was expected

I got an issue in DeviceActivity of demoApp

.switchMapSingle { device.connectRxGatt(applicationContext as Application) }
Type mismatch: inferred type is Application but Boolean was expected

Also ScanActivty
.rxScan(this, flushEvery = 1L to TimeUnit.SECONDS)
Type mismatch: inferred type is ScanActivity but Pair<List, ScanSettings>? was expected

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.