Projeção de mídia em telas grandes

As APIs de projeção de mídia 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.

Uma projeção de mídia envolve três representações da tela do dispositivo:

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 real do dispositivo projetada na tela virtual. Conteúdo da tela virtual criado para a Surface fornecida pelo aplicativo.

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

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

MediaProjection

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, o áudio do dispositivo ou ambos. O token é representado por uma instância da classe MediaProjection. É possível criar uma instância dessa classe ao iniciar uma nova atividade.

Abordagem legada

Para acessar um token de projeção de mídia usando a abordagem legada, chame startActivityForResult() com uma intent retornada do método createScreenCaptureIntent() do serviço do sistema MediaProjectionManager:

Kotlin

startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(),
                       REQUEST_MEDIA_PROJECTION)

Java

startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(),
                       REQUEST_MEDIA_PROJECTION);

A chamada exibe uma caixa de diálogo de confirmação informando ao usuário que a projeção de mídia captura todas as informações exibidas, incluindo informações confidenciais ou de identificação pessoal.

Se o usuário confirmar, o método startActivityForResult() transmitirá um código de resultado e os dados para o callback onActivityResult().

Você pode transmitir os dados e o código de resultado ao método getMediaProjection() do MediaProjectionManager para criar uma instância da MediaProjection:

Kotlin

mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, resultData)

Java

mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, resultData);

A abordagem recomendada para acessar um token de projeção de mídia usa APIs da biblioteca Jetpack Activity:

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<Intent> startMediaProjection = registerForActivityResult(
  new StartActivityForResult(),
  result -> {
    if (result.getResultCode() == Activity.RESULT_OK) {
      mediaProjection[0] = mediaProjectionManager
        .getMediaProjection(result.getResultCode(), result.getData());
    }
  }
);

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 a largura e a altura da tela virtual. Para usar valores que correspondam à largura e altura da projeção de mídia, use as APIs WindowMetrics introduzidas no Android 11 (nível 30 da API).

WindowMetrics

Uma projeção de mídia captura toda a tela, independente do app que cria a projeção de mídia estar em execução em tela cheia ou no modo de várias janelas.

O método correto para acessar as dimensões de uma projeção de mídia é WindowManager#getMaximumWindowMetrics(). Esse método retorna um objeto WindowMetrics para a tela toda, mesmo que o app 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 WindowMetricsCalculator#computeMaximumWindowMetrics() da biblioteca Jetpack WindowManager.

Chame o método WindowMetrics#getBounds() para acessar a largura e a altura corretas da tela virtual da projeção de mídia (consulte Tela virtual).

Sempre crie apps de projeção de mídia que podem ser redimensionados. Os apps redimensionáveis oferecem suporte a mudanças de configuração de dispositivos 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 extrair as WindowMetrics da área máxima disponível da tela para o app usando o método WindowManager#getMaximumWindowMetrics():

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

Superfície

É necessário dimensionar a superfície da projeção de mídia para produzir a saída na resolução pretendida. Aumente o tamanho (baixa resolução) para a transmissão da tela em TVs ou monitores de computador e pequeno (alta resolução) para a gravação da tela do dispositivo.

A partir do 12L (nível Sv2 da API), quando o sistema renderiza uma tela virtual na superfície, ele dimensiona a tela virtual para caber na superfície usando um processo semelhante à opção centerInside da ImageView.

A nova abordagem de escalonamento melhora a transmissão de tela para televisores e outras telas grandes, maximizando o tamanho da imagem da superfície e garantindo a proporção adequada.

Recomendações

Para ter os melhores resultados com a projeção de mídia, siga estas recomendações:

  • Torne seu app redimensionável. 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. No manifesto do app, defina resizeableActivity="true". No Android 7.0 (nível 24 da API), essa configuração é definida como "true" (verdadeira) por padrão.
  • Ative seus apps para oferecer suporte a orientações de paisagem e retrato, porque as duas orientações são comuns em smartphones, tablets e formatos dobráveis.
  • Use WindowManager#getMaximumWindowMetrics() para acessar os limites de uma projeção de mídia. Para compatibilidade com o nível 14 da API e mais recentes, use o Jetpack WindowManager. Consulte a seção WindowMetrics.
  • Se o app não for redimensionável, extraia os limites de projeção de mídia de um contexto de janela. Consulte a seção WindowMetrics.

Recursos

Para mais informações, consulte Capturar reproduções de vídeo e áudio.