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
Move Snackbar Above The Bottom Bar
End Incoming Call Programmatically
How to Display a One Time Welcome Screen
Is There a Maximum Size to Android Internal Storage Allocated for an App
Restarting a Device Programmatically
How to Import Eclipse Library Project from Github to Android Studio Project
How to Get a View from an Event Coordinates in Android
Stuck with Gradle Build Running
How to Open The Avd Manager on Ubuntu Linux from The Command Line
Can't Create New Kotlin Project After Updating to Android Studio 4.2
How to Replace a String for a Buildvariant with Gradle in Android Studio
Android Studio Where Is Gradle.Properties File
Is There Another Place to Get a Google-Services.JSON
Export The Contacts as Vcf File