For this tutorial, you'll need a physical Android device (I haven't found a way to make the emulator proxy the computer's bluetooth signal), and I guess at least another bluetooth device in range for you to test things.
The source code for this Android Studio project is linked at the bottom.
Note: This tutorial DOES NOT explain how to pair, connect and send data to/from another Bluetooth device. I might make another post for those specific subjects.
Also, this tutorial is based on regular Bluetooth and not Bluetooth Low Energy (LE)
Turn on Bluetooth
While a user can turn Bluetooth on from multiple places or ways in an Android device, one of the issue I had on a client's project was the need for a unified way to show the user how to turn on Bluetooth in their android device. So instead of making X number of screenshots for "Getting started" guides depending on the OS level and the skin of the device, this approach is mainly universal (sure, the dialog might look slightly different on a different OS level)So, the plan is to have a button in the app that says something like "Enable Bluetooth" and prompts the user for permission to enable Bluetooth:
In here Bluetooth is disabled
After selecting the "Enable BT" in the app, the user gets prompt for permission to turn on Bluetooth.
Now Bluetooth is enabled on this device
The first thing you need to do is get the permissions in the Manifest.xml file to access Bluetooth:
<uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
Then in our activity, which in my case will be the MainActivity.java, we need to create a few variables:
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.widget.Toast; public class MainActivity { private String LOG_TAG; // Just for logging purposes. Could be anything. Set to app_name private int REQUEST_ENABLE_BT = 99; // Any positive integer should work. private BluetoothAdapter mBluetoothAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); LOG_TAG = getResources().getString(R.string.app_name); mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); } }
What did I do here?
- I create a constant String named LOG_TAG with just the name of the app to use it in LogCat
- We will be starting the Bluetooth adapter with an intent and we will need a number for the request in the Intent. Because of this I created a variable named REQUEST_ENABLE_BT with a number of 99. Any positive integer (unsigned int) should work.
- Create a new BluetoothAdapter variable, and instantiate it once the app starts to get the defaultAdapter() from the BluetoothAdapter.
private void enableBluetoothOnDevice() { if (mBluetoothAdapter == null) { Log.e(LOG_TAG, "This device does not have a bluetooth adapter"); finish(); // If the android device does not have bluetooth, just return and get out. // There's nothing the app can do in this case. Closing app. } // Check to see if bluetooth is enabled. Prompt to enable it if( !mBluetoothAdapter.isEnabled()) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); } }
In here:
- I check to see if the varible from the BluetoothAdapter.getDefaultAdapter() (so the mBluetoothAdapter variable) is null. If it is, it means this Android device does not have Bluetooth. In my case I'm logging this as an error and I'm closing the app. There's no point on doing a tutorial on Bluetooth using a device without Bluetooth.
- If there is a Bluetooth adapter, I check if it's not enabled (!mBluetoothAdapter.isEnabled()). If the device is not enabled, we need to turn it on.
- Create a new Intent that calles the BluetoothAdapter.ACTION_REQUEST_ENABLE intent, start it with the arbitrary variable of 99 created in the previous step.
But, umm.....how do we know if the user says no?
Since we started the Intent to start turn on Bluetooth with the "startActivityForResult", we can then get a result back using the "onActivityResult" method, like this:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_ENABLE_BT) { if (resultCode == 0) { // If the resultCode is 0, the user selected "No" when prompt to // allow the app to enable bluetooth. // You may want to display a dialog explaining what would happen if // the user doesn't enable bluetooth. Toast.makeText(this, "The user decided to deny bluetooth access", Toast.LENGTH_LONG).show(); } else Log.i(LOG_TAG, "User allowed bluetooth access!"); } }
Since this is a default Android method I'm overriding it. I then check if the requestCode that comes back is the same 99 I created, and if it is I know this answer is coming from the user prompt for Bluetooth I just created.
If the resultCode of this request is 0, it means the user selected no. At this point its up to you what to do but in my case I'm giving a Toast dialog blaming the user for pushing the wrong button.
Any other resultCode means the user accepted the promp.
And...we're done with the prompt to turn on Bluetooth!
Moving on to the next portion of this tutorial.
Show list of already paired (bonded) Bluetooth devices
Pairing with a device is usually a one time thing, but connecting with already paired devices is a recurring thing.Luckily for us the OS keeps a list (a Set technically...) of all of the Bluetooth devices this Android device has already paired with, and some information out of them. In this portion we're going to show a list of already paired devices and what information the OS is giving us:
Note: different devices will show different information, so don't worry much about null values at this point.
What you see above is what my Note 4 device has been paired with. The first is a Nexus 7 tablet, and the second one is a music player. The information displayed here is whatever the OS already knows of the devices, which is not a lot.
In my app I have a second button for displaying already paired devices, which calls a local method named "getArrayOfAlreadyPairedBluetoothDevices()", which has this block of code:
private ArrayListgetArrayOfAlreadyPairedBluetoothDevices() { ArrayList <BluetoothObject> arrayOfAlreadyPairedBTDevices = null; // Query paired devices Set <BluetoothObject> pairedDevices = mBluetoothAdapter.getBondedDevices(); // If there are any paired devices if (pairedDevices.size() > 0) { arrayOfAlreadyPairedBTDevices = new ArrayList<BluetoothObject> (); // Loop through paired devices for (BluetoothDevice device : pairedDevices) { // Create the device object and add it to the arrayList of devices BluetoothObject bluetoothObject = new BluetoothObject(); bluetoothObject.setBluetooth_name(device.getName()); bluetoothObject.setBluetooth_address(device.getAddress()); bluetoothObject.setBluetooth_state(device.getBondState()); bluetoothObject.setBluetooth_type(device.getType()); // requires API 18 or higher bluetoothObject.setBluetooth_uuids(device.getUuids()); arrayOfAlreadyPairedBTDevices.add(bluetoothObject); } } return arrayOfAlreadyPairedBTDevices; }
Let's break this down:
- I created a new BluetoothObject class to create an object that I can pass around with data
- I also know my final product is going to be a list of BluetoothObjects, so I need to make this method return an ArrayList of BluetoothObjects
- I then ask the mBluetoothAdapter to give me the set of already bonded devices (mBluetoothAdapter.getBondedDevices())
- I check to see if the set contains at least 1 element.
- I then iterate the set of elements, extract each BluetoothDevice (OS provided type), and then set the values of my custom BluetoothObject with the values of the BluetoothDevice
- Finally I add my custom BluetoothDevice to a new ArrayList I created, and I return the ArrayList
Again, this is super easy.
Finally we will scan for new Bluetooth devices in range.
Scan for Bluetooth devices in range
Out of the whole project this is the most complicated section, but even then its super easy as well.There are a few things you need to know before we start this section:
- You can only scan for other Bluetooth devices that are already set to be discoverable.
- Scanning for Bluetooth devices is Asynchronous, which means the OS will do this in the background at its own speed and rate
- We can only scan for X amount of time (currently 12 seconds) and then the discovery ends. Restarting the discovery usually works but if you restart it many times in a row without a break, the OS returns weird Bluetooth devices. This is the same on the OS' settings app
- You're on charge of stopping/canceling the discovery of Bluetooth devices
We click on the "Scan for Bluetooth devices" and in this case I decided to start a new Activity to do this process.
Here's the code of this new "FoundBTDevices.java" class:
private void displayListOfFoundDevices() { arrayOfFoundBTDevices = new ArrayList<BluetoothObject>(); // start looking for bluetooth devices mBluetoothAdapter.startDiscovery(); // Discover new devices // Create a BroadcastReceiver for ACTION_FOUND final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); // When discovery finds a device if (BluetoothDevice.ACTION_FOUND.equals(action)) { // Get the bluetoothDevice object from the Intent BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); // Get the "RSSI" to get the signal strength as integer, // but should be displayed in "dBm" units int rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI,Short.MIN_VALUE); // Create the device object and add it to the arrayList of devices BluetoothObject bluetoothObject = new BluetoothObject(); bluetoothObject.setBluetooth_name(device.getName()); bluetoothObject.setBluetooth_address(device.getAddress()); bluetoothObject.setBluetooth_state(device.getBondState()); bluetoothObject.setBluetooth_type(device.getType()); // requires API 18 or higher bluetoothObject.setBluetooth_uuids(device.getUuids()); bluetoothObject.setBluetooth_rssi(rssi); arrayOfFoundBTDevices.add(bluetoothObject); // 1. Pass context and data to the custom adapter FoundBTDevicesAdapter adapter = new FoundBTDevicesAdapter(getApplicationContext(), arrayOfFoundBTDevices); // 2. setListAdapter setListAdapter(adapter); } } }; // Register the BroadcastReceiver IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); registerReceiver(mReceiver, filter); }
This works by receiving data in a BroadcastReceiver, so let's break it down:
- Tell the BluetoothAdapter to start the discovery of new devices
- Create a Broadcast receiver and override the "onReceive" method
- The oddest part on this is the line of "BluetoothDevice.ACTION_FOUND.equals(Intent.getAction())" simply because this part you have to take it on faith as is "just do that and it'll work". Those kind of suck
- Just like in the Set of already paired devices, we need to get a BluetoothDevice object, and in this case we're getting it from the intent: "BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);"
- Create a new object, put it on an ArrayList and start the list from here by setting the list adapter to the adapter just created
- Finally, register the BroadcastReceiver
The reason for this is because this is an Asynchronous task. This means the discovery of the devices might take all 12 seconds (or more) so returning an ArrayList immediately won't really work.
Also, by passing this data directly this way the list can be updated automatically with new devices coming in.
Alternately you could display a "wait while we look for devices" message and then return an ArrayList after X amount of seconds...it's up to you.
And finally, we're on charge of canceling the discovery. This doesn't get done automatically so if you start it and 2 seconds later you decide to close your app, the OS will continue searching for devices and eating up battery.
So:
@Override protected void onPause() { super.onPause(); mBluetoothAdapter.cancelDiscovery(); }
I hope this has helped.
The source code for this project can be found here.
Eduardo.
FoundBTDevicesAdapter adapter = new FoundBTDevicesAdapter(getApplicationContext(), arrayOfFoundBTDevices);
ReplyDeleteis this a object of class ?
and what are methods in it ?
Can we keep the process going on after discovery done!
ReplyDeletenice tutorial. explanation is really good
ReplyDeleteI found your this post while searching for information about blog-related research ... It's a good post .. keep posting and updating information. best-bluetooth-transmitters
ReplyDeleteA biometric fingerprint scanner consists of a sensor that scans each finger before sending that image for entry into the computer or for comparison to a ready database. Since each human finger consists of ridges and valleys that make up a unique pattern specific only to that human being, there are no chances of any errors. barcode scanning devices
ReplyDeleteI followed your tutorial to a T and my application continually crashes while trying to scan for paired devices
ReplyDeleteThanks for sharing the useful blog about the Source Code for Scanning the Bluetooth Devices in Android.
ReplyDeleteMobile Application Development Company in Coimbatore
Really I like your Blog! Thanks to Admin for Sharing the above information about Bluetooth Technology. I bookmarked this link for Feature reference. Keep sharing such good Articles. Addition to your Story here I am Contributing one more Similar Story What is the Need for a Bluetooth Device on Wheels?.
ReplyDeleteI would like to thank you for the efforts you have made in writing this article, Its good and Informative.
ReplyDeleteandroid online training
why i don't get value of scan devices? they give me the error :- Activity has leaked IntentReceiver that was originally registered here. Are you missing a call to unregisterReceiver()?
ReplyDeleteso plz tell me what i do?
Same with me am facing same problem
DeleteHi,
ReplyDeleteI tested this tutorial everything works normally, activation of bluetooth, display of devices already connected, but with bluetooth scan nothing is displayed unfortunately, I do not know what is the cause. Please, please help me
same here... cant scan for any bluetooth devices..
DeleteHello,
ReplyDeleteHow to Scanning for Bluetooth devices when app is closed ? It must scan in background when app is closed by user.
This comment has been removed by the author.
DeleteThe code doesnt show discoverable devices .it only display paired devices
ReplyDelete