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) introduz o compartilhamento de tela do app, que permite que os usuários compartilhem uma única janela do app em vez da tela inteira 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 é usado para capturar um app em tela cheia. Apenas o conteúdo do app selecionado é compartilhado.
O compartilhamento de tela do 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 apps, mas restrinja o compartilhamento de conteúdo a apenas um.
Três representações de tela
Uma projeção de mídia captura o conteúdo de uma tela de dispositivo ou janela de um app e
projeta a imagem capturada em uma tela virtual que renderiza a imagem em
uma Surface
.
O app fornece o Surface
usando um
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 as transmita para uma TV ou outro dispositivo.
Display 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 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<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 da tela virtual. Para receber valores de largura e altura, use as
APIs WindowMetrics
introduzidas
no Android 11 (nível 30 da API). Para saber mais, 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. Torne a superfície grande (baixa resolução) para transmitir a tela para TVs ou monitores de computador e pequena (alta resolução) para gravação de telas do dispositivo.
No Android 12L (nível 32 da API) e versões mais recentes, ao renderizar o conteúdo capturado na superfície, 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 é centralizado na superfície.
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 superfície e garantindo a proporção correta.
Permissão para serviços de primeiro plano
Caso o app seja destinado ao Android 14 ou versões mais recentes, o manifesto precisa 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 o tipo de serviço em primeiro plano não for especificado na chamada, o tipo será definido por 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 vai gerar uma
MissingForegroundServiceTypeException
.
Consentimento do usuário
Seu 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 versões mais recentes, o método createVirtualDisplay()
vai gerar uma
SecurityException
se o
app realizar uma das seguintes ações:
- Transmite uma instância
Intent
retornada decreateScreenCaptureIntent()
paragetMediaProjection()
mais de uma vez - Chama
createVirtualDisplay()
mais de uma vez na mesma instância deMediaProjection
Tamanho da projeção de mídia
Uma projeção de mídia pode capturar toda a tela do dispositivo ou uma janela do app, independente do modo de janelas.
Tamanho inicial
Com a projeção de mídia em tela cheia, seu app precisa determinar o tamanho da 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 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 WindowMetricsCalculator
computeMaximumWindowMetrics()
da biblioteca WindowManager
do Jetpack.
Chame o método WindowMetrics
getBounds()
para conferir 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 a região de captura no compartilhamento de tela do app. A projeção de mídia poderá receber efeito letterbox se o conteúdo capturado tiver um tamanho diferente das métricas máximas da janela recebidas quando a projeção de mídia foi configurada.
Para garantir que a projeção de mídia se alinhe com precisão ao tamanho do conteúdo
capturado para qualquer região capturada e entre 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 estas
APIs de MediaProjection.Callback
:
onCapturedContentVisibilityChanged()
: ativa o app host (que iniciou a projeção de mídia) para mostrar ou ocultar 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, estiver exibindo o conteúdo capturado na interface do app, e o app capturado também estiver visível para o usuário (como indicado neste callback), o usuário verá o mesmo conteúdo duas vezes. Use o callback para atualizar a interface do app para ocultar o conteúdo capturado e liberar espaço de layout no app para outros conteúdos.
onCapturedContentResize()
: permite que o app host mude o tamanho da projeção de mídia na tela virtual e na projeção de mídiaSurface
com base no tamanho da região de exibição capturada.Acionado sempre que o conteúdo capturado, uma única janela do app ou uma tela completa 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 superfície para garantir que a proporção corresponda ao conteúdo capturado e que a captura não tenha efeito letterbox.
Recuperação de recursos
Seu app precisa registrar o callback MediaProjection
onStop()
para liberar recursos retidos pelo app, como a tela virtual e a
superfície de projeção.
O callback é chamado quando a projeção de mídia é encerrada ou quando o usuário não dá consentimento para continuar uma sessão de captura.
Se o app não registrar o callback e o usuário não consentir com a
sessão de projeção de mídia, as chamadas para createVirtualDisplay()
gerarão
IllegalStateException
.
Desativar
O Android 14 ou versões mais recentes 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 do app ou a tela inteira.
O 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 torne seus apps de projeção de mídia redimensionáveis (resizeableActivity="true"
). Os apps
redimensionáveis oferecem suporte a mudanças de configuração do dispositivo e ao 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();
Outros recursos
Para saber mais sobre projeção de mídia, consulte Capturar reproduções de vídeo e áudio.