This is the second part of the Bluetooth series tutorials. We continue where we left of from the first part, Turn Bluetooth service on the Android device ON and OFF
After thats done, first thing that we should do is adding a SCAN button in the layout and in the MainActivity. We are going to use simple button for this example and bind that button with our favorite library ButterKnife with @OnClick annotation.
Here is the layout file upgradet with the new SCAN button and Recycler View element:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:id="@+id/title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:layout_marginTop="20dp" android:text="@string/bluetooth_example" android:textColor="@color/black" android:textSize="20sp" /> <Switch android:id="@+id/switchButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/title" android:layout_centerHorizontal="true" android:layout_marginTop="10dp" android:text="@string/bluetooth" android:textSize="20sp" /> <Button android:id="@+id/scan" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/switchButton" android:layout_centerHorizontal="true" android:layout_marginTop="10dp" android:text="@string/scan" android:textSize="18sp" /> <android.support.v7.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@id/scan" android:layout_marginTop="10dp"> </android.support.v7.widget.RecyclerView> </RelativeLayout> |
And here is the button in our MainActivity:
1 2 3 4 |
@OnClick(R.id.scan) public void onScan() { BluetoothManager.scan(bluetoothAdapter, MainActivity.this); } |
As you can see we use a new static method declared in our Bluetooth Manager class called scan that receive BluetoothAdapter and Context as arguments. The bluetoothAdapter is instantiated in our onCreate method, explained in part 1.
Here is the new .scan() method in BluetoothManager class:
1 2 3 4 5 |
public static void scan(BluetoothAdapter bluetoothAdapter, Activity activity) { bluetoothAdapter.startDiscovery(); IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); activity.registerReceiver(mReceiver, filter); } |
When this method is called, first we are starting the discovery of detectable bluetooth devices. The discovery process usually involves an inquiry scan of about 12 seconds, followed by a page scan of each new device to retrieve its Bluetooth name. This is an asynchronous call, it will return immediately.
Next we add IntentFilter that is translated in XML form for registration of our BroadcastReceiver to listen to newly discovered bluetooth devices. After that we register an receiver to our activity with .registerReceiver method, passing, previously initialized BluetoothBroadcastReceiver in our BluetoothManager class:
1 |
private static BluetoothBroadcastReceiver mReceiver = new BluetoothBroadcastReceiver(); |
As that been said, we move on to describe how our BluetoothBroadcastReceiver works:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// Create a BroadcastReceiver for ACTION_FOUND. public class BluetoothBroadcastReceiver extends BroadcastReceiver { private static String TAG = "DEVICE"; @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (BluetoothDevice.ACTION_FOUND.equals(action)) { // Discovery has found a device. Get the BluetoothDevice // object and its info from the Intent. BluetoothDevice bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); String deviceName = bluetoothDevice.getName(); // Device name String deviceHardwareAddress = bluetoothDevice.getAddress(); // MAC address Device device = new Device(deviceName, deviceHardwareAddress); EventBus.getDefault().post(new DeviceEventHandler(device)); Toast.makeText(context, deviceName + " " + deviceHardwareAddress, Toast.LENGTH_SHORT).show(); } } } |
The Broadcast Receiver component is explained in this post before: Third Android Component – Broadcast Receiver. After we check for action found with our registered intent filter, we are getting the nearby blluetooth devices. Adding only the name and MAC address in our created Device model, for trigering an event so that our MainActivity can listen when some device is discovered and add that device in list.
Here is the Device model. In here we override equals() and hashCode() so we can compare when device is turned ON and OFF and ON again, so it wont appear twice or more times in our discovered bluetooth devices:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public class Device { public final String deviceName; public final String deviceMAC; public Device(String deviceName, String deviceMAC) { this.deviceName = deviceName; this.deviceMAC = deviceMAC; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Device device = (Device) o; return Objects.equals(deviceName, device.deviceName) && Objects.equals(deviceMAC, device.deviceMAC); } @Override public int hashCode() { return Objects.hash(deviceName, deviceMAC); } } |
And the EventHandler class for listening when device is discovered and trasfer device`s info to our MainClass (NOTE: EventBus is also a useful Android library):
1 2 3 4 5 6 7 8 |
public class DeviceEventHandler { public final Device device; public DeviceEventHandler(Device device) { this.device = device; } } |
And here how is handled the event in our MainClass with EventBus registering and unregistering :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@Subscribe public void onDeviceFindEvent(DeviceEventHandler deviceEventHandler) { deviceListAdapter.addItem(deviceEventHandler.device); } @Override public void onStart() { super.onStart(); EventBus.getDefault().register(this); } @Override public void onStop() { super.onStop(); EventBus.getDefault().unregister(this); } |
Here is explanation how to create Recycler View and Adapter with Model so you can create and diplay the List of discovered devices.