Como usar uma sessão de mídia

As sessões de mídia oferecem uma maneira universal de interagir com um player de áudio ou vídeo. Ao informar ao Android que a mídia está sendo reproduzida em um app, os controles de reprodução podem ser delegados ao app. A integração com a sessão de mídia permite que o app anuncie a reprodução de mídia externamente e receba comandos de fontes externas. Essas fontes podem ser botões físicos (como o botão de reprodução em um fone de ouvido ou controle remoto da TV) ou comandos indiretos (como instruir "pausar" para o Google Assistente). Em seguida, a sessão de mídia delega esses comandos ao app que os aplica ao player de mídia para o qual fica transparente onde os comandos foram originados.

Uma sessão de mídia convive com o player que a gerencia. Crie e inicialize uma sessão de mídia no método onCreate() da atividade ou do serviço que tem a sessão de mídia e o player associado a ela.

Inicializar a sessão de mídia

Uma sessão de mídia recém-criada não tem recursos. Inicialize a sessão executando estas etapas:

  • Defina sinalizações para que a sessão de mídia receba callbacks de controladores de mídia e botões de mídia.
  • Crie e inicialize uma instância de PlaybackStateCompat e a atribua à sessão. O estado de reprodução muda durante a sessão, por isso recomendamos o cache de PlaybackStateCompat.Builder para reutilização.
  • Crie uma instância de MediaSessionCompat.Callback e a atribua à sessão. Veja abaixo mais informações sobre callbacks.

Crie e inicialize uma sessão de mídia no método onCreate() da atividade ou do serviço que tem a sessão.

Para que botões de mídia funcionem quando o app for recém-inicializado (ou interrompido), o PlaybackState precisa conter uma ação de reprodução correspondente à intent que o botão de mídia envia. É por isso que ACTION_PLAY é atribuído ao estado da sessão durante a inicialização. Para saber mais, consulte Como responder a botões de mídia.

Manter o estado de reprodução e os metadados

Há duas classes que representam o estado de uma sessão de mídia.

A classe PlaybackStateCompat descreve o estado operacional atual do player. Isso inclui:

  • O estado de transporte, isto é, se o player está em reprodução/pausado/em buffer etc. Veja getState().
  • Um código de erro e uma mensagem de erro opcional, quando aplicável. Veja getErrorCode() e leia Estados e erros, abaixo.
  • A posição do player
  • As ações válidas do controlador que podem ser processadas no estado atual

A classe MediaMetadataCompat descreve o material que está sendo reproduzido:

  • O nome do artista, álbum e faixa
  • A duração da faixa
  • Arte do álbum para exibição na tela de bloqueio. A imagem é um bitmap com tamanho máximo de 320 x 320 dp. Se for maior, ela será reduzida.
  • Uma instância de ContentUris que aponta para uma versão maior da obra de arte

O estado do player e os metadados podem mudar durante a vida útil de uma sessão de mídia. Sempre que o estado ou os metadados mudarem, use o builder correspondente para cada classe, PlaybackStateCompat.Builder() ou MediaMetadataCompat.Builder(), e transmita a nova instância para a sessão de mídia chamando setPlaybackState() ou setMetaData(). Para reduzir o consumo geral de memória em função dessas operações frequentes, uma boa sugestão é criar os builders uma vez e reutilizá-los ao longo da vida da sessão.

Estados e erros

Observe que PlaybackState é um objeto que contém valores separados para o estado de reprodução da sessão (getState()) e, quando necessário, um código de erro associado (getErrorCode()). Os erros podem ser fatais ou não fatais:

Sempre que a reprodução for interrompida, você precisará gerar um erro fatal: defina o estado de transporte como STATE_ERROR e especifique um erro associado a setErrorMessage(int, CharSequence). Enquanto a reprodução estiver bloqueada pelo erro, o PlaybackState vai continuar informando STATE_ERROR e o erro.

Um erro não fatal ocorre quando seu app não consegue processar uma solicitação, mas pode continuar a reprodução: o transporte permanece em um estado "normal" (como STATE_PLAYING), mas o PlaybackState contém um código de erro. Por exemplo, se a última música estiver sendo tocada e o usuário solicitar um salto para a próxima música, a reprodução poderá continuar, mas você precisará criar um novo PlaybackState com o código de erro ERROR_CODE_END_OF_QUEUE e chamar setPlaybackState(). Os controladores de mídia anexados à sessão vão receber o callback onPlaybackStateChanged() e explicar ao usuário o que aconteceu. Um erro não fatal precisar ser informado apenas uma vez, no momento em que ocorre. Na próxima vez que a sessão atualizar o PlaybackState, não defina o mesmo erro não fatal novamente, a menos que ele tenha ocorrido em resposta a uma nova solicitação.

Telas de bloqueio de sessão de mídia

No Android 4.0 (nível 14 da API) e versões mais recentes, o sistema pode acessar o estado de reprodução e os metadados de uma sessão de mídia. É assim que a tela de bloqueio pode exibir controles de mídia e obras de arte. O comportamento varia de acordo com a versão do Android.

Arte do álbum

Do Android 4.0 (nível 14 da API) ao Android 10 (nível 29 da API), o plano de fundo da tela de bloqueio mostra a capa do álbum, mas apenas se os metadados da sessão de mídia incluirem um bitmap em segundo plano.

Controles de transporte

Do Android 4.0 (API de nível 14) até o Android 4.4 (API de nível 19), quando uma sessão de mídia está ativa e os metadados da sessão incluem um bitmap em segundo plano, a tela de bloqueio exibe automaticamente os controles de transporte.

No Android 5.0 (nível 21 da API) ou versões mais recentes, o sistema não oferece controles de transporte na tela de bloqueio. Em vez disso, use uma notificação do MediaStyle para mostrar controles de transporte.

Adicionar ações personalizadas

Os aplicativos de mídia podem definir ações personalizadas, por exemplo, "Gostei", "Gostei" ou voltar 30 segundos. Uma ação personalizada precisa implementar um comportamento completamente novo. Não use uma ação personalizada para substituir uma das ações de controle de transporte padrão definidas em PlaybackStateCompat.

Adicione ações personalizadas com addCustomAction(). O exemplo a seguir mostra como adicionar um controle para a ação "Gostei":

Kotlin

stateBuilder.addCustomAction(
        PlaybackStateCompat.CustomAction.Builder(
                CUSTOM_ACTION_THUMBS_UP,
                resources.getString(R.string.thumbs_up),
                thumbsUpIcon
        ).run {
            setExtras(customActionExtras)
            build()
        }
)

Java

stateBuilder.addCustomAction(new PlaybackStateCompat.CustomAction.Builder(
    CUSTOM_ACTION_THUMBS_UP, resources.getString(R.string.thumbs_up), thumbsUpIcon)
    .setExtras(customActionExtras)
    .build());

Consulte Universal Music Player para ver um exemplo completo.

Você responde à ação com onCustomAction().

Kotlin

override fun onCustomAction(action: String, extras: Bundle?) {
    when(action) {
        CUSTOM_ACTION_THUMBS_UP -> {
            ...
        }
    }
}

Java

@Override
public void onCustomAction(@NonNull String action, Bundle extras) {
    if (CUSTOM_ACTION_THUMBS_UP.equals(action)) {
        ...
    }
}

Consulte também Universal Music Player (link em inglês).

Callbacks de sessão de mídia

Os principais métodos de callback de sessão de mídia são onPlay(), onPause() e onStop(). É aqui que você adiciona o código que controla o player.

Como você instancia e define o callback da sessão no tempo de execução (em onCreate()), o app pode definir callbacks alternativos que usam players diferentes e escolher a combinação apropriada de callback/player de acordo com o dispositivo e/ou nível do sistema. Você pode mudar o player sem mudar o restante do app. Por exemplo, você pode usar ExoPlayer ao executar no Android 4.1 (API de nível 16) ou versões posteriores e usar MediaPlayer em sistemas anteriores.

Além de controlar o player e gerenciar as transições de estado da sessão de mídia, os callbacks também ativam e desativam recursos do app e controlam a interação com outros apps e o hardware do dispositivo. Consulte Como controlar a saída de áudio.

A implementação dos métodos de callback da sessão de mídia depende da estrutura do app. Consulte as páginas separadas que descrevem como usar callbacks em apps de áudio e apps de vídeo. Descreva como os callbacks precisam ser implementados para cada tipo de app.