Projection multimédia

Les API android.media.projection introduites dans Android 5 (niveau d'API 21) vous permettent de capturer le contenu d'un écran d'appareil en tant que flux multimédia que vous pouvez lire, enregistrer ou caster sur d'autres appareils, comme les téléviseurs.

Android 14 (niveau d'API 34) introduit le partage d'écran d'application, qui permet aux utilisateurs de partager une seule fenêtre d'application au lieu de l'ensemble de l'écran de l'appareil, quel que soit le mode de fenêtrage. Le partage d'écran de l'application exclut la barre d'état, la barre de navigation, les notifications et d'autres éléments d'interface utilisateur du système de l'écran partagé, même lorsque le partage d'écran de l'application est utilisé pour capturer une application en plein écran. Seuls les contenus de l'application sélectionnée sont partagés.

Le partage d'écran de l'application garantit la confidentialité des utilisateurs, améliore leur productivité et améliore le multitâche en leur permettant d'exécuter plusieurs applications, mais en limitant le partage de contenu à une seule application.

Trois représentations d'affichage

Une projection de contenu multimédia capture le contenu d'un écran d'appareil ou d'une fenêtre d'application, puis projette l'image capturée sur un écran virtuel qui affiche l'image sur une Surface.

Écran réel de l'appareil projeté sur l'écran virtuel Contenu de l'écran virtuel écrit sur la "surface" fournie par l'application
Figure 1 : Écran réel de l'appareil ou fenêtre de l'application projeté sur l'écran virtuel. Écran virtuel écrit dans la Surface fournie par l'application.

L'application fournit cette Surface au moyen d'un élément MediaRecorder, SurfaceTexture ou ImageReader, qui consomme le contenu de l'écran capturé et vous permet de gérer les images affichées sur la Surface en temps réel. Vous pouvez conserver les images sous forme d'enregistrement ou les caster sur un téléviseur ou un autre appareil.

Affichage réel

Pour démarrer une session de projection multimédia, vous avez besoin d'un jeton permettant à votre application de capturer le contenu de l'écran de l'appareil ou de la fenêtre de l'application. Le jeton est représenté par une instance de la classe MediaProjection.

Utilisez la méthode getMediaProjection() du service système MediaProjectionManager pour créer une instance MediaProjection lorsque vous démarrez une nouvelle activité. Démarrez l'activité avec un intent à partir de la méthode createScreenCaptureIntent() pour spécifier une opération de capture d'écran:

Kotlin

val mediaProjectionManager = getSystemService(MediaProjectionManager::class.java)
var mediaProjection : MediaProjection
val startMediaProjection = registerForActivityResult( StartActivityForResult() ) { result -> if (result.resultCode == RESULT_OK) { mediaProjection = mediaProjectionManager .getMediaProjection(result.resultCode, result.data!!) } }
startMediaProjection.launch(mediaProjectionManager.createScreenCaptureIntent())

Java

final MediaProjectionManager mediaProjectionManager =
    getSystemService(MediaProjectionManager.class);
final MediaProjection[] mediaProjection = new MediaProjection[1];
ActivityResultLauncher startMediaProjection = registerForActivityResult( new StartActivityForResult(), result -> { if (result.getResultCode() == Activity.RESULT_OK) { mediaProjection[0] = mediaProjectionManager .getMediaProjection(result.getResultCode(), result.getData()); } } );
startMediaProjection.launch(mediaProjectionManager.createScreenCaptureIntent());

Écran virtuel

La pièce maîtresse d'une projection multimédia est l'écran virtuel, que vous créez en appelant createVirtualDisplay() sur une instance MediaProjection:

Kotlin

virtualDisplay = mediaProjection.createVirtualDisplay(
                     "ScreenCapture",
                     width,
                     height,
                     screenDensity,
                     DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                     surface,
                     null, null)

Java

virtualDisplay = mediaProjection.createVirtualDisplay(
                     "ScreenCapture",
                     width,
                     height,
                     screenDensity,
                     DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                     surface,
                     null, null);

Les paramètres width et height spécifient les dimensions de l'écran virtuel. Pour obtenir des valeurs de largeur et de hauteur, utilisez les API WindowMetrics introduites dans Android 11 (niveau d'API 30). (Pour en savoir plus, consultez la section Taille de la projection multimédia.)

Surface

Dimensionnez la surface de projection multimédia pour que la sortie générée ait la résolution appropriée. Faites en sorte que la surface soit grande (basse résolution) pour caster du contenu sur des écrans de téléviseur ou d'ordinateur, et petite (haute résolution) pour enregistrer le contenu sur l'écran de l'appareil.

Depuis Android 12L (niveau d'API 32), lorsque le système affiche le contenu capturé sur la surface, il le redimensionne de manière uniforme, en conservant le format, de sorte que les deux dimensions du contenu (largeur et hauteur) soient égales ou inférieures aux dimensions correspondantes de la surface. Le contenu capturé est ensuite centré sur la surface.

L'approche de mise à l'échelle d'Android 12L améliore la diffusion de contenu sur les écrans de télévision et autres grands écrans, en maximisant la taille de l'image de la surface, tout en garantissant des proportions correctes.

Autorisation de service de premier plan

Si votre application cible Android 14 ou version ultérieure, le fichier manifeste de l'application doit inclure une déclaration d'autorisation pour le type de service de premier plan mediaProjection:

<manifest ...>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
    <application ...>
        <service
            android:name=".MyMediaProjectionService"
            android:foregroundServiceType="mediaProjection"
            android:exported="false">
        </service>
    </application>
</manifest>

Démarrez le service de projection multimédia en appelant startForeground().

Si vous ne spécifiez pas le type de service de premier plan dans l'appel, le type est défini par défaut sur un entier au niveau du bit des types de services de premier plan définis dans le fichier manifeste. Si le fichier manifeste ne spécifie aucun type de service, le système génère MissingForegroundServiceTypeException.

Votre application doit demander le consentement de l'utilisateur avant chaque session de projection multimédia. Une session correspond à un seul appel à createVirtualDisplay(). Un jeton MediaProjection ne doit être utilisé qu'une seule fois pour passer l'appel.

Sous Android 14 ou version ultérieure, la méthode createVirtualDisplay() génère une exception SecurityException si votre application effectue l'une des opérations suivantes:

  • Transmet une instance Intent renvoyée de createScreenCaptureIntent() à getMediaProjection() plusieurs fois
  • Appels de createVirtualDisplay() plusieurs fois sur la même instance MediaProjection

Taille de la projection multimédia

Une projection multimédia peut capturer l'intégralité de l'écran de l'appareil ou une fenêtre d'application, quel que soit le mode de fenêtrage.

Taille initiale

Avec la projection multimédia en plein écran, votre application doit déterminer la taille de l'écran de l'appareil. Dans le partage d'écran de l'application, votre application ne pourra pas déterminer la taille de l'écran capturé tant que l'utilisateur n'aura pas sélectionné la région de capture. Par conséquent, la taille initiale de toute projection multimédia correspond à la taille de l'écran de l'appareil.

Utilisez la méthode WindowManager getMaximumWindowMetrics() de la plate-forme pour renvoyer un objet WindowMetrics pour l'écran de l'appareil, même si l'application hôte de projection multimédia est en mode multifenêtre et qu'elle n'occupe qu'une partie de l'écran.

Pour assurer la compatibilité des versions remontant jusqu'au niveau d'API 14, utilisez la méthode WindowMetricsCalculator computeMaximumWindowMetrics() de la bibliothèque Jetpack WindowManager.

Appelez la méthode WindowMetrics getBounds() pour obtenir la largeur et la hauteur de l'écran de l'appareil.

Changements de taille

La taille de la projection multimédia peut changer lorsque l'appareil est pivoté ou que l'utilisateur sélectionne une fenêtre d'application comme région de capture dans le partage d'écran de l'application. La projection multimédia peut être au format letterbox si le contenu capturé est d'une taille différente des métriques de fenêtre maximales obtenues lors de la configuration de la projection multimédia.

Pour vous assurer que la projection multimédia s'aligne précisément sur la taille du contenu capturé pour toute région capturée et lors des rotations de l'appareil, utilisez le rappel onCapturedContentResize() pour redimensionner la capture. (Pour en savoir plus, consultez la section Personnalisation ci-dessous.)

Personnalisation

Votre application peut personnaliser l'expérience utilisateur de la projection multimédia à l'aide des API MediaProjection.Callback suivantes:

  • onCapturedContentVisibilityChanged(): permet à l'application hôte (l'application qui a lancé la projection multimédia) d'afficher ou de masquer le contenu partagé.

    Utilisez ce rappel pour personnaliser l'UI de votre application en fonction de la visibilité de la région capturée par l'utilisateur. Par exemple, si votre application est visible par l'utilisateur et affiche le contenu capturé dans l'UI de l'application, et que l'application capturée est également visible par l'utilisateur (comme indiqué par ce rappel), l'utilisateur voit le même contenu deux fois. Utilisez le rappel pour mettre à jour l'interface utilisateur de votre application afin de masquer le contenu capturé et de libérer de l'espace de mise en page dans votre application pour d'autres contenus.

  • onCapturedContentResize(): permet à l'application hôte de modifier la taille de la projection multimédia sur l'écran virtuel et la projection multimédia Surface en fonction de la taille de la région d'affichage capturée.

    Se déclenche chaque fois que le contenu capturé (une seule fenêtre d'application ou l'écran complet de l'appareil) change de taille (en raison de la rotation de l'appareil ou de l'entrée de l'application capturée dans un autre mode de fenêtrage). Utilisez cette API pour redimensionner à la fois l'écran virtuel et la surface afin de vous assurer que le format correspond au contenu capturé et que la capture n'est pas au format letterbox.

Récupération de ressources

Votre application doit enregistrer le rappel MediaProjection onStop() pour être informé lorsque la session de projection multimédia est arrêtée et devient non valide. Lorsque la session est arrêtée, votre application doit libérer les ressources qu'elle détient, telles que l'écran virtuel et la surface de projection. Une session de projection multimédia arrêtée ne peut plus créer d'écran virtuel, même si votre application n'a pas encore créé d'écran virtuel pour cette projection multimédia.

Le système appelle le rappel lorsque la projection multimédia se termine. Cette résiliation peut se produire pour plusieurs raisons, par exemple:

  • l'utilisateur arrête la session à l'aide de l'UI de l'application ou du chip de barre d'état de la projection multimédia du système ;
  • l'écran est verrouillé ;
  • une autre session de projection multimédia démarre ;
  • le processus de l'application est arrêté

Si votre application n'enregistre pas le rappel, tout appel à createVirtualDisplay() génère une exception IllegalStateException.

Désactiver

Android 14 ou version ultérieure active le partage d'écran de l'application par défaut. Chaque session de projection multimédia permet aux utilisateurs de partager une fenêtre d'application ou l'intégralité de l'écran.

Votre application peut désactiver le partage d'écran en appelant la méthode createScreenCaptureIntent(MediaProjectionConfig) avec un argument MediaProjectionConfig renvoyé à partir d'un appel à createConfigForDefaultDisplay().

Un appel à createScreenCaptureIntent(MediaProjectionConfig) avec un argument MediaProjectionConfig renvoyé à partir d'un appel à createConfigForUserChoice() est identique au comportement par défaut, c'est-à-dire un appel à createScreenCaptureIntent().

Applications redimensionnables

Les applications de projection multimédia doivent toujours être redimensionnables (resizeableActivity="true"). En effet, les applications redimensionnables sont compatibles avec les modifications de configuration des appareils et le mode multifenêtre (voir Compatibilité avec le mode multifenêtre).

Si votre application n'est pas redimensionnable, elle doit déterminer les limites de l'écran dans le contexte d'une fenêtre et utiliser getMaximumWindowMetrics() pour récupérer la valeur WindowMetrics de la zone d'affichage maximale disponible pour l'application :

Kotlin

val windowContext = context.createWindowContext(context.display!!,
      WindowManager.LayoutParams.TYPE_APPLICATION, null)
val projectionMetrics = windowContext.getSystemService(WindowManager::class.java)
      .maximumWindowMetrics

Java

Context windowContext = context.createWindowContext(context.getDisplay(),
      WindowManager.LayoutParams.TYPE_APPLICATION, null);
WindowMetrics projectionMetrics = windowContext.getSystemService(WindowManager.class)
      .getMaximumWindowMetrics();

Chip de la barre d'état et arrêt automatique

Les failles de projection d'écran exposent des données utilisateur privées telles que des informations financières, car les utilisateurs ne se rendent pas compte que l'écran de leur appareil est partagé.

Android 15 (niveau d'API 35) et versions ultérieures affichent un chip de barre d'état de grande taille et bien visible pour alerter les utilisateurs de toute projection d'écran en cours. Les utilisateurs peuvent appuyer sur le chip pour empêcher le partage, la diffusion ou l'enregistrement de leur écran. De plus, la projection d'écran s'arrête automatiquement lorsque l'écran de l'appareil est verrouillé.

Figure 2 : Chip de la barre d'état pour le partage d'écran, le cast et l'enregistrement.

Testez la disponibilité du chip de la barre d'état de la projection multimédia en démarrant le partage d'écran, la diffusion ou l'enregistrement. Le chip doit s'afficher dans la barre d'état.

Pour vous assurer que votre application libère des ressources et met à jour son UI lorsque la projection d'écran est arrêtée par l'interaction de l'utilisateur avec le chip de la barre d'état ou par l'activation de l'écran de verrouillage, procédez comme suit:

  • Créez une instance de MediaProjection.Callback.

  • Implémentez la méthode de rappel onStop(). La méthode est appelée lorsque la projection d'écran s'arrête. Libérez toutes les ressources détenues par votre application et mettez à jour l'UI de l'application si nécessaire.

Pour tester le rappel, appuyez sur le chip de la barre d'état ou verrouillez l'écran de l'appareil pour arrêter la projection d'écran. Vérifiez que la méthode onStop() est appelée et que votre application répond comme prévu.

Ressources supplémentaires

Pour en savoir plus sur la projection multimédia, consultez la section Capturer la lecture vidéo et audio.