Android 语音通话类开发、耳机(蓝牙有线)和 AudioManager 的一些知识点
AudioManager介绍
官方注释:AudioManager provides access to volume and ringer mode control.
也就是说AudioManager
提供对音量和铃声模式控制的访问,涉及到手机音量及铃声的有一些实际的应用场景,比如:来电响铃,通话中,语音视频网络电话中等。针对各种场景,AudioManager
提供了几个模式,通过setMode
方法进行设置,用于将手机的音频环境切换到相应的状态,下面介绍一下AudioManager
的setMode
方法。
AudioManager.setMode
/**
* 设置音频模式.
* <p>
* 音频模式包括音频路由和电话层的行为。因此,此方法仅应由类似音频管理的平台范围的应用程序或电话类应用程序使用
* 。特别注意,MODE_IN_CALL模式仅应在电话应用程序拨打电话时使用,因为它将导致来自无线电层的信号流入平台混音器。
* @param mode 可用的模式参数为 MODE_NORMAL, MODE_RINGTONE, MODE_IN_CALL 或者 MODE_IN_COMMUNICATION.
* 通知HAL当前的音频状态,以便它可以适当地路由音频.
*/
public void setMode(int mode) {
final IAudioService service = getService();
try {
service.setMode(mode, mICallBack, mApplicationContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
setMode
可用的模式为MODE_NORMAL
, MODE_RINGTONE
,MODE_IN_CALL
或者 MODE_IN_COMMUNICATION
.
-
MODE_NORMAL
:正常的音频模式,不振铃,不建立通话。 -
MODE_RINGTONE
:振铃模式,用于手机来电、音视频通话来电、网络电话来电提醒。 -
MODE_IN_CALL
:通话模式,仅应在电话应用程序拨打电话时使用。 -
MODE_IN_COMMUNICATION
:通话模式,用于音频/视频聊天建立之后或网络电话接通之后。
下面这两个模式也是Mode常量里的,但是不用于setMode方法:
-
MODE_INVALID
:无效的音频模式 -
MODE_CURRENT
:当前的音频模式,用于将音频路由应用于当前模式。
一个音视频通话APP从响铃到挂断各个阶段都需要调用setMode方法手动设置AudioManager的Mode,系统并不会给你自动转换。
设置流程为:MODE_NORMAL
(正常状态)-接到呼叫-> MODE_RINGTONE
(振铃)-接通->MODE_IN_COMMUNICATION
(通话)-挂断->MODE_NORMAL
(正常状态)
声音播放
接通通话之后我们将mode设置为MODE_IN_COMMUNICATION
,这时需要播放对方的声音,默认在MODE_IN_COMMUNICATION
模式下使用听筒播放。
有线耳机
有线耳机插入之后就可以将音频从听筒转到耳机里,如果耳机有麦克风的话也会直接应用。
判断是否连接了有线耳机
fun isWiredHeadsetOn(): Boolean {
val audioManager = Utils.getApp().getSystemService(Context.AUDIO_SERVICE) as AudioManager
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val devices = audioManager.getDevices(AudioManager.GET_DEVICES_ALL)
devices.any { device ->
device.type == AudioDeviceInfo.TYPE_USB_HEADSET || device.type == AudioDeviceInfo.TYPE_WIRED_HEADSET
}
} else {
audioManager.isWiredHeadsetOn
}
}
注册有线耳机插入拔出广播接收者
class HeadsetReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// 耳机插入状态 0 拔出,1 插入
val state = intent.getIntExtra("state", 0) != 0
// 耳机类型
val name = intent.getStringExtra("name")
// 耳机是否带有麦克风 0 没有,1 有
val mic = intent.getIntExtra("microphone", 0) != 0
}
}
val headsetFilter = IntentFilter(Intent.ACTION_HEADSET_PLUG)
mContext.registerReceiver(headsetReceiver, headsetFilter)
蓝牙耳机
判断是否连接了蓝牙耳机
fun isBluetoothHeadsetConnected(): Boolean {
val adapter = BluetoothAdapter.getDefaultAdapter()
return BluetoothProfile.STATE_CONNECTED == adapter.getProfileConnectionState(
BluetoothProfile.HEADSET
)
}
注册蓝牙耳机连接断开广播接收者
class HeadsetReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val adapter = BluetoothAdapter.getDefaultAdapter()
val state = adapter.getProfileConnectionState(BluetoothProfile.HEADSET)
if (BluetoothProfile.STATE_DISCONNECTED == state) {
LogUtils.d("蓝牙耳机断开连接")
}
if (BluetoothProfile.STATE_CONNECTED == state) {
LogUtils.d("蓝牙耳机连接")
}
}
}
val headsetFilter = IntentFilter(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)
mContext.registerReceiver(headsetReceiver, headsetFilter)
蓝牙耳机开启音频传输
接通语音电话之后,我们需要使用蓝牙耳机来播放对方音频并使用蓝牙的mic传输自己的音频给对方,需要用到audioManager.startBluetoothSco()
开启了SCO,这是一个异步的方法,开启SCO成功之后会发送一个Action为ACTION_SCO_AUDIO_STATE_UPDATED
的广播,我们注册这个广播的接受者并判断返回的state为SCO_AUDIO_STATE_CONNECTED
时即可确定SCO开启成功,此时我们通过audioManager.setBluetoothScoOn(true)
请求使用SCO耳机开始通信。
申请权限
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
//开启SCO
audioManager.startBluetoothSco()
registerReceiver(object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent) {
val state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1)
if (AudioManager.SCO_AUDIO_STATE_CONNECTED == state) {
//请求使用SCO
audioManager.setBluetoothScoOn(true)
unregisterReceiver(this)
} else { //等待一秒后再尝试启动SCO
try {
Thread.sleep(1000)
} catch (e: InterruptedException) {
e.printStackTrace()
}
audioManager.startBluetoothSco()
}
}
}, IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED))
蓝牙耳机关闭音频传输
if(mAudioManager.isBluetoothScoOn()){
//停止使用SCO通信
mAudioManager.setBluetoothScoOn(false);
//关闭SCO
mAudioManager.stopBluetoothSco();
}
免提
通过AudioManager
的成员函数setSpeakerphoneOn(true)
开启免提模式之后,会将已经连接的耳机(蓝牙、有线)声音转换至扬声器播放,麦克风也会使用手机的。
val audioManager= mContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
//开启免提
audioManager.isSpeakerphoneOn=true
//关闭免提
audioManager.isSpeakerphoneOn=false
参考资料
www.cnblogs.com/wsfjlagr/p/…