Como responder a botões de mídia

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 aplicativos devem ser capazes de 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 na onKeyDown(). . Dependendo da versão em execução do Android, o sistema pode direcionar o evento de duas maneiras para um controlador de mídia:

  • 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 o método dispatchMediaButtonEvent(), que converte o código de tecla em um callback de sessão de mídia.
  • Em versões anteriores ao Android 5.0 (nível 21 da API), é necessário modificar onKeyDown() para processar o evento. 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 e chamar DispatcherMediaButtonEvent(). Devolva true para indicam que o evento foi tratado:

    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 tentará encontrar uma sessão de mídia que pode lidar com 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 (API de nível 26) ou posterior, o sistema tentará encontre o último app com uma MediaSession que reproduziu áudio localmente. Se a sessão ainda estiver ativo, o Android enviará o evento diretamente para ele. Caso contrário, se o sessão não está ativa e tem um receptor de botão de mídia, o Android envia o evento ao receptor, que reiniciará a sessão e poderá 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 descartará a mídia e nada acontecerá. A lógica é mostrada diagrama:

  • 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 tentará para escolher uma sessão de mídia que está se preparando para reproduzir (armazenamento em buffer/conexão), em reprodução ou pausada, em vez de uma que esteja interrompida. Consulte Como gerenciar botões de mídia em uma sessão de mídia ativa para mais detalhes. Se não houver o Android 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.

Antes do Android 5.0 (API de nível 21), o Android processa eventos de botão de mídia transmitindo uma intent com a ação ACTION_MEDIA_BUTTON. Seu app precisa registrar um BroadcastReceiver para interceptar essas intents. A MediaButtonReceiver foi criada especificamente para para esse propósito. É uma classe de conveniência no No Android media-compatibilidade, que processa ACTION_MEDIA_BUTTON e traduz as intents recebidas para o chamadas de método MediaSessionCompat.Callback adequadas.

Um MediaButtonReceiver é um BroadcastReceiver de curta duração. Ele encaminha mensagens intents para o serviço que está gerenciando sua sessão de mídia. Se você quiser usar de mídia em sistemas anteriores ao Android 5.0, é necessário incluir 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 a 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 até 25.1.1, você precisa chamar setMediaButtonReceiver para permitir que um botão de mídia reinicie uma sessão de mídia inativa.

Para desativar esse comportamento no Android 5.0 (nível 21 da API) 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 consistente de botão de mídia em todos os apps, use o comportamento padrão e desviam apenas para uma finalidade específica. Se um botão de mídia precisa de tratamento personalizado, substitua o método onMediaButtonEvent() extraia o KeyEvent usando intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT), processar o evento e retornar 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 com as quais você pretende oferecer suporte, você também precisa atender a estes requisitos:

Ao executar no Android 5.0 ou versões posteriores:

  • Chame MediaControllerCompat.setMediaController() do callback do controlador de mídia onConnected()
  • Para permitir que um botão de mídia reinicie uma sessão inativa, crie dinamicamente um MediaButtonReceiver chamando setMediaButtonReceiver() e transmitindo um PendingIntent

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