La bibliothèque Jetpack Picture-in-Picture (PIP) offre une solution simplifiée et robuste aux développeurs d'applications Android pour implémenter la fonctionnalité PIP, en particulier pour les applications de lecture multimédia, de communication vidéo et de navigation. En fournissant une API unifiée, la bibliothèque permet d'éliminer le code récurrent, les bugs courants dans l'application et d'améliorer la qualité globale de l'expérience utilisateur PIP.
La bibliothèque Jetpack PIP facilite les API PIP existantes en résolvant plusieurs problèmes et incohérences clés dans l'écosystème Android :
- Fragmentation du système d'exploitation : la bibliothèque gère automatiquement les différences dans les appels d'API PIP
entre les différentes versions d'Android, par exemple en utilisant
enterPictureInPictureModeavant Android 12 etisAutoEnterEnabledaprès. Les développeurs n'ont donc pas besoin de gérer les différences de version . - Paramètres PIP incorrects : elle fournit une solution unifiée pour définir correctement
les paramètres PIP, par exemple
setSourceRectHint, afin de créer des animations fluides et de haute qualité lors de la lecture multimédia. - Rappels d'état PIP unifiés : elle consolide
onPictureInPictureModeChangedetonPictureInPictureUiStateChangeddans une seule interface de rappel unifiée (PictureInPictureDelegate.OnPictureInPictureEventListener) pour simplifier la gestion de l'état et de l'interface utilisateur. - Réduction du code récurrent : la bibliothèque réduit la quantité de code récurrent répétitif en proposant des ensembles prédéfinis de
RemoteActionspour les cas d'utilisation courants, tels que les commandes de lecture et les actions d'appel vidéo. - Pérennisation : d'autres fonctionnalités PIP sont fournies via la bibliothèque Jetpack , ce qui permet aux utilisateurs d'accéder à des fonctionnalités supplémentaires avec un effort minimal, voire nul.
Workflow de migration
Identifiez la catégorie de cas d'utilisation de l'application et la logique PIP héritée :
Catégories : lecture vidéo, navigation ou appel vidéo.
Logique PIP héritée à identifier :
onUserLeaveHintsetAutoEnterEnabledonPictureInPictureModeChangedonPictureInPictureUiStateChangedsetPictureInPictureParams.
2. Configuration d'AndroidManifest
Assurez-vous que l'activité qui passe en mode PIP déclare la compatibilité dans AndroidManifest.xml avec les configChanges nécessaires pour éviter les redémarrages inutiles :
<activity
android:name="VideoActivity" android:supportsPictureInPicture="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">
</activity>
3. Configuration de l'environnement
Ajoutez les dépendances requises à 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") }
Utilisez les dernières bibliothèques AndroidX pour les dépendances et consultez la page des versions pour obtenir ces informations.
4. Sélection et initialisation du modèle
Choisissez le modèle d'implémentation qui correspond le mieux au cas d'utilisation de l'application :
- Navigation et appel vidéo :
BasicPictureInPicture; le redimensionnement fluide n'est généralement pas pris en charge et vous n'avez pas besoin d'un indicateur de rectangle source. - Lecture vidéo :
VideoPlaybackPictureInPicture; suit automatiquement les limites de la vue du lecteur pour l'indicateur de rectangle source et active le redimensionnement fluide par défaut.
Pour adopter la bibliothèque Jetpack, remplacez votre implémentation PIP personnalisée existante par les API de la bibliothèque Jetpack. La complexité et le coût de l'adoption varient en fonction de l'implémentation actuelle de l'application.
Les sections suivantes décrivent certains des cas d'utilisation types de PIP et les étapes d'implémentation nécessaires :
Navigation
L'application informe la bibliothèque de l'état actif ou inactif de la navigation et définit le format. La bibliothèque Jetpack s'occupe du reste.
Différences majeures :
- Inutile de différencier l'entrée automatique et l'entrée héritée côté application.
- Interfaces de rappel consolidées.
- Nouveau compilateur
PictureInPictureParamspour la rétrocompatibilité.
Appel vidéo
L'application informe la bibliothèque de l'état actif ou inactif de l'appel et définit le format.
Différences majeures :
- Inutile de différencier l'entrée automatique et l'entrée héritée côté application.
- Interfaces de rappel consolidées.
- Nouveau compilateur
PictureInPictureParamspour la rétrocompatibilité. - Icônes d'action standardisées pour les appels vidéo.
5. Migration de code
- Logique d'entrée : remplacez la logique spécifique à l'API, telle que
setAutoEnterEnabledpour Android 12 et versions ultérieures, ouonUserLeaveHintpour Android 11 et versions antérieures parsetEnabled. Déclenchez cette action chaque fois que l'état d'éligibilité PIP change. - Rappels : consolidez
onPictureInPictureModeChanged(bascule de mise en page) etonPictureInPictureUiStateChanged(animation/états) dans un rappel unifié basé sur les événementsonPictureInPictureEvent. - Actions et paramètres : mettez à jour les paramètres à l'aide de
setActionsetsetAspectRatiosur l'instance de modèle chaque fois qu'ils changent. - Gestion spéciale des vidéos : pour les applications vidéo, utilisez
setPlayerViewpour automatiser les mises à jour de l'indicateur de rectangle source et garantir des transitions fluides. ` ### 6. Nettoyage
Pour VideoPlaybackPictureInPicture, appelez close dans
onDispose ou onDestroy pour libérer des ressources telles que les trackers de vue.
Modèles d'implémentation de référence
Exemples d'implémentations.
Navigation et appel vidéo
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 */ } } } }
Lecture de vidéos
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() } } } }