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 PiP en tu app.
  2. Agrega configChanges y configúralo como orientation|screenLayout|screenSize|smallestScreenSize para especificar que tu actividad controla los cambios de configuración de diseño. De esta manera, tu actividad no se reinicia cuando se realizan cambios de 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 a lo largo de 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")
    }

Agrega el modo PIP en la app para salir en versiones anteriores a Android 12

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

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

Se agregó el modo PIP en la app de salir para versiones posteriores a Android 12.

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

  1. Crea un modifier y llama a onGloballyPositioned. 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 una versión posterior. Si es así, agrega setAutoEnterEnabled al compilador y configúralo como true para ingresar al modo PiP cuando se deslice el dedo. Esto proporciona una animación más fluida que pasar por 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 media3, verifica que no sea nulo y que el tamaño del video no sea igual a [VideoSize.UNKNOWN][6] 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 el reproductor cambia de tamaño durante la inicialización y se encuentra fuera de los límites válidos de la relación de aspecto, tu app fallará. Es posible que debas agregar verificaciones sobre cuándo se puede calcular la relación de aspecto, de manera similar a como se hace para un reproductor media3.