La biblioteca de Jetpack de pantalla en pantalla (PIP) ofrece una solución optimizada y sólida para que los desarrolladores de apps para Android implementen la funcionalidad de PIP, en especial para apps de reproducción de contenido multimedia, comunicación por video y navegación. Al proporcionar una API unificada, la biblioteca ayuda a eliminar el código estándar, los errores comunes en la app y a mejorar la calidad general de la experiencia del usuario de PiP.
La biblioteca de Jetpack PiP facilita las APIs de PiP existentes abordando varios desafíos clave y las incoherencias en todo el ecosistema de Android:
- Fragmentación del SO: La biblioteca controla automáticamente las diferencias en las llamadas a la API de PIP en varias versiones de Android, como el uso de
enterPictureInPictureModeantes de Android 12 yisAutoEnterEnableddespués, por lo que los desarrolladores no necesitan administrar las diferencias de versión. - Parámetros de PIP incorrectos: Proporciona una solución unificada para configurar correctamente los parámetros de PIP, por ejemplo,
setSourceRectHint, para crear animaciones fluidas y de alta calidad durante la reproducción de contenido multimedia. - Devoluciones de llamadas unificadas del estado de PIP: Consolida
onPictureInPictureModeChangedyonPictureInPictureUiStateChangeden una sola interfaz de devolución de llamada unificada (PictureInPictureDelegate.OnPictureInPictureEventListener) para simplificar la administración del estado y la IU. - Reducción del código estándar: La biblioteca reduce la cantidad de código estándar repetitivo, ya que ofrece conjuntos predefinidos de
RemoteActionspara casos de uso comunes, como controles de reproducción y acciones de videollamadas. - Preparación para el futuro: Se proporcionan más funciones de PIP a través de la biblioteca de Jetpack, lo que permite a los adoptantes acceder a funciones adicionales con un esfuerzo mínimo o nulo.
Flujo de trabajo de migración
Identifica la categoría de caso de uso y la lógica de PIP heredada de la app:
Categorías: Reproducción de video, Navegación o Videollamada
Lógica de PIP heredada para identificar:
onUserLeaveHintsetAutoEnterEnabledonPictureInPictureModeChangedonPictureInPictureUiStateChangedsetPictureInPictureParams.
2. Configuración de AndroidManifest
Asegúrate de que la actividad que ingresa al PIP declare la compatibilidad en AndroidManifest.xml con el configChanges necesario para evitar reinicios innecesarios:
<activity
android:name="VideoActivity" android:supportsPictureInPicture="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">
</activity>
3. Configuración del entorno
Agrega las dependencias necesarias a build.gradle:
dependencies {
implementation("androidx.core:core:1.18.0")
implementation("androidx.activity:activity:1.13.0")
implementation("androidx.core:core-pip:1.0.0-alpha02") }
Usa las bibliotecas de AndroidX más recientes para las dependencias y consulta la página de versiones para obtener esa información.
4. Selección e inicialización de plantillas
Elige la plantilla de implementación que mejor se adapte al caso de uso de la app:
- Navegación y videollamada:
BasicPictureInPicture; por lo general, no se admite el cambio de tamaño sin interrupciones, y no necesitas una sugerencia de rect de origen. - Reproducción de video:
VideoPlaybackPictureInPicture; realiza un seguimiento automático de los límites de visualización del reproductor para la sugerencia de rect del origen y habilita el cambio de tamaño sin problemas de forma predeterminada.
Para adoptar la biblioteca de Jetpack, reemplaza tu implementación personalizada existente de PIP con las APIs de la biblioteca de Jetpack. La complejidad y el costo de la adopción variarán según la implementación actual de la app.
En las siguientes secciones, se describen algunos de los casos de uso típicos de PiP y los pasos de implementación necesarios:
Navegación
La app informa a la biblioteca sobre el estado activo o inactivo de la navegación y establece la relación de aspecto. La biblioteca de Jetpack se encarga del resto.
Diferencias clave:
- No es necesario diferenciar el ingreso automático del ingreso heredado en el lado de la app.
- Interfaces de devolución de llamada consolidadas.
- Nuevo compilador de
PictureInPictureParamspara la retrocompatibilidad.
Videollamada
La app informa a la biblioteca sobre el estado activo o inactivo de la llamada y establece la relación de aspecto.
Diferencias clave:
- No es necesario diferenciar el ingreso automático del ingreso heredado en el lado de la app.
- Interfaces de devolución de llamada consolidadas.
- Nuevo compilador de
PictureInPictureParamspara la retrocompatibilidad. - Íconos de acción estandarizados para videollamadas.
5. Migración de código
- Lógica de entrada: Reemplaza la lógica específica de la API, como
setAutoEnterEnabledpara Android 12 y versiones posteriores, oonUserLeaveHintpara Android 11 y versiones anteriores, porsetEnabled. Activa este evento cada vez que cambie el estado de elegibilidad del PIP. - Devoluciones de llamada: Consolida
onPictureInPictureModeChanged(alternancia de diseño) yonPictureInPictureUiStateChanged(animación/estados) en una devolución de llamada unificada basada en eventosonPictureInPictureEvent. - Acciones y parámetros: Actualiza los parámetros con
setActionsysetAspectRatioen la instancia de la plantilla cada vez que cambien. - Manejo especial de videos: En el caso de las apps de video, usa
setPlayerViewpara automatizar las actualizaciones de sugerencias de rectángulos de origen y garantizar transiciones fluidas. ` ### 6. Limpieza
En VideoPlaybackPictureInPicture, llama a close en onDispose o onDestroy para liberar recursos, como los rastreadores de vistas.
Patrones de implementación de referencia
Ejemplos de implementaciones.
Navegación y videollamada
class NavOrVideoCallJpipActivity : ComponentActivity(), PictureInPictureDelegate.OnPictureInPictureEventListener { private lateinit var pictureInPictureImpl: BasicPictureInPicture override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) pictureInPictureImpl = BasicPictureInPicture(this) // BasicPictureInPicture is ideal for Navigation and Video call use cases. pictureInPictureImpl.addOnPictureInPictureEventListener( ContextCompat.getMainExecutor(this), this ) setContent { } } override fun onPictureInPictureEvent( event: PictureInPictureDelegate.Event, config: Configuration? ) { when (event) { PictureInPictureDelegate.Event.ENTERED -> { /* Toggle to PiP layout */ } PictureInPictureDelegate.Event.EXITED -> { /* Toggle to Full-screen layout */ } PictureInPictureDelegate.Event.STASHED -> { /* Optional: PiP is stashed */ } PictureInPictureDelegate.Event.UNSTASHED -> { /* Optional: PiP is unstashed */ } } } }
Reproducción de los videos
class VideoPlaybackJpipActivity : ComponentActivity(), PictureInPictureDelegate.OnPictureInPictureEventListener { private lateinit var pictureInPictureImpl: VideoPlaybackPictureInPicture override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) pictureInPictureImpl = VideoPlaybackPictureInPicture(this) pictureInPictureImpl.addOnPictureInPictureEventListener( ContextCompat.getMainExecutor(this), this ) setContent { ContentScreen(pictureInPictureImpl) } } override fun onPictureInPictureEvent( event: PictureInPictureDelegate.Event, config: Configuration? ) { when (event) { PictureInPictureDelegate.Event.ENTER_ANIMATION_START -> { /* Hide overlays */ } PictureInPictureDelegate.Event.ENTER_ANIMATION_END -> { /* Animation finished */ } PictureInPictureDelegate.Event.ENTERED -> { /* Switch to PiP layout */ } PictureInPictureDelegate.Event.STASHED -> { /* PiP stashed */ } PictureInPictureDelegate.Event.UNSTASHED -> { /* PiP unstashed */ } PictureInPictureDelegate.Event.EXITED -> { /* Return to full-screen */ } } } @Composable fun ContentScreen(pipController: VideoPlaybackPictureInPicture) { DisposableEffect(pipController) { onDispose { pipController.close() } } } }