Blog Infos
Author
Published
Topics
Published
Topics
Posted By: Fabio Chiarani
Why? and How?
1) Define actions

s magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc,

class BluetoothUtils {
companion object {
val ACTION_DISCOVERY_STARTED = "ACTION_DISCOVERY_STARTED"
val ACTION_DISCOVERY_STOPPED = "ACTION_DISCOVERY_STOPPED"
val ACTION_DEVICE_FOUND = "ACTION_DEVICE_FOUND"
val ACTION_DEVICE_CONNECTED = "ACTION_DEVICE_CONNECTED"
val ACTION_DEVICE_DISCONNECTED = "ACTION_DEVICE_DISCONNECTED"
val ACTION_MESSAGE_RECEIVED = "ACTION_MESSAGE_RECEIVED"
val ACTION_MESSAGE_SENT = "ACTION_MESSAGE_SENT"
val ACTION_CONNECTION_ERROR = "ACTION_CONNECTION_ERROR"
val EXTRA_DEVICE = "EXTRA_DEVICE"
val EXTRA_MESSAGE = "EXTRA_MESSAGE"
}
}
2) Define events-callback functions
interface IBluetoothSDKListener {
/**
* from action BluetoothUtils.ACTION_DISCOVERY_STARTED
*/
fun onDiscoveryStarted()
/**
* from action BluetoothUtils.ACTION_DISCOVERY_STOPPED
*/
fun onDiscoveryStopped()
/**
* from action BluetoothUtils.ACTION_DEVICE_FOUND
*/
fun onDeviceDiscovered(device: BluetoothDevice?)
/**
* from action BluetoothUtils.ACTION_DEVICE_CONNECTED
*/
fun onDeviceConnected(device: BluetoothDevice?)
/**
* from action BluetoothUtils.ACTION_MESSAGE_RECEIVED
*/
fun onMessageReceived(device: BluetoothDevice?, message: String?)
/**
* from action BluetoothUtils.ACTION_MESSAGE_SENT
*/
fun onMessageSent(device: BluetoothDevice?)
/**
* from action BluetoothUtils.ACTION_CONNECTION_ERROR
*/
fun onError(message: String?)
/**
* from action BluetoothUtils.ACTION_DEVICE_DISCONNECTED
*/
fun onDeviceDisconnected()
}
3) Define BroadcastReceiver
class BluetoothSDKListenerHelper {
companion object {
private var mBluetoothSDKBroadcastReceiver: BluetoothSDKBroadcastReceiver? = null
class BluetoothSDKBroadcastReceiver : BroadcastReceiver() {
private var mGlobalListener: IBluetoothSDKListener? = null
public fun setBluetoothSDKListener(listener: IBluetoothSDKListener) {
mGlobalListener = listener
}
public fun removeBluetoothSDKListener(listener: IBluetoothSDKListener): Boolean {
if (mGlobalListener == listener) {
mGlobalListener = null
}
return mGlobalListener == null
}
override fun onReceive(context: Context?, intent: Intent?) {
val device =
intent!!.getParcelableExtra<BluetoothDevice>(BluetoothUtils.EXTRA_DEVICE)
val message = intent.getStringExtra(BluetoothUtils.EXTRA_MESSAGE)
when (intent.action) {
BluetoothUtils.ACTION_DEVICE_FOUND -> {
mGlobalListener!!.onDeviceDiscovered(device)
}
BluetoothUtils.ACTION_DISCOVERY_STARTED -> {
mGlobalListener!!.onDiscoveryStarted()
}
BluetoothUtils.ACTION_DISCOVERY_STOPPED -> {
mGlobalListener!!.onDiscoveryStopped()
}
BluetoothUtils.ACTION_DEVICE_CONNECTED -> {
mGlobalListener!!.onDeviceConnected(device)
}
BluetoothUtils.ACTION_MESSAGE_RECEIVED -> {
mGlobalListener!!.onMessageReceived(device, message)
}
BluetoothUtils.ACTION_MESSAGE_SENT -> {
mGlobalListener!!.onMessageSent(device)
}
BluetoothUtils.ACTION_CONNECTION_ERROR -> {
mGlobalListener!!.onError(message)
}
BluetoothUtils.ACTION_DEVICE_DISCONNECTED -> {
mGlobalListener!!.onDeviceDisconnected()
}
}
}
}
public fun registerBluetoothSDKListener(
context: Context?,
listener: IBluetoothSDKListener
) {
if (mBluetoothSDKBroadcastReceiver == null) {
mBluetoothSDKBroadcastReceiver = BluetoothSDKBroadcastReceiver()
val intentFilter = IntentFilter().also {
it.addAction(BluetoothUtils.ACTION_DEVICE_FOUND)
it.addAction(BluetoothUtils.ACTION_DISCOVERY_STARTED)
it.addAction(BluetoothUtils.ACTION_DISCOVERY_STOPPED)
it.addAction(BluetoothUtils.ACTION_DEVICE_CONNECTED)
it.addAction(BluetoothUtils.ACTION_MESSAGE_RECEIVED)
it.addAction(BluetoothUtils.ACTION_MESSAGE_SENT)
it.addAction(BluetoothUtils.ACTION_CONNECTION_ERROR)
it.addAction(BluetoothUtils.ACTION_DEVICE_DISCONNECTED)
}
LocalBroadcastManager.getInstance(context!!).registerReceiver(
mBluetoothSDKBroadcastReceiver!!, intentFilter
)
}
mBluetoothSDKBroadcastReceiver!!.setBluetoothSDKListener(listener)
}
public fun unregisterBluetoothSDKListener(
context: Context?,
listener: IBluetoothSDKListener
) {
if (mBluetoothSDKBroadcastReceiver != null) {
val empty = mBluetoothSDKBroadcastReceiver!!.removeBluetoothSDKListener(listener)
if (empty) {
LocalBroadcastManager.getInstance(context!!)
.unregisterReceiver(mBluetoothSDKBroadcastReceiver!!)
mBluetoothSDKBroadcastReceiver = null
}
}
}
}
}

In the activity, or fragment we will implement our IBluetoothSDKListener, which we will register through the two functions registerBluetoothSDKListner() and unregisterBluetoothSDKListner() . For example:

class CoolFragment() : BottomSheetDialogFragment() {
private lateinit var mService: BluetoothSDKService
private lateinit var binding: FragmentPopupDiscoveredLabelerDeviceBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_popup_discovered_labeler_device, container,false)
binding = FragmentPopupDiscoveredLabelerDeviceBinding.bind(view)
bindBluetoothService()
// Register Listener
BluetoothSDKListenerHelper.registerBluetoothSDKListener(requireContext(), mBluetoothListener)
return view
}
/**
* Bind Bluetooth Service
*/
private fun bindBluetoothService() {
// Bind to LocalService
Intent(
requireActivity().applicationContext,
BluetoothSDKService::class.java
).also { intent ->
requireActivity().applicationContext.bindService(
intent,
connection,
Context.BIND_AUTO_CREATE
)
}
}
/**
* Handle service connection
*/
private val connection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
val binder = service as BluetoothSDKService.LocalBinder
mService = binder.getService()
}
override fun onServiceDisconnected(arg0: ComponentName) {
}
}
private val mBluetoothListener: IBluetoothSDKListener = object : IBluetoothSDKListener {
override fun onDiscoveryStarted() {
}
override fun onDiscoveryStopped() {
}
override fun onDeviceDiscovered(device: BluetoothDevice?) {
}
override fun onDeviceConnected(device: BluetoothDevice?) {
// Do stuff when is connected
}
override fun onMessageReceived(device: BluetoothDevice?, message: String?) {
}
override fun onMessageSent(device: BluetoothDevice?) {
}
override fun onError(message: String?) {
}
}
override fun onDestroy() {
super.onDestroy()
// Unregister Listener
BluetoothSDKListenerHelper.unregisterBluetoothSDKListener(requireContext(), mBluetoothListener)
}
}
view raw CoolFragment.kt hosted with ❤ by GitHub
4) Define Bluetooth Service
class BluetoothSDKService : Service() {
// Service Binder
private val binder = LocalBinder()
// Bluetooth stuff
private lateinit var bluetoothAdapter: BluetoothAdapter
private lateinit var pairedDevices: MutableSet<BluetoothDevice>
private var connectedDevice: BluetoothDevice? = null
private val MY_UUID = "..."
private val RESULT_INTENT = 15
// Bluetooth connections
private var connectThread: ConnectThread? = null
private var connectedThread: ConnectedThread? = null
private var mAcceptThread: AcceptThread? = null
// Invoked only first time
override fun onCreate() {
super.onCreate()
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
}
// Invoked every service star
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return START_STICKY
}
/**
* Class used for the client Binder.
*/
inner class LocalBinder : Binder() {
/*
Function that can be called from Activity or Fragment
*/
}
/**
* Broadcast Receiver for catching ACTION_FOUND aka new device discovered
*/
private val discoveryBroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
/*
Our broadcast receiver for manage Bluetooth actions
*/
}
}
private inner class AcceptThread : Thread() {
// Body
}
private inner class ConnectThread(device: BluetoothDevice) : Thread() {
// Body
}
@Synchronized
private fun startConnectedThread(
bluetoothSocket: BluetoothSocket?,
) {
connectedThread = ConnectedThread(bluetoothSocket!!)
connectedThread!!.start()
}
private inner class ConnectedThread(private val mmSocket: BluetoothSocket) : Thread() {
// Body
}
override fun onDestroy() {
super.onDestroy()
try {
unregisterReceiver(discoveryBroadcastReceiver)
} catch (e: Exception) {
// already unregistered
}
}
override fun onBind(intent: Intent?): IBinder? {
return binder
}
private fun pushBroadcastMessage(action: String, device: BluetoothDevice?, message: String?) {
val intent = Intent(action)
if (device != null) {
intent.putExtra(BluetoothUtils.EXTRA_DEVICE, device)
}
if (message != null) {
intent.putExtra(BluetoothUtils.EXTRA_MESSAGE, message)
}
LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(intent)
}
}

Job Offers

Job Offers


    Senior Android Software Engineer (f/m/d)

    Paradox Cat GmbH
    Munich
    • Full Time
    apply now

    Kotlin Multiplatform Mobile Developer

    Touchlab
    Remote
    • Full Time
    apply now

    Android Software Engineer (f/m/d)

    Paradox Cat GmbH
    Munich
    • Full Time
    apply now
Load more listings

OUR VIDEO RECOMMENDATION

Jobs

/**
* Class used for the client Binder.
*/
inner class LocalBinder : Binder() {
/**
* Enable the discovery, registering a broadcastreceiver {@link discoveryBroadcastReceiver}
* The discovery filter by LABELER_SERVER_TOKEN_NAME
*/
public fun startDiscovery(context: Context) {
val filter = IntentFilter(BluetoothDevice.ACTION_FOUND)
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED)
registerReceiver(discoveryBroadcastReceiver, filter)
bluetoothAdapter.startDiscovery()
pushBroadcastMessage(BluetoothUtils.ACTION_DISCOVERY_STARTED, null, null)
}
/**
* stop discovery
*/
public fun stopDiscovery() {
bluetoothAdapter.cancelDiscovery()
pushBroadcastMessage(BluetoothUtils.ACTION_DISCOVERY_STOPPED, null, null)
}
// other stuff
}

Then, within the threads that manage the sockets, you can use the pushBroadcastMessage() function to generate events and add a payload such as the remote device and a message. For example:

private inner class ConnectedThread(private val mmSocket: BluetoothSocket) : Thread() {
private val mmInStream: InputStream = mmSocket.inputStream
private val mmOutStream: OutputStream = mmSocket.outputStream
private val mmBuffer: ByteArray = ByteArray(1024) // mmBuffer store for the stream
override fun run() {
var numBytes: Int // bytes returned from read()
// Keep listening to the InputStream until an exception occurs.
while (true) {
// Read from the InputStream.
numBytes = try {
mmInStream.read(mmBuffer)
} catch (e: IOException) {
pushBroadcastMessage(
BluetoothUtils.ACTION_CONNECTION_ERROR,
null,
"Input stream was disconnected"
)
break
}
val message = String(mmBuffer, 0, numBytes)
// Send to broadcast the message
pushBroadcastMessage(
BluetoothUtils.ACTION_MESSAGE_RECEIVED,
mmSocket.remoteDevice,
message
)
}
}
// Call this from the main activity to send data to the remote device.
fun write(bytes: ByteArray) {
try {
mmOutStream.write(bytes)
// Send to broadcast the message
pushBroadcastMessage(
BluetoothUtils.ACTION_MESSAGE_SENT,
mmSocket.remoteDevice,
null
)
} catch (e: IOException) {
pushBroadcastMessage(
BluetoothUtils.ACTION_CONNECTION_ERROR,
null,
"Error occurred when sending data"
)
return
}
}
// Call this method from the main activity to shut down the connection.
fun cancel() {
try {
mmSocket.close()
} catch (e: IOException) {
pushBroadcastMessage(
BluetoothUtils.ACTION_CONNECTION_ERROR,
null,
"Could not close the connect socket"
)
}
}
}
And you are done!

Conclusion

My advice is to always follow the official guidelines and clean code writing guidelines.

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
It’s one of the common UX across apps to provide swipe to dismiss so…
READ MORE
blog
In this part of our series on introducing Jetpack Compose into an existing project,…
READ MORE
blog
This is the second article in an article series that will discuss the dependency…
READ MORE
blog
Let’s suppose that for some reason we are interested in doing some tests with…
READ MORE

1 Comment. Leave new

  • Jan
    16.05.2022 13:40

    Thanks for this tutorial. What I’m trying to do is the following: an app should provide a list of Bluetooth devices and if I select 1 then it should send a string message about it to the selected device. I now have this tutorial in my project but nothing happens can you help me on what to do

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

Menu