Os botões de mídia são botões de hardware encontrados em dispositivos Android e outros dispositivos periféricos, por exemplo, o botão de pausa/reprodução em um fone de ouvido Bluetooth. Quando um usuário pressiona um botão de mídia, o Android gera um KeyEvent
, que contém um código de tecla que identifica o botão. Os códigos de tecla para KeyEvents de mídia são constantes que começam com KEYCODE_MEDIA
(por exemplo, KEYCODE_MEDIA_PLAY
).
Os apps precisam conseguir processar eventos de botão de mídia em três casos, nesta ordem de prioridade:
- quando a atividade da IU do app estiver visível;
- quando a atividade da IU estiver oculta, e a sessão de mídia do app estiver ativa;
- quando a atividade da IU estiver oculta, e a sessão de mídia do app estiver inativa e precisar ser reiniciada.
Como processar botões de mídia em uma atividade em primeiro plano
A atividade em primeiro plano recebe o evento de tecla do botão de mídia no método
onKeyDown()
. Dependendo da versão em execução do Android, o sistema roteia o evento para
um controlador de mídia de duas maneiras:
- Se você estiver executando o Android 5.0 (nível 21 da API) ou mais recente, chame
FLAG_HANDLES_MEDIA_BUTTONS
MediaBrowserCompat.ConnectionCallback.onConnected
. Isso vai chamar automaticamente odispatchMediaButtonEvent()
do controlador de mídia, que converte o código de tecla em um callback de sessão de mídia. - Em versões anteriores ao Android 5.0 (API de nível 21), é necessário modificar
onKeyDown()
para processar o evento por conta própria. Consulte Como processar botões de mídia em uma sessão de mídia ativa para ver mais detalhes. O snippet de código a seguir mostra como interceptar o código da chave e chamar dispatchMediaButtonEvent(). Lembre-se de retornartrue
para indicar que o evento foi processado:Kotlin
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) }
Java
@Override boolean onKeyDown(int keyCode, KeyEvent event) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { return super.onKeyDown(keyCode, event); } switch (keyCode) { case KeyEvent.KEYCODE_MEDIA_PLAY: yourMediaController.dispatchMediaButtonEvent(event); return true; } return super.onKeyDown(keyCode, event); }
Como encontrar uma sessão de mídia
Se a atividade em primeiro plano não processar o evento, o Android vai tentar encontrar uma sessão de mídia que faça isso. Novamente, dependendo da versão em execução do Android, há duas maneiras de pesquisar uma sessão de mídia:
Se você estiver executando o Android 8.0 (nível 26 da API) ou versões mais recentes, o sistema vai tentar encontrar o último app com uma MediaSession que reproduziu áudio localmente. Se a sessão ainda estiver ativa, o Android enviará o evento diretamente para ela. Caso contrário, se a sessão não estiver ativa e tiver um receptor de botão de mídia, o Android vai enviar o evento ao receptor, que vai reiniciar a sessão para receber o evento. Consulte Como usar botões de mídia para reiniciar uma sessão de mídia inativa para ver detalhes. Se a sessão não tiver um receptor de botão de mídia, o sistema vai descartar o evento e nada acontecerá. A lógica é mostrada no diagrama a seguir:
Em versões anteriores ao Android 8.0 (API de nível 26), o sistema tenta enviar o evento para uma sessão de mídia ativa. Se houver várias sessões de mídia ativas, o Android vai tentar escolher uma sessão que esteja se preparando para reproduzir (armazenando em buffer/conectando), em reprodução ou pausada, em vez de uma que foi interrompida. Consulte Como processar botões de mídia em uma sessão de mídia ativa para saber mais. Se não houver uma sessão ativa, o Android vai tentar enviar o evento para a sessão ativa mais recente. Consulte Como usar botões de mídia para reiniciar uma sessão de mídia inativa para ver detalhes. A lógica é mostrada no diagrama a seguir.
Como processar botões de mídia em uma sessão de mídia ativa
No Android 5.0 (nível 21 da API) e versões mais recentes, o Android envia automaticamente eventos do botão de mídia para a sessão de mídia ativa chamando
onMediaButtonEvent()
.
Por padrão, esse chamado converte o KeyEvent no método de callback de sessão de mídia apropriado que corresponde ao código de tecla.
Em versões anteriores, o Android processa eventos do botão de mídia transmitindo uma intent
com a ação ACTION_MEDIA_BUTTON
. O app precisa registrar um
BroadcastReceiver para interceptar essas intents. A classe
MediaButtonReceiver
foi criada especificamente para
essa finalidade. É uma classe de conveniência na
biblioteca media-compat do Android que
processa ACTION_MEDIA_BUTTON
e converte as intents recebidas nas
chamadas de método MediaSessionCompat.Callback
apropriadas.
Um MediaButtonReceiver
é um BroadcastReceiver de curta duração. Ele encaminha as intents
recebidas para o serviço que está gerenciando sua sessão de mídia. Se você quiser usar
botões de mídia em sistemas anteriores ao Android 5.0, inclua
o MediaButtonReceiver
no manifesto com um filtro de intent MEDIA_BUTTON
:
<receiver android:name="android.support.v4.media.session.MediaButtonReceiver" >
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
O BroadcastReceiver
encaminha o intent para o serviço. Para analisar a intent
e gerar o callback para sua sessão de mídia, inclua o método MediaButtonReceiver.handleIntent()
no onStartCommand()
do serviço.
Isso converte o código de tecla em um método apropriado de callback de sessão.
Kotlin
private val mediaSessionCompat: MediaSessionCompat = ... override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { MediaButtonReceiver.handleIntent(mediaSessionCompat, intent) return super.onStartCommand(intent, flags, startId) }
Java
private MediaSessionCompat mediaSessionCompat = ...; public int onStartCommand(Intent intent, int flags, int startId) { MediaButtonReceiver.handleIntent(mediaSessionCompat, intent); return super.onStartCommand(intent, flags, startId); }
Como usar botões de mídia para reiniciar uma sessão de mídia inativa
Se o Android puder identificar a última sessão de mídia ativa, ele tentará reiniciar a sessão enviando um Intent ACTION_MEDIA_BUTTON
para um componente registrado pelo manifesto (como um serviço ou BroadcastReceiver
).
Isso permite que o app reinicie a reprodução enquanto a IU não estiver visível, como é o caso da maioria dos apps de áudio.
Esse comportamento é ativado automaticamente quando você usa MediaSessionCompat
. Se você
usar a MediaSession
do framework do Android ou a Biblioteca de Suporte 24.0.0 a
25.1.1, chame setMediaButtonReceiver
para permitir que um botão de mídia reinicie uma
sessão de mídia inativa.
Você pode desativar esse comportamento no Android 5.0 (API de nível 21) e versões mais recentes definindo um receptor de botão de mídia nulo:
Kotlin
// Create a MediaSessionCompat mediaSession = MediaSessionCompat(context, LOG_TAG) mediaSession.setMediaButtonReceiver(null)
Java
// Create a MediaSessionCompat mediaSession = new MediaSessionCompat(context, LOG_TAG); mediaSession.setMediaButtonReceiver(null);
Como personalizar gerenciadores de botão de mídia
O comportamento padrão de onMediaButtonEvent()
extrai o código da tecla e usa o estado atual da sessão de mídia e a lista de ações compatíveis para determinar qual método chamar. Por exemplo, KEYCODE_MEDIA_PLAY
invoca onPlay()
.
Para oferecer uma experiência de botão de mídia consistente em todos os apps, use
o comportamento padrão e desvie apenas para uma finalidade específica. Se um botão de mídia
precisar de gerenciamento personalizado, modifique o método
onMediaButtonEvent()
do callback, extraia o KeyEvent
usando
intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT)
,
processe o evento e retorne true
.
Resumo
Para processar corretamente os eventos do botão de mídia em todas as versões do Android, é necessário
especificar FLAG_HANDLES_MEDIA_BUTTONS
ao criar uma sessão de mídia.
Além disso, dependendo das versões do Android para as quais você pretende oferecer suporte, também é necessário atender a estes requisitos:
Ao executar no Android 5.0 ou versões posteriores:
- Chame
MediaControllerCompat.setMediaController()
do callback do controlador de mídiaonConnected()
- Para permitir que um botão de mídia reinicie uma sessão inativa, crie dinamicamente um
MediaButtonReceiver
chamandosetMediaButtonReceiver()
e transmitindo umPendingIntent
.
Ao executar em sistemas anteriores ao Android 5.0:
- Substitua o
onKeyDown()
da atividade para gerenciar botões de mídia - Crie estaticamente um
MediaButtonReceiver
adicionando-o ao manifesto do app