How can I intercept a bluetooth headset CALL button?

4.4k views Asked by At

I have a single button Bluetooth headset (Jabra BT2045) and would like to intercept the button presses. I want this so that a background assistant Service can interpret the user's voice command and act accordingly.

I have defined a BroadcastReceiver named MediaButtonIntentReceiver which can intercept ACTION_MEDIA_BUTTON just fine (using a wired media headset) but it is important that I intercept the BT call button. I have tried intercepting the CALL_BUTTON action based on another suggestion I have read but this also fails.

Alternatively, when I double tap the button it calls the most recent person and when I long press it brings up a voice dialler.

How can I intercept the single button or these procedures (+prevent them)? Hacks are welcome since I am prototyping an idea, but solutions that work regardless of Android versions are best.

I am using a Galaxy Nexus with Android 4.3.

AndroidManifest.xml (incl. receiver definition)

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="nz.ac.waikato.ssc10.BlindAssistant">

    <uses-sdk android:minSdkVersion="17"/>

    <uses-permission android:name="android.permission.ACCESS_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.INTERNET"/>

    <!-- Bluetooth Headset -->
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BROADCAST_STICKY"/>
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>

    <uses-permission android:name="android.permission.RECORD_AUDIO"/>

    <application
        android:icon="@drawable/icon"
        android:label="@string/app_name">
        <activity
            android:name=".activities.VoicePromptActivity"
            android:screenOrientation="portrait"
            android:label="Voice Prompt">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <service android:name=".services.BlindAssistantService"/>
        <service android:name=".services.WalkingDirectionsUpdateService"/>

        <receiver android:name=".receivers.MediaButtonIntentReceiver">
            <intent-filter>
                <action android:name="android.intent.action.CALL_BUTTON"/>
            </intent-filter>
        </receiver>
    </application>
</manifest>

Receiver definition:

public class CallButtonIntentReceiver extends BroadcastReceiver {
    private static String TAG = "CallButtonIntentReceiver";

    @Override
    public void onReceive(Context context, Intent intent) {
        String intentAction = intent.getAction(); // I never get to this point in debugger (breakpoint set here)

        // Code to actually handle button press ...
    }
}

Update 1:

If I set an additional intent filter (see below) for the activity it will launch on a long press (after setting it as the default). This seems like I am going in the right direction but now the activity starts many times and causes weird effects.

Now I am investigating only starting the activity once and when the activity resumes I must be able to detect if it was started from the launcher or from the VOICE_COMMAND action. In order for this method to work I'll need to enforce that the activity is only started once (pressing the button should simply resume the activity).

Additionally, if possible, I would like to make it so the activity is only the default while the service is running.

<intent-filter>
    <action android:name="android.intent.action.VOICE_COMMAND"/>
    <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>

Update 2:

Unfortunately, the activity will not continue to be created while the Bluetooth headset SCO channel is activated. My application requires this channel to be open as the user will receive periodic updates (via TTS) which MUST come out of the BT headset. This method may incur a lot of overhead.

1

There are 1 answers

1
Louis T On

You could maybe identify the headset button's keycode with a test activity's onKeyDown method:

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
  Log.d("KeyCode", keycode + "");
  Log.d("KeyEvent", event + "");
  return super.onKeyDown(keyCode, event);
}

Then just run the activity and hit the headset button while looking at the logcat. You can refer to http://developer.android.com/reference/android/view/KeyEvent.html

You could also override its behaviour from onKeyDown, but I don't think you can listen to key events outside of an Activity, let alone in a Service, for obvious security reasons.