Configurar seu app para picture-in-picture

Na tag de atividade do arquivo AndroidManifest.xml, faça o seguinte:

  1. Adicione supportsPictureInPicture e defina como true para declarar que você vai usar o picture-in-picture (PiP) no app.
  2. Adicione configChanges e defina como orientation|screenLayout|screenSize|smallestScreenSize para especificar que sua atividade gerencia mudanças de configuração de layout. Assim, sua atividade não será reiniciada quando mudanças de layout ocorrerem durante as transições do modo picture-in-picture.
<activity
    android:name=".SnippetsActivity"
    android:exported="true"
    android:supportsPictureInPicture="true"
    android:configChanges="orientation|screenLayout|screenSize|smallestScreenSize"
    android:theme="@style/Theme.Snippets">

No seu código do Compose, faça o seguinte:

  1. Adicione essa extensão em Context. Você vai usar essa extensão várias vezes ao longo do guia para acessar a atividade.
    internal fun Context.findActivity(): ComponentActivity {
        var context = this
        while (context is ContextWrapper) {
            if (context is ComponentActivity) return context
            context = context.baseContext
        }
        throw IllegalStateException("Picture in picture should be called in the context of an Activity")
    }

Adicionar PiP ao sair do app para versões anteriores ao Android 12

Para adicionar o PiP em versões anteriores ao Android 12, use addOnUserLeaveHintProvider. Siga estas etapas para adicionar o PiP em versões anteriores ao Android 12:

  1. Adicione um controle de versão para que esse código só seja acessado nas versões O até R.
  2. Use um DisposableEffect com Context como chave.
  3. Dentro do DisposableEffect, defina o comportamento para quando o onUserLeaveHintProvider for acionado usando uma lambda. Na lambda, chame enterPictureInPictureMode() em findActivity() e transmita PictureInPictureParams.Builder().build().
  4. Adicione addOnUserLeaveHintListener usando findActivity() e transmita a lambda.
  5. Em onDispose, adicione removeOnUserLeaveHintListener usando findActivity() e transmita a lambda.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
    Build.VERSION.SDK_INT < Build.VERSION_CODES.S
) {
    val context = LocalContext.current
    DisposableEffect(context) {
        val onUserLeaveBehavior = Runnable {
            context.findActivity()
                .enterPictureInPictureMode(PictureInPictureParams.Builder().build())
        }
        context.findActivity().addOnUserLeaveHintListener(
            onUserLeaveBehavior
        )
        onDispose {
            context.findActivity().removeOnUserLeaveHintListener(
                onUserLeaveBehavior
            )
        }
    }
} else {
    Log.i("PiP info", "API does not support PiP")
}

Adicionar picture-in-picture ao sair do app para versões posteriores ao Android 12

Após o Android 12, o PictureInPictureParams.Builder é adicionado por um modificador transmitido ao player de vídeo do app.

  1. Crie um modifier e chame onGloballyPositioned nele. As coordenadas de layout serão usadas em uma etapa posterior.
  2. Crie uma variável para o PictureInPictureParams.Builder().
  3. Adicione uma instrução if para verificar se o SDK é S ou mais recente. Nesse caso, adicione setAutoEnterEnabled ao builder e defina como true para entrar no modo PiP ao deslizar. Isso proporciona uma animação mais suave do que passar por enterPictureInPictureMode.
  4. Use findActivity() para chamar setPictureInPictureParams(). Chame build() no builder e transmita-o.

val pipModifier = modifier.onGloballyPositioned { layoutCoordinates ->
    val builder = PictureInPictureParams.Builder()

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        builder.setAutoEnterEnabled(true)
    }
    context.findActivity().setPictureInPictureParams(builder.build())
}
VideoPlayer(pipModifier)

Usar setAspectRatio para definir a proporção da janela PiP

Para definir a proporção da janela PiP, escolha uma proporção específica ou use a largura e a altura do tamanho do vídeo do player. Se você estiver usando um player media3, verifique se ele não é nulo e se o tamanho do vídeo não é igual a VideoSize.UNKNOWN antes de definir a proporção de tela.

val context = LocalContext.current

val pipModifier = modifier.onGloballyPositioned { layoutCoordinates ->
    val builder = PictureInPictureParams.Builder()
    if (shouldEnterPipMode && player != null && player.videoSize != VideoSize.UNKNOWN) {
        val sourceRect = layoutCoordinates.boundsInWindow().toAndroidRectF().toRect()
        builder.setSourceRectHint(sourceRect)
        builder.setAspectRatio(
            Rational(player.videoSize.width, player.videoSize.height)
        )
    }

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        builder.setAutoEnterEnabled(shouldEnterPipMode)
    }
    context.findActivity().setPictureInPictureParams(builder.build())
}

VideoPlayer(pipModifier)

Se você estiver usando um player personalizado, defina a proporção na altura e na largura do player usando a sintaxe específica dele. Se o player for redimensionado durante a inicialização ou ficar fora dos limites válidos da proporção, o app vai falhar. Talvez seja necessário adicionar verificações sobre quando a proporção pode ser calculada, semelhante a como é feito para um player media3.