How to Capture Key Events from Bluetooth Headset with Android

Android: How to capture key events from Bluetooth headphones?

If anyone is facing the same issue, this is what worked for me now:

fun configureMediaSession(){
s_mediaSession = MediaSession(this, "MyMediaSession")
s_mediaSession.setCallback(object : MediaSession.Callback() {
override fun onMediaButtonEvent(mediaButtonIntent: Intent): Boolean {
val ke: KeyEvent? = mediaButtonIntent.getParcelableExtra(Intent.EXTRA_KEY_EVENT)
if (ke != null && ke.action == KeyEvent.ACTION_DOWN) {
// Calling my method
return true
}
return super.onMediaButtonEvent(mediaButtonIntent)
}
})
}

It would be great if anyone can share a better solution.

Handle bluetooth headset clicks (ACTION_VOICE_COMMAND and ACTION_WEB_SEARCH) on Android

I finally managed to detect the events. I didn't know about this class:

http://developer.android.com/reference/android/bluetooth/BluetoothHeadset.html

Using the classes BluetoothAdapter, BluetoothHeadset and BluetoothDevice I can register a receiver using IntentFilter BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED and I'm able to detect clicks on my headset.

The problem with this is that the broadcast is non-ordered, so I can't abort it. I can close the VoiceDialer activity right after it opens, but that's not what I want.

I'll keep struggling with this.

Thank you @Toaster for your efforts :)

EDIT:

Code used to detect the events:

protected BluetoothAdapter mBluetoothAdapter;
protected BluetoothHeadset mBluetoothHeadset;
protected BluetoothDevice mConnectedHeadset;
protected AudioManager mAudioManager;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

if (mBluetoothAdapter != null)
{

mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
if (mAudioManager.isBluetoothScoAvailableOffCall())
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
{
mBluetoothAdapter.getProfileProxy(this, mHeadsetProfileListener, BluetoothProfile.HEADSET);
}
}
}
}

protected BluetoothProfile.ServiceListener mHeadsetProfileListener = new BluetoothProfile.ServiceListener()
{

/**
* This method is never called, even when we closeProfileProxy on onPause.
* When or will it ever be called???
*/
@Override
public void onServiceDisconnected(int profile)
{
mBluetoothHeadset.stopVoiceRecognition(mConnectedHeadset);
unregisterReceiver(mHeadsetBroadcastReceiver);
mBluetoothHeadset = null;
}

@Override
public void onServiceConnected(int profile, BluetoothProfile proxy)
{
// mBluetoothHeadset is just a head set profile,
// it does not represent a head set device.
mBluetoothHeadset = (BluetoothHeadset) proxy;

// If a head set is connected before this application starts,
// ACTION_CONNECTION_STATE_CHANGED will not be broadcast.
// So we need to check for already connected head set.
List<BluetoothDevice> devices = mBluetoothHeadset.getConnectedDevices();
if (devices.size() > 0)
{
// Only one head set can be connected at a time,
// so the connected head set is at index 0.
mConnectedHeadset = devices.get(0);

String log;

// The audio should not yet be connected at this stage.
// But just to make sure we check.
if (mBluetoothHeadset.isAudioConnected(mConnectedHeadset))
{
log = "Profile listener audio already connected"; //$NON-NLS-1$
}
else
{
// The if statement is just for debug. So far startVoiceRecognition always
// returns true here. What can we do if it returns false? Perhaps the only
// sensible thing is to inform the user.
// Well actually, it only returns true if a call to stopVoiceRecognition is
// call somewhere after a call to startVoiceRecognition. Otherwise, if
// stopVoiceRecognition is never called, then when the application is restarted
// startVoiceRecognition always returns false whenever it is called.
if (mBluetoothHeadset.startVoiceRecognition(mConnectedHeadset))
{
log = "Profile listener startVoiceRecognition returns true"; //$NON-NLS-1$
}
else
{
log = "Profile listener startVoiceRecognition returns false"; //$NON-NLS-1$
}
}

Log.d(TAG, log);
}

// During the active life time of the app, a user may turn on and off the head set.
// So register for broadcast of connection states.
registerReceiver(mHeadsetBroadcastReceiver,
new IntentFilter(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED));

// Calling startVoiceRecognition does not result in immediate audio connection.
// So register for broadcast of audio connection states. This broadcast will
// only be sent if startVoiceRecognition returns true.
IntentFilter f = new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
f.setPriority(Integer.MAX_VALUE);
registerReceiver(mHeadsetBroadcastReceiver, f);
}
};

protected BroadcastReceiver mHeadsetBroadcastReceiver = new BroadcastReceiver()
{

@Override
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
int state;
int previousState = intent.getIntExtra(BluetoothHeadset.EXTRA_PREVIOUS_STATE, BluetoothHeadset.STATE_DISCONNECTED);
String log = "";

if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED))
{
state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_DISCONNECTED);
if (state == BluetoothHeadset.STATE_CONNECTED)
{
mConnectedHeadset = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

// Audio should not be connected yet but just to make sure.
if (mBluetoothHeadset.isAudioConnected(mConnectedHeadset))
{
log = "Headset connected audio already connected";
}
else
{

// Calling startVoiceRecognition always returns false here,
// that why a count down timer is implemented to call
// startVoiceRecognition in the onTick and onFinish.
if (mBluetoothHeadset.startVoiceRecognition(mConnectedHeadset))
{
log = "Headset connected startVoiceRecognition returns true"; $NON-NLS-1$
}
else
{
log = "Headset connected startVoiceRecognition returns false";
}
}
}
else if (state == BluetoothHeadset.STATE_DISCONNECTED)
{
// Calling stopVoiceRecognition always returns false here
// as it should since the headset is no longer connected.
mConnectedHeadset = null;
}
}
else // audio
{
state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_AUDIO_DISCONNECTED);

mBluetoothHeadset.stopVoiceRecognition(mConnectedHeadset);

if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED)
{
log = "Head set audio connected, cancel countdown timer";
}
else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED)
{
// The headset audio is disconnected, but calling
// stopVoiceRecognition always returns true here.
boolean returnValue = mBluetoothHeadset.stopVoiceRecognition(mConnectedHeadset);
log = "Audio disconnected stopVoiceRecognition return " + returnValue;
}
}

log += "\nAction = " + action + "\nState = " + state
+ " previous state = " + previousState;
Log.d(TAG, log);

}
};

Like I said, I can detect the events, but I can't aboard the broadcast.

Capturing Bluetooth Headset longpress event in android

As per my understanding from bluedroid and bluetooth app(packages/apps/bluetooth) in android

Whenever bluetooth button in headset is pressed, it sends AT+BVRA command(check bluetooth hfp spec 1.5/1.6), this command expects phone to launch voice recognition

bluedroid source code
btif/src/btif_hf.c->BTA_AG_AT_BVRA_EVT calls BTHF_VR_STATE_STARTED

which gets translated to

/packages/apps/Bluetooth/+/android-4.4.2_r1/src/com/android/bluetooth/hfp/HeadsetStateMachine.java

processVrEvent->startActivity(sVoiceCommandIntent);
where sVoiceCommandIntent is ACTION_VOICE_COMMAND

so no intent is broadcasted for voice recognition .



Related Topics



Leave a reply



Submit