Android使用AudioManager切换到听筒模式

Android使用AudioManager切换到听筒模式

经过几个阶段的开发,神马笔记已经实现了录音和播放录音的功能。

目前音频输出设备都是手机的扬声器,如果是在公共场合,又没有耳机,使用扬声器就不是特别方便。

因此,需要切换到听筒输出。

一、目标

将音频切换到听筒输出。

二、参考资料

1. 相关的功能类

功能类 描述
AudioManager 音频管理类,提供了音频管理的所有接口。
BroadcastReceiver 获取音频设备变化事件。

2. 参考文章

文章 说明
Android监听靠近听筒,音频播放切换听筒和外放 实用
android播放语音,切换听筒模式遇到的问题
Android5.0版本之后切换听筒模式
关于Android音频焦点的简单实现
《Android优化专题》——音频播放

三、测试结果

测试设备:荣耀畅玩7

MODE_RINGTONEMODE_NORMAL测试结果完全一致。

系统会根据当前连接的音频输出设备,决定Speakerphone最终的输出设备。

Speakerphone是逻辑设备,并不是最终的物理设备。

最终的物理设备根据设备连接情况以及所设置的模式而决定,可能是

  • 手机听筒
  • 手机扬声器
  • 有线耳机
  • 蓝牙音箱

1. 手机

Mode SpeakerphoneOn 结果
MODE_IN_COMMUNICATION true 手机扬声器
false 手机听筒
MODE_NORMAL true 手机扬声器
false 手机扬声器

2. 有线耳机

Mode SpeakerphoneOn 结果
MODE_IN_COMMUNICATION true 手机扬声器
false 有线耳机
MODE_NORMAL true 有线耳机
false 有线耳机

3. 蓝牙音箱

Mode SpeakerphoneOn 结果
MODE_IN_COMMUNICATION true 手机扬声器
false 手机听筒
MODE_NORMAL true 蓝牙音箱
false 蓝牙音箱

4. 有线耳机+蓝牙音箱

测试结果与有线耳机一致,音频优先输出到有线耳机上。

四、实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
public class AudioHelper {

private static final String TAG = "AudioHelper";

public static final int TYPE_RECEIVER = 1;
public static final int TYPE_SPEAKER = 2;

AudioManager audioManager;

HeadsetPlugReceiver headsetPlugReceiver;
BiConsumer<AudioHelper, Integer> headsetPlugListener;

NoisyAudioStreamReceiver noisyAudioStreamReceiver;
Consumer<AudioHelper> noisyAudioListener;

FragmentActivity context;

public AudioHelper(FragmentActivity context) {
this.context = context;

this.audioManager = (AudioManager)(context.getSystemService(Context.AUDIO_SERVICE));
context.getLifecycle().addObserver(new LifecycleListener());
}

public void setDestination(int type) {

switch (type) {
case TYPE_RECEIVER: {
this.setDestination(AudioManager.MODE_IN_COMMUNICATION, false);
break;
}
case TYPE_SPEAKER: {
this.setDestination(AudioManager.MODE_NORMAL, true);
break;
}
}
}

public void setMode(int mode) {

// setMode directly
audioManager.setMode(mode);
}

public int getMode() {
return audioManager.getMode();
}

public void setSpeakerphoneOn(boolean value) {

// setSpeakerphone directly
audioManager.setSpeakerphoneOn(value);
}

public boolean isSpeakerphoneOn() {
return audioManager.isSpeakerphoneOn();
}

public boolean isWiredHeadsetOn() {

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
AudioDeviceInfo[] audioDevices = audioManager.getDevices(AudioManager.GET_DEVICES_ALL);

for (AudioDeviceInfo deviceInfo : audioDevices) {
if (deviceInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADPHONES
|| deviceInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET) {
return true;
}

}

return false;
} else {
return audioManager.isWiredHeadsetOn();
}
}

public void setOnHeadsetPlugListener(BiConsumer<AudioHelper, Integer> listener) {
this.headsetPlugListener = listener;
}

public void setOnNoisyAudioListener(Consumer<AudioHelper> listener) {
this.noisyAudioListener = listener;
}

void setDestination(int mode, boolean speaker) {

// set params directly

// must be first
audioManager.setMode(mode);

// and second
audioManager.setSpeakerphoneOn(speaker);
}

private void registerHeadsetPlugReceiver(){
if (headsetPlugReceiver == null) {
headsetPlugReceiver = new HeadsetPlugReceiver();

IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_HEADSET_PLUG);

context.registerReceiver(headsetPlugReceiver, filter);
}
}

private void unregisterHeadsetPlugReceiver(){
if (headsetPlugReceiver != null) {

context.unregisterReceiver(headsetPlugReceiver);

headsetPlugReceiver = null;
}
}

private void registerNoisyAudioStreamReceiver(){
if (noisyAudioStreamReceiver == null) {
this.noisyAudioStreamReceiver = new NoisyAudioStreamReceiver();

IntentFilter filter = new IntentFilter();
filter.addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY);

context.registerReceiver(noisyAudioStreamReceiver, filter);
}
}

private void unregisterNoisyAudioStreamReceiver(){
if (noisyAudioStreamReceiver != null) {

context.unregisterReceiver(noisyAudioStreamReceiver);

this.noisyAudioStreamReceiver = null;
}
}

/**
*
*/
private class LifecycleListener implements LifecycleObserver {

@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
void onCreate() {

registerHeadsetPlugReceiver();
registerNoisyAudioStreamReceiver();

}

@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
void onDestroy() {

unregisterHeadsetPlugReceiver();
unregisterNoisyAudioStreamReceiver();

context.getLifecycle().removeObserver(this);
}
}

/**
*
*/
private class HeadsetPlugReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
String name = intent.getStringExtra("name");
name = (name == null)? "": name;

int state = intent.getIntExtra("state", 0);

int microphone = intent.getIntExtra("microphone", 0);

if (headsetPlugListener != null) {
headsetPlugListener.accept(AudioHelper.this, state);
}

Log.w(TAG, name + ", state = " + state + ", microphone = " + microphone);
}

}

/**
*
*/
private class NoisyAudioStreamReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
String action = AudioManager.ACTION_AUDIO_BECOMING_NOISY;

if (action.equals(intent.getAction())) {

if (noisyAudioListener != null) {
noisyAudioListener.accept(AudioHelper.this);
}
}
}
}
}

五、遗留问题

因为是笔记应用,不是音乐应用,不会长时间占用音频设备。

因此暂未处理音频焦点的问题。

六、接下来

使用距离感应器,实现自动切换到听筒模式。

七、Finally

云何得长寿,金刚不坏身。 复以何因缘,得大坚固力。 云何于此经,究竟到彼岸。 愿佛开微密,广为众生说。