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
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
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
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
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
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.