Réaction aux boutons multimédias

Les boutons multimédias sont des boutons physiques que l'on trouve sur les appareils Android et d'autres périphériques, comme le bouton pause/lecture sur un casque Bluetooth. Lorsqu'un utilisateur appuie sur un bouton multimédia, Android génère une KeyEvent, qui contient un code de touche qui identifie le bouton. Les codes de touche pour les KeyEvents du bouton multimédia sont des constantes commençant par KEYCODE_MEDIA (par exemple, KEYCODE_MEDIA_PLAY).

Les applications doivent pouvoir gérer les événements du bouton multimédia dans trois cas, dans cet ordre de priorité:

  • Lorsque l'activité de l'interface utilisateur de l'application est visible
  • Lorsque l'activité de l'interface utilisateur est masquée et que la session multimédia de l'application est active
  • Lorsque l'activité de l'interface utilisateur est masquée, et que la session multimédia de l'application est inactive et doit être redémarrée

Gérer les boutons multimédias dans une activité de premier plan

L'activité de premier plan reçoit l'événement de touche du bouton multimédia dans sa méthode onKeyDown(). Selon la version en cours d'exécution d'Android, le système achemine l'événement vers un contrôleur multimédia de deux manières:

  • Si vous êtes équipé d'Android 5.0 (niveau d'API 21) ou version ultérieure, appelez FLAG_HANDLES_MEDIA_BUTTONS MediaBrowserCompat.ConnectionCallback.onConnected. Cette action appelle automatiquement la méthode dispatchMediaButtonEvent() de votre contrôleur multimédia, qui convertit le code de touche en rappel de session multimédia.
  • Avant Android 5.0 (niveau d'API 21), vous devez modifier onKeyDown() pour gérer vous-même l'événement. Pour en savoir plus, consultez Gérer les boutons multimédias dans une session multimédia active. L'extrait de code suivant montre comment intercepter le code de clé et appeler dispatchMediaButtonEvent(). Veillez à renvoyer true pour indiquer que l'événement a été géré:

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

Recherche d'une session multimédia...

Si l'activité de premier plan ne gère pas l'événement, Android tente de trouver une session multimédia capable de le gérer. Là encore, selon la version en cours d'exécution d'Android, il existe deux façons de rechercher une session multimédia:

  • Si vous exécutez Android 8.0 (niveau d'API 26) ou une version ultérieure, le système tente de trouver la dernière application avec une MediaSession qui a lu l'audio localement. Si la session est toujours active, Android lui envoie directement l'événement. Sinon, si la session n'est pas active et qu'elle dispose d'un récepteur Mediabutton, Android envoie l'événement au récepteur, qui redémarrera la session et pourra recevoir l'événement. Pour en savoir plus, consultez Utiliser des boutons multimédias pour redémarrer une session multimédia inactive. Si la session ne comporte pas de récepteur de bouton multimédia, le système supprime l'événement du bouton multimédia, et rien ne se passe. La logique est illustrée dans le schéma suivant:

  • Avant Android 8.0 (niveau d'API 26), le système tente d'envoyer l'événement à une session multimédia active. S'il existe plusieurs sessions multimédias actives, Android tente de choisir une session multimédia en cours de préparation (mise en mémoire tampon/connexion), de lecture ou de mise en pause, plutôt que d'en choisir une qui est arrêtée. Pour en savoir plus, consultez Gérer les boutons multimédias dans une session multimédia active. En l'absence de session active, Android tente d'envoyer l'événement à la dernière session active. Pour en savoir plus, consultez Utiliser des boutons multimédias pour redémarrer une session multimédia inactive. La logique est illustrée dans le schéma suivant:

Gérer les boutons multimédias dans une session multimédia active

Sur Android 5.0 (niveau d'API 21) ou version ultérieure, Android envoie automatiquement les événements du bouton multimédia à votre session multimédia active en appelant onMediaButtonEvent(). Par défaut, ce rappel traduit l'événement KeyEvent en méthode de rappel de la session multimédia appropriée, correspondant au code de touche.

Avant Android 5.0 (niveau d'API 21), Android gère les événements du bouton multimédia en diffusant un intent avec l'action ACTION_MEDIA_BUTTON. Votre application doit enregistrer un BroadcastReceiver pour intercepter ces intents. La classe MediaButtonReceiver a été conçue spécifiquement à cet effet. Il s'agit d'une classe pratique de la bibliothèque Media-Compat Android qui gère ACTION_MEDIA_BUTTON et traduit les intents entrants en appels de méthode MediaSessionCompat.Callback appropriés.

Un MediaButtonReceiver est un BroadcastReceiver de courte durée. Il transfère les intents entrants au service qui gère votre session multimédia. Si vous souhaitez utiliser des boutons multimédias dans des systèmes antérieurs à Android 5.0, vous devez inclure MediaButtonReceiver dans votre fichier manifeste avec un filtre d'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>

BroadcastReceiver transmet l'intent à votre service. Pour analyser l'intent et générer le rappel de votre session multimédia, incluez la méthode MediaButtonReceiver.handleIntent() dans le fichier onStartCommand() de votre service. Le code de touche est ainsi traduit dans la méthode de rappel de session appropriée.

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

Redémarrer une session multimédia inactive à l'aide des boutons multimédias

Si Android parvient à identifier la dernière session multimédia active, il tente de la redémarrer en envoyant un intent ACTION_MEDIA_BUTTON à un composant enregistré dans le fichier manifeste (un service ou BroadcastReceiver, par exemple).

Cela permet à votre application de redémarrer la lecture lorsque son UI n'est pas visible, ce qui est le cas pour la plupart des applications audio.

Ce comportement est automatiquement activé lorsque vous utilisez MediaSessionCompat. Si vous utilisez MediaSession du framework Android ou la bibliothèque Support 24.0.0 à 25.1.1, vous devez appeler setMediaButtonReceiver pour permettre à un bouton multimédia de redémarrer une session multimédia inactive.

Vous pouvez désactiver ce comportement sur Android 5.0 (niveau d'API 21) ou version ultérieure en définissant un récepteur de bouton multimédia nul:

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

Personnaliser les gestionnaires de boutons multimédias

Le comportement par défaut de onMediaButtonEvent() extrait le code de clé et utilise l'état actuel de la session multimédia et la liste des actions compatibles pour déterminer la méthode à appeler. Par exemple, KEYCODE_MEDIA_PLAY appelle onPlay().

Pour offrir une expérience cohérente aux boutons multimédias dans toutes les applications, vous devez utiliser le comportement par défaut et ne vous y éloigner que dans un but spécifique. Si un bouton multimédia nécessite une gestion personnalisée, remplacez la méthode onMediaButtonEvent() de votre rappel, extrayez KeyEvent à l'aide de intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT), gérez l'événement vous-même et renvoyez true.

Résumé

Pour gérer correctement les événements de bouton multimédia dans toutes les versions d'Android, vous devez spécifier FLAG_HANDLES_MEDIA_BUTTONS lorsque vous créez une session multimédia.

De plus, en fonction des versions d'Android que vous prévoyez de prendre en charge, vous devez également répondre aux exigences suivantes:

Sous Android 5.0 ou version ultérieure:

  • Appeler MediaControllerCompat.setMediaController() à partir du rappel onConnected() du contrôleur multimédia
  • Pour autoriser un bouton multimédia à redémarrer une session inactive, créez un MediaButtonReceiver de manière dynamique en appelant setMediaButtonReceiver() et en lui transmettant un PendingIntent

Sur des systèmes antérieurs à Android 5.0:

  • Remplacer le onKeyDown() de l'activité pour gérer les boutons multimédias
  • Créer un MediaButtonReceiver de manière statique en l'ajoutant au fichier manifeste de l'application