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.

El framework de Android muestra una tarjeta Está sonando en la pantalla principal cuando hay un MediaSession activo. 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.

Esta lección te muestra cómo utilizar 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 un 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 Está sonando solo se mostrará para una sesión multimedia con la marca FLAG_HANDLES_TRANSPORT_CONTROLS.

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 comience 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 multimedia, o bien cuando otra app inicia la reproducción 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

Actualiza el estado de reproducción en 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

Configura el 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 diferentes íconos. El siguiente ejemplo supone que los datos de tu pista están almacenados en una clase de datos personalizados: 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 usuario selecciona la tarjeta de Está sonando, el sistema abre la app a la que corresponde la sesión. Si tu app proporciona un PendingIntent a 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);