[Android] MediaBrowserServiceCompat이용하여 MediaButtonClickEvent 구현 (feat. AudioFocus받아오기)

2022. 8. 17. 17:35Android

https://fanxy0n.tistory.com/8

 

[Android] MediaButtonReceiver 이용하여 Bluetooth Earbuds Button Click Event 받기

VR 특성상 사용자 입력을 받아 이벤트를 처리하는 것이 2D Display에 비해 제한적이기 때문에 어떻게 컨트롤러의 버튼 onClick 이벤트를 받을 것인지 고민이 되었다. 모바일 디바이스에서 VR 앱이라

fanxy0n.tistory.com

⬆️(이전 포스팅 참조)

MediaBrowserServiceCompat을 상속받은 Service로 블루투스 earbuds의 onClickEvent를 처리하던 중 문제가 발생하였다.

그것은 바로 미디어 버튼 이벤트의 포커스가 가장 최근에 오디오 패스 열었던 앱에 한정되어 있다는 것.쉽게 말해 유튜브 뮤직 앱을 실행하여 백그라운드 서비스로 음악을 플레이하면서 나의 앱을 켜 earbuds의 버튼을 클릭하면, 이벤트(MEDIA_BUTTON_EVENT)가 모두 유튜브 뮤직으로 가버리는 것이다.이 문제를 해결하기 위해 audioFocus를 현재 나의 앱에 가져올 수 있는 방법을 고안하였다.

 

1.  오디오 포커스 얻기: AudioManager의 requestAudioFocus() 메소드 호출
2. 이벤트 포커스 얻기: MediaPlayer로 임의의 효과음 start()

public class MediaButtonService extends MediaBrowserServiceCompat {
    ...

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) { // == onResume in Activity
        MediaButtonReceiver.handleIntent(mediaSession, intent); //first parameter should be null
        getAudioFocus();
        return super.onStartCommand(intent, flags, startId);
    }

    ...

    private void getAudioFocus() {
        audioManager = (AudioManager) getBaseContext().getSystemService(Context.AUDIO_SERVICE);
        AudioAttributes audioAttributes = new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_MEDIA)
                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                .build();
        audioFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
                .setAudioAttributes(audioAttributes)
                .setOnAudioFocusChangeListener(audioFocusChangeListener)
                .setAcceptsDelayedFocusGain(true)
                .setWillPauseWhenDucked(true)
                .build();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            Log.d(TAG, "AudioFocus >> requestAudioFocus");
            audioManager.requestAudioFocus(audioFocusRequest);
        }
        MediaPlayer mediaPlayer = MediaPlayer.create(this, R.raw.sound_magical_effect);
        mediaPlayer.start();
    }

    private AudioManager.OnAudioFocusChangeListener audioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() {
        @Override
        public void onAudioFocusChange(int i) {
            switch (i) {
                case AudioManager.AUDIOFOCUS_GAIN:
                    // 이제부터 AudioFocus는 우리 앱의 소유!
                    Log.d(TAG, "AudioFocus >> AUDIOFOCUS_GAIN");
                    break;
                case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
                    // 일시적으로 AudioFocus를 가져온다.
                    Log.d(TAG, "AudioFocus >> AUDIOFOCUS_GAIN_TRANSIENT");
                    break;
                case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
                    // 15초 이상 점유하면 안 된다. 앱의 소리가 나지만, Background App의 사운드가 작게 들릴 수 있다.
                    Log.d(TAG, "AudioFocus >> AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK");
                    break;
                case AudioManager.AUDIOFOCUS_LOSS:
                    Log.d(TAG, "AudioFocus >> AUDIOFOCUS_LOSS");
                    break;
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                    // 일시적인 LOSS로, 잠깐 사용한 App이 점유를 끝내면 가장 마지막에 GAIN한 App이 AudioFocus를 점유한다.
                    Log.d(TAG, "AudioFocus >> AUDIOFOCUS_LOSS_TRANSIENT");
                    break;
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                    // 볼륨을 낮추는 것을 권장한다.
                    Log.d(TAG, "AudioFocus >> AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK");
                    break;
                case AudioManager.AUDIOFOCUS_REQUEST_FAILED:
                    // 요청 실패할 경우의 로직을 추가한다.
                    Log.d(TAG, "AudioFocus >> AUDIOFOCUS_REQUEST_FAILED");
                    break;
            }
        }
    };
}

Service의 onStartCommand()가 불릴때마다(Service가 Active상태로 진입할 때마다) 오디오의 포커스를 가져온다.

이렇게 하면 백그라운드에서 다른 앱(편의상 이하 YT music)에서 재생되고 있던 오디오가 fade out되면서 audio의 포커스를 가져올 수 있다.

그러나 가장 최근에 열린 오디오 패스는 여전히 YT music에 있으므로 현재 나의 앱에서 강제로 미디어를 실행하기 전까지 모든 미디어 버튼 이벤트는 YT music에 전달된다. 

(실제로 earbuds의 버튼을 눌러 미디어 버튼 이벤트를 발생시키면 YT music에서 듣고 있던 음악이 실행된다)

 

때문에 오디오 포커스를 가져온 직후에 MediaPlayer로 진입효과음을 플레이 해주어 오디오 패스를 열고 미디어 버튼 이벤트의 포커스도 현재 나의 앱으로 가져온다.

이렇게 하면 다른 앱에서 미디어 재생 중에 나의 앱을 실행한다고 해도 MediaButtonEvent를 다른 앱에 뺏기지 않고 문제없이 가지고 올 수 있다.

 

Reference

https://d2fault.github.io/2021/07/19/20210710-android-audio-focus-1/

 

[Android] AudioFocus 알아보기(1) - 왜, 어떻게 사용하는가

AudioFocus가 적용되어 있지 않은 애플리케이션에 AudioFocus를 적용해야만 하는 일이 생겼다. 지금까지는 쓸 일도, 그럴 필요도 없었기에 이 친구의 존재를 모르고 있었는데 적용할 일이 생겼으니 공

d2fault.github.io

https://m.blog.naver.com/10hsb04/221636354098

 

[Android] Background에서 음원 재생하기

Background 에서 음원 재생하기 1. 사용할 음원 넣기 2. 서비스 생성 3. 서비스 시작하기 및 종료하기 1. ...

blog.naver.com