NeatoCode Techniques
Device Song Survive: Bluetooth Fallback on Android

Background

A friend asked me for help dealing with crashes in her Bluetooth app. It turns out Bluetooth can be in a lot of states on an Android phone: not present, present but turned off in the settings, present but no devices discovered yet, etc.. I’ll present some sample code here for how you can handle them.

I call this sample Device Song Survive. It’s a game that prompts the user to select a Bluetooth device, then generates a song from it, and runs a game where you try to tap the notes of the song as they appear. If Bluetooth isn’t present, it generates a random song. If Bluetooth is disabled in the settings, it prompts the user to enable it and offers a handy button for going right to the settings. Here’s what the game looks like:

The white circle is your life gauge in the game. The colored dots are notes of the music. You tap the notes to keep your life gauge full. The music is generated via jMusic and JSyn libraries using cellular automata, so you actually can figure out the pattern usually. :) Don’t tap enough notes and your circle will shrink. No circle by the end of the song and you are dead, otherwise you win.

You can get the source code from the GitHub repositoryAPK to install on Android phones is here, be sure to check the unknown sources option in your phone settings to install an APK from somewhere other than the Android market.

Coding Techniques

To check if an Android device supports Bluetooth, call BluetoothAdapter.getDefaultAdapter() and check if the result is null or an instance. In this example I’ll show a dialog with the result and the let the user proceed without the Bluetooth feature of the game. Strings are in the code for clarity, but in a real app you would keep them in a separate file.

Define:

  private BluetoothAdapter mBluetooth;

Later:

    mBluetooth = BluetoothAdapter.getDefaultAdapter();
    if (mBluetooth == null) {
      showBluetoothNotSupportedDialog();
    }

With:

private void showBluetoothNotSupportedDialog() {
  new AlertDialog.Builder(this)
    .setMessage(
      "Tap notes in generated music to survive!\n\n" + 
      "Play on a device with Bluetooth for extra features!")
    // Start game without Bluetooth.
    .setPositiveButton("OK", new OnClickListener() {
      @Override
      public void onClick(DialogInterface dialog, int which) {
        mMidiView.startGame(null);
      }
    // Leave game.
    }).setOnCancelListener(new OnCancelListener() {
      @Override
      public void onCancel(DialogInterface dialog) {
        finish();
      }
    }).show();
  }

To check if Bluetooth is enabled, you check BluetoothAdapter#isEnabled:

if (!mBluetooth.isEnabled()) {
  startEnableBluetoothActivity();
  return;
}

With the start call prompting the user to enable it and handling their response:

private static final int REQUEST_ENABLE_BLUETOOTH = 0;
	
private void startEnableBluetoothActivity() {
  final Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
  if ( getPackageManager().queryIntentActivities(enableBtIntent, 0).isEmpty() ) {
      startSettingsActivity();
    return;
  }
  startActivityForResult(enableBtIntent, REQUEST_ENABLE_BLUETOOTH);
}
	
@Override
protected void onActivityResult(final int requestCode,
  final int resultCode, final Intent data) {
		
  if (requestCode == REQUEST_ENABLE_BLUETOOTH) {
    // Bluetooth was enabled.
    if (mBluetooth.isEnabled()) {
      // Pick device.
      mBluetooth.startDiscovery();
      startSelectDeviceActivity();
      return;
    }
    // Start game without Bluetooth.
    mMidiView.startGame(null);
    return;
  }
  ... 

If Bluetooth is present and enabled you can have the user pick a device:

private void startSelectDeviceActivity() {
  Intent newIntent = new Intent(this, DeviceListActivity.class);
  startActivityForResult(newIntent, REQUEST_SELECT_DEVICE);
}

This uses the DeviceListActivity activity also in the source code. This activity returns a result based on the user’s choice. If the user picked a device you can do one thing, otherwise fallback to no Bluetooth behavior:

private static final int REQUEST_SELECT_DEVICE = 1;
	
@Override
protected void onActivityResult(final int requestCode,
  final int resultCode, final Intent data) {
  ...		
  if (requestCode == REQUEST_SELECT_DEVICE) {
    if (resultCode == Activity.RESULT_OK && data != null) {
      // Start game with Bluetooth and selected device.
      final String deviceAddress = data.getStringExtra(BluetoothDevice.EXTRA_DEVICE);
      // use address of picked device as a random seed to always generate the same song.
      mMidiView.startGame((long) deviceAddress.hashCode());
      return;
    }

    // Start game without Bluetooth.
    mMidiView.startGame(null);
    return;
  }
}// onActivityResult

Just handle things step by step and try to give the user minimum friction using the full power of the app, and some sort of fallback otherwise. Even if your app can’t do anything without Bluetooth, you can show a pretty screen letting the user know they might want to try it on a different device or at another time.

Closing

Hope this helps your own coding exploits! At HTC the hot thing with Bluetooth right now is Bluetooth Low Energy. We’ve helped a lot of companies get running on Android with our Bluetooth Low Energy SDK. Bluetooth Low Energy is bringing the internet of things to real life! It allows lots of small devices that can talk to each other, needing only a coin cell battery that will last a year. If you’d like to get an Android app talking to a Bluetooth Low Energy device, be sure to fill out our form to get access to our APIs!

  1. neatocode posted this
Blog comments powered by Disqus