Lecture 21 Bluetooth
In this chapter you will learn about some of the pieces for creating a connection between two co-located devices using Bluetooth. This will let you gain some familiarity with the Bluetooth API, as well as further practice working with Intents.
The code for this tutorial can be found at https://github.com/info448-s17/lab-bluetooth.
This tutorial involves filling in the remaining pieces from a Sample Project provided by Google. Google includes lots of samples demonstrating how to use particular pieces of functionality; reading and adapting the provided projects is a great way to learn new skills. There are a lot of comments, though that sometimes makes it hard to follow all the pieces. Read carefully!
- Also be sure to open the API documentation for reference!
The emulator doesn’t support Bluetooth, so you will need to run this project on a physical device.
Your task is to fill in the missing pieces of code, following the instructions below. I’ve marked each location with a TODO comment, which should show up in blue in Android Studio.
Start by reading through The Basics to get a sense for what classes will be used and what their roles are. You only need to focus on the first 4:
BluetoothAdapter,BluetoothDevice,BluetoothSocket, andBluetoothServerSocket(the rest are for other kinds of Bluetooth connections, like audio transfer and stuff). You don’t need to know all the methods or details of these classes, but should be familiar with their general, one-sentence purposes!You’ll need to request permission to use Bluetooth. Add the appropriate
<uses-permission>attributes: one forBLUETOOTH(for communication; included) and one forBLUETOOTH_ADMIN(to “discover” devices and make connections).The main UI is defined in the
BluetoothChatFragmentclass, which is a Fragment that holds the chat system. Start by filling in theonCreate()callback by fetching the default Bluetooth adapter and saving it to an instance variable (mBluetoothAdapter). If the adapter doesn’t exist (isnull), you shouldToasta message that Bluetooth isn’t available (using theActivity'sApplication Contextso that the Toast lasts), and then callfinish()on the Fragment’s Activity (to close the application).You’ll want your app to make sure that the user has Bluetooth turned on. In the Fragment’s
onCreate(), check whether the theBluetoothAdapterisenabled. If not, you’ll want to prompt the user to enable it, such as by launching the “Settings” app. Create an Implicit Intent for the actionBluetoothAdapter.ACTION_REQUEST_ENABLE, and send this Intent for a result (with the result code ofREQUEST_ENABLE_BT). Look in the Fragment’sonActivityResult()method to see what happens when we get a response back!- The
BluetoothChatService(stored in the instance variablemChatService) is an object representing a “background service”—think an AsyncTask but with a much longer lifespan. This particular service handles sending bytes of data back and forth over Bluetooth. We’ll talk aboutServicesmore later in the course.
- The
In order for a device to connect to yours over Bluetooth, your device will need to be discoverable: effectively, it has to respond to public queries about its existence (sort of like having your instant messaging status as “Online/Available”). In the Fragment’s
ensureDiscoverable()helper method, check if the device is currently discoverable by callinggetScanMode()on theBluetoothAdapter; it should return a value ofBluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE.- If this IS NOT the case, then you should send another Implicit Intent to handle the
BluetoothAdapter.ACTION_REQUEST_DISCOVERABLEaction.
This intent should include (put) an extra that has the key
BluetoothAdapter.EXTRA_DISCOVERABLE_DURATIONand a value of300, so that we are in “discoverable” mode for 300 seconds.Note that this intent does NOT need to be started for a result!
- If this IS NOT the case, then you should send another Implicit Intent to handle the
The discovery of devices is controlled by the
DeviceListActivityActivity. This is a separate Activity that will actually appear as a popup dialog (though it doesn’t useDialogFragment; it just “themes” the Activity as a dialog in theManifest). The Activity’sonCreate()does a lot of UI work (including setting up an Adapter!), but it also needs to set up aBroadcastReceiverto listen for events like when devices are found. (This is the equivalent of declaring a<receiver>and<intent-filter>in theManifest, but we need to do it in Java since the Receiver isn’t a separate class and since we want to do it dynamically).First instantiate a new
IntentFilterobject (giving it theBluetoothDevice.ACTION_FOUNDaction).Then use the
registerReceiver(receiver, intentFilter)method, passing it the already-existing receiver (mReceiver) and theIntentFilteryou just created!Then repeat the above two steps, but this time for the
Bluetooth.ACTION_DISCOVERY_FINISHEDaction. This will register an additionalIntentFilteron the same receiver.
We can actually begin searching for devices by filling in the Activity’s
doDiscovery()helper method (which is called when the Scan button is pressed).Add a check to see if the
BluetoothAdaptercurrentlyisDiscovering(). If so, then you should tell the adapter tocancelDiscovery().Whether or not the check was
true(so even if we canceled the discovery), tell the adapter tostartDiscovery()to begin searching for devices!
Once the user has selected a device to connect to, we handle that connection back in the
BluetoothChatFragment. Fill in that class’sconnectDevice()helper method to connect to the device!First you’ll want to get the device’s “address” (a MAC address that acts as a unique identifier) from the Intent’s extras: get the
Bundleof extras from the Intent, then get the String with the keyDeviceListActivity.EXTRA_DEVICE_ADDRESS.You can then find the device (a
BluetoothDeviceobject) by calling the.getRemoteDevice()method on theBluetoothAdapterand passing this address.Finally, you can use the
mChatService’s.connect()method to connect to this device (passing down thesecureoption as a second parameter). TheBluetoothChatService#connect()method creates a newThreadto do the communication work, and opens up network sockets so that messages can be passed between the devices. (This is actually part of the hard part or working with Bluetooth; luckily we have a class to abstract that for us!)
The last part is to actually send a message! In the
sendMessage()helper inBluetoothChatFragment, fill in the details so that the String can be sent to the socket in the chat service.First you need to convert the message String into a
byte[](for communication over the socket). Use the String’sgetBytes()method to convert.Then you can tell
mChatServiceto.write()those bytes!We then need to reset the
mOutStringBufferinstance variable (which keeps track of the message that has been typed so far). Use.setLength()to give it a length of 0, which will effectively make it empty.And finally, because we’ve changed the outgoing message, set the text of the
mOutEditTextTextView to be the (now empty)mOutStringBuffer.
And that’s it! You should now have a working chat system! Search for and connect to someone else’s device and try saying “hello”!