This example shows how to access and list Bluetooth devices via the BluetoothAdapter API.
We import the classes and modules needed by our application. The most
relevant ones for this example are the BluetoothAdapter
and BluetoothDevice
classes.
from java.lang import String
from android.bluetooth import BluetoothAdapter, BluetoothDevice
from android.content import BroadcastReceiver, Context, Intent, IntentFilter
from android.graphics import Typeface
from android.os import Build
from android.view import View
from android.widget import Button, LinearLayout, ListView, Space, TextView
from serpentine.activities import Activity
from serpentine.adapters import StringListAdapter
from serpentine.widgets import HBox, VBox
The BluetoothDevicesActivity is derived from the custom Activity class
provided by the serpentine
package and represents the application. Android
will create an instance of this class when the user runs it.
class BluetoothDevicesActivity(Activity):
__interfaces__ = [View.OnClickListener]
The initialisation method only needs to call the corresponding method in the base class.
def __init__(self):
Activity.__init__(self)
The onCreate method is called when the activity is created. Our implementation calls the onCreate method of the base class, queries the available Bluetooth devices and displays them in a graphical layout.
def onCreate(self, bundle):
Activity.onCreate(self, bundle)
Information about the available Bluetooth devices is obtained from the device's Bluetooth adapter which is itself obtained from the Bluetooth service. The custom activity provides a method to do this for us.
self.adapter = self.getBluetoothAdapter()
We want the application to receive notifications about the Bluetooth
adapter and other devices. These are delivered by intents that are
broadcast by the operating system. We create an IntentFilter
to
specify the ones we are interested in and register a custom
BroadcastReceiver
instance to handle them.
intentFilter = IntentFilter()
intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED)
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
intentFilter.addAction(BluetoothDevice.ACTION_FOUND)
self.receiver = Receiver(self)
self.registerReceiver(self.receiver, intentFilter)
We set up the user interface, putting information about the
Bluetooth adapter first, followed by a ListView
for showing
information about remote devices, and a button that the user presses to
start a scan.
localLabel = TextView(self)
localLabel.setText("Local Device:")
localLabel.setTypeface(Typeface.create(None, Typeface.BOLD))
self.localNameLabel = TextView(self)
self.localAddressLabel = TextView(self)
hbox = HBox(self)
hbox.addWeightedView(self.localNameLabel, 1)
hbox.addWeightedView(self.localAddressLabel, 1)
remoteLabel = TextView(self)
remoteLabel.setText("Remote Devices:")
remoteLabel.setTypeface(Typeface.create(None, Typeface.BOLD))
self.listView = ListView(self)
self.stringAdapter = StringListAdapter([])
self.listView.setAdapter(self.stringAdapter)
self.scanButton = Button(self)
self.scanButton.setText("Scan")
self.scanButton.setOnClickListener(self)
self.setScanningState(self.adapter.isEnabled())
The state of the user interface depends on whether a Bluetooth adapter is available and ready. Initially, this is set using the current state of the adapter, but changes to this will be made when the application receives certain intents.
We create a vertical layout in which to place the user interface elements.
layout = VBox(self)
layout.addView(localLabel)
layout.addView(hbox)
layout.addView(remoteLabel)
layout.addView(self.listView)
layout.addWeightedView(Space(self), 1)
layout.addView(self.scanButton)
self.setContentView(layout)
When the user clicks the scan button the following method is called.
We disable the button while a scan is in progress, starting the scan by
calling the adapter's startDiscovery
method. We clear the existing list
of strings in the StringListAdapter
used by the ListView
and refresh
the view itself by setting the string adapter on it again.
def onClick(self, view):
self.scanButton.setEnabled(False)
self.adapter.startDiscovery()
self.stringAdapter.items.clear()
self.listView.setAdapter(self.stringAdapter)
The following method changes the user interface to reflect the ability
of the Bluetooth adapter to scan for other devices. The enable
parameter
is used to enable or disable the scan button and the local device labels.
@args(void, [bool])
def setScanningState(self, enable):
if enable:
self.localNameLabel.setText(self.adapter.getName())
self.localAddressLabel.setText(self.adapter.getAddress())
self.localNameLabel.setEnabled(enable)
self.localAddressLabel.setEnabled(enable)
self.scanButton.setEnabled(enable)
When the application receives information about a new remote device we
add its name to the ListView
containing the names of other devices, using
the same process as described above to refresh the view.
@args(void, [String])
def addDevice(self, name):
if name in self.stringAdapter.items:
return
self.stringAdapter.items.add(name)
self.listView.setAdapter(self.stringAdapter)
The following class is used to receive broadcasts from the operating system that we are interested in, handle them according to their actions, and call the relevant methods in the activity class when appropriate.
class Receiver(BroadcastReceiver):
@args(void, [BluetoothDevicesActivity])
def __init__(self, parent):
BroadcastReceiver.__init__(self)
self.parent = parent
This method is called when broadcast intents are delivered to the
application. The context in which the receiver is running is supplied, but
we use the parent
field of this custom class to call methods in the
activity because its type is more specific.
When the adapter's state changes we call the activity's setScanningState
method to update the user interface.
def onReceive(self, context, intent):
if intent.getAction() == BluetoothAdapter.ACTION_STATE_CHANGED:
state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
BluetoothAdapter.STATE_OFF)
self.parent.setScanningState(state == BluetoothAdapter.STATE_ON)
elif intent.getAction() == BluetoothDevice.ACTION_FOUND:
extra = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
device = CAST(extra, BluetoothDevice)
name = device.getName()
if name != None:
self.parent.addDevice(name)
elif intent.getAction() == BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
self.parent.setScanningState(True)
During a scan for remote devices, intents are delivered to report the
devices found. We respond to these by unpacking data about the device from
each intent and calling the activity's addDevice
method.
When the scan finishes, an intent is delivered reporting that discovery is
finished. We call the activity's setScanningState
method to reset the
user interface to be ready for another scan.