Proiezione di contenuti multimediali

Le API android.media.projection introdotte in Android 5 (livello API 21) ti consentono di acquisire i contenuti del display di un dispositivo sotto forma di stream multimediale che puoi riprodurre, registrare o trasmettere ad altri dispositivi, ad esempio le TV.

Android 14 (livello API 34) introduce la condivisione schermo delle app, che consente agli utenti di condividere una singola finestra dell'app invece dell'intero schermo del dispositivo, indipendentemente dalla modalità finestra. La condivisione schermo delle app esclude la barra di stato, la barra di navigazione, le notifiche e altri elementi dell'interfaccia utente di sistema dalla visualizzazione condivisa, anche quando viene utilizzata la condivisione schermo delle app per acquisire un'app a schermo intero. Vengono condivisi solo i contenuti dell'app selezionata.

La condivisione schermo delle app garantisce la privacy degli utenti, aumenta la produttività degli utenti e migliora il multitasking consentendo agli utenti di eseguire più app, ma limitando la condivisione dei contenuti a una sola app.

Tre rappresentazioni del display

Una proiezione multimediale acquisisce i contenuti del display di un dispositivo o della finestra dell'app, quindi proietta l'immagine acquisita su un display virtuale che esegue il rendering dell'immagine su un elemento Surface.

Display di un dispositivo reale proiettato su un display virtuale. Contenuti del display virtuale scritti nel "Surface" fornito dall'applicazione.
Figura 1. Schermo reale del dispositivo o finestra dell'app proiettata sul display virtuale. Display virtuale scritto nell'elemento Surface fornito dall'applicazione.

L'applicazione fornisce Surface tramite un elemento MediaRecorder, SurfaceTexture o ImageReader, che consuma i contenuti del display acquisito e consente di gestire le immagini visualizzate su Surface in tempo reale. Puoi salvare le immagini come registrazione o trasmetterle alla TV o a un altro dispositivo.

Display reale

Avvia una sessione di proiezione multimediale ottenendo un token che conceda alla tua app la capacità di acquisire i contenuti del display o della finestra dell'app del dispositivo. Il token è rappresentato da un'istanza della classe MediaProjection.

Utilizza il metodo getMediaProjection() del servizio di sistema MediaProjectionManager per creare un'istanza MediaProjection quando inizi una nuova attività. Avvia l'attività con un intent dal metodo createScreenCaptureIntent() per specificare un'operazione di acquisizione schermata:

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<Intent> startMediaProjection = registerForActivityResult(
    new StartActivityForResult(),
    result -> {
        if (result.getResultCode() == Activity.RESULT_OK) {
            mediaProjection[0] = mediaProjectionManager
                .getMediaProjection(result.getResultCode(), result.getData());
        }
    }
);

startMediaProjection.launch(mediaProjectionManager.createScreenCaptureIntent());

Display virtuale

Il fulcro di una proiezione multimediale è il display virtuale, che crei chiamando createVirtualDisplay() su un'istanza 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);

I parametri width e height specificano le dimensioni del display virtuale. Per ottenere i valori per larghezza e altezza, utilizza le API WindowMetrics introdotte in Android 11 (livello API 30). Per maggiori dettagli, consulta la sezione Dimensioni proiezione contenuti multimediali.

Surface

Ridimensiona la superficie di proiezione dei contenuti multimediali per produrre un output con la risoluzione appropriata. Utilizza dimensioni grandi (bassa risoluzione) per la trasmissione dello schermo alle TV o ai monitor dei computer e dimensioni ridotte (alta risoluzione) per la registrazione dal display dei dispositivi.

A partire da Android 12L (livello API 32), durante il rendering dei contenuti acquisiti sulla superficie, il sistema scala i contenuti in modo uniforme, mantenendo le proporzioni, in modo che entrambe le dimensioni (larghezza e altezza) siano uguali o inferiori alle dimensioni corrispondenti della superficie. I contenuti acquisiti vengono quindi centrati sulla superficie.

L'approccio alla scalabilità di Android 12L migliora la trasmissione dello schermo ai televisori e ad altri display di grandi dimensioni massimizzando le dimensioni dell'immagine superficiale, garantendo al contempo le proporzioni corrette.

Autorizzazione per i servizi in primo piano

Se la tua app ha come target Android 14 o versioni successive, il file manifest dell'app deve includere una dichiarazione delle autorizzazioni per il tipo di servizio in primo piano 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>

Avvia il servizio di proiezione di contenuti multimediali con una chiamata a startForeground().

Se non specifichi il tipo di servizio in primo piano nella chiamata, per impostazione predefinita sarà un numero intero a bit dei tipi di servizio in primo piano definiti nel manifest. Se il manifest non specifica alcun tipo di servizio, il sistema genera MissingForegroundServiceTypeException.

L'app deve richiedere il consenso dell'utente prima di ogni sessione di proiezione di contenuti multimediali. Per sessione si intende una singola chiamata a createVirtualDisplay(). Un token MediaProjection deve essere utilizzato una sola volta per effettuare la chiamata.

Su Android 14 o versioni successive, il metodo createVirtualDisplay() genera SecurityException se la tua app esegue una delle seguenti operazioni:

  • Passa un'istanza Intent restituita da createScreenCaptureIntent() a getMediaProjection() più di una volta
  • Richiama createVirtualDisplay() più volte nella stessa istanza MediaProjection

Dimensioni di proiezione dei contenuti multimediali

Una proiezione multimediale può acquisire l'intero display del dispositivo o una finestra dell'app indipendentemente dalla modalità windowing.

Dimensione iniziale

Con la proiezione di contenuti multimediali a schermo intero, l'app deve determinare le dimensioni dello schermo del dispositivo. Nella condivisione schermo dell'app, l'app non sarà in grado di determinare le dimensioni del display acquisito finché l'utente non avrà selezionato la regione di acquisizione. Quindi, le dimensioni iniziali di qualsiasi proiezione di contenuti multimediali corrispondono alle dimensioni dello schermo del dispositivo.

Utilizza il metodo della piattaforma WindowManager getMaximumWindowMetrics() per restituire un oggetto WindowMetrics per lo schermo del dispositivo anche se l'app host per la proiezione di contenuti multimediali è in modalità multi-finestra e occupa solo una parte del display.

Per una compatibilità fino al livello API 14, usa il metodo WindowMetricsCalculator computeMaximumWindowMetrics() della libreria Jetpack WindowManager.

Richiama il metodo WindowMetrics getBounds() per ottenere la larghezza e l'altezza del display del dispositivo.

Modifiche alle dimensioni

Le dimensioni della proiezione di contenuti multimediali possono cambiare quando il dispositivo viene ruotato o quando l'utente seleziona una finestra dell'app come regione di acquisizione nella condivisione schermo dell'app. La proiezione dei contenuti multimediali potrebbe essere modificata se i contenuti acquisiti hanno dimensioni diverse rispetto alle metriche di finestra massima ottenute al momento della configurazione della proiezione multimediale.

Per assicurarti che la proiezione dei contenuti multimediali sia in linea con le dimensioni dei contenuti acquisiti per qualsiasi regione acquisita e nelle rotazioni dei dispositivi, utilizza il callback onCapturedContentResize() per ridimensionare l'acquisizione. Per ulteriori informazioni, consulta la sezione Personalizzazione, riportata di seguito.

Funzionalità di

La tua app può personalizzare l'esperienza utente di proiezione di contenuti multimediali con le seguenti API di MediaProjection.Callback:

  • onCapturedContentVisibilityChanged(): consente all'app host (l'app che ha avviato la proiezione di contenuti multimediali) di mostrare o nascondere i contenuti condivisi.

    Utilizza questo callback per personalizzare l'interfaccia utente della tua app a seconda che la regione acquisita sia visibile all'utente. Ad esempio, se l'app è visibile all'utente e mostra i contenuti acquisiti nell'interfaccia utente dell'app e l'app acquisita è visibile anche all'utente (come indicato tramite questo callback), l'utente vede gli stessi contenuti due volte. Utilizza il callback per aggiornare l'interfaccia utente dell'app in modo da nascondere i contenuti acquisiti e liberare spazio di layout nell'app per gli altri contenuti.

  • onCapturedContentResize(): consente all'app host di modificare le dimensioni della proiezione di contenuti multimediali sul display virtuale e sulla proiezione di contenuti multimediali Surface in base alle dimensioni della regione di visualizzazione acquisita.

    Si attiva ogni volta che i contenuti acquisiti (una singola finestra dell'app o l'intero display del dispositivo) cambiano dimensione (a causa della rotazione del dispositivo o dell'ingresso dell'app acquisita in una modalità windowing diversa). Utilizza questa API per ridimensionare sia il display virtuale sia la superficie per assicurarti che le proporzioni corrispondano ai contenuti acquisiti e che l'acquisizione non sia letterbox.

Recupero delle risorse

La tua app deve registrare il callback MediaProjection onStop() per rilasciare risorse in possesso dell'app, ad esempio il display virtuale e la superficie di proiezione.

Il callback viene chiamato quando termina la proiezione di contenuti multimediali o quando l'utente non concede il consenso per continuare una sessione di acquisizione.

Se la tua app non registra il callback e l'utente non concede il consenso alla sessione di proiezione multimediale, le chiamate a createVirtualDisplay() generano IllegalStateException.

Disattiva

Android 14 o versioni successive abilita la condivisione schermo delle app per impostazione predefinita. Ogni sessione di proiezione di contenuti multimediali offre agli utenti la possibilità di condividere una finestra dell'app o l'intero display.

La tua app può disattivare la condivisione schermo delle app chiamando il metodo createScreenCaptureIntent(MediaProjectionConfig) con un argomento MediaProjectionConfig restituito da una chiamata a createConfigForDefaultDisplay().

Una chiamata a createScreenCaptureIntent(MediaProjectionConfig) con un argomento MediaProjectionConfig restituito da una chiamata a createConfigForUserChoice() equivale al comportamento predefinito, ovvero una chiamata a createScreenCaptureIntent().

App ridimensionabili

Rendi sempre ridimensionabili le app di proiezione di contenuti multimediali (resizeableActivity="true"). Le app ridimensionabili supportano le modifiche alla configurazione del dispositivo e la modalità multi-finestra (vedi Supporto della modalità multi-finestra).

Se l'app non è ridimensionabile, deve eseguire una query sui limiti di visualizzazione dal contesto di una finestra e utilizzare getMaximumWindowMetrics() per recuperare il valore WindowMetrics dell'area di visualizzazione massima disponibile per l'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();

Risorse aggiuntive

Per ulteriori informazioni sulla proiezione di contenuti multimediali, consulta Acquisire la riproduzione video e audio.