I am trying to connect a bluetooth headset to my android device using the android developer page as a reference. http://developer.android.com/guide/topics/connectivity/bluetooth.html
My problem is when i trying calling the getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET) method, I am unsure of what to pass for context? I located this error from the question here:
can not connect to bluetooth headset in android
I am extremely new to this so I will apologize in advance if this is a silly question. I have spent a lot of time trying to research this but every example and documentation I find just has a context variable passed in so I am not sure where I am going wrong. My code, which is more or less a copy from the android documentation is:
// Establish connection to the proxy.
boolean mProfileProxy = mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET);
Log.d(TAGP,"Get Adapter Success: "+mProfileProxy);
Log.d(TAGP,"Context: "+context);
BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (profile == BluetoothProfile.HEADSET) {
mBluetoothHeadset = (BluetoothHeadset) proxy;
Log.d(TAGP,"BLuetooth Headset: "+mBluetoothHeadset);
Log.d(TAGP,"Proxy: "+proxy);
}
}
public void onServiceDisconnected(int profile) {
if (profile == BluetoothProfile.HEADSET) {
mBluetoothHeadset = null;
}
}
};
The context can be an activity or service context. So if the code above is in a class that extends Activity or Service you can pass this.
You can use my answer at Using the Android RecognizerIntent with a bluetooth headset
Related
I have a xamarin project (API 28, soon to be 29), and I need to catch the event of bluetooth a2dp device.
I have a broadcast receiver with the following intent filter:
IntentFilter bluetoothFilter = new IntentFilter();
bluetoothFilter.AddAction(BluetoothDevice.ActionAclConnected);
bluetoothFilter.AddAction(BluetoothDevice.ActionAclDisconnectRequested);
bluetoothFilter.AddAction(BluetoothDevice.ActionAclDisconnected);
var btReciever = new BluetoothReceiver();
this.RegisterReceiver(btReciever, bluetoothFilter);
In my manisfest, I got the following permission:
<uses-permission android:name="android.permission.BLUETOOTH" />
In the Receiver.OnReceive, I got this code:
public override void OnReceive(Context context, Intent intent) {
String action = intent.Action;
BluetoothDevice device = (BluetoothDevice)intent.GetParcelableExtra(BluetoothDevice.ExtraDevice);
There I have a switch:
switch (action)
{
case BluetoothDevice.ActionFound:
Android.Util.Log.Debug(TAG, "Device Found");
//Device found
break;
case BluetoothDevice.ActionAclConnected:
Android.Util.Log.Debug(TAG, "Device Connected");
BluetoothAdapter.DefaultAdapter.GetProfileProxy(context, btsListener, ProfileType.A2dp);
Thread.Sleep(1500);
var manager = context.GetSystemService(Context.AudioService) as AudioManager;
var devices = manager.GetDevices(GetDevicesTargets.Outputs);
Android.Util.Log.Debug(TAG, "devices=" + devices.Length);
foreach (var dev in devices)
{
Android.Util.Log.Debug(TAG, "dev: id={0} name={1} type={2}", dev.Id, dev.ProductName, dev.Type);
if (dev.ProductName == device.Name)
{
if (dev.Type.ToString().Contains("a2dp"))
{
BluetoothAdapter.DefaultAdapter.GetProfileProxy(context, btsListener, ProfileType.A2dp);
}
Android.Util.Log.Debug(TAG, "Found output device");
}
}
//Device is now connected
break;
...
}
And I got a listener that implements the IBluetoothProfileServiceListener interface, and looks like this:
var btsListener = new BTServiceListener();
class BTServiceListener : AppCompatActivity, IBluetoothProfileServiceListener
{
public void OnServiceConnected([GeneratedEnum] ProfileType profile, IBluetoothProfile proxy)
{
if (profile == ProfileType.A2dp)
{
Android.Util.Log.Debug(TAG, "A2dp");
}
}
...
}
I need to catch the event onConnect of the bluetooth a2dp (and later headset), but I have no idea how exactly I should do it.
This code in the receiver, shows the bluetooth onConnect event (BluetoothDevice.ActionAclConnected in the switch), then I check the device list, there is not yet the connected device, then I wait 1500ms (I need somehow to improve this method, this cannot stay like this), for the audioService to add the actual a2dp device to the list, and in the for loop, I find the additional device via its name, and I am certain it is the right one. BUT, I have no programmaticaly way to find out what type of device was connected (remote, headset, a2bp...) other than to check is the name contains a2dp (see for loop)
After my research, I found this line:
BluetoothAdapter.DefaultAdapter.GetProfileProxy(context, btsListener, ProfileType.A2dp);
This uses the context, the listener, and the desierd type of device (see Listener: BTServiceListener ), the problem is, I don't know if the proxy in the listener is the same device as the device in the broadcast receiver onConnect, and I have no idea how to use that function.
So my questions:
How and when should I use the BluetoothAdapter.DefaultAdapter.GetProfileProxy function and be certain that I have the same device in the listener and in the onConnect function?
How to get all the devices from the manager without putting the thread to sleep? Because, without the Thread.Sleep, the actual device is not in the list because the onConnect function is called earlyer then the addition of the device to the audioService.
Thread.Sleep(1500); // <-- this needs to go
var manager = context.GetSystemService(Context.AudioService) as AudioManager;
var devices = manager.GetDevices(GetDevicesTargets.Outputs);
How should I distinguish between the device types? Because, I have a feeling that my method of String.Contains(string) is not the way to go
Sorry for the long question, thank you for your help and time.
Let me know, if you need anything else.
I've been writing a chat app to work with bluetooth headsets/earphones.
So far I've been able to record audio files via the mic in a bluetooth headset and
I've been able to get Speech-to-text working with the Android device's built in microphone, using RecogniserIntent etc.
But I can't find a way of getting SpeechRecogniser to listen through the Bluetooth mic.Is it even possible to do so, and if so, how?
Current Device: Samsung Galax
Android Version: 4.4.2
Edit: I found some options hidden in my tablets settings for the Speech Recognizer, one of these is a tick box labeled "use bluetooth microphone" but it seems to have no effect.
Found the answer to my own question so I'm posting it for others to use:
In order to get speak recognition to work with a Bluetooth Mic you first need to get the device as a BluetoothHeadset Object and then call .startVoiceRecognition() on it, this will set the mode to Voice recognition.
Once finished you need to call .stopVoiceRecognition().
You get the BluetoothHeadset as such:
private void SetupBluetooth()
{
btAdapter = BluetoothAdapter.getDefaultAdapter();
pairedDevices = btAdapter.getBondedDevices();
BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy)
{
if (profile == BluetoothProfile.HEADSET)
{
btHeadset = (BluetoothHeadset) proxy;
}
}
public void onServiceDisconnected(int profile)
{
if (profile == BluetoothProfile.HEADSET) {
btHeadset = null;
}
}
};
btAdapter.getProfileProxy(SpeechActivity.this, mProfileListener, BluetoothProfile.HEADSET);
}
Then you get call startVoiceRecognition() and send off your voice recognition intent like so:
private void startVoice()
{
if(btAdapter.isEnabled())
{
for (BluetoothDevice tryDevice : pairedDevices)
{
//This loop tries to start VoiceRecognition mode on every paired device until it finds one that works(which will be the currently in use bluetooth headset)
if (btHeadset.startVoiceRecognition(tryDevice))
{
break;
}
}
}
recogIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
recogIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
recog = SpeechRecognizer.createSpeechRecognizer(SpeechActivity.this);
recog.setRecognitionListener(new RecognitionListener()
{
.........
});
recog.startListening(recogIntent);
}
I am using Twilio android client. device.connect() is working fine but can't able to disconnect the connected call, though the user (I dialed to) if hangup then the call will be disconnected. I have tried with device.disconnectAll() and connection.disconnect(), both are not working. Please guide me wher i'm wrong.
Simple:
Caller can't be able to disconnect the call while callee can.
Thank in Advance.
To connect the call
HashMap<String, String> p = new HashMap<String, String>();
p.put("PhoneNumber", personToCall);
p.put(Connection.IncomingParameterFromKey, (String) device.getCapabilities().get(Device.Capability.CLIENT_NAME));
p.put(Connection.IncomingParameterToKey, personToCall);
device.connect(p, this);
and to disconnect
connection.disconnect()
It's not clear in your code how you're obtaining the connection object, which is one of two pivotal pieces of the Twilio client.
The Twilio Client documentation has an example on making outbound calls:
import com.twilio.client.Connection;
import com.twilio.client.Device;
import com.twilio.client.Twilio;
public class MonkeyPhone implements Twilio.InitListener
{
private Device device;
private Connection connection;
/* ... other methods ... */
public void connect()
{
connection = device.connect(null /* parameters */, null /* ConnectionListener */);
if (connection == null)
Log.w(TAG, "Failed to create new connection");
}
#Override
protected void finalize()
{
if (connection != null)
connection.disconnect();
if (device != null)
device.release();
}
}
In the example, when you call device.connect(), a connection object is returned and stored as a property in the object so it can be referenced again later on. In the finalize method, we call connection.disconnect() to terminate the call.
If you're not calling disconnect on the connection object returned from device.connect() the call may not disconnect properly. See the Twilio documentation for Device.connect for more details.
I am trying to send all the audio of an application via SCO.
I am able to successfully send the audio,
But when an incoming call comes I need to disconnect form SCO so that the application audio will not interfere with the call,
The problem is that, when I try to reroute the audio to SCO after the call, it does not work.
Here is the code I use to send the audio to SCO:
public class BluetoothManager {
// For Bluetooth connectvity
private static String TAG = "BluetoothManager";
private static BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
private static AudioManager aM;
/**
* Set the audio manager of the device.
* #param c: The context this method is called from
*/
public static void setAudioManager(Context c) {
aM = (android.media.AudioManager)c.getSystemService(Context.AUDIO_SERVICE);
}
/**
* Check if a Bluetooth headset is connected. If so, route audio to Bluetooth SCO.
*/
private static void initializeAudioMode(Context context) {
BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (profile == BluetoothProfile.HEADSET) {
BluetoothHeadset bh = (BluetoothHeadset) proxy;
List<BluetoothDevice> devices = bh.getConnectedDevices();
if (devices.size() > 0) {
enableBluetoothSCO();
}
}
mBluetoothAdapter.closeProfileProxy(profile, proxy);
}
public void onServiceDisconnected(int profile) {}
};
mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET);
}
/**
* Bluetooth Connectvity
* The following methods are associated with enabling/disabling Bluetooth.
* In the future we may want to disable other sources of audio.
*/
private static void enableBluetoothSCO() {
aM.setMode(AudioManager.MODE_IN_CALL);
aM.startBluetoothSco();
aM.setBluetoothScoOn(true);
}
/** Right now, this simply enables Bluetooth */
#SuppressLint("NewApi")
public static boolean enableBluetooth(Context c) {
// If there is an adapter, enable it if not already enabled
if (mBluetoothAdapter != null) {
if (!mBluetoothAdapter.isEnabled()) {
mBluetoothAdapter.enable();
}
setAudioManager(c);
initializeAudioMode(c);
Log.e(TAG, "SCO: " + aM.isBluetoothScoOn());
Log.e(TAG, "A2DP: " + aM.isSpeakerphoneOn());
return true;
} else {
Log.v(TAG, "There is no bluetooth adapter");
return false;
}
}
/** Right now, this simply disables Bluetooth */
public static void disableBluetooth() {
// If there is an adapter, disabled it if not already disabled
if (mBluetoothAdapter != null) {
if (mBluetoothAdapter.isEnabled()) {
mBluetoothAdapter.disable();
}
} else {
Log.v(TAG, "There is no bluetooth adapter");
}
}
public static void restartBluetooth(){
aM.setMode(AudioManager.MODE_IN_CALL);
}
public static void stopBluetooth(){
aM.setMode(AudioManager.MODE_NORMAL);
}
}
When I call stopBluetooth() correctly the audio of the application is not sent to the headset anymore,
But when I call restartBluetooth() the audio plays NOT form the headset as intended, but from the phone speakers.
Is it possible that the SCO link was brought down after the call ended? If this is the case then the SCO link would also have to be brought up along with routing the audio.
Have you tried calling enableBluetoothSCO() within restartBluetooth()
You probably need to call:
aM.startBluetoothSco();
aM.setBluetoothScoOn(true);
after you set the mode.
inside your restart function initialize everything again, and see if it works. like so:
public static void restartBluetooth(){
enableBluetooth(getApplicationContext());
}
if this works then it means that when the call is ended the last initialization is lost for some reason.
Google Doc say's that
"Phone application always has the priority on the usage of the SCO connection for telephony. If this method is called while the phone is in call it will be ignored. Similarly, if a call is received or sent while an application is using the SCO connection, the connection will be lost for the application and NOT returned automatically when the call ends."
So when call is disconnected you must have to re-establish the connection by calling startBluetoothSco()
For anyone that is still having issues with this, there are a few things that need to be done. The first thing you need to do is to keep track of the phone state. You can see how to do that here:
How to know Phone call has ended?
When the state is idle that means the incoming call has ended. Now if you try to reconnect the bluetooth at this point you'll find it still does not work since it takes a while (roughly 2 seconds) for the call to "release" the bluetooth device.
So you have two option, wait a bit then try to reconnect, or you can add another listener to BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED.
You can then add a global boolean value isIdle that is true when TelephonyManager.CALL_STATE_IDLE or false when TelephonyManager.CALL_STATE_OFFHOOK (Otherwise you'll reconnect to BlueTooth during the incoming call). At this point when BluetoothHeadset.STATE_DISCONNECTED and isIdle is true, then reconnect to Bluetooth.
#Override public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals((BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED))){
int state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
switch(state) {
case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
if (isIdle){
//reconnect bluetooth
}
break;
}
}
if(("OFFHOOK").equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE))) {
isIdle = false;
// turn bluetooth off
}
if(("IDLE").equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE))) {
isIdle = true;
}
}
I tried example code google refers as below to detect connected bluetooth devices
BluetoothHeadset mBluetoothHeadset;
// Get the default adapter
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
// Establish connection to the proxy.
mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET);
private BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (profile == BluetoothProfile.HEADSET) {
mBluetoothHeadset = (BluetoothHeadset) proxy;
}
}
public void onServiceDisconnected(int profile) {
if (profile == BluetoothProfile.HEADSET) {
mBluetoothHeadset = null;
}
}
};
// ... call functions on mBluetoothHeadset
But I got the following problems:
mBluetoothHeadset is only available inside onServiceConnected. I use getConnectedDevices to detect live bluetooth headset. but if I place the code below
List ConnectedDevices = mBluetoothHeadset.getConnectedDevices();
out of onServiceConnected, running program lead always crash. What's wrong here?
is there any possibility to use mBluetoothHeadset value outside onServiceConnected ? like the example show? Or May I trans some parameter/value from onServiceConnected to outside?
Actually the example codes don't work. i have to place additional code after mProfileListener:
if (mBluetoothAdapter.getProfileProxy(this, mProfileListener,BluetoothProfile.HEADSET)==false) { ....
What's the reason? or what's wrong with my code?
From system log the code seems work, but when I run it, program stay in onServiceConnected, never go to onServiceDisconnected, or outside if no other action the user perform(e.g press a confirm button). What's wrong?