Projeção de mídia

As APIs android.media.projection introduzidas no Android 5 (nível 21 da API) permitem capturar o conteúdo da tela de um dispositivo como um stream de mídia que pode ser reproduzido, gravado ou transmitido para outros dispositivos, como TVs.

O Android 14 (nível 34 da API) apresenta o compartilhamento de tela do app, que permite que os usuários compartilhem uma única janela de app em vez de toda a tela do dispositivo, independente do modo de janela. O compartilhamento de tela do app exclui a barra de status, a barra de navegação, as notificações e outros elementos da interface do sistema da tela compartilhada, mesmo quando o compartilhamento de tela do app é usado para capturar um app em tela cheia. Somente o conteúdo do app selecionado é compartilhado.

O compartilhamento de tela do app garante a privacidade do usuário, aumenta a produtividade e melhora a multitarefa, permitindo que os usuários executem vários apps, mas restringem o compartilhamento de conteúdo a apenas um app.

Três representações de tela

Uma projeção de mídia captura o conteúdo de uma tela do dispositivo ou janela do app e projeta a imagem capturada em uma tela virtual que renderiza a imagem em uma Surface.

Tela do dispositivo real projetada em exibição virtual. Conteúdo da
              tela virtual gravada para a "Superfície" fornecida pelo aplicativo.
Figura 1. Tela do dispositivo real ou janela do app projetada na tela virtual. Tela virtual gravada para a Surface fornecida pelo aplicativo.

O aplicativo fornece a Surface usando uma MediaRecorder, SurfaceTexture ou ImageReader, que consome o conteúdo da tela capturada e permite gerenciar imagens renderizadas na Surface em tempo real. Salve as imagens como uma gravação ou transmita para uma TV ou outro dispositivo.

Tela real

Inicie uma sessão de projeção de mídia usando um token que conceda ao app a capacidade de capturar o conteúdo da tela do dispositivo ou da janela do app. O token é representado por uma instância da classe MediaProjection.

Use o método getMediaProjection() do serviço do sistema MediaProjectionManager para criar uma instância de MediaProjection ao iniciar uma nova atividade. Inicie a atividade com uma intent do método createScreenCaptureIntent() para especificar uma operação de captura de tela:

Kotlin

val mediaProjectionManager = getSystemService(MediaProjectionManager::class.java)
var mediaProjection : MediaProjection
val startMediaProjection = registerForActivityResult( StartActivityForResult() ) { result -> if (result.resultCode == RESULT_OK) { mediaProjection = mediaProjectionManager .getMediaProjection(result.resultCode, result.data!!) } }
startMediaProjection.launch(mediaProjectionManager.createScreenCaptureIntent())

Java

final MediaProjectionManager mediaProjectionManager =
    getSystemService(MediaProjectionManager.class);
final MediaProjection[] mediaProjection = new MediaProjection[1];
ActivityResultLauncher startMediaProjection = registerForActivityResult( new StartActivityForResult(), result -> { if (result.getResultCode() == Activity.RESULT_OK) { mediaProjection[0] = mediaProjectionManager .getMediaProjection(result.getResultCode(), result.getData()); } } );
startMediaProjection.launch(mediaProjectionManager.createScreenCaptureIntent());

Tela virtual

O centro de uma projeção de mídia é a tela virtual, que é criada chamando o método createVirtualDisplay() em uma instância MediaProjection:

Kotlin

virtualDisplay = mediaProjection.createVirtualDisplay(
                     "ScreenCapture",
                     width,
                     height,
                     screenDensity,
                     DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                     surface,
                     null, null)

Java

virtualDisplay = mediaProjection.createVirtualDisplay(
                     "ScreenCapture",
                     width,
                     height,
                     screenDensity,
                     DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                     surface,
                     null, null);

Os parâmetros width e height especificam as dimensões da tela virtual. Para conferir os valores de largura e altura, use as APIs WindowMetrics introduzidas no Android 11 (nível 30 da API). Para mais detalhes, consulte a seção Tamanho da projeção de mídia.

Superfície

Dimensione a superfície da projeção de mídia para produzir a saída na resolução adequada. Aumente a superfície (baixa resolução) para a transmissão da tela em TVs ou monitores de computador e diminua (alta resolução) para a gravação da tela do dispositivo.

A partir do Android 12L (nível 32 da API), ao renderizar o conteúdo capturado na plataforma, o sistema dimensiona o conteúdo de maneira uniforme, mantendo a proporção, de modo que as duas dimensões do conteúdo (largura e altura) sejam iguais ou menores do que as dimensões correspondentes da plataforma. O conteúdo capturado é centralizado na plataforma.

A abordagem de dimensionamento do Android 12L melhora a transmissão de tela para televisões e outras telas grandes, maximizando o tamanho da imagem da plataforma e garantindo a proporção adequada.

Permissão para serviços de primeiro plano

Se o app for destinado ao Android 14 ou mais recente, o manifesto do app precisará incluir uma declaração de permissão para o tipo de serviço em primeiro plano mediaProjection:

<manifest ...>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
    <application ...>
        <service
            android:name=".MyMediaProjectionService"
            android:foregroundServiceType="mediaProjection"
            android:exported="false">
        </service>
    </application>
</manifest>

Inicie o serviço de projeção de mídia com uma chamada para startForeground().

Se você não especificar o tipo de serviço em primeiro plano na chamada, o tipo será definido como um número inteiro de bits dos tipos de serviço em primeiro plano definidos no manifesto. Se o manifesto não especificar nenhum tipo de serviço, o sistema vai gerar uma MissingForegroundServiceTypeException.

O app precisa solicitar o consentimento do usuário antes de cada sessão de projeção de mídia. Uma sessão é uma única chamada para createVirtualDisplay(). Um token MediaProjection precisa ser usado apenas uma vez para fazer a chamada.

No Android 14 ou mais recente, o método createVirtualDisplay() gera uma SecurityException se o app fizer uma das seguintes ações:

  • Transmite uma instância Intent retornada de createScreenCaptureIntent() para getMediaProjection() mais de uma vez
  • Chama createVirtualDisplay() mais de uma vez na mesma instância de MediaProjection

Tamanho da projeção de mídia

Uma projeção de mídia pode capturar toda a tela do dispositivo ou uma janela de app, independente do modo de janela.

Tamanho inicial

Com a projeção de mídia em tela cheia, o app precisa determinar o tamanho da tela do dispositivo. No compartilhamento de tela do app, o app não poderá determinar o tamanho da tela capturada até que o usuário selecione a região de captura. Portanto, o tamanho inicial de qualquer projeção de mídia é o tamanho da tela do dispositivo.

Use o método WindowManager getMaximumWindowMetrics() da plataforma para retornar um objeto WindowMetrics para a tela do dispositivo, mesmo que o app host de projeção de mídia esteja no modo de várias janelas, ocupando apenas parte da tela.

Para compatibilidade com o nível 14 da API e mais recentes, use o método computeMaximumWindowMetrics() do WindowMetricsCalculator da biblioteca WindowManager do Jetpack.

Chame o método WindowMetrics getBounds() para receber a largura e a altura da tela do dispositivo.

Mudanças de tamanho

O tamanho da projeção de mídia pode mudar quando o dispositivo é girado ou o usuário seleciona uma janela de app como a região de captura no compartilhamento de tela do app. A projeção de mídia pode ser letterbox se o conteúdo capturado for de um tamanho diferente das métricas de janela máxima obtidas quando a projeção de mídia foi configurada.

Para garantir que a projeção de mídia seja alinhada com precisão ao tamanho do conteúdo capturado para qualquer região capturada e em todas as rotações do dispositivo, use o callback onCapturedContentResize() para redimensionar a captura. Para mais informações, consulte a seção Personalização a seguir.

Personalização

Seu app pode personalizar a experiência do usuário de projeção de mídia com as seguintes APIs MediaProjection.Callback:

  • onCapturedContentVisibilityChanged(): permite que o app host (o app que iniciou a projeção de mídia) mostre ou oculte o conteúdo compartilhado.

    Use esse callback para personalizar a interface do app com base na visibilidade da região capturada para o usuário. Por exemplo, se o app estiver visível para o usuário e exibir o conteúdo capturado na interface do app, e o app capturado também estiver visível para o usuário (conforme indicado por esse callback), o usuário vai ver o mesmo conteúdo duas vezes. Use o callback para atualizar a interface do app e ocultar o conteúdo capturado, liberando espaço de layout para outro conteúdo.

  • onCapturedContentResize(): permite que o app host mude o tamanho da projeção de mídia na tela virtual e a projeção de mídia Surface com base no tamanho da região de exibição capturada.

    É acionado sempre que o conteúdo capturado, uma única janela do app ou a tela inteira do dispositivo, muda de tamanho (por causa da rotação do dispositivo ou do app capturado entrar em um modo de janela diferente). Use essa API para redimensionar a tela virtual e a plataforma para garantir que a proporção corresponda ao conteúdo capturado e que a captura não seja letterbox.

Recuperação de recursos

O app precisa registrar o callback MediaProjection onStop() para ser informado quando a sessão de projeção de mídia for interrompida e se tornar inválida. Quando a sessão for interrompida, o app precisará liberar os recursos que ele tem, como a tela virtual e a superfície de projeção. Uma sessão de projeção de mídia interrompida não pode mais criar uma nova tela virtual, mesmo que o app não tenha criado uma tela virtual para essa projeção de mídia.

O sistema invoca o callback quando a projeção de mídia é encerrada. Essa rescisão pode ocorrer por vários motivos, como:

  • o usuário interromper a sessão usando a interface do app ou o chip da barra de status de projeção de mídia do sistema;
  • a tela está sendo bloqueada
  • outra sessão de projeção de mídia é iniciada
  • o processo do app é encerrado

Se o app não registrar o callback, qualquer chamada para createVirtualDisplay() gerará IllegalStateException.

Desativar

O Android 14 ou mais recente ativa o compartilhamento de tela do app por padrão. Cada sessão de projeção de mídia oferece aos usuários a opção de compartilhar uma janela de app ou toda a tela.

Seu app pode desativar o compartilhamento de tela chamando o método createScreenCaptureIntent(MediaProjectionConfig) com um argumento MediaProjectionConfig retornado de uma chamada para createConfigForDefaultDisplay().

Uma chamada para createScreenCaptureIntent(MediaProjectionConfig) com um argumento MediaProjectionConfig retornado de uma chamada para createConfigForUserChoice() é igual ao comportamento padrão, ou seja, uma chamada para createScreenCaptureIntent().

Apps redimensionáveis

Sempre crie apps de projeção de mídia que possam ser redimensionados (resizeableActivity="true"). Os apps redimensionáveis oferecem suporte a mudanças de configuração do dispositivo e do modo de várias janelas. Consulte Suporte a várias janelas.

Se o app não for redimensionável, ele precisará consultar os limites da tela em um contexto de janela e usar getMaximumWindowMetrics() para extrair as WindowMetrics da área máxima de exibição disponível para o app :

Kotlin

val windowContext = context.createWindowContext(context.display!!,
      WindowManager.LayoutParams.TYPE_APPLICATION, null)
val projectionMetrics = windowContext.getSystemService(WindowManager::class.java)
      .maximumWindowMetrics

Java

Context windowContext = context.createWindowContext(context.getDisplay(),
      WindowManager.LayoutParams.TYPE_APPLICATION, null);
WindowMetrics projectionMetrics = windowContext.getSystemService(WindowManager.class)
      .getMaximumWindowMetrics();

Ícone da barra de status e parada automática

As explorações de projeção de tela expõem dados privados do usuário, como informações financeiras, porque os usuários não percebem que a tela do dispositivo está sendo compartilhada.

O Android 15 (nível 35 da API) e versões mais recentes mostram um ícone grande e proeminente na barra de status para alertar os usuários sobre qualquer projeção de tela em andamento. Os usuários podem tocar no ícone para impedir que a tela seja compartilhada, transmitida ou gravada. Além disso, a projeção de tela é interrompida automaticamente quando a tela do dispositivo é bloqueada.

Figura 2. Ícone da barra de status para compartilhamento, transmissão e gravação de tela.

Teste a disponibilidade do ícone da barra de status da projeção de mídia iniciando o compartilhamento de tela, a transmissão ou a gravação. O ícone vai aparecer na barra de status.

Para garantir que o app libere recursos e atualize a interface quando a projeção de tela for interrompida pela interação do usuário com o ícone da barra de status ou pela ativação da tela de bloqueio, faça o seguinte:

  • Crie uma instância de MediaProjection.Callback.

  • Implemente o método de callback onStop(). O método é chamado quando a projeção de tela é interrompida. Libere todos os recursos que o app está segurando e atualize a interface do app conforme necessário.

Para testar o callback, toque no ícone da barra de status ou bloqueie a tela do dispositivo para interromper a projeção. Verifique se o método onStop() é chamado e se o app responde conforme o esperado.

Outros recursos

Para saber mais sobre projeção de mídia, consulte Capturar reproduções de vídeo e áudio.