Cómo mostrar una tarjeta de Está sonando

Las apps para TV deben mostrar una tarjeta de Está sonando cuando reproducen contenido multimedia detrás del selector o en segundo plano. Esta tarjeta permite a los usuarios volver a la app que está reproduciendo el contenido.

En el marco de trabajo de Android, se muestra una tarjeta de Está sonando en la pantalla principal cuando hay una MediaSession activa. En la tarjeta, se incluyen metadatos de contenido multimedia, como la portada del álbum, el título y el ícono de la app. Cuando el usuario selecciona la tarjeta, el sistema abre la app.

En esta lección, se muestra cómo usar la clase MediaSession para implementar la nueva tarjeta de Está sonando.

Figura 1: Muestra una tarjeta de Está sonando cuando se reproduce contenido multimedia en segundo plano.

Cómo iniciar una sesión multimedia

Crea una MediaSession cuando tu app se esté preparando para reproducir contenido multimedia. En el siguiente fragmento de código, se muestra un ejemplo de cómo establecer indicadores y una devolución de llamada apropiados:

Kotlin

    session = MediaSession(this, "MusicService").apply {
        setCallback(MediaSessionCallback())
        setFlags(
                MediaSession.FLAG_HANDLES_MEDIA_BUTTONS or MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS
        )
    }
    

Java

    session = new MediaSession(this, "MusicService");
    session.setCallback(new MediaSessionCallback());
    session.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS |
            MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
    

Nota: La tarjeta de Está sonando solo se mostrará para una sesión de medios que tenga el indicador FLAG_HANDLES_TRANSPORT_CONTROLS establecido.

Cómo mostrar una tarjeta de Está sonando

La tarjeta de Está sonando solo aparece para las sesiones activas. Debes llamar a setActive(true) cuando se inicia la reproducción. Además, tu app debe solicitar foco de audio, como se describe en Cómo administrar el foco de audio.

Kotlin

    private fun handlePlayRequest() {

        tryToGetAudioFocus()

        if (!session.isActive) {
            session.isActive = true
        }
        ...
    }
    

Java

    private void handlePlayRequest() {

        tryToGetAudioFocus();

        if (!session.isActive()) {
            session.setActive(true);
        }
        ...
    }
    

La tarjeta se quita de la pantalla del selector cuando una llamada a setActive(false) desactiva la sesión de medios o cuando otra app comienza a reproducir contenido multimedia. Si la reproducción se detiene por completo y no hay ningún contenido multimedia activo, tu app debe desactivar inmediatamente la sesión de medios. Si se pausa la reproducción, tu app debe desactivar la sesión de medios luego de un período de inactividad, por lo general, de entre 5 y 30 minutos.

Cómo actualizar el estado de reproducción

Debes actualizar el estado de reproducción en la MediaSession para que la tarjeta pueda mostrar el estado del contenido multimedia actual.

Kotlin

    private fun updatePlaybackState() {
        val position: Long =
                mediaPlayer
                        ?.takeIf { it.isPlaying }
                        ?.currentPosition?.toLong()
                        ?: PlaybackState.PLAYBACK_POSITION_UNKNOWN

        val stateBuilder = PlaybackState.Builder()
                .setActions(getAvailableActions()).apply {
                    setState(mState, position, 1.0f)
                }
        session.setPlaybackState(stateBuilder.build())
    }

    private fun getAvailableActions(): Long {
        var actions = (PlaybackState.ACTION_PLAY_PAUSE
                or PlaybackState.ACTION_PLAY_FROM_MEDIA_ID
                or PlaybackState.ACTION_PLAY_FROM_SEARCH)

        playingQueue?.takeIf { it.isNotEmpty() }?.apply {
            actions = if (mState == PlaybackState.STATE_PLAYING) {
                actions or PlaybackState.ACTION_PAUSE
            } else {
                actions or PlaybackState.ACTION_PLAY
            }
            if (currentIndexOnQueue > 0) {
                actions = actions or PlaybackState.ACTION_SKIP_TO_PREVIOUS
            }
            if (currentIndexOnQueue < size - 1) {
                actions = actions or PlaybackState.ACTION_SKIP_TO_NEXT
            }
        }
        return actions
    }
    

Java

    private void updatePlaybackState() {
        long position = PlaybackState.PLAYBACK_POSITION_UNKNOWN;
        if (mediaPlayer != null && mediaPlayer.isPlaying()) {
            position = mediaPlayer.getCurrentPosition();
        }
        PlaybackState.Builder stateBuilder = new PlaybackState.Builder()
                .setActions(getAvailableActions());
        stateBuilder.setState(mState, position, 1.0f);
        session.setPlaybackState(stateBuilder.build());
    }

    private long getAvailableActions() {
        long actions = PlaybackState.ACTION_PLAY_PAUSE |
                PlaybackState.ACTION_PLAY_FROM_MEDIA_ID |
                PlaybackState.ACTION_PLAY_FROM_SEARCH;
        if (playingQueue == null || playingQueue.isEmpty()) {
            return actions;
        }
        if (mState == PlaybackState.STATE_PLAYING) {
            actions |= PlaybackState.ACTION_PAUSE;
        } else {
            actions |= PlaybackState.ACTION_PLAY;
        }
        if (currentIndexOnQueue > 0) {
            actions |= PlaybackState.ACTION_SKIP_TO_PREVIOUS;
        }
        if (currentIndexOnQueue < playingQueue.size() - 1) {
            actions |= PlaybackState.ACTION_SKIP_TO_NEXT;
        }
        return actions;
    }
    

Cómo mostrar los metadatos del contenido multimedia

Establece MediaMetadata con el método setMetadata(). Este método del objeto de la sesión de medios te permite brindar información a la tarjeta de Está sonando sobre la pista, como el título, el subtítulo y varios íconos. En el siguiente ejemplo, se asume que los datos de la pista se almacenan en una clase de datos personalizada: MediaData.

Kotlin

    private fun updateMetadata(myData: MediaData) {
        val metadataBuilder = MediaMetadata.Builder().apply {
            // To provide most control over how an item is displayed set the
            // display fields in the metadata
            putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE, myData.displayTitle)
            putString(MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE, myData.displaySubtitle)
            putString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI, myData.artUri)
            // And at minimum the title and artist for legacy support
            putString(MediaMetadata.METADATA_KEY_TITLE, myData.title)
            putString(MediaMetadata.METADATA_KEY_ARTIST, myData.artist)
            // A small bitmap for the artwork is also recommended
            putBitmap(MediaMetadata.METADATA_KEY_ART, myData.artBitmap)
            // Add any other fields you have for your data as well
        }
        session.setMetadata(metadataBuilder.build())
    }
    

Java

    private void updateMetadata(MediaData myData) {
        MediaMetadata.Builder metadataBuilder = new MediaMetadata.Builder();
        // To provide most control over how an item is displayed set the
        // display fields in the metadata
        metadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE,
                myData.displayTitle);
        metadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE,
                myData.displaySubtitle);
        metadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI,
                myData.artUri);
        // And at minimum the title and artist for legacy support
        metadataBuilder.putString(MediaMetadata.METADATA_KEY_TITLE,
                myData.title);
        metadataBuilder.putString(MediaMetadata.METADATA_KEY_ARTIST,
                myData.artist);
        // A small bitmap for the artwork is also recommended
        metadataBuilder.putBitmap(MediaMetadata.METADATA_KEY_ART,
                myData.artBitmap);
        // Add any other fields you have for your data as well
        session.setMetadata(metadataBuilder.build());
    }
    

Cómo responder a la acción del usuario

Cuando el selector abre la tarjeta de Está sonando, el sistema abre la app a la que corresponde la sesión. Si tu app proporciona un PendingIntent para setSessionActivity(), el sistema inicia la actividad que especifiques, como se muestra a continuación. En caso contrario, se abre el intent predeterminado del sistema. La actividad que especifiques debe proporcionar controles de reproducción para que los usuarios puedan pausarla o detenerla.

Kotlin

    val pi: PendingIntent = Intent(context, MyActivity::class.java).let { intent ->
        PendingIntent.getActivity(
                context, 99 /*request code*/,
                intent,
                PendingIntent.FLAG_UPDATE_CURRENT
        )
    }
    session.setSessionActivity(pi)
    

Java

    Intent intent = new Intent(context, MyActivity.class);
    PendingIntent pi = PendingIntent.getActivity(context, 99 /*request code*/,
            intent, PendingIntent.FLAG_UPDATE_CURRENT);
    session.setSessionActivity(pi);