Projeção de mídia

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

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

O compartilhamento de tela de app garante a privacidade do usuário, aumenta a produtividade do usuário e melhora a multitarefa ao permitir que os usuários executem vários aplicativos, mas com restrições o compartilhamento de conteúdo em apenas um app.

Três representações de exibição

Uma projeção de mídia captura o conteúdo de uma tela de dispositivo ou janela de app e e projeta a imagem capturada em uma tela virtual que renderiza a imagem no 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 em exibição virtual. Tela virtual gravada para recursos fornecidos pelo aplicativo Surface:

O aplicativo fornece o Surface usando uma MediaRecorder, SurfaceTexture ou ImageReader, que consome o conteúdo da tela capturada e permite que você gerencie as imagens renderizadas na Surface em tempo real. Você pode salvar as imagens como uma gravação ou uma transmissão para uma TV ou outro dispositivo.

Exibição real

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

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

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

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 do ambiente exibição. Para obter os valores de largura e altura, use o método As APIs WindowMetrics foram lançadas no Android 11 (nível 30 da API). Para mais detalhes, consulte seção Tamanho da projeção de mídia.)

Surface

Dimensione a superfície da projeção de mídia para produzir a saída da forma correta e resolução. Deixe a superfície grande (baixa resolução) para transmissão de tela em TVs ou monitores de computador e pequeno (alta resolução) para gravação de telas do dispositivo.

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

A abordagem de escalonamento do Android 12L melhora a transmissão de tela para televisões e outras telas grandes maximizando o tamanho da imagem da superfície e, ao mesmo tempo, garantir a proporção adequada.

Permissão para serviços de primeiro plano

Caso o app seja destinado ao Android 14 ou versões mais recentes, o manifesto dele vai precisar incluir uma declaração de permissão para o Tipo de serviço em primeiro plano do 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á o padrão como um número inteiro bit a bit dos tipos de serviço em primeiro plano definidos no manifesto. Se o manifesto não especificar nenhum tipo de serviço, o sistema emitirá MissingForegroundServiceTypeException.

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

No Android 14 ou em versões mais recentes, o método createVirtualDisplay() gera uma SecurityException se as app realiza uma das seguintes ações:

  • Transmite uma instância Intent retornada de createScreenCaptureIntent() para getMediaProjection() mais de uma vez
  • Liga createVirtualDisplay() mais de uma vez no mesmo MediaProjection instância

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 seja qual for o modo de janelamento.

Tamanho inicial

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

Usar a plataforma WindowManager getMaximumWindowMetrics() para retornar um Objeto WindowMetrics do 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 WindowMetricsCalculator computeMaximumWindowMetrics() da biblioteca WindowManager do Jetpack.

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

Alterações de tamanho

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

Para garantir que a projeção de mídia se alinhe com precisão ao tamanho do conteúdo para qualquer região capturada e entre rotações de dispositivos, use o Callback onCapturedContentResize() para redimensionar a captura. Para mais informações do produto, consulte a seção Personalização, a seguir).

Personalização

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

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

    Use esse callback para personalizar a interface do app com base na captura ou não fica visível para o usuário. Por exemplo, se o app estiver visível para e está exibindo o conteúdo capturado na interface do usuário do aplicativo, e o aplicativo capturado também é visível para o usuário (como indicado por este ), ele verá o mesmo conteúdo duas vezes. Usar o callback para atualizar interface do app para ocultar o conteúdo capturado e liberar espaço de layout na para outros conteúdos.

  • onCapturedContentResize() Permite que o app host altere o tamanho da projeção de mídia na VM exibição e projeção de mídia Surface com base no tamanho dos região de exibição.

    Acionado sempre que o conteúdo capturado (uma única janela do app ou tela do dispositivo: muda de tamanho (devido à rotação do dispositivo ou ao entrar em um modo de janelamento diferente). Use essa API para redimensionar tela e superfície virtuais para garantir que a proporção corresponda ao tamanho conteúdo sem o efeito letterbox.

Recuperação de recursos

Seu app precisa registrar o MediaProjection onStop() para ser informado quando a sessão de projeção de mídia for interrompida e se tornar inválido. Quando a sessão é interrompida, seu aplicativo deve liberar os recursos que ela mantém, como a tela virtual e a superfície de projeção. A parou sessão de projeção de mídia não poderá mais criar uma tela virtual, mesmo se o app não tiver criado anteriormente uma tela virtual para essa projeção de mídia.

O callback é chamado quando a projeção de mídia é encerrada, seja porque o o usuário interrompe manualmente a sessão ou porque o sistema interrompe a sessão por por algum motivo.

Caso o app não registre o callback, qualquer chamada para createVirtualDisplay() lança IllegalStateException.

Desativar

O Android 14 ou versões mais recentes ativam o compartilhamento de tela de apps por padrão. Cada mídia de projeção oferece aos usuários a opção de compartilhar uma janela de aplicativo ou a 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() é o mesmo como comportamento padrão, ou seja, uma chamada para createScreenCaptureIntent().

Apps redimensionáveis

Sempre crie apps de projeção de mídia que podem ser redimensionados (resizeableActivity="true"). Redimensionável os apps são compatíveis com mudanças de configuração do dispositivo e com o modo de várias janelas. Consulte suporte a várias janelas).

Se o app não for redimensionável, ele vai precisar consultar os limites da tela em uma janela. contexto e usar getMaximumWindowMetrics() para recuperar o WindowMetrics do a á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();

Outros recursos

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