Rispondere ai pulsanti multimediali

I pulsanti multimediali sono pulsanti hardware presenti su dispositivi Android e altre periferiche, ad esempio il pulsante di pausa/riproduzione sulle cuffie Bluetooth. Quando un utente preme un pulsante di contenuti multimediali, Android genera un KeyEvent, che contiene un codice chiave che identifica il pulsante. I codici chiave per KeyEvents del pulsante multimediale sono costanti che iniziano con KEYCODE_MEDIA (ad esempio, KEYCODE_MEDIA_PLAY).

Le app dovrebbero essere in grado di gestire gli eventi dei pulsanti multimediali in tre casi, in questo ordine di priorità:

  • Quando l'attività UI dell'app è visibile
  • Quando l'attività UI è nascosta e la sessione multimediale dell'app è attiva
  • Quando l'attività UI è nascosta e la sessione multimediale dell'app non è attiva e deve essere riavviata

Gestione dei pulsanti multimediali in un'attività in primo piano

L'attività in primo piano riceve l'evento chiave del pulsante multimediale nel relativo metodo onKeyDown(). A seconda della versione di Android installata, il sistema può indirizzare l'evento a un controller multimediale in due modi:

  • Se utilizzi Android 5.0 (livello API 21) o versioni successive, chiama FLAG_HANDLES_MEDIA_BUTTONS MediaBrowserCompat.ConnectionCallback.onConnected. Questa operazione chiamerà automaticamente il controller dispatchMediaButtonEvent() del controller multimediale, in modo da tradurre il codice della chiave in un callback di sessione multimediale.
  • Prima di Android 5.0 (livello API 21), devi modificare onKeyDown() per gestire autonomamente l'evento. Per maggiori dettagli, consulta l'articolo Gestione dei pulsanti multimediali in una sessione multimediale attiva. Lo snippet di codice riportato di seguito mostra come intercettare il codice della chiave e chiamare dispatchMediaButtonEvent(). Assicurati di restituire true per indicare che l'evento è stato gestito:

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

Trovare una sessione multimediale

Se l'attività in primo piano non gestisce l'evento, Android cercherà di trovare una sessione multimediale in grado di gestirlo. Anche in questo caso, a seconda della versione di Android disponibile, esistono due modi per cercare una sessione multimediale:

  • Se esegui Android 8.0 (livello API 26) o versioni successive, il sistema tenta di trovare l'ultima app con una MediaSession che ha riprodotto audio localmente. Se la sessione è ancora attiva, Android le invia l'evento direttamente. Altrimenti, se la sessione non è attiva e ha un ricevitore mediabutton, Android invia l'evento al destinatario, che riavvia la sessione e può ricevere l'evento. Per maggiori dettagli, vedi Utilizzo dei pulsanti multimediali per riavviare una sessione multimediale inattiva. Se la sessione non dispone di un ricevitore del pulsante multimediale, il sistema ignora l'evento del pulsante multimediale e non accade nulla. La logica è mostrata nel seguente schema:

  • Prima di Android 8.0 (livello API 26), il sistema tenta di inviare l'evento a una sessione multimediale attiva. Se sono presenti più sessioni multimediali attive, Android prova a scegliere una sessione multimediale che si sta preparando per la riproduzione (buffering/connessione), in riproduzione o in pausa, anziché una che viene interrotta. Per ulteriori dettagli, consulta Gestione dei pulsanti multimediali in una sessione multimediale attiva. Se non sono presenti sessioni attive, Android tenta di inviare l'evento alla sessione attiva più recente. Per maggiori dettagli, vedi Utilizzo dei pulsanti multimediali per riavviare una sessione multimediale inattiva. La logica è mostrata nel seguente diagramma:

Gestione dei pulsanti multimediali in una sessione multimediale attiva

Su Android 5.0 (livello API 21) e versioni successive, Android invia automaticamente gli eventi del pulsante multimediale alla sessione multimediale attiva chiamando onMediaButtonEvent(). Per impostazione predefinita, questo callback converte l'evento KeyEvent nel metodo di callback della sessione multimediale appropriato che corrisponde al codice della chiave.

Prima di Android 5.0 (livello API 21), Android gestisce gli eventi dei pulsanti multimediali trasmettendo un intent con l'azione ACTION_MEDIA_BUTTON. L'app deve registrare un BroadcastRicevir per intercettare questi intent. La classe MediaButtonReceiver è stata progettata appositamente per questo scopo. È una classe di convenienza nella libreria media-compat di Android che gestisce ACTION_MEDIA_BUTTON e traduce gli intent in entrata nelle chiamate del metodo MediaSessionCompat.Callback appropriate.

Un MediaButtonReceiver è un BroadcastRicevir di breve durata. Inoltra gli intent in entrata al servizio che gestisce la sessione multimediale. Se vuoi utilizzare i pulsanti multimediali nei sistemi precedenti ad Android 5.0, devi includere MediaButtonReceiver nel file manifest con un filtro per 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 inoltra l'intent al tuo servizio. Per analizzare l'intent e generare il callback per la tua sessione multimediale, includi il metodo MediaButtonReceiver.handleIntent() nell'onStartCommand() del tuo servizio. In questo modo il codice chiave viene convertito nel metodo di callback della sessione appropriato.

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

Utilizzo dei pulsanti multimediali per riavviare una sessione multimediale non attiva

Se Android è in grado di identificare l'ultima sessione multimediale attiva, prova a riavviarla inviando un intent ACTION_MEDIA_BUTTON a un componente registrato con manifest (ad esempio un servizio o BroadcastReceiver).

In questo modo l'app può riavviare la riproduzione mentre la sua UI non è visibile, come avviene per la maggior parte delle app audio.

Questo comportamento viene attivato automaticamente quando utilizzi MediaSessionCompat. Se utilizzi MediaSession del framework Android o Support Library da 24.0.0 a 25.1.1, devi chiamare setMediaButtonReceiver per consentire a un pulsante multimediale di riavviare una sessione multimediale non attiva.

Puoi disattivare questo comportamento in Android 5.0 (livello API 21) e versioni successive impostando un ricevitore con pulsante multimediale null:

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

Personalizzazione dei gestori dei pulsanti multimediali

Il comportamento predefinito di onMediaButtonEvent() estrae il codice della chiave e utilizza lo stato corrente della sessione multimediale e l'elenco di azioni supportate per determinare il metodo da chiamare. Ad esempio, KEYCODE_MEDIA_PLAY richiama onPlay().

Per offrire un'esperienza coerente con il pulsante multimediale in tutte le app, devi utilizzare il comportamento predefinito e deviare soltanto per uno scopo specifico. Se un pulsante multimediale richiede una gestione personalizzata, sostituisci il metodo onMediaButtonEvent() di callback, estrai KeyEvent utilizzando intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT), gestisci l'evento autonomamente e restituisci true.

Riepilogo

Per gestire correttamente gli eventi dei pulsanti multimediali in tutte le versioni di Android, devi specificare FLAG_HANDLES_MEDIA_BUTTONS quando crei una sessione multimediale.

Inoltre, a seconda delle versioni di Android che intendi supportare, devi soddisfare anche i seguenti requisiti:

Su Android 5.0 o versioni successive:

  • Chiama MediaControllerCompat.setMediaController() dal callback onConnected() del controller multimediale
  • Per consentire a un pulsante di contenuti multimediali di riavviare una sessione inattiva, crea in modo dinamico un MediaButtonReceiver chiamando setMediaButtonReceiver() e trasmettendo un PendingIntent

In caso di esecuzione su sistemi precedenti ad Android 5.0:

  • Esegui l'override di onKeyDown() dell'attività per gestire i pulsanti multimediali
  • Crea in modo statico un MediaButtonReceiver aggiungendolo al file manifest dell'app