显示许可

BluetoothHelper extends BluetoothHeadSetUtils在你的Activity或中创建一个内部类Service。声明一个类成员mBluetoothHelper并将其实例化onCreate()BluetoothHelper mBluetoothHelper;@Overridepublic void onCreate(){

mBluetoothHelper = new BluetoothHelper(this);}  @OverrideonResume(){

mBluetoothHelper.start();}@OverrideonPause(){

mBluetoothHelper.stop();}// inner class// BluetoothHeadsetUtils is an abstract class that has// 4 abstracts methods that need to be implemented.private class BluetoothHelper extends BluetoothHeadSetUtils{

public BluetoothHelper(Context context)

{

super(context);

}

@Override

public void onScoAudioDisconnected()

{

// Cancel speech recognizer if desired

}

@Override

public void onScoAudioConnected()

{

// Should start speech recognition here if not already started

}

@Override

public void onHeadsetDisconnected()

{

}

@Override

public void onHeadsetConnected()

{

}}

要将蓝牙耳机与文本转语音一起使用,您需要在调用speak之前将AudioManager设置为STREAM_VOICE_CALL。或者使用下面的代码protected void speak(String text){

HashMap myHashRender = new HashMap();

if (mBluetoothHelper.isOnHeadsetSco())

{

myHashRender.put(TextToSpeech.Engine.KEY_PARAM_STREAM,

String.valueOf(AudioManager.STREAM_VOICE_CALL));

}

mTts.speak(text, TextToSpeech.QUEUE_FLUSH, myHashRender);}

将BluetoothHeadsetUtils类复制到您的项目中。import java.util.List;import android.annotation.SuppressLint;import android.bluetooth.BluetoothAdapter;import android.bluetooth.BluetoothClass;import android.bluetooth.BluetoothDevice;import android.bluetooth.BluetoothHeadset;import android.bluetooth.BluetoothProfile;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.content.IntentFilter;import android.media.AudioManager;import android.os.Build;import android.os.CountDownTimer;import android.util.Log;/**

* This is a utility to detect bluetooth headset connection and establish audio connection

* for android API >= 8. This includes a work around for  API 

* before the application starts. This work around would only fails if Sco audio

* connection is accepted but the connected device is not a headset.

*

* @author Hoan Nguyen

*

*/public abstract class BluetoothHeadsetUtils{

private  Context mContext;

private BluetoothAdapter mBluetoothAdapter;

private BluetoothHeadset mBluetoothHeadset;

private BluetoothDevice mConnectedHeadset;

private AudioManager mAudioManager;

private boolean mIsCountDownOn;

private boolean mIsStarting;

private boolean mIsOnHeadsetSco;

private boolean mIsStarted;

private static final String TAG = "BluetoothHeadsetUtils"; //$NON-NLS-1$

/**

* Constructor

* @param context

*/

public BluetoothHeadsetUtils(Context context)

{

mContext = context;

mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);

}

/**

* Call this to start BluetoothHeadsetUtils functionalities.

* @return  The return value of startBluetooth() or startBluetooth11()

*/

public boolean start()

{

if (!mIsStarted)

{

mIsStarted = true;

if (Build.VERSION.SDK_INT 

{

mIsStarted = startBluetooth();

}

else

{

mIsStarted = startBluetooth11();

}

}

return mIsStarted;

}

/**

* Should call this on onResume or onDestroy.

* Unregister broadcast receivers and stop Sco audio connection

* and cancel count down.

*/

public void stop()

{

if (mIsStarted)

{

mIsStarted = false;

if (Build.VERSION.SDK_INT 

{

stopBluetooth();

}

else

{

stopBluetooth11();

}

}

}

/**

*

* @return  true if audio is connected through headset.

*/

public boolean isOnHeadsetSco()

{

return mIsOnHeadsetSco;

}

public abstract void onHeadsetDisconnected();

public abstract void onHeadsetConnected();

public abstract void onScoAudioDisconnected();

public abstract void onScoAudioConnected();

/**

* Register for bluetooth headset connection states and Sco audio states.

* Try to connect to bluetooth headset audio by calling startBluetoothSco().

* This is a work around for API 

* the application starts.

*

* The official documentation for startBluetoothSco() states

*

* "This method can be used by applications wanting to send and received audio to/from

*  a bluetooth SCO headset while the phone is not in call."

*

* Does this mean that startBluetoothSco() would fail if the connected bluetooth device

* is not a headset?

*

* Thus if a call to startBluetoothSco() is successful, i.e mBroadcastReceiver will receive

* an ACTION_SCO_AUDIO_STATE_CHANGED with intent extra SCO_AUDIO_STATE_CONNECTED, then

* we assume that a headset is connected.

*

* @return  false if device does not support bluetooth or current platform does not supports

*                use of SCO for off call.

*/

@SuppressWarnings("deprecation")

private boolean startBluetooth()

{

Log.d(TAG, "startBluetooth"); //$NON-NLS-1$

// Device support bluetooth

if (mBluetoothAdapter != null)

{

if (mAudioManager.isBluetoothScoAvailableOffCall())

{

mContext.registerReceiver(mBroadcastReceiver,

new IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED));

mContext.registerReceiver(mBroadcastReceiver,

new IntentFilter(BluetoothDevice.ACTION_ACL_DISCONNECTED));

mContext.registerReceiver(mBroadcastReceiver,

new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED));

// Need to set audio mode to MODE_IN_CALL for call to startBluetoothSco() to succeed.

mAudioManager.setMode(AudioManager.MODE_IN_CALL);

mIsCountDownOn = true;

// mCountDown repeatedly tries to start bluetooth Sco audio connection.

mCountDown.start();

// need for audio sco, see mBroadcastReceiver

mIsStarting = true;

return true;

}

}

return false;

}

/**

* Register a headset profile listener

* @return false    if device does not support bluetooth or current platform does not supports

*                  use of SCO for off call or error in getting profile proxy.

*/

@TargetApi(Build.VERSION_CODES.HONEYCOMB)

private boolean startBluetooth11()

{

Log.d(TAG, "startBluetooth11"); //$NON-NLS-1$

// Device support bluetooth

if (mBluetoothAdapter != null)

{

if (mAudioManager.isBluetoothScoAvailableOffCall())

{

// All the detection and audio connection are done in mHeadsetProfileListener

if (mBluetoothAdapter.getProfileProxy(mContext,

mHeadsetProfileListener,

BluetoothProfile.HEADSET))

{

return true;

}

}

}

return false;

}

/**

* API 

* Unregister broadcast receivers and stop Sco audio connection

* and cancel count down.

*/

private void stopBluetooth()

{

Log.d(TAG, "stopBluetooth"); //$NON-NLS-1$

if (mIsCountDownOn)

{

mIsCountDownOn = false;

mCountDown.cancel();

}

// Need to stop Sco audio connection here when the app

// change orientation or close with headset still turns on.

mContext.unregisterReceiver(mBroadcastReceiver);

mAudioManager.stopBluetoothSco();

mAudioManager.setMode(AudioManager.MODE_NORMAL);

}

/**

* API >= 11

* Unregister broadcast receivers and stop Sco audio connection

* and cancel count down.

*/

@TargetApi(Build.VERSION_CODES.HONEYCOMB)

protected void stopBluetooth11()

{

Log.d(TAG, "stopBluetooth11"); //$NON-NLS-1$

if (mIsCountDownOn)

{

mIsCountDownOn = false;

mCountDown11.cancel();

}

if (mBluetoothHeadset != null)

{

// Need to call stopVoiceRecognition here when the app

// change orientation or close with headset still turns on.

mBluetoothHeadset.stopVoiceRecognition(mConnectedHeadset);

mContext.unregisterReceiver(mHeadsetBroadcastReceiver);

mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);

mBluetoothHeadset = null;

}

}

/**

* Broadcast receiver for API 

* Handle headset and Sco audio connection states.

*/

private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver()

{

@SuppressWarnings({"deprecation", "synthetic-access"})

@Override

public void onReceive(Context context, Intent intent)

{

String action = intent.getAction();

if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED))

{

mConnectedHeadset = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

BluetoothClass bluetoothClass = mConnectedHeadset.getBluetoothClass();

if (bluetoothClass != null)

{

// Check if device is a headset. Besides the 2 below, are there other

// device classes also qualified as headset?

int deviceClass = bluetoothClass.getDeviceClass();

if (deviceClass == BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE

|| deviceClass == BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET)

{

// start bluetooth Sco audio connection.

// Calling startBluetoothSco() always returns faIL here,

// that why a count down timer is implemented to call

// startBluetoothSco() in the onTick.

mAudioManager.setMode(AudioManager.MODE_IN_CALL);

mIsCountDownOn = true;

mCountDown.start();

// override this if you want to do other thing when the device is connected.

onHeadsetConnected();

}

}

Log.d(TAG, mConnectedHeadset.getName() + " connected"); //$NON-NLS-1$

}

else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED))

{

Log.d(TAG, "Headset disconnected"); //$NON-NLS-1$

if (mIsCountDownOn)

{

mIsCountDownOn = false;

mCountDown.cancel();

}

mAudioManager.setMode(AudioManager.MODE_NORMAL);

// override this if you want to do other thing when the device is disconnected.

onHeadsetDisconnected();

}

else if (action.equals(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED))

{

int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,

AudioManager.SCO_AUDIO_STATE_ERROR);

if (state == AudioManager.SCO_AUDIO_STATE_CONNECTED)

{

mIsOnHeadsetSco = true;

if (mIsStarting)

{

// When the device is connected before the application starts,

// ACTION_ACL_CONNECTED will not be received, so call onHeadsetConnected here

mIsStarting = false;

onHeadsetConnected();

}

if (mIsCountDownOn)

{

mIsCountDownOn = false;

mCountDown.cancel();

}

// override this if you want to do other thing when Sco audio is connected.

onScoAudioConnected();

Log.d(TAG, "Sco connected"); //$NON-NLS-1$

}

else if (state == AudioManager.SCO_AUDIO_STATE_DISCONNECTED)

{

Log.d(TAG, "Sco disconnected"); //$NON-NLS-1$

// Always receive SCO_AUDIO_STATE_DISCONNECTED on call to startBluetooth()

// which at that stage we do not want to do anything. Thus the if condition.

if (!mIsStarting)

{

mIsOnHeadsetSco = false;

// Need to call stopBluetoothSco(), otherwise startBluetoothSco()

// will not be successful.

mAudioManager.stopBluetoothSco();

// override this if you want to do other thing when Sco audio is disconnected.

onScoAudioDisconnected();

}

}

}

}

};

/**

* API 

* Try to connect to audio headset in onTick.

*/

private CountDownTimer mCountDown = new CountDownTimer(10000, 1000)

{

@SuppressWarnings("synthetic-access")

@Override

public void onTick(long millisUntilFinished)

{

// When this call is successful, this count down timer will be canceled.

mAudioManager.startBluetoothSco();

Log.d(TAG, "\nonTick start bluetooth Sco"); //$NON-NLS-1$

}

@SuppressWarnings("synthetic-access")

@Override

public void onFinish()

{

// Calls to startBluetoothSco() in onStick are not successful.

// Should implement something to inform user of this failure

mIsCountDownOn = false;

mAudioManager.setMode(AudioManager.MODE_NORMAL);

Log.d(TAG, "\nonFinish fail to connect to headset audio"); //$NON-NLS-1$

}

};

/**

* API >= 11

* Check for already connected headset and if so start audio connection.

* Register for broadcast of headset and Sco audio connection states.

*/

private 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)

{

Log.d(TAG, "Profile listener onServiceDisconnected"); //$NON-NLS-1$

stopBluetooth11();

}

@SuppressWarnings("synthetic-access")

@TargetApi(Build.VERSION_CODES.HONEYCOMB)

@Override

public void onServiceConnected(int profile, BluetoothProfile proxy)

{

Log.d(TAG, "Profile listener onServiceConnected"); //$NON-NLS-1$

// mBluetoothHeadset is just a headset profile,

// it does not represent a headset device.

mBluetoothHeadset = (BluetoothHeadset) proxy;

// If a headset is connected before this application starts,

// ACTION_CONNECTION_STATE_CHANGED will not be broadcast.

// So we need to check for already connected headset.

List devices = mBluetoothHeadset.getConnectedDevices();

if (devices.size() > 0)

{

// Only one headset can be connected at a time,

// so the connected headset is at index 0.

mConnectedHeadset = devices.get(0);

onHeadsetConnected();

// Should not need count down timer, but just in case.

// See comment below in mHeadsetBroadcastReceiver onReceive()

mIsCountDownOn = true;

mCountDown11.start();

Log.d(TAG, "Start count down"); //$NON-NLS-1$

}

// During the active life time of the app, a user may turn on and off the headset.

// So register for broadcast of connection states.

mContext.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.

mContext.registerReceiver(mHeadsetBroadcastReceiver,

new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED));

}

};

/**

*  API >= 11

*  Handle headset and Sco audio connection states.

*/

private BroadcastReceiver mHeadsetBroadcastReceiver = new BroadcastReceiver()

{

@SuppressWarnings("synthetic-access")

@TargetApi(Build.VERSION_CODES.HONEYCOMB)

@Override

public void onReceive(Context context, Intent intent)

{

String action = intent.getAction();

int state;

if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED))

{

state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,

BluetoothHeadset.STATE_DISCONNECTED);

Log.d(TAG, "\nAction = " + action + "\nState = " + state); //$NON-NLS-1$ //$NON-NLS-2$

if (state == BluetoothHeadset.STATE_CONNECTED)

{

mConnectedHeadset = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

// Calling startVoiceRecognition always returns false here,

// that why a count down timer is implemented to call

// startVoiceRecognition in the onTick.

mIsCountDownOn = true;

mCountDown11.start();

// override this if you want to do other thing when the device is connected.

onHeadsetConnected();

Log.d(TAG, "Start count down"); //$NON-NLS-1$

}

else if (state == BluetoothHeadset.STATE_DISCONNECTED)

{

// Calling stopVoiceRecognition always returns false here

// as it should since the headset is no longer connected.

if (mIsCountDownOn)

{

mIsCountDownOn = false;

mCountDown11.cancel();

}

mConnectedHeadset = null;

// override this if you want to do other thing when the device is disconnected.

onHeadsetDisconnected();

Log.d(TAG, "Headset disconnected"); //$NON-NLS-1$

}

}

else // audio

{

state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_AUDIO_DISCONNECTED);

Log.d(TAG, "\nAction = " + action + "\nState = " + state); //$NON-NLS-1$ //$NON-NLS-2$

if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED)

{

Log.d(TAG, "\nHeadset audio connected");  //$NON-NLS-1$

mIsOnHeadsetSco = true;

if (mIsCountDownOn)

{

mIsCountDownOn = false;

mCountDown11.cancel();

}

// override this if you want to do other thing when headset audio is connected.

onScoAudioConnected();

}

else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED)

{

mIsOnHeadsetSco = false;

// The headset audio is disconnected, but calling

// stopVoiceRecognition always returns true here.

mBluetoothHeadset.stopVoiceRecognition(mConnectedHeadset);

// override this if you want to do other thing when headset audio is disconnected.

onScoAudioDisconnected();

Log.d(TAG, "Headset audio disconnected"); //$NON-NLS-1$

}

}

}

};

/**

* API >= 11

* Try to connect to audio headset in onTick.

*/

private CountDownTimer mCountDown11 = new CountDownTimer(10000, 1000)

{

@TargetApi(Build.VERSION_CODES.HONEYCOMB)

@SuppressWarnings("synthetic-access")

@Override

public void onTick(long millisUntilFinished)

{

// First stick calls always returns false. The second stick

// always returns true if the countDownInterval is set to 1000.

// It is somewhere in between 500 to a 1000.

mBluetoothHeadset.startVoiceRecognition(mConnectedHeadset);

Log.d(TAG, "onTick startVoiceRecognition"); //$NON-NLS-1$

}

@SuppressWarnings("synthetic-access")

@Override

public void onFinish()

{

// Calls to startVoiceRecognition in onStick are not successful.

// Should implement something to inform user of this failure

mIsCountDownOn = false;

Log.d(TAG, "\nonFinish fail to connect to headset audio"); //$NON-NLS-1$

}

};}

(2013年4月30日)编辑以在必要时更改为@TargetApi(Build.VERSION_CODES.HONEYCOMB)。如果您对API 8或9的java.lang.NoClassDefFoundError有问题,只需删除所有API> = 11代码。startBluetoothSco()适用于所有API版本。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐