An easy to use bluetooth library for RFCOMM and OBEX on Android. Powered by Rx!
- Find bluetooth devices
- Send & listen for RFCOMM commands
- OBEX file transfer
These slides where part of my Bachelors thesis, have a look at them if you want.
Search for specific bluetooth devices
finder
.search(checkPaired = true, indefinite = true)
.distinct()
.filter { it.name?.contains("your-device-name", ignoreCase = true) ?: false }
.subscribeBy(
onNext = { Log.d(TAG, "Found device: ${it.address}") },
onError = { Log.e(TAG, "Error occurred ${it.message}") },
onComplete = { Log.d(TAG, "Completed search") }
)
Send files via OBEX
rxOBEX
.putFile(file, "remote/directory")
.subscribeBy(
onComplete = { Log.d(TAG, "Succesfully sent a testfile to ${device.address}") },
onError = { Log.e(TAG, "Error occurred ${it.message}") }
)
Send RFCOMM commands
rxSpp
.send("your-command")
.subscribeBy(
onComplete = { Log.d(TAG, "Succesfully sent $command to ${device.address}") },
onError = { Log.e(TAG, "Error occurred ${it.message}") }
)
- Add JitPack to your root build.gradle
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
- Add Funker to your project level build.gradle
dependencies {
implementation 'com.github.ddibiasi:Funker:0.0.x'
}
implementation 'io.reactivex.rxjava2:rxjava:2.x.x'
implementation 'io.reactivex.rxjava2:rxandroid:2.x.x'
implementation("io.reactivex.rxjava2:rxkotlin:2.x.x")
- AutoDispose by Uber is recommended but not necessary
implementation 'com.uber.autodispose:autodispose:1.x.x'
implementation 'com.uber.autodispose:autodispose-lifecycle:1.x.x'
implementation 'com.uber.autodispose:autodispose-android:1.x.x'
implementation 'com.uber.autodispose:autodispose-android-archcomponents:1.x.x'
implementation 'com.uber.autodispose:autodispose-rxlifecycle:1.x.x'
The RFCOMM package wraps everything neccessary to communicate via RFCOMM in a user friendly way.
Connect to a device
val rxSpp = RxSpp(device)
rxSpp
.connect()
.subscribeBy(
onError = { Log.e(TAG, "Received an error", it) },
onComplete = {Log.d(TAG, "Connected to device")}
)
Read data from connected device
rxSpp
.read()
.retryConditional(
predicate = { it is IOException },
maxRetry = 5,
delayBeforeRetryInMillis = 100
)
.subscribeBy (
onNext = {
Log.d(TAG, "Received: $it")
},
onError = { Log.e(TAG, "Received an error", it) },
onComplete = { Log.d(TAG, "Read completed")}
)
Send data to a device
rxSpp
.send("light on")
.subscribeBy(
onError = { e ->
Log.e(TAG, "Received error!")
},
onComplete = {
Log.d(TAG, "Succesfully sent $command to device")
}
)
Send file to device
val rxOBEX = RxObex(device)
rxOBEX
.putFile("rubberduck.txt", "text/plain", "oh hi mark".toByteArray(), "example/directory") // Name of file, mimetype, bytes of file, directory
.subscribeBy(
onComplete = {
Log.d(TAG, "Succesfully sent a testfile to device")
},
onError = { e ->
Log.e(TAG, "Received error!")
}
)
Delete file from device
rxOBEX
.deleteFile("rubberduck.txt", "example/directory")
.subscribeBy(
onComplete = {
Log.d(TAG, "Succesfully deleted rubberduck.txt from device")
},
onError = { e ->
Log.e(TAG, "Received error!")
}
)
List files in given directory on device. The remote file structure gets automatically mapped to a FolderListing object.
rxOBEX
.listFiles("example/directory") // List files in /example/directory
.subscribeBy(
onSuccess = {
folderlisting ->
Log.d(TAG, "Retrieved folderlisting")
Log.d(TAG, folderlisting.toString())
},
onError = { e ->
Log.e(TAG, "Received error!")
}
)
The utils package includes multiple bluetooth related helper methods, to make your life as a developer easier.
If you want to search for bluetooth devies, you can use the BluetoothDeviceFinder.
val finder = BluetoothDeviceFinder(applicationContext)
finder
.search(checkPaired = true, indefinite = true)
.subscribeOn(Schedulers.io()) // run in background
.observeOn(Schedulers.io())
.distinct() // don't emit devices multiple times
.filter { // you can filter for specific things, like the name, bondstate, address, etc..
it.name?.contains("rubberduck", ignoreCase = true) ?: false // will only emit devices with the string "rubberduck" in them
}
.autoDisposable(scopeProvider) // See the AutoDispose GitHub page from Uber
.subscribeBy(
onNext = { device ->
Log.d(TAG, "Found device: ${device.name}") // A device has been found
},
onError = {
Log.e(TAG, "Error occoured")
it.printStackTrace()
},
onComplete = { Log.d(TAG, "Completed search") }
)
Check if bluetooth is enabled
FunkerUtils.isBluetoothEnabled()
If you want to completely remove the bond to a bluetooth device
var device: BluetoothDevice = ..
device.removeBond()
...
If you want to retry (resend) a specific command, you can use retryConditional. The following example retries reading 5 times, when an IOException occours and waits 100 millis before restarting.
rxSpp
.read()
.retryConditional(
predicate = { it is IOException },
maxRetry = 5,
delayBeforeRetryInMillis = 100
)
....