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 processa mudanças na configuração do layout. Dessa forma, a atividade não é reiniciada quando ocorrem mudanças de layout 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 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 seja acessado apenas nas versões O até R.
  2. Use um DisposableEffect com Context como chave.
  3. Dentro de 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 o PiP ao sair do app para versões posteriores ao Android 12

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

  1. Crie um modifier e chame onGloballyPositioned nele. As coordenadas do 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. Se sim, adicione setAutoEnterEnabled ao builder e defina como true para entrar no modo PIP ao deslizar. Isso fornece uma animação mais suave do que passar por enterPictureInPictureMode.
  4. Use findActivity() para ligar para 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.

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 e 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, de forma semelhante ao que é feito para um player media3.