Cómo configurar tu app para PIP

En la etiqueta de actividad de tu archivo AndroidManifest.xml, haz lo siguiente:

  1. Agrega supportsPictureInPicture y configúralo como true para declarar que usarás la pantalla en pantalla (PiP) en tu app.
  2. Agrega configChanges y establécelo en orientation|screenLayout|screenSize|smallestScreenSize para especificar que tu actividad controla los cambios de configuración del diseño. De esta manera, tu actividad no se vuelve a iniciar cuando se producen cambios en el diseño durante las transiciones del modo de PIP.
<activity
    android:name=".SnippetsActivity"
    android:exported="true"
    android:supportsPictureInPicture="true"
    android:configChanges="orientation|screenLayout|screenSize|smallestScreenSize"
    android:theme="@style/Theme.Snippets">

En tu código de Compose, haz lo siguiente:

  1. Agrega esta extensión en Context. Usarás esta extensión varias veces en la guía para acceder a la actividad.
    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")
    }

Cómo agregar el modo PIP cuando se sale de la app en versiones anteriores a Android 12

Para agregar PiP en versiones anteriores a Android 12, usa addOnUserLeaveHintProvider. Sigue estos pasos para agregar PiP en versiones anteriores a Android 12:

  1. Agrega una puerta de versión para que solo se acceda a este código en las versiones O a R.
  2. Usa un DisposableEffect con Context como clave.
  3. Dentro de DisposableEffect, define el comportamiento para cuando se active onUserLeaveHintProvider con una expresión lambda. En la expresión lambda, llama a enterPictureInPictureMode() en findActivity() y pasa PictureInPictureParams.Builder().build().
  4. Agrega addOnUserLeaveHintListener con findActivity() y pasa la expresión lambda.
  5. En onDispose, agrega removeOnUserLeaveHintListener con findActivity() y pasa la 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")
}

Cómo agregar el modo PIP cuando se sale de la app en versiones posteriores a Android 12

Después de Android 12, se agrega PictureInPictureParams.Builder a través de un modificador que se pasa al reproductor de video de la app.

  1. Crea un modifier y llama a onGloballyPositioned en él. Las coordenadas del diseño se usarán en un paso posterior.
  2. Crea una variable para el PictureInPictureParams.Builder().
  3. Agrega una sentencia if para verificar si el SDK es S o posterior. Si es así, agrega setAutoEnterEnabled al compilador y configúralo en true para ingresar al modo de PiP cuando se deslice el dedo. Esto proporciona una animación más fluida que la que se obtiene con enterPictureInPictureMode.
  4. Usa findActivity() para llamar a setPictureInPictureParams(). Llama a build() en el builder y pásalo.

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)

Usa setAspectRatio para establecer la relación de aspecto de la ventana de PIP.

Para establecer la relación de aspecto de la ventana de PIP, puedes elegir una relación de aspecto específica o usar el ancho y la altura del tamaño del video del reproductor. Si usas un reproductor de Media3, verifica que el reproductor no sea nulo y que el tamaño del video del reproductor no sea igual a VideoSize.UNKNOWN antes de establecer la relación de aspecto.

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)

Si usas un reproductor personalizado, establece la relación de aspecto en la altura y el ancho del reproductor con la sintaxis específica de tu reproductor. Ten en cuenta que, si se cambia el tamaño del reproductor durante la inicialización y queda fuera de los límites válidos de la relación de aspecto, la app fallará. Es posible que debas agregar verificaciones en torno a cuándo se puede calcular la relación de aspecto, de manera similar a como se hace para un reproductor de Media3.