Cómo responder a los botones multimedia

Los botones de medios son botones de hardware que se encuentran en dispositivos Android y otros dispositivos periféricos, por ejemplo, el botón de pausa/reproducción en auriculares Bluetooth. Cuando un usuario presiona un botón de medios, Android genera un KeyEvent, que contiene un código de tecla que identifica el botón. Los códigos de tecla para los KeyEvents del botón de medios son constantes que comienzan con KEYCODE_MEDIA (por ejemplo, KEYCODE_MEDIA_PLAY).

Las apps deben poder controlar los eventos de botones multimedia en tres casos, en el orden de prioridad:

  • Cuando la actividad de la IU de la app es visible
  • Cuando la actividad de la IU está oculta y la sesión multimedia de la app está activa
  • Cuando la actividad de IU está oculta y la sesión multimedia de la app está inactiva y debe reiniciarse

Cómo manejar los botones de medios en una actividad en primer plano

La actividad en primer plano recibe el evento de tecla del botón multimedia en su onKeyDown(). . Según la versión de ejecución de Android, el sistema enruta el evento de dos formas un controlador multimedia:

  • Si ejecutas Android 5.0 (nivel de API 21) o una versión posterior, llama FLAG_HANDLES_MEDIA_BUTTONS MediaBrowserCompat.ConnectionCallback.onConnected Si confirmas esta acción, llamar automáticamente al controlador de contenido multimedia dispatchMediaButtonEvent(), que traduce el código de tecla a una devolución de llamada de sesión multimedia.
  • En versiones anteriores a Android 5.0 (nivel de API 21), debes modificar onKeyDown() para que maneje el evento por tu cuenta. (Obtén más información en Cómo manejar los botones de medios en una sesión multimedia activa). En el siguiente fragmento de código, se muestra cómo interceptar el código de tecla y llama a distribuMediaButtonEvent(). Asegúrate de devolver true a indican que el evento se gestionó:

    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);
        }
        

Cómo encontrar una sesión multimedia

Si la actividad en primer plano no controla el evento, Android intentará encontrar un con una sesión multimedia que pueda controlarlo. Dependiendo de la versión en ejecución de En Android, existen dos maneras de buscar una sesión multimedia:

  • Si estás ejecutando Android 8.0 (nivel de API 26) o una versión posterior, el sistema intenta Buscar la última app con una MediaSession que reprodujo audio de forma local Si la sesión sigue activo. Android le envía el evento directamente. De lo contrario, si la sesión no está activa y tiene un receptor mediabutton, Android envía el evento al receptor, lo que reiniciará la sesión para que pueda recibir el evento. (Obtén más información en Cómo usar los botones de medios para reiniciar una sesión multimedia inactiva). Si la sesión no tiene un receptor de botones de medios, el sistema lo descarta. evento de botón y no sucederá nada. La lógica se muestra en el siguiente ejemplo: diagrama:

  • En versiones anteriores a Android 8.0 (nivel de API 26), el sistema intenta enviar el evento a un durante la sesión multimedia activa. Si hay varias sesiones multimedia activas, Android intenta para elegir una sesión multimedia que se esté preparando para reproducir (almacenando en búfer/conectando) en reproducción o en pausa, en lugar de uno que se detiene. (Consulta Cómo manejar los botones de medios en una sesión multimedia activa para obtener más información). Si no hay activos , Android intenta enviar el evento a la sesión activa más reciente. (Obtén más información en Cómo usar los botones de medios para reiniciar una sesión multimedia inactiva). La lógica se muestra en el siguiente diagrama:

Cómo manejar botones de medios en una sesión multimedia activa

En Android 5.0 (nivel de API 21) y versiones posteriores, Android envía automáticamente eventos de botones de medios a tu sesión multimedia activa con una llamada a onMediaButtonEvent() De forma predeterminada, esta devolución de llamada convierte el KeyEvent en el método apropiado de devolución de llamada de la sesión multimedia que coincide con el código de tecla.

En versiones anteriores a Android 5.0 (nivel de API 21), Android maneja los eventos de botones multimedia transmitiendo un intent. con la acción ACTION_MEDIA_BUTTON. Tu app debe registrar un BroadcastReceiver para interceptar estos intents. El MediaButtonReceiver se diseñó específicamente para con este propósito. Es una clase de conveniencia En Android biblioteca media-compat que controla ACTION_MEDIA_BUTTON y traduce los intents entrantes al llamadas al método MediaSessionCompat.Callback adecuadas.

Un MediaButtonReceiver es un BroadcastReceiver de corta duración. Reenvía los mensajes entrantes al servicio que administra tu sesión multimedia. Si quieres utilizar botones multimedia en sistemas anteriores a Android 5.0, debes incluir el MediaButtonReceiver en tu manifiesto con un filtro de intents MEDIA_BUTTON:

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

El BroadcastReceiver reenvía el intent a tu servicio. Cómo analizar el intent y generar la devolución de llamada a tu sesión multimedia, incluye el método MediaButtonReceiver.handleIntent() en el onStartCommand() de tu servicio. Así, el código de tecla se convierte en el método de devolución de llamada de sesión adecuado.

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);
 }

Cómo usar los botones de medios para reiniciar una sesión multimedia inactiva

Si Android puede identificar la última sesión multimedia activa, intentará reiniciar la sesión enviando un intent ACTION_MEDIA_BUTTON a un componente registrado en el manifiesto (como un servicio o BroadcastReceiver).

De este modo, tu app puede reiniciar la reproducción mientras su IU no está visible, como sucede con la mayoría de las apps de audio.

Este comportamiento se habilita automáticamente cuando usas MediaSessionCompat. Si usa el MediaSession del framework de Android o la biblioteca de compatibilidad 24.0.0 hasta 25.1.1, debes llamar a setMediaButtonReceiver para permitir que un botón de medios reinicie una sesión multimedia inactiva.

Puedes inhabilitar este comportamiento en Android 5.0 (nivel de API 21) y versiones posteriores si haces lo siguiente: Configura un receptor de botón multimedia 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);

Cómo personalizar los controladores de botones de medios

El comportamiento predeterminado para onMediaButtonEvent() extrae el código de tecla y utiliza el estado actual de la sesión multimedia y la lista de acciones admitidas para determinar a qué método llamar. Por ejemplo, KEYCODE_MEDIA_PLAY invoca a onPlay().

Para proporcionar una experiencia de botones multimedia coherente en todas las apps, debes usar el comportamiento predeterminado y solo se desvía para un propósito específico. Si un botón multimedia necesita un manejo personalizado, anula los campos onMediaButtonEvent() método, extrae KeyEvent con intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT), manejar el evento por tu cuenta y mostrar true.

Resumen

Para manejar correctamente los eventos de botones de medios en todas las versiones de Android, debes hacer lo siguiente: especificar FLAG_HANDLES_MEDIA_BUTTONS cuando creas una sesión multimedia.

Además, según las versiones de Android que quieras admitir, también debes cumplir con estos requisitos:

Cuando uses Android 5.0 o versiones posteriores:

  • Llama a MediaControllerCompat.setMediaController() desde la devolución de llamada onConnected() del controlador multimedia.
  • Para permitir que un botón de medios reinicie una sesión inactiva, crea un MediaButtonReceiver de forma dinámica llamando a setMediaButtonReceiver() y pásale un PendingIntent

Cuando uses sistemas previos a Android 5.0:

  • Anula el objeto onKeyDown() de la actividad para manejar botones de medios.
  • Crea un MediaButtonReceiver de forma estática agregándolo al manifiesto de la app.