Proyección de contenido multimedia

Las APIs de android.media.projection que se introdujeron en Android 5 (nivel de API 21) te permiten capturar el contenido de la pantalla de un dispositivo como una transmisión de contenido multimedia que puedes reproducir, grabar o transmitir a otros dispositivos, como TVs.

Android 14 (nivel de API 34) introduce la función de compartir pantalla de una app, que permite a los usuarios compartir una sola ventana de la app en lugar de la pantalla completa del dispositivo, independientemente del modo de ventanas. Compartir pantalla de una app excluye la barra de estado, la barra de navegación, las notificaciones y otros elementos de la IU del sistema de la pantalla compartida, incluso cuando se usa compartir pantalla de una app para capturar una app en pantalla completa. Solo se comparte el contenido de la app seleccionada.

El uso compartido de pantalla de una app garantiza la privacidad del usuario, aumenta su productividad y mejora la realización de varias tareas a la vez, ya que permite a los usuarios ejecutar varias apps, pero restringe el uso compartido de contenido a una sola app.

Tres representaciones de pantalla

Una proyección de contenido multimedia captura el contenido de la pantalla de un dispositivo o la ventana de una app y luego, proyecta la imagen capturada en una pantalla virtual que renderiza la imagen en un Surface.

Pantalla real de un dispositivo proyectada en una pantalla virtual. Contenido de la pantalla virtual escrito en "Surface" que brinda la aplicación.
Figura 1. Pantalla real de un dispositivo o ventana de la app proyectada en una pantalla virtual. Pantalla virtual escrita en Surface que brinda la aplicación.

La aplicación brinda Surface por medio de MediaRecorder, SurfaceTexture o ImageReader, que consumen el contenido de la pantalla capturada y te permiten administrar las imágenes renderizadas en Surface en tiempo real. Puedes guardar las imágenes como una grabación, o transmitirlas a una TV o a otro dispositivo.

Pantalla real

Para iniciar una sesión de proyección de contenido multimedia, obtén un token que le otorgue a tu app la capacidad de capturar el contenido de la pantalla del dispositivo o la ventana de la app. El token es representado por una instancia de la MediaProjection clase.

Usa el método getMediaProjection() del servicio del sistema MediaProjectionManager para crear una instancia de MediaProjection cuando inicies una actividad nueva. Inicia la actividad con un intent del método createScreenCaptureIntent() para especificar una operación de captura de pantalla:

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());

Pantalla virtual

La pieza central de una proyección de contenido multimedia es la pantalla virtual, que creas cuando llamas a createVirtualDisplay() en una instancia de 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);

Los parámetros width y height especifican las dimensiones de la pantalla virtual. Para obtener valores de ancho y alto, usa las WindowMetrics APIs que se introdujeron en Android 11 (nivel de API 30). (Para obtener más información, consulta la sección Tamaño de la proyección de contenido multimedia).

Superficie

Dimensiona la superficie de proyección de contenido multimedia para producir resultados en la resolución adecuada. Configura un tamaño grande (baja resolución) para transmitir la pantalla a televisiones o monitores de computadora, y un tamaño pequeño (alta resolución) para grabar la pantalla de dispositivos.

A partir de Android 12L (nivel de API 32), cuando se renderiza el contenido capturado en la superficie, el sistema lo ajusta de manera uniforme, manteniendo la relación de aspecto, de modo que ambas dimensiones del contenido (ancho y alto) sean iguales o menores que las dimensiones correspondientes de la superficie. Luego, el contenido capturado se centra en la superficie.

El enfoque de ajuste de Android 12L mejora la transmisión de la pantalla a televisiones y otras pantallas grandes, ya que se maximiza el tamaño de la imagen de la superficie, a la vez que se garantiza la relación de aspecto correcta.

Permiso de servicio en primer plano

Si tu app está orientada a Android 14 o versiones posteriores, el manifiesto de la app debe incluir una declaración de permiso para el mediaProjection tipo de servicio en primer plano:

<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>

Inicia el servicio de proyección de contenido multimedia con una llamada a startForeground().

Si no especificas el tipo de servicio en primer plano en la llamada, el tipo se establece de forma predeterminada en un número entero bit a bit de los tipos de servicio en primer plano definidos en el manifiesto. Si el manifiesto no especifica ningún tipo de servicio, el sistema arroja MissingForegroundServiceTypeException.

Tu app debe solicitar el consentimiento del usuario antes de cada sesión de proyección de contenido multimedia. Una sesión es una sola llamada a createVirtualDisplay(). Un token MediaProjection se debe usar solo una vez para realizar la llamada.

En Android 14 o versiones posteriores, el createVirtualDisplay() método arroja una SecurityException si tu app realiza alguna de las siguientes acciones:

  • Pasa una instancia que se muestra desde a más de una vez.IntentcreateScreenCaptureIntent()getMediaProjection()
  • Llama a createVirtualDisplay() más de una vez en la misma instancia de MediaProjection.

Tamaño de la proyección de contenido multimedia

Una proyección de contenido multimedia puede capturar la pantalla completa del dispositivo o la ventana de una app, independientemente del modo de ventanas.

Tamaño inicial

Con la proyección de contenido multimedia en pantalla completa, tu app debe determinar el tamaño de la pantalla del dispositivo. En el uso compartido de pantalla de una app, tu app no podrá determinar el tamaño de la pantalla capturada hasta que el usuario haya seleccionado la región de captura. Por lo tanto, el tamaño inicial de cualquier proyección de contenido multimedia es el tamaño de la pantalla del dispositivo.

Usa el método WindowManager getMaximumWindowMetrics() de la plataforma para mostrar un objeto WindowMetrics para la pantalla del dispositivo, incluso si la app host de proyección de contenido multimedia está en el modo multiventana y ocupa solo una parte de la pantalla.

Para brindar compatibilidad hasta el nivel de API 14, usa el WindowMetricsCalculator computeMaximumWindowMetrics() método de la biblioteca de WindowManager de Jetpack.

Llama al método WindowMetrics getBounds() para obtener el ancho y la altura de la pantalla del dispositivo.

Cambios de tamaño

El tamaño de la proyección de contenido multimedia puede cambiar cuando se rota el dispositivo o el usuario selecciona una ventana de la app como la región de captura en compartir pantalla de una app. Es posible que la proyección de contenido multimedia tenga barras laterales si el contenido capturado tiene un tamaño diferente de las métricas máximas de la ventana obtenidas cuando se configuró la proyección de contenido multimedia.

Para garantizar que la proyección de contenido multimedia se alinee con precisión con el tamaño del contenido capturado para cualquier región capturada y en las rotaciones del dispositivo, usa la devolución de llamada onCapturedContentResize() para cambiar el tamaño de la captura. (Para obtener más información, consulta la sección Personalización que sigue).

Personalización

Tu app puede personalizar la experiencia del usuario de la proyección de contenido multimedia con las siguientes MediaProjection.Callback APIs:

  • onCapturedContentVisibilityChanged(): Permite que la app host (la app que inició la proyección de contenido multimedia) muestre u oculte el contenido compartido.

    Usa esta devolución de llamada para personalizar la IU de tu app en función de si la región capturada es visible para el usuario. Por ejemplo, si tu app es visible para el usuario y muestra el contenido capturado dentro de la IU de la app, y la app capturada también es visible para el usuario (como se indica a través de esta devolución de llamada), el usuario ve el mismo contenido dos veces. Usa la devolución de llamada para actualizar la IU de tu app para ocultar el contenido capturado y liberar espacio de diseño en tu app para otro contenido.

  • onCapturedContentResize(): Permite que la app host cambie el tamaño de la proyección de contenido multimedia en la pantalla virtual y la Surface de proyección de contenido multimedia en función del tamaño de la región de la pantalla capturada.

    Se activa cada vez que el contenido capturado (una sola ventana de la app o la pantalla completa del dispositivo) cambia de tamaño (debido a la rotación del dispositivo o a que la app capturada ingresa a un modo de ventanas diferente). Usa esta API para cambiar el tamaño de la pantalla virtual y la superficie para garantizar que la relación de aspecto coincida con el contenido capturado y que la captura no tenga barras laterales.

Recuperación de recursos

Tu app debe registrar la devolución de llamada MediaProjection onStop() para recibir información cuando se detenga la sesión de proyección de contenido multimedia y deje de ser válida. Cuando se detiene la sesión, tu app debe liberar los recursos que contiene, como la pantalla virtual y la superficie de proyección. Una sesión de proyección de contenido multimedia detenida ya no puede crear una pantalla virtual nueva, incluso si tu app no creó una pantalla virtual para esa proyección de contenido multimedia.

El sistema invoca la devolución de llamada cuando finaliza la proyección de contenido multimedia. Esta finalización puede ocurrir por varios motivos, como los siguientes:

  • El usuario detiene la sesión con la IU de la app o el chip de la barra de estado de proyección de contenido multimedia del sistema.
  • Se bloquea la pantalla.
  • Se inicia otra sesión de proyección de contenido multimedia.
  • Se elimina el proceso de la app.

Si tu app no registra la devolución de llamada, cualquier llamada a createVirtualDisplay() arroja IllegalStateException.

Inhabilitar

Android 14 o versiones posteriores habilita compartir pantalla de una app de forma predeterminada. Cada sesión de proyección de contenido multimedia les da a los usuarios la opción de compartir una ventana de la app o la pantalla completa.

Tu app puede inhabilitar el uso compartido de pantalla de una app llamando al createScreenCaptureIntent(MediaProjectionConfig) método con un MediaProjectionConfig argumento que se muestra desde una llamada a createConfigForDefaultDisplay().

Una llamada a createScreenCaptureIntent(MediaProjectionConfig) con un MediaProjectionConfig argumento que se muestra desde una llamada a createConfigForUserChoice() es la misma que el comportamiento predeterminado, es decir, una llamada a createScreenCaptureIntent().

Apps que pueden cambiar de tamaño

Siempre configura las apps de proyección de contenido multimedia para que puedan cambiar de tamaño (resizeableActivity="true"). Las apps que pueden cambiar de tamaño admiten cambios en la configuración del dispositivo y el modo multiventana (consulta Compatibilidad con el modo Multiventana).

Si no se puede cambiar el tamaño de la app, esta debe consultar los límites de la pantalla desde un contexto de ventana y usar getMaximumWindowMetrics() para recuperar la clase WindowMetrics del área de visualización máxima disponible para la app :

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 barra de estado y detención automática

Los exploits de proyección de pantalla exponen datos privados del usuario, como información financiera, porque los usuarios no se dan cuenta de que se está compartiendo la pantalla de su dispositivo.

En el caso de las apps que se ejecutan en dispositivos con Android 15 QPR1 o versiones posteriores, un chip de la barra de estado grande y destacado alerta a los usuarios sobre cualquier proyección de pantalla en curso. Los usuarios pueden presionar el chip para evitar que se comparta, transmita o grabe su pantalla. Además, la proyección de pantalla se detiene automáticamente cuando se bloquea la pantalla del dispositivo.

Figura 2. Chip de la barra de estado para compartir, transmitir y grabar la pantalla.

Para probar la disponibilidad del chip de la barra de estado de proyección de contenido multimedia, inicia el uso compartido, la transmisión o la grabación de la pantalla. El chip debería aparecer en la barra de estado.

Para asegurarte de que tu app libere recursos y actualice su IU cuando la proyección de la pantalla se detenga por la interacción del usuario con el chip de la barra de estado o por la activación de la pantalla de bloqueo, haz lo siguiente:

  • Crea una instancia de MediaProjection.Callback.

  • Implementa el método de devolución de llamada onStop(). Se llama al método cuando se detiene la proyección de la pantalla. Libera los recursos que contiene tu app y actualiza la IU de la app según sea necesario.

Para probar la devolución de llamada, presiona el chip de la barra de estado o bloquea la pantalla del dispositivo para detener la proyección de la pantalla. Verifica que se llame al método onStop() y que tu app responda como se espera.

Recursos adicionales

Para obtener más información sobre la proyección de contenido multimedia, consulta Cómo capturar la reproducción de audio y video.