A partir do Android 8.0 (API de nível 26), o Android permite que as atividades sejam iniciadas no modo picture-in-picture. O PiP é um tipo especial de modo de várias janelas usado para reprodução de vídeo, videochamadas e navegação. Ele permite que o usuário fixe a janela de atividade atual em um canto da tela enquanto navega entre apps ou pelo conteúdo na tela principal.
O modo picture-in-picture aproveita as APIs de várias janelas disponíveis no Android 7.0 para fornecer a janela fixa de sobreposição de vídeo. Para adicionar o picture-in-picture ao seu app, você precisa registrar suas atividades compatíveis com esse modo, alternar sua atividade para ele conforme necessário e verificar se os elementos da IU estão ocultos e se a reprodução continua quando a atividade está no modo picture-in-picture.
A janela PIP aparece na camada superior da tela, em um canto escolhido pelo sistema.
O PiP também é compatível com dispositivos Android TV OS com o Android 14 (nível 34 da API) ou mais recente. Embora haja muitas semelhanças, há outras considerações ao usar o PiP na TV.
Como os usuários podem interagir com a janela picture-in-picture
É possível arrastar a janela de picture-in-picture para outro local. A partir do Android 12, os usuários também podem fazer o seguinte:
Tocar na janela para exibir um botão de alternância de tela cheia, um botão "Fechar", um botão de configurações e ações personalizadas fornecidas pelo seu app, por exemplo, controles de reprodução.
Toque duas vezes na janela para alternar entre o tamanho atual do PiP e o tamanho máximo ou mínimo. Por exemplo, tocar duas vezes em uma janela maximizada a minimiza, e o inverso também é verdadeiro.
Oculte a janela arrastando-a para a borda esquerda ou direita. Para reexibir a janela, toque na parte visível dela ou arraste-a para fora.
Redimensionar a janela de picture-in-picture usando o gesto de pinça para aplicar zoom.
Seu app controla quando a atividade atual entra no modo picture-in-picture. Veja alguns exemplos:
Uma atividade pode entrar no modo picture-in-picture quando o usuário toca no botão home ou desliza para cima. É assim que o Google Maps continua exibindo rotas enquanto o usuário executa outra atividade ao mesmo tempo.
O app pode mover um vídeo para o modo picture-in-picture quando o usuário volta do vídeo para navegar por outro conteúdo.
O app pode alternar um vídeo para o modo picture-in-picture enquanto um usuário assiste o final de um episódio de conteúdo. A tela principal exibe informações promocionais ou resumidas sobre o próximo episódio da série.
Seu app pode oferecer uma forma de os usuários colocarem outros conteúdos em fila enquanto assistem um vídeo. O vídeo continua sendo reproduzido no modo PIP enquanto a tela principal mostra uma atividade de seleção de conteúdo.
Declarar suporte ao PiP
Por padrão, o sistema não é automaticamente compatível com o picture-in-picture para apps. Se você quiser
oferecer suporte ao PiP no seu app, registre a atividade de vídeo no manifesto
definindo android:supportsPictureInPicture como true. Além disso, especifique que a
atividade processa as mudanças de configuração de layout para que ela não seja
reiniciada quando ocorrerem mudanças de layout durante as transições do modo PIP.
<activity android:name="VideoActivity"
android:supportsPictureInPicture="true"
android:configChanges=
"screenSize|smallestScreenSize|screenLayout|orientation"
...
Implement PiP with Jetpack
Use the Jetpack Picture-in-Picture library to implement picture-in-picture experience as it streamlines integration and reduces common in-app issues. Refer to our platform sample app to see an example of its usage. However, if you prefer to implement PiP using the platform APIs, refer to the following documentation.
Mudar sua atividade para PiP
No Android 12 e versões mais recentes, é possível mudar a atividade para o modo PIP definindo
a flag setAutoEnterEnabled como true. Com essa configuração, uma atividade
muda automaticamente para o modo PIP, conforme necessário, sem precisar chamar explicitamente
enterPictureInPictureMode() em onUserLeaveHint. Isso tem o benefício adicional de fornecer transições muito mais suaves. Para mais detalhes, consulte Suavizar
as transições para o modo PIP na navegação por gestos.
Se você estiver segmentando o Android 11 ou versões anteriores, uma atividade precisará chamar
enterPictureInPictureMode() para alternar para o modo PIP. Por exemplo, o
código abaixo alterna uma atividade para o modo PIP quando o usuário clica em um botão
dedicado na interface do app:
override fun onActionClicked(action: Action) {
if (action.id.toInt() == R.id.lb_control_picture_in_picture) {
activity?.enterPictureInPictureMode()
return
}
}
Talvez você queira incluir uma lógica que mude uma atividade para o modo PIP em vez
de ir para o segundo plano. Por exemplo, o Google Maps alternará para o modo picture-in-picture se o usuário pressionar o botão "Início" ou "Recentes"
enquanto o app estiver navegando. É possível
capturar esse caso modificando onUserLeaveHint():
override fun onUserLeaveHint() {
if (iWantToBeInPipModeNow()) {
enterPictureInPictureMode()
}
}
Recomendado: oferecer aos usuários uma experiência de transição de PiP refinada
O Android 12 adicionou melhorias estéticas significativas às transições animadas entre as janelas de tela cheia e PiP. Recomendamos implementar todas as mudanças aplicáveis. Depois disso, elas serão dimensionadas automaticamente para telas grandes, como dobráveis e tablets, sem que você precise fazer mais nada.
Se o app não incluir as atualizações aplicáveis, as transições de PIP ainda vão funcionar, mas as animações serão menos refinadas. Por exemplo, a transição do modo de tela cheia para o PiP pode fazer com que a janela do PiP desapareça durante a transição antes de reaparecer quando ela for concluída.
Essas mudanças envolvem o seguinte:
- Transições mais suaves para o modo PIP na navegação por gestos
- Definir um
sourceRectHintadequado para entrar e sair do modo PIP - Desativar o redimensionamento contínuo para conteúdo que não é de vídeo
Consulte o exemplo de PictureInPicture do Android Kotlin como referência para ativar uma experiência de transição refinada.
Suavizar transições no modo picture-in-picture pela navegação por gestos
No Android 12, a flag setAutoEnterEnabled oferece uma animação
muito mais suave para a transição para o conteúdo de vídeo no modo picture-in-picture usando a navegação
por gestos. Por exemplo, ao deslizar para cima para voltar à tela inicial no modo de tela cheia.
Siga estas etapas para fazer essa mudança:
- Use
setAutoEnterEnabledpara criarPictureInPictureParams.Builder:
setPictureInPictureParams(PictureInPictureParams.Builder()
.setAspectRatio(aspectRatio)
.setSourceRectHint(sourceRectHint)
.setAutoEnterEnabled(true)
.build())
Note: When `setAutoEnterEnabled` is enabled, you don't need to explicitly
call [`enterPictureInPictureMode`][5] in [`onUserLeaveHint`][6].
Chame
setPictureInPictureParamsusando osPictureInPictureParamsatualizados anteriormente. O app não espera o callbackonUserLeaveHint, como faria no Android 11.Por exemplo, talvez você queira chamar
setPictureInPictureParamsna primeira reprodução e em qualquer outra se a proporção for alterada.Chame
setAutoEnterEnabled(false), mas apenas quando necessário. Por exemplo, provavelmente não será ideal entrar no modo PIP se a reprodução atual estiver no estado pausado.
Defina um sourceRectHint adequado para entrar e sair do modo PIP.
Desde a introdução do PiP no Android 8.0, setSourceRectHint
indica a área da atividade que fica visível após a transição para o
picture-in-picture, por exemplo, os limites de visualização de vídeo em um player de vídeo.
Com o Android 12, o sistema usa sourceRectHint para implementar uma animação muito mais suave
ao entrar e sair do modo picture-in-picture.
Para definir corretamente sourceRectHint para entrar e sair do modo PIP:
- Crie
PictureInPictureParamsusando os limites adequados comosourceRectHint. Também recomendamos anexar um listener de mudança de layout ao player de vídeo:
val mOnLayoutChangeListener =
OnLayoutChangeListener { v: View?, left: Int,
top: Int, right: Int, bottom: Int, oldLeft: Int, oldTop:
Int, oldRight: Int, oldBottom: Int ->
val sourceRectHint = Rect()
mYourVideoView.getGlobalVisibleRect(sourceRectHint)
val builder = PictureInPictureParams.Builder()
.setSourceRectHint(sourceRectHint)
setPictureInPictureParams(builder.build())
}
mYourVideoView.addOnLayoutChangeListener(mOnLayoutChangeListener)
- Se necessário, atualize o
sourceRectHintantes que o sistema inicie a transição de saída. Quando o sistema está prestes a sair do modo picture-in-picture, a hierarquia de visualização da atividade é definida para a configuração de destino (por exemplo, tela cheia). O app pode anexar um listener de mudança de layout à visualização raiz ou de destino, como a do player de vídeo, para detectar o evento e atualizar asourceRectHintantes do início da animação.
// Listener is called immediately after the user exits PiP but before animating.
playerView.addOnLayoutChangeListener { _, left, top, right, bottom,
oldLeft, oldTop, oldRight, oldBottom ->
if (left != oldLeft
|| right != oldRight
|| top != oldTop
|| bottom != oldBottom) {
// The playerView's bounds changed, update the source hint rect to
// reflect its new bounds.
val sourceRectHint = Rect()
playerView.getGlobalVisibleRect(sourceRectHint)
setPictureInPictureParams(
PictureInPictureParams.Builder()
.setSourceRectHint(sourceRectHint)
.build()
)
}
}
Desativar o redimensionamento contínuo de conteúdos que não são de vídeo
O Android 12 adiciona a flag setSeamlessResizeEnabled, que oferece uma animação de transição
muito mais suave ao redimensionar conteúdo que não é de vídeo na janela
PIP. Antes, o redimensionamento de conteúdo que não era de vídeo em uma janela PiP podia criar
artefatos visuais desagradáveis.
Para ativar o redimensionamento contínuo para conteúdo de vídeo:
setPictureInPictureParams(PictureInPictureParams.Builder()
.setSeamlessResizeEnabled(true)
.build())
Processar a interface durante o PiP
Quando a atividade entra ou sai do modo picture-in-picture (PiP), o sistema
chama Activity.onPictureInPictureModeChanged() ou
Fragment.onPictureInPictureModeChanged().
O Android 15 apresenta mudanças que garantem uma transição ainda mais suave ao entrar no modo PIP. Isso é útil para apps que têm elementos de interface sobrepostos à interface principal, que entra no PiP.
Os desenvolvedores usam o callback onPictureInPictureModeChanged() para definir a lógica que alterna a visibilidade dos elementos da interface sobrepostos. Esse callback é
acionado quando a animação de entrada ou saída do PiP é concluída. No
Android 15, a classe PictureInPictureUiState inclui um novo estado.
Com esse novo estado da interface, os apps destinados ao Android 15 observam o callback
Activity#onPictureInPictureUiStateChanged() sendo invocado com
isTransitioningToPip() assim que a animação do PiP começa. Há
muitos elementos da interface que não são relevantes para o app quando ele está no modo PIP, por
exemplo, visualizações ou layouts que incluem informações como sugestões, próximos
vídeos, classificações e títulos. Quando o app entrar no modo PIP, use o
callback onPictureInPictureUiStateChanged() para ocultar esses elementos da interface. Quando
o app entrar no modo de tela cheia pela janela PIP, use o
callback onPictureInPictureModeChanged() para reexibir esses elementos, conforme mostrado nos
exemplos abaixo:
override fun onPictureInPictureUiStateChanged(pipState: PictureInPictureUiState) {
if (pipState.isTransitioningToPip()) {
// Hide UI elements.
}
}
override fun onPictureInPictureModeChanged(
isInPictureInPictureMode: Boolean,
newConfig: Configuration
) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
if (!isInPictureInPictureMode) {
// Unhide UI elements.
}
}
Essa alternância rápida de visibilidade de elementos irrelevantes da interface (para uma janela PiP) ajuda a garantir uma animação de entrada PiP mais suave e sem oscilações.
Substitua esses callbacks para redesenhar os elementos da interface da atividade. No modo PIP, sua atividade é mostrada em uma janela pequena. Os usuários não podem interagir com os elementos da interface do app quando ele está no modo picture-in-picture, e os detalhes de pequenos elementos da interface podem ser difíceis de ver. As atividades de reprodução de vídeo com IU mínima proporcionam a melhor experiência do usuário.
Se o app precisar fornecer ações personalizadas para o PiP, consulte Adicionar controles nesta página. Remova outros elementos da interface antes que a atividade entre no PiP e restaure-os quando ela voltar ao modo de tela cheia.
Adicionar controles
A janela de picture-in-picture pode exibir controles quando o usuário abre o menu da janela, tocando nela em um dispositivo móvel ou selecionando o menu no controle remoto da TV.
Se um app tiver uma sessão de mídia ativa, os controles de reprodução, pausa, avanço e retorno vão aparecer.
Também é possível especificar ações personalizadas explicitamente criando
PictureInPictureParams com
PictureInPictureParams.Builder.setActions() antes de entrar no modo PIP
e transmitir os parâmetros ao entrar no modo PIP usando
enterPictureInPictureMode(android.app.PictureInPictureParams) ou
setPictureInPictureParams(android.app.PictureInPictureParams). Tome
cuidado. Se você tentar adicionar mais de getMaxNumPictureInPictureActions(),
vai receber apenas o número máximo.
Continuar a reprodução do vídeo no PiP
Quando a atividade muda para o PiP, o sistema a coloca no estado pausado
e chama o método onPause(). A reprodução de vídeo
não deve ser pausada, mas sim continuar se a atividade for pausada durante
a transição para o modo PIP.
No Android 7.0 (nível 24 da API) e versões mais recentes, pause e retome a reprodução de vídeo
quando o sistema chamar onStop() e
onStart() da atividade. Ao fazer isso, você evita ter que verificar se o app está
no modo PIP em onPause() e continuar explicitamente a reprodução.
Se você não tiver definido a flag setAutoEnterEnabled como true e precisar
pausar a reprodução na implementação de onPause(), verifique o modo PIP chamando
isInPictureInPictureMode() e processe a reprodução adequadamente. Exemplo:
override fun onPause() {
super.onPause()
// If called while in PiP mode, don't pause playback.
if (isInPictureInPictureMode) {
// Continue playback.
} else {
// Use existing playback logic for paused activity behavior.
}
}
Quando a atividade sai do modo PIP e volta para o modo de tela cheia, o sistema
retoma a atividade e chama o método onResume().
Usar uma única atividade de reprodução para o PIP
No seu app, um usuário pode selecionar um novo vídeo ao procurar conteúdo na tela principal, enquanto uma atividade de reprodução de vídeo está no modo picture-in-picture. Reproduza o novo vídeo na atividade de reprodução atual no modo de tela cheia, em vez de iniciar uma nova atividade que possa confundir o usuário.
Para garantir que uma única atividade seja usada para solicitações de reprodução de vídeo e alternada
para o modo PIP ou fora dele, conforme necessário, defina o android:launchMode da atividade como
singleTask no manifesto:
<activity android:name="VideoActivity"
...
android:supportsPictureInPicture="true"
android:launchMode="singleTask"
...
Na sua atividade, substitua onNewIntent() e processe o novo vídeo,
interrompendo a reprodução de qualquer vídeo atual, se necessário.
Suporte ao PiP para apps de câmera
Para ativar o PiP em apps de câmera, garanta que a câmera permaneça ativa no
modo PiP, não fechando a câmera quando onPause() for chamado:
override fun onPause() {
super.onPause()
// Don't close the camera if the app is entering PiP mode
if (!isInPictureInPictureMode) {
closeCamera()
}
}
Como em outros casos de uso, oculte elementos não essenciais da interface (como controles e sobreposições) e adicione ações personalizadas para controlar a câmera (por exemplo, interromper a gravação ou inverter a câmera).
Calcular sourceRectHint para transições suaves
Fornecer um sourceRectHint preciso com as coordenadas exatas da tela do visor da câmera é essencial para uma animação de entrada suave. É possível receber os
limites da visualização de prévia usando getGlobalVisibleRect() da seguinte maneira:
val previewView = findViewById<View>(R.id.preview_view)
val globalRect = Rect()
// Ensure the view is laid out before calling getGlobalVisibleRect() to get valid screen coordinates.
previewView.getGlobalVisibleRect(globalRect)
val params = PictureInPictureParams.Builder()
.setSourceRectHint(globalRect)
.build()
setPictureInPictureParams(params)
Outras considerações
O modo picture-in-picture pode ser desativado em dispositivos com pouca RAM. Antes que seu app use o modo picture-in-picture,
confira se ele está disponível chamando
hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE).
O modo picture-in-picture é destinado a atividades que exibem vídeos em tela cheia. Ao mudar sua atividade para o modo picture-in-picture, evite exibir conteúdo que não seja de vídeo. Acompanhe quando sua atividade entra no modo PIP e oculte elementos da interface, conforme descrito em Como processar a interface durante o PIP.
Quando uma atividade está no modo picture-in-picture, por padrão, ela não recebe o foco de entrada. Para
receber eventos de entrada no modo picture-in-picture, use MediaSession.setCallback().
Para ver mais informações sobre o uso de
setCallback(), consulte
Mostrar
um card do Tocando agora.
Quando o app está no modo picture-in-picture, a reprodução de vídeo na janela picture-in-picture pode causar interferência de áudio com outro app, como um app de player de música ou de pesquisa por voz. Para evitar isso, solicite o foco de áudio ao iniciar a reprodução do vídeo e processe as notificações de mudança de foco de áudio, conforme descrito em Como gerenciar o foco de áudio. Se você receber uma notificação de perda de foco de áudio no modo PIP, pause ou interrompa a reprodução do vídeo.
Quando o app estiver prestes a entrar no PiP, observe que apenas a atividade de cima entra
no picture-in-picture. Em algumas situações, como em dispositivos com várias janelas, é
possível que a atividade de base seja mostrada e fique visível novamente
ao lado da atividade de PIP. Trate esse caso de acordo, incluindo
a atividade que recebe um callback onResume() ou onPause().
Também é possível que o usuário interaja com a atividade.
Por exemplo, se você tiver uma atividade de lista de vídeos exibida e a atividade de reprodução de vídeo
no modo PIP, o usuário poderá selecionar um novo vídeo na lista, e a
atividade PIP será atualizada de acordo.
Outra amostra de código
Para baixar um app de exemplo escrito em Kotlin, consulte Exemplo de PictureInPicture do Android (Kotlin).