Utiliser le mode Picture-in-picture (PIP)

Essayer la méthode Compose
Jetpack Compose est le kit d'UI recommandé pour Android. Découvrez comment prendre en charge le mode Picture-in-picture dans Compose.

À partir d'Android 8.0 (niveau d'API 26), Android permet de lancer des activités en mode Picture-in-picture (PIP). Le mode Picture-in-picture est un type spécial de mode multifenêtre utilisé pour la lecture de vidéos, les appels vidéo et la navigation. Il permet à l'utilisateur d'épingler la fenêtre d'activité existante dans un coin de l'écran tout en naviguant entre les applications ou en parcourant le contenu de l'écran principal.

Le mode PIP exploite les API multifenêtres disponibles dans Android 7.0 pour fournir la fenêtre de superposition vidéo épinglée. Pour ajouter le mode PIP à votre application, vous devez enregistrer les activités compatibles avec le mode PIP, passer votre activité en mode PIP si nécessaire, et vous assurer que les éléments d'interface utilisateur sont masqués et que la lecture de la vidéo se poursuit lorsque l'activité est en mode PIP.

La fenêtre PiP s'affiche dans la couche supérieure de l'écran, dans un angle choisi par le système.

Le mode Picture-in-picture est également compatible avec les appareils Android TV OS compatibles équipés d'Android 14 (niveau d'API 34) ou version ultérieure. Bien qu'il existe de nombreuses similitudes, il existe des considérations supplémentaires lors de l'utilisation du PIP sur un téléviseur.

Comment les utilisateurs peuvent-ils interagir avec la fenêtre PiP ?

Les utilisateurs peuvent faire glisser la fenêtre PiP vers un autre emplacement. À partir d'Android 12, les utilisateurs peuvent également :

  • Appuyez une fois sur la fenêtre pour afficher un bouton d'activation/de désactivation du plein écran, un bouton de fermeture, un bouton de paramètres et des actions personnalisées fournies par votre application (par exemple, les commandes de lecture).

  • Appuyez deux fois sur la fenêtre pour basculer entre la taille actuelle du PIP et sa taille maximale ou minimale. Par exemple, appuyer deux fois sur une fenêtre agrandie la réduit, et inversement.

  • Masquez la fenêtre en la faisant glisser vers le bord gauche ou droit. Pour la restaurer, appuyez sur la partie visible de la fenêtre ou faites-la glisser vers l'extérieur.

  • Redimensionnez la fenêtre PIP en pinçant l'écran pour zoomer.

Votre application contrôle le moment où l'activité en cours passe en mode PIP. Voici quelques exemples :

  • Une activité peut passer en mode PIP lorsque l'utilisateur appuie sur le bouton d'accueil ou balaye l'écran vers le haut pour accéder à l'écran d'accueil. C'est ainsi que Google Maps continue d'afficher un itinéraire pendant que l'utilisateur exécute une autre activité en même temps.

  • Votre application peut passer une vidéo en mode PIP lorsque l'utilisateur revient en arrière pour parcourir d'autres contenus.

  • Votre application peut passer une vidéo en mode PiP pendant qu'un utilisateur regarde la fin d'un épisode. L'écran principal affiche des informations promotionnelles ou un résumé de l'épisode suivant de la série.

  • Votre application peut permettre aux utilisateurs de mettre en file d'attente des contenus supplémentaires pendant qu'ils regardent une vidéo. La lecture de la vidéo se poursuit en mode PIP, tandis que l'écran principal affiche une activité de sélection de contenu.

Déclarer la prise en charge du PIP

Par défaut, le système n'est pas automatiquement compatible avec le mode Picture-in-picture pour les applications. Si vous souhaitez prendre en charge le mode PIP dans votre application, enregistrez votre activité vidéo dans votre fichier manifeste en définissant android:supportsPictureInPicture sur true. Spécifiez également que votre activité gère les modifications de configuration de la mise en page afin qu'elle ne soit pas relancée lorsque des modifications de mise en page se produisent pendant les transitions en mode PIP.

<activity android:name="VideoActivity"
    android:supportsPictureInPicture="true"
    android:configChanges=
        "screenSize|smallestScreenSize|screenLayout|orientation"
    ...

Implémenter le mode PIP avec Jetpack

Utilisez la bibliothèque Picture-in-picture Jetpack pour implémenter l'expérience Picture-in-picture, car elle simplifie l'intégration et réduit les problèmes courants dans l'application. Consultez notre application exemple de plate-forme pour voir un exemple de son utilisation. Toutefois, si vous préférez implémenter le mode PIP à l'aide des API de plate-forme, consultez la documentation suivante.

Passer votre activité en mode Picture-in-picture

À partir d'Android 12, vous pouvez passer votre activité en mode PIP en définissant l'indicateur setAutoEnterEnabled sur true. Avec ce paramètre, une activité passe automatiquement en mode PIP si nécessaire, sans avoir à appeler explicitement enterPictureInPictureMode() dans onUserLeaveHint. Cela présente l'avantage supplémentaire de fournir des transitions beaucoup plus fluides. Pour en savoir plus, consultez Fluidifier les transitions vers le mode Picture-in-picture à partir de la navigation par gestes.

Si vous ciblez Android 11 ou une version antérieure, une activité doit appeler enterPictureInPictureMode() pour passer en mode PIP. Par exemple, le code suivant fait passer une activité en mode PIP lorsque l'utilisateur clique sur un bouton dédié dans l'interface utilisateur de l'application :

override fun onActionClicked(action: Action) {
    if (action.id.toInt() == R.id.lb_control_picture_in_picture) {
        activity?.enterPictureInPictureMode()
        return
    }
}

Vous pouvez inclure une logique qui fait passer une activité en mode Picture-in-picture au lieu de la placer en arrière-plan. Par exemple, Google Maps passe en mode PIP si l'utilisateur appuie sur le bouton d'accueil ou sur le bouton des applications récentes pendant que l'application est en cours de navigation. Vous pouvez intercepter ce cas en remplaçant onUserLeaveHint() :

override fun onUserLeaveHint() {
    if (iWantToBeInPipModeNow()) {
        enterPictureInPictureMode()
    }
}

Recommandé : Offrez aux utilisateurs une expérience de transition Picture-in-picture fluide

Android 12 a apporté des améliorations esthétiques significatives aux transitions animées entre les fenêtres plein écran et PiP. Nous vous recommandons vivement d'implémenter toutes les modifications applicables. Une fois que vous l'avez fait, ces modifications s'adaptent automatiquement aux grands écrans, tels que les pliables et les tablettes, sans aucune autre intervention de votre part.

Si votre application n'inclut pas les mises à jour applicables, les transitions PiP sont toujours fonctionnelles, mais les animations sont moins soignées. Par exemple, lors du passage du mode plein écran au mode PIP, la fenêtre PIP peut disparaître pendant la transition, puis réapparaître une fois la transition terminée.

Ces modifications sont les suivantes :

  • Amélioration de la fluidité des transitions vers le mode PIP à partir de la navigation par gestes
  • Définir un sourceRectHint approprié pour l'entrée et la sortie du mode PIP
  • Désactivation du redimensionnement fluide pour les contenus non vidéo

Consultez l'exemple PictureInPicture Android Kotlin pour savoir comment activer une transition fluide.

Fluidifier les transitions vers le mode PIP à partir de la navigation par gestes

À partir d'Android 12, l'indicateur setAutoEnterEnabled offre une animation beaucoup plus fluide pour la transition vers le contenu vidéo en mode PIP à l'aide de la navigation par gestes (par exemple, lorsque vous balayez l'écran vers le haut pour revenir à l'écran d'accueil depuis le mode plein écran).

Pour effectuer cette modification, procédez comme suit :

  1. Utilisez setAutoEnterEnabled pour créer PictureInPictureParams.Builder :
setPictureInPictureParams(PictureInPictureParams.Builder()
  .setAspectRatio(aspectRatio)
  .setSourceRectHint(sourceRectHint)
  .setAutoEnterEnabled(true)
  .build())
Note: When `setAutoEnterEnabled` is enabled, you don't need to explicitly
call [`enterPictureInPictureMode`][5] in [`onUserLeaveHint`][6].
  1. Appelez setPictureInPictureParams avec la PictureInPictureParams à jour dès que possible. L'application n'attend pas le rappel onUserLeaveHint (comme elle l'aurait fait sous Android 11).

    Par exemple, vous pouvez appeler setPictureInPictureParams lors de la toute première lecture et de toute lecture suivante si le format est modifié.

  2. Appelez setAutoEnterEnabled(false), mais uniquement si nécessaire. Par exemple, vous ne souhaitez probablement pas passer en mode PIP si la lecture est actuellement en pause.

Définir un sourceRectHint approprié pour l'entrée et la sortie du mode PIP

Depuis l'introduction du mode Picture-in-picture dans Android 8.0, setSourceRectHint indique la zone de l'activité visible après la transition vers le mode Picture-in-picture (par exemple, les limites de la vue vidéo dans un lecteur vidéo).

Avec Android 12, le système utilise sourceRectHint pour implémenter une animation beaucoup plus fluide lors de l'activation et de la désactivation du mode PIP.

Pour définir correctement sourceRectHint pour l'entrée et la sortie du mode PIP :

  1. Créez PictureInPictureParams en utilisant les limites appropriées comme sourceRectHint. Nous vous recommandons également d'associer un écouteur de changement de mise en page au lecteur vidéo :
val mOnLayoutChangeListener =
    OnLayoutChangeListener { v: View?, left: Int,
            top: Int, right: Int, bottom: Int, oldLeft: Int, oldTop:
            Int, oldRight: Int, oldBottom: Int ->
        val sourceRectHint = Rect()
        mYourVideoView.getGlobalVisibleRect(sourceRectHint)
        val builder = PictureInPictureParams.Builder()
            .setSourceRectHint(sourceRectHint)
        setPictureInPictureParams(builder.build())
    }

mYourVideoView.addOnLayoutChangeListener(mOnLayoutChangeListener)
  1. Si nécessaire, mettez à jour sourceRectHint avant que le système ne démarre la transition de sortie. Lorsque le système est sur le point de quitter le mode PIP, la hiérarchie des vues de l'activité est disposée selon sa configuration de destination (par exemple, plein écran). L'application peut associer un écouteur de changement de mise en page à sa vue racine ou à sa vue cible (comme la vue du lecteur vidéo) pour détecter l'événement et mettre à jour le sourceRectHint avant le début de l'animation.
// Listener is called immediately after the user exits PiP but before animating.
playerView.addOnLayoutChangeListener { _, left, top, right, bottom,
                    oldLeft, oldTop, oldRight, oldBottom ->
    if (left != oldLeft
        || right != oldRight
        || top != oldTop
        || bottom != oldBottom) {
        // The playerView's bounds changed, update the source hint rect to
        // reflect its new bounds.
        val sourceRectHint = Rect()
        playerView.getGlobalVisibleRect(sourceRectHint)
        setPictureInPictureParams(
            PictureInPictureParams.Builder()
                .setSourceRectHint(sourceRectHint)
                .build()
        )
    }
}

Désactiver le redimensionnement fluide pour les contenus non vidéo

Android 12 ajoute l'indicateur setSeamlessResizeEnabled, qui fournit une animation de fondu enchaîné beaucoup plus fluide lors du redimensionnement de contenus non vidéo dans la fenêtre PIP. Auparavant, le redimensionnement de contenus non vidéo dans une fenêtre PiP pouvait créer des artefacts visuels gênants.

Pour activer le redimensionnement fluide du contenu vidéo :

setPictureInPictureParams(PictureInPictureParams.Builder()
    .setSeamlessResizeEnabled(true)
    .build())

Gérer l'UI en mode PIP

Lorsque l'activité passe en mode Picture-in-picture (PIP) ou le quitte, le système appelle Activity.onPictureInPictureModeChanged() ou Fragment.onPictureInPictureModeChanged().

Android 15 introduit des modifications qui garantissent une transition encore plus fluide lors du passage en mode PIP. Cela est utile pour les applications dont les éléments d'interface utilisateur sont superposés à l'interface utilisateur principale, qui passe en mode PIP.

Les développeurs utilisent le rappel onPictureInPictureModeChanged() pour définir la logique qui active ou désactive la visibilité des éléments d'interface utilisateur superposés. Ce rappel est déclenché lorsque l'animation d'entrée ou de sortie du mode PIP est terminée. À partir d'Android 15, la classe PictureInPictureUiState inclut un nouvel état.

Avec ce nouvel état de l'interface utilisateur, les applications ciblant Android 15 observent le rappel Activity#onPictureInPictureUiStateChanged() appelé avec isTransitioningToPip() dès que l'animation Picture-in-picture démarre. De nombreux éléments d'interface utilisateur ne sont pas pertinents pour l'application lorsqu'elle est en mode PIP, par exemple les vues ou la mise en page qui incluent des informations telles que des suggestions, la vidéo suivante, les notes et les titres. Lorsque l'application passe en mode PIP, utilisez le rappel onPictureInPictureUiStateChanged() pour masquer ces éléments d'interface utilisateur. Lorsque l'application passe en mode plein écran à partir de la fenêtre PIP, utilisez le rappel onPictureInPictureModeChanged() pour afficher ces éléments, comme illustré dans les exemples suivants :

override fun onPictureInPictureUiStateChanged(pipState: PictureInPictureUiState) {
    if (pipState.isTransitioningToPip()) {
        // Hide UI elements.
    }
}
override fun onPictureInPictureModeChanged(
    isInPictureInPictureMode: Boolean,
    newConfig: Configuration
) {
    super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
    if (!isInPictureInPictureMode) {
        // Unhide UI elements.
    }
}

Cette option d'activation/désactivation rapide de la visibilité des éléments d'interface utilisateur non pertinents (pour une fenêtre PiP) permet d'assurer une animation d'entrée PiP plus fluide et sans scintillement.

Remplacez ces rappels pour redessiner les éléments d'interface utilisateur de l'activité. N'oubliez pas qu'en mode Picture-in-picture, votre activité s'affiche dans une petite fenêtre. Les utilisateurs ne peuvent pas interagir avec les éléments d'interface utilisateur de votre application lorsque celle-ci est en mode PIP, et les détails des petits éléments d'interface utilisateur peuvent être difficiles à voir. Les activités de lecture vidéo avec une UI minimale offrent la meilleure expérience utilisateur.

Si votre application doit fournir des actions personnalisées pour le mode PIP, consultez Ajouter des commandes sur cette page. Supprimez les autres éléments d'interface utilisateur avant que votre activité ne passe en mode PIP, puis restaurez-les lorsqu'elle repasse en plein écran.

Ajouter des commandes

La fenêtre PIP peut afficher des commandes lorsque l'utilisateur ouvre le menu de la fenêtre (en appuyant sur la fenêtre sur un appareil mobile ou en sélectionnant le menu à l'aide de la télécommande du téléviseur).

Si une application dispose d'une session multimédia active, les commandes de lecture, de mise en pause, de passage au titre suivant et de retour au titre précédent s'affichent.

Vous pouvez également spécifier des actions personnalisées de manière explicite en créant PictureInPictureParams avec PictureInPictureParams.Builder.setActions() avant d'activer le mode PIP, puis transmettre les paramètres lorsque vous activez le mode PIP à l'aide de enterPictureInPictureMode(android.app.PictureInPictureParams) ou setPictureInPictureParams(android.app.PictureInPictureParams). Soyez prudent. Si vous essayez d'en ajouter plus de getMaxNumPictureInPictureActions(), vous n'obtiendrez que le nombre maximal.

Poursuite de la lecture de la vidéo en mode PIP

Lorsque votre activité passe en mode Picture-in-picture, le système la met en pause et appelle la méthode onPause() de l'activité. La lecture de la vidéo ne doit pas être mise en pause, mais continuer si l'activité est mise en pause lors du passage en mode PIP.

Sur Android 7.0 (niveau d'API 24) ou version ultérieure, vous devez mettre en pause et reprendre la lecture de la vidéo lorsque le système appelle les méthodes onStop() et onStart() de votre activité. Vous n'avez ainsi pas besoin de vérifier si votre application est en mode PIP dans onPause() et de poursuivre explicitement la lecture.

Si vous n'avez pas défini l'option setAutoEnterEnabled sur true et que vous devez mettre en pause la lecture dans votre implémentation de onPause(), vérifiez si le mode PIP est activé en appelant isInPictureInPictureMode() et gérez la lecture de manière appropriée. Exemple :

override fun onPause() {
    super.onPause()
    // If called while in PiP mode, don't pause playback.
    if (isInPictureInPictureMode) {
        // Continue playback.
    } else {
        // Use existing playback logic for paused activity behavior.
    }
}

Lorsque votre activité quitte le mode PIP pour revenir au mode plein écran, le système reprend votre activité et appelle votre méthode onResume().

Utiliser une seule activité de lecture pour le mode PIP

Dans votre application, un utilisateur peut sélectionner une nouvelle vidéo lorsqu'il parcourt du contenu sur l'écran principal, tandis qu'une activité de lecture vidéo est en mode PIP. Lisez la nouvelle vidéo dans l'activité de lecture existante en mode plein écran, au lieu de lancer une nouvelle activité qui pourrait dérouter l'utilisateur.

Pour vous assurer qu'une seule activité est utilisée pour les demandes de lecture vidéo et qu'elle passe en mode PIP ou en sort selon les besoins, définissez l'attribut android:launchMode de l'activité sur singleTask dans votre fichier manifeste :

<activity android:name="VideoActivity"
    ...
    android:supportsPictureInPicture="true"
    android:launchMode="singleTask"
    ...

Dans votre activité, remplacez onNewIntent() et gérez la nouvelle vidéo, en arrêtant la lecture de la vidéo existante si nécessaire.

Prendre en charge le mode PIP pour les applications d'appareil photo

Pour activer le mode PiP pour les applications d'appareil photo, vous devez vous assurer que l'appareil photo reste actif en mode PiP en ne le fermant pas lorsque onPause() est appelé :

override fun onPause() {
    super.onPause()
    // Don't close the camera if the app is entering PiP mode
    if (!isInPictureInPictureMode) {
        closeCamera()
    }
}

Comme pour les autres cas d'utilisation, masquez les éléments d'interface utilisateur non essentiels (tels que les commandes et les superpositions) et ajoutez des actions personnalisées pour contrôler la caméra (par exemple, arrêter l'enregistrement ou retourner la caméra).

Calculer sourceRectHint pour des transitions fluides

Il est essentiel de fournir un sourceRectHint précis avec les coordonnées d'écran exactes du viseur de l'appareil photo pour une animation d'entrée fluide. Vous pouvez obtenir les limites de la vue d'aperçu à l'aide de getGlobalVisibleRect() comme suit :

val previewView = findViewById<View>(R.id.preview_view)
val globalRect = Rect()
// Ensure the view is laid out before calling getGlobalVisibleRect() to get valid screen coordinates.
previewView.getGlobalVisibleRect(globalRect)
val params = PictureInPictureParams.Builder()
    .setSourceRectHint(globalRect)
    .build()
setPictureInPictureParams(params)

Informations complémentaires

Le mode Picture-in-picture peut être désactivé sur les appareils disposant de peu de RAM. Avant que votre application n'utilise le mode PIP, vérifiez qu'il est disponible en appelant hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE).

Le mode PIP est destiné aux activités qui lisent des vidéos en plein écran. Lorsque vous passez votre activité en mode PIP, évitez d'afficher autre chose que du contenu vidéo. Suivez le moment où votre activité passe en mode PIP et masquez les éléments d'interface utilisateur, comme décrit dans Gérer l'interface utilisateur en mode PIP.

Lorsqu'une activité est en mode Picture-in-picture, elle n'est pas sélectionnée par défaut. Pour recevoir des événements d'entrée en mode PIP, utilisez MediaSession.setCallback(). Pour en savoir plus sur l'utilisation de setCallback(), consultez Afficher une carte "En écoute".

Lorsque votre application est en mode PIP, la lecture vidéo dans la fenêtre PIP peut provoquer des interférences audio avec une autre application, telle qu'un lecteur de musique ou une application de recherche vocale. Pour éviter cela, demandez la priorité audio lorsque vous lancez la lecture de la vidéo et gérez les notifications de changement de priorité audio, comme décrit dans Gérer la priorité audio. Si vous recevez une notification de perte de focus audio en mode PIP, mettez en pause ou arrêtez la lecture de la vidéo.

Lorsque votre application est sur le point de passer en mode PIP, notez que seule l'activité principale passe en mode Picture-in-picture. Dans certains cas, par exemple sur les appareils en mode multifenêtre, il est possible que l'activité sous-jacente soit désormais affichée et redevienne visible à côté de l'activité PIP. Vous devez gérer ce cas en conséquence, y compris l'activité sous-jacente qui reçoit un rappel onResume() ou onPause(). Il est également possible que l'utilisateur interagisse avec l'activité. Par exemple, si vous avez une activité de liste de vidéos affichée et l'activité de lecture de vidéo en mode PIP, l'utilisateur peut sélectionner une nouvelle vidéo dans la liste, et l'activité PIP doit être mise à jour en conséquence.

Exemples de code supplémentaires

Pour télécharger une application exemple écrite en Kotlin, consultez Android PictureInPicture Sample (Kotlin).