Android使用AudioManager切换到听筒模式

Android使用AudioManager切换到听筒模式

    • 一、目标
    • 二、参考资料
      • 1. 相关的功能类
      • 2. 参考文章
    • 三、测试结果
      • 1. 手机
      • 2. 有线耳机
      • 3. 蓝牙音箱
      • 4. 有线耳机+蓝牙音箱
    • 四、实现代码
    • 五、遗留问题
    • 六、接下来
    • 七、Finally

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

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

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

一、目标

将音频切换到听筒输出。

二、参考资料

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. 有线耳机+蓝牙音箱

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

四、实现代码

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

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

你可能感兴趣的:(神马笔记)