A partir do Android 8.0 (nível 26 da API), o Android permite que as atividades sejam iniciadas no modo picture-in-picture (PiP). O modo picture-in-picture é 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 do picture-in-picture é exibida na camada superior da tela, em um canto definido pelo sistema.
O modo picture-in-picture também é compatível com dispositivos Android TV OS que executam o Android 14 (nível 34 da API) ou versões mais recentes. Embora haja muitas semelhanças, há outras considerações ao usar o picture-in-picture 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.
Tocar duas vezes na janela para alternar entre o tamanho atual e o tamanho máximo ou mínimo do picture-in-picture. Por exemplo, tocar duas vezes em uma janela maximizada a minimiza, e o contrário também é verdadeiro.
Ocultar a janela arrastando-a para a borda esquerda ou direita. Para exibi-la, basta tocar na parte visível da janela oculta ou arrastá-la 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 para ir à tela inicial. É assim que o Google Maps continua exibindo rotas enquanto o usuário executa outra atividade ao mesmo tempo.
Seu app pode colocar um vídeo no modo picture-in-picture quando o usuário sai do vídeo para procurar 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 continuará sendo reproduzido no modo picture-in-picture enquanto a tela principal exibe uma atividade de seleção de conteúdo.
Declarar suporte ao picture-in-picture
Por padrão, o sistema não é automaticamente compatível com o picture-in-picture para apps. Se você quiser oferecer suporte ao picture-in-picture no seu app, registre a atividade de vídeo no manifesto configurando android:supportsPictureInPicture como true. Especifique também que sua atividade gerencia as mudanças de configuração de layout para que ela não seja reiniciada quando essas mudanças ocorrerem durante as transições do modo picture-in-picture.
<activity android:name="VideoActivity"
android:supportsPictureInPicture="true"
android:configChanges=
"screenSize|smallestScreenSize|screenLayout|orientation"
...
Implementar o PiP com o Jetpack
Use a biblioteca picture-in-picture do Jetpack para implementar a experiência picture-in-picture, já que ela simplifica a integração e reduz problemas comuns no app. Consulte nosso app de exemplo da plataforma para ver um exemplo de uso. No entanto, se você preferir implementar o PiP usando as APIs da plataforma, consulte a documentação a seguir.
Alternar sua atividade para o picture-in-picture
A partir do Android 12, você pode alternar sua atividade para o modo picture-in-picture definindo
a setAutoEnterEnabled flag como true. Com essa configuração, uma atividade
é alternada automaticamente para o modo picture-in-picture conforme necessário, sem precisar chamar explicitamente
enterPictureInPictureMode() em onUserLeaveHint. Isso tem o benefício adicional de proporcionar transições muito mais suaves. Para mais detalhes, consulte Suavizar
transições no modo picture-in-picture pela 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 picture-in-picture. Por exemplo, o código a seguir alterna uma atividade para o modo picture-in-picture quando o usuário clica em um botão dedicado a isso na IU do app:
Kotlin
override fun onActionClicked(action: Action) { if (action.id.toInt() == R.id.lb_control_picture_in_picture) { activity?.enterPictureInPictureMode() return } }
Java
@Override public void onActionClicked(Action action) { if (action.getId() == R.id.lb_control_picture_in_picture) { getActivity().enterPictureInPictureMode(); return; } ... }
Recomendamos que você inclua uma lógica que alterna uma atividade para o modo picture-in-picture em vez de colocá-la em 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. Para usar esse
caso, modifique
onUserLeaveHint():
Kotlin
override fun onUserLeaveHint() { if (iWantToBeInPipModeNow()) { enterPictureInPictureMode() } }
Java
@Override public void onUserLeaveHint () { if (iWantToBeInPipModeNow()) { enterPictureInPictureMode(); } }
Recomendado: oferecer aos usuários uma experiência de transição de picture-in-picture refinada
O Android 12 adicionou melhorias estéticas significativas às transições animadas entre janelas de tela cheia e picture-in-picture. Recomendamos implementar todas as mudanças aplicáveis. Depois de fazer isso, essas mudanças serão dimensionadas automaticamente para telas grandes, como dobráveis e tablets, sem necessidade de mais trabalho.
Se o app não incluir atualizações aplicáveis, as transições de picture-in-picture ainda vão funcionar, mas as animações serão menos refinadas. Por exemplo, a transição do modo de tela cheia para o modo picture-in-picture pode fazer com que a janela picture-in-picture desapareça durante a transição antes de reaparecer quando a transição for concluída.
Essas mudanças envolvem o seguinte:
- Suavizar transições no modo picture-in-picture pela navegação por gestos
- Definir um
sourceRectHintadequado para entrar e sair do modo picture-in-picture - Desativar o redimensionamento contínuo de conteúdos que não são de vídeo
Consulte a amostra 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
A partir do Android 12, a flag setAutoEnterEnabled oferece uma animação muito
mais suave para a transição para conteúdo de vídeo no modo picture-in-picture usando a navegação por gestos
, por exemplo, ao deslizar para cima para ir à tela inicial no modo de tela cheia.
Conclua as etapas a seguir para fazer essa mudança:
Use
setAutoEnterEnabledpara construirPictureInPictureParams.Builder:Kotlin
setPictureInPictureParams(PictureInPictureParams.Builder() .setAspectRatio(aspectRatio) .setSourceRectHint(sourceRectHint) .setAutoEnterEnabled(true) .build())
Java
setPictureInPictureParams(new PictureInPictureParams.Builder() .setAspectRatio(aspectRatio) .setSourceRectHint(sourceRectHint) .setAutoEnterEnabled(true) .build());
Chame
setPictureInPictureParamsusando os atualizadosPictureInPictureParamsanteriormente. O app não aguarda o callbackonUserLeaveHint, como faria no Android 11.Por exemplo, talvez você queira chamar
setPictureInPictureParamsna primeira reprodução e em qualquer reprodução seguinte se a proporção mudar.Chame
setAutoEnterEnabled(false), mas apenas quando necessário. Por exemplo, provavelmente não será ideal entrar no modo picture-in-picture se a reprodução atual estiver no estado pausado.
Definir um sourceRectHint adequado para entrar e sair do modo picture-in-picture
A partir da introdução do picture-in-picture no Android 8.0, setSourceRectHint
indicava a área da atividade que fica visível após a transição para o modo
picture-in-picture, por exemplo, os limites da 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 picture-in-picture:
Construa
PictureInPictureParamsusando os limites adequados comosourceRectHint. Também recomendamos anexar um listener de mudança de layout ao player de vídeo:Kotlin
val mOnLayoutChangeListener = OnLayoutChangeListener { v: View?, oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int, newLeft: Int, newTop: Int, newRight: Int, newBottom: Int -> val sourceRectHint = Rect() mYourVideoView.getGlobalVisibleRect(sourceRectHint) val builder = PictureInPictureParams.Builder() .setSourceRectHint(sourceRectHint) setPictureInPictureParams(builder.build()) } mYourVideoView.addOnLayoutChangeListener(mOnLayoutChangeListener)
Java
private final View.OnLayoutChangeListener mOnLayoutChangeListener = (v, oldLeft, oldTop, oldRight, oldBottom, newLeft, newTop, newRight, newBottom) -> { final Rect sourceRectHint = new Rect(); mYourVideoView.getGlobalVisibleRect(sourceRectHint); final PictureInPictureParams.Builder builder = new PictureInPictureParams.Builder() .setSourceRectHint(sourceRectHint); setPictureInPictureParams(builder.build()); }; mYourVideoView.addOnLayoutChangeListener(mOnLayoutChangeListener);
Se necessário, atualize a
sourceRectHintantes do sistema iniciar 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 é destinada à 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.Kotlin
// 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() ) } }
Java
// Listener is called right after the user exits PiP but before animating. playerView.addOnLayoutChangeListener((v, 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. final Rect sourceRectHint = new Rect(); playerView.getGlobalVisibleRect(sourceRectHint); setPictureInPictureParams( new 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 setSeamlessResizeEnabled flag, que fornece uma animação de esmaecimento muito
mais suave ao redimensionar conteúdo que não é de vídeo na janela do modo picture-in-picture. Anteriormente, o redimensionamento desse tipo de conteúdo em uma janela picture-in-picture poderia criar artefatos visuais conflitantes.
Para ativar o redimensionamento contínuo de conteúdo de vídeo:
Kotlin
setPictureInPictureParams(PictureInPictureParams.Builder() .setSeamlessResizeEnabled(true) .build())
Java
setPictureInPictureParams(new PictureInPictureParams.Builder() .setSeamlessResizeEnabled(true) .build());
Gerenciar a interface durante o picture-in-picture
Quando a atividade entra ou sai do modo picture-in-picture, o sistema chama Activity.onPictureInPictureModeChanged()
ou Fragment.onPictureInPictureModeChanged().
O Android 15 introduz mudanças que garantem uma transição ainda mais suave ao entrar no modo picture-in-picture. Isso é benéfico para apps que têm elementos de interface sobrepostos à interface principal, que entra no modo picture-in-picture.
Os desenvolvedores usam o onPictureInPictureModeChanged() callback para definir a lógica que alterna a visibilidade dos elementos de interface sobrepostos.
Esse callback é acionado quando a animação de entrada ou saída do picture-in-picture é concluída.
A partir do Android 15, a PictureInPictureUiState classe inclui um novo estado.
Com esse novo estado de interface, os apps destinados ao Android 15 observam o Activity#onPictureInPictureUiStateChanged()
callback sendo invocado com isTransitioningToPip() assim que a animação do picture-in-picture começa.
Há muitos elementos de interface que não são relevantes para o app quando ele está no modo picture-in-picture, por exemplo, visualizações ou layouts que incluem informações como sugestões, vídeos futuros, classificações e títulos. Quando o app entra no modo picture-in-picture, use o callback onPictureInPictureUiStateChanged() para ocultar esses elementos de interface. Quando o app passa para o modo de tela cheia na janela picture-in-picture, use o callback onPictureInPictureModeChanged() para mostrar esses elementos, conforme mostrado nos exemplos a seguir:
Kotlin
override fun onPictureInPictureUiStateChanged(pipState: PictureInPictureUiState) { if (pipState.isTransitioningToPip()) { // Hide UI elements. } }
Java
@Override public void onPictureInPictureUiStateChanged(PictureInPictureUiState pipState) { if (pipState.isTransitioningToPip()) { // Hide UI elements. } }
Kotlin
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean) { if (isInPictureInPictureMode) { // Unhide UI elements. } }
Java
@Override public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) { if (isInPictureInPictureMode) { // Unhide UI elements. } }
Essa alternância rápida de visibilidade de elementos de interface irrelevantes (para uma janela picture-in-picture) ajuda a garantir uma animação de entrada de picture-in-picture mais suave e sem oscilações.
Modifique esses callbacks para redesenhar os elementos de IU da atividade. Lembre-se de que sua atividade é exibida em uma pequena janela no modo picture-in-picture. 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 elementos pequenos da interface podem ser difíceis de ver. As atividades de reprodução de vídeo com interface mínima proporcionam a melhor experiência do usuário.
Caso seu app precise fornecer ações personalizadas para picture-in-picture, consulte Adicionar controles nesta página. Remova outros elementos da interface antes de sua atividade entrar no modo picture-in-picture e restaure-os quando ela voltar à 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 tocar, pausar, próxima e anterior serão exibidos.
Também é possível especificar ações personalizadas de forma explícita criando
PictureInPictureParams
com
PictureInPictureParams.Builder.setActions()
antes de entrar no modo picture-in-picture, além de transmitir os parâmetros ao entrar nesse modo usando
enterPictureInPictureMode(android.app.PictureInPictureParams)
ou
setPictureInPictureParams(android.app.PictureInPictureParams).
Tenha cuidado. Se você tentar adicionar mais do que
getMaxNumPictureInPictureActions(),
conseguirá apenas o número máximo.
Continuar a reprodução de vídeo no picture-in-picture
Quando sua atividade alterna para o modo picture-in-picture, o sistema coloca a atividade no estado pausado
e chama o método
onPause() da atividade. A reprodução do vídeo não será pausada e continuará se a atividade for pausada durante a transição para o modo picture-in-picture.
No Android 7.0 e versões mais recentes, pause e retome a reprodução do vídeo quando o
sistema chamar o
onStop() e
onStart() da atividade. Ao fazer isso, você evita a necessidade de verificar se o app está no modo picture-in-picture em onPause() e continua a reprodução de forma explícita.
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 picture-in-picture chamando
isInPictureInPictureMode() e gerencie a reprodução de maneira adequada. Exemplo:
Kotlin
override fun onPause() { super.onPause() // If called while in PiP mode, do not pause playback. if (isInPictureInPictureMode) { // Continue playback. } else { // Use existing playback logic for paused activity behavior. } }
Java
@Override public void onPause() { // If called while in PiP mode, do not pause playback. if (isInPictureInPictureMode()) { // Continue playback. ... } else { // Use existing playback logic for paused activity behavior. ... } }
Quando a atividade sai do modo picture-in-picture e volta ao modo de tela cheia, o sistema
retoma sua atividade e chama o método
onResume().
Usar uma única atividade de reprodução para o picture-in-picture
No seu app, um usuário pode selecionar um novo vídeo ao navegar pelo conteúdo na tela principal, enquanto uma atividade de reprodução de vídeo estiver no modo picture-in-picture. Reproduza o novo vídeo na atividade de reprodução existente no modo de tela cheia em vez de iniciar uma nova atividade que poderia confundir o usuário.
Para garantir que uma única atividade seja usada para solicitações de reprodução de vídeo e ativada ou desativada no modo picture-in-picture, defina o android:launchMode da atividade como singleTask no seu manifesto:
<activity android:name="VideoActivity"
...
android:supportsPictureInPicture="true"
android:launchMode="singleTask"
...
Na sua atividade, substitua
onNewIntent()
e gerencie o novo vídeo, interrompendo qualquer reprodução de vídeo já existente, se necessário.
Oferecer suporte ao picture-in-picture para apps de câmera
Para ativar o picture-in-picture para apps de câmera, verifique se a câmera permanece ativa no modo picture-in-picture, não fechando a câmera quando onPause() é chamado:
Java
@Override
public void 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 de interface não essenciais (como controles e sobreposições) e adicione ações personalizadas para controlar a câmera (por exemplo, parar 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. Você pode acessar os limites da visualização de prévia usando getGlobalVisibleRect() da seguinte maneira:
Java
View previewView = findViewById(R.id.preview_view);
Rect globalRect = new Rect();
// Ensure the view is laid out before calling getGlobalVisibleRect() to get valid screen coordinates.
previewView.getGlobalVisibleRect(globalRect);
PictureInPictureParams params = new PictureInPictureParams.Builder()
.setSourceRectHint(globalRect)
.build();
setPictureInPictureParams(params);
Práticas recomendadas
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. Rastreie quando sua atividade entra no modo picture-in-picture e oculte elementos da interface, conforme descrito em Gerenciar a interface durante o picture-in-picture.
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 mais informações sobre o uso de setCallback(), consulte Mostrar um card do Tocando agora.
Quando seu app estiver no modo picture-in-picture, a reprodução de vídeo nessa janela poderá causar interferência de áudio com outro app, como um de player de música ou de pesquisa por voz. Para evitar isso, solicite a seleção de áudio quando iniciar o vídeo e gerencie as notificações para mudança de seleção de áudio, conforme descrito em Como gerenciar a seleção de áudio. Se você receber uma notificação sobre perda de seleção de áudio quando estiver no modo picture-in-picture, pause ou pare a reprodução do vídeo.
Quando seu app estiver prestes a entrar no modo picture-in-picture, apenas a atividade principal entrará nesse modo. Em algumas situações, como em dispositivos com várias janelas, é possível que a atividade abaixo seja exibida e se torne visível novamente junto com a atividade do modo picture-in-picture. Gerencie esse caso adequadamente, incluindo a
atividade abaixo, que recebe um callback onResume() ou onPause(). Também é
possível que o usuário interaja com a atividade. Por exemplo, se uma atividade
de lista de vídeos estiver sendo exibida e a atividade de reprodução de vídeo estiver no modo picture-in-picture, o
usuário poderá selecionar um novo vídeo da lista, e essa atividade será atualizada
de acordo.
Exemplo de código adicional
Para fazer o download de um app de exemplo criado em Kotlin, consulte a Amostra PictureInPicture do Android (Kotlin) (link em inglês).