Responding to media buttons Android


Responding to media buttons

https://developer.android.com/guide/topics/media-apps/mediabuttons

미디어 버튼은 안드로이드 장치상에서 또는 보조장치에서 볼 수 있는 하드웨어 버튼이다. 예를 들어, 블루투스 헤드셋에 있는 일시정지/재생 버튼과 같은 것이다. 사용자가 미디어 버튼을 누르면 안드로이드는 키이벤트를 생성하는데 키 코드를 담고 있어 어떤 버튼인지 나타낸다. 미디어 버튼 키이벤트를 위한 키 코드는 상수로서 KEYCODE_MEDIA로 시작하는 상수이다. (예를 들어, KEYCODE_MEDIA_PLAY)

앱은 우선순위에 따라 나열된 세가지 상황에서 미디어 버튼 이벤트를 다룰 수 있어야 한다.
- 앱의 UI액티비티가 보여질 때
- UI액티비티가 가려졌고 앱의 미디어 세션이 활성화되었을 때
- UI액티비티가 가려졌고 앱의 미디어 세션이 비활성화되었고 재시작할 필요가 있을 때

포어그라운드 액티비티에서 미디어 버튼 다루기

포어그라운드 액티비티는 onKeyDown() 메소드를 통해 미디어 버튼 키 이벤트를 받는다. 안드로이드의 버전에 다라서 미디어 컨트롤러에 이벤트를 전달하는 두가지 방법이 있다.

- 안드로이드 5.0 또는 이 후를 실행한다면 FLAG_HANDLES_MEDIA_BUTTONS MediaBrowserCompat.ConnectionCallback.onConnected 를 호출한다. 이는 미디어 세션 콜백으로 키코드로 해석하는 미디어 컨트롤러의 dispatchMediaButtonEvent() 를 호출한다. 
- 안드로이드 5.0 이전은 자체적으로 onKeyDown() 를 수정할 필요가 있다. (세부사항을 위해서는 Handling media buttons in an active media session을 살펴보라) 다음 코드 스닙샷은 어떻게 키 코드를 가로채고 dispatchMediaButtonEvent() 를 호출한다. 이벤트가 처리되었는지를 가리키려면 true를 반환한다.

fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    return super.onKeyDown(keyCode, event)
  }
  when (keyCode) {
    KeyEvent.KEYCODE_MEDIA_PLAY -> {
      yourMediaController.dispatchMediaButtonEvent(event)
      return true
    }
  }
  return super.onKeyDown(keyCode, event)
}

미디어 세션 찾기

만약 포어그라운드 액티비티가 이벤트를 다루지 않으면 안드로이드는 다룰 수 있는 미디어 세션을 찾는다. 다시, 안드로이드의 실행중인 버전에 기반해 미디어 세션을 찾는 두가지의 방법이 있다.

- 만약 안드로이드 8.0 이상이면 시스템은 오디오를 지역적으로 재생한 미디어세션을 가진 마지막 앱을 찾는다. 만약 세션이 여전히 활성화되어 있으면 안드로이드는 그 곳에 직접적으로 이벤트를 보낸다. 그렇지 않으면, 만약 세션이 활성화 상태가 아니고 미디어 버튼 리시버가 있으면 안드로이드는 리시버에게 이벤트를 보내는데 세션을 재시작하고 이벤트를 받을 수 있게 된다. (자세한 사항은 Using media buttons to restart an inactive media session을 살펴본다) 만약 세션이 미디어 버튼 리시버를 가지고 있지 않으면 시스템은 미디어 버튼 이벤트를 무시하고 아무 일도 발생하지 않는다. 로직은 다음 다이어그램에 보여진다.


안드로이드 8.0 이전이면 시스템은 활성 미디어 세션으로 이벤트를 보낸다. 만약 다중 활성 미디어 세션이 있으면 안드로이드는 미디어 세션을 선택시도 하는데 중지된 것 보다 재생 준비중 (버퍼링/연결중), 재생중, 또는 일시정지됨 을 선택한다. 만약 활성 세션이 없으면 안드로이드는 이벤트를 최근 활성 세션에 보낸다. (자세한 사항은 Using media buttons to restart an inactive media session 을 살펴본다) 로직은 다음 다이어그램에 보여진다.


활성 미디어 세션에서 미디어 버튼 다루기

안드로이드 5.0 이상에서 안드로이드는 미디어 버튼 이벤트는 자동적으로 활성 미디어 세션의 onMediaButtonEvent()를 디스패치한다. 기본적으로 이 콜백은 키이벤트를 키 코드에 맞춰 적절한 미디어 세션 콜백 메소드로 해석한다.

안드로이드 5.0 이 전에서 안드로이드는 미디어 버튼 이벤트를 ACTION_MEDIA_BUTTON 액션으로 인텐트를 브로드캐스트함으로서 미디어 버튼이벤트를 처리한다. 앱은 반드시 이들 인텐트를 가로채려면 BroadcastReceiver 를 등록해야 한다. MediaButtonReceiver 클래스는 이 목적으로 특별히 디자인 된 클래스 이다. 이 것은 안드로이드 미디어 컴팻 라이브러리의 편의 클래스로 ACTION_MEDIA_BUTTON 을 다루고 적절한 MediaSessionCompat.Callback 메소드 콜로 들어오는 인텐트를 해석한다.

MediaButtonReceiver 는 짧은 생명주기를 가진 브로드캐스트 리시버이다. 들어오는 인텐트를 미디어 세션을 관리하는 서비스로 전달한다. 만약 안드로이드 5.0 이전에서 시스템에 미디어 버튼을 사용하려면 반드시 MediaButtonReceiver 를 MEDIA_BUTTON 인텐트 필터와 함께 매니페스트에 추가해야 한다.

<receiver android:name="android.support.v4.media.session.MediaButtonReceiver">
  <intent-filter>
    <action android:name="android.intent.action.MEDIA_BUTTON"/>
  </intent-filter>
</receiver>

BroadcastReceiver 는 인턴트를 서비스에 전달한다. 인텐트를 해석하고 미디어 세션에 콜백을 생성하려면 서비스의 onStartCommand() 내의 MediaButtonReceiver.handleIntent() 메소드를 포함한다. 이 는 키코드를 해석해 적절한 세션 콜백 메소드로 해석한다.

private val mMediaSessionCompat: MediaSessionCompat = ...

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
  MediaButtonReceiver.handleIntent(mMediaSessionCompat, intent)
  return super.onStartCommand(intent, flags, startId)
}

일러두기: MediaBrowserServiceCompat 을 가지지 않는다면 ACTION_MEDIA_BUTTON인텐트 필터를 서비스에 추가할 수 있다. 더 자세한 사항은 MediaButtonReceiver 문서를 참고한다.

비활성 미디어 세션을 재시작하기위해 미디어 버튼 사용하기

만약 안드로이드가 마지막 활성 미디어 세션을 확인한다면 매니페스트 등록된 컴포넌트(서비스나  BroadcastReceiver와 같은)로 ACTION_MEDIA_BUTTON 을 보냄으로서 세션을 재시작한다.

이는 대부분의 오디오 앱의 경우인 UI가 보이지 않는 동안 앱의 재생 재시작하게 한다.

이 작용은 MediaSessionCompat 을 사용할 때 자동적으로 활성화된다. 만약 안드로이드 프레임워크의 미디어 세션이나 지원라이브러리 24.0.0 ~ 25.1.1 을 사용한다면 반드시 setMediaButtonReceiver 를 호출해 미디어버튼이 비활성 미디어 세션을 재시작할 수 있게 한다.

안드로이드 5.0 이 후에서는 널 미디어 버튼 리시버를 설정해 비활성화 할 수 있다.

// MediaSessionCompat 생성하기
mMediaSession = MediaSessionCompat(context, LOG_TAG)
mMediaSession.setMediaButtonReceiver(null)

일러두기: 안드로이드 5.0 이전의 시스템에서 구동한다면 활성 세션을 위해 등록한 MediaButtonReceiver 는 비활성 세션에서도 이벤트를 받을 수 있다. 이를 비활성화 할 수 있는 방법은 없다.

미디어 버튼 핸들러 커스터마이징

onMediaButtonEvent() 를 위한 기본 작용은 키코드, 미디어 세션의 현 상태, 지원되는 액션의 리스트를 얻는 것이다. 예를 들면, KEYCODE_MEDIA_PLAY는 onPlay()를 호출한다.

모든 앱에서 일관된 미디어 버튼 경험을 제공하려면 기본작용을 사용하고 특정 목적에서만 변경하도록 한다. 만약 미디어 버튼이 커스텀 핸들링이 필요하다면 콜백의 onMediaButtonEvent() 를 오버라이드하고 intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT) 를 사용하여 키 이벤트를 얻고, 자체적으로 이벤트를 다루고, true를 반환한다.

갈무리

안드로이드의 모든 버전에서 미디어 버튼 이벤트를 적절히 처리하려면 미디어 세션을 생성할 때 FLAG_HANDLES_MEDIA_BUTTONS를 지정한다.

추가로, 지원할 안드로이드 버전에 따라 다음 요구사항을 처리해야 한다.

안드로이드 5.0 이상에서 구동될 때
- MediaControllerCompat.setMediaController() 를 미디어 컨트롤러의 onConnected()콜백에서 호출
- 미디어 버튼을 통해 비활성 세션에서 재시작하려면 setMediaButtonReceiver()를 호출해 MediaButtonReceiver 를 동적으로 생성하고 PendingIntent를 전달한다

안드로이드 5.0 이전에서 구동될 때
- 미디어 버튼을 다루기 위해 액티비티의 onKeyDown() 을 오버라이드
- 앱의 매니페스트에서 이를 추가함으로서 MediaButtonReceiver 정적 생성









덧글

댓글 입력 영역