Le API android.media.projection introdotte in Android 5 (livello API 21)
consentono di acquisire i contenuti del display di un dispositivo come flusso multimediale che
puoi riprodurre, registrare o trasmettere ad altri dispositivi, come le TV.
Android 14 (livello API 34) introduce la condivisione dello schermo delle app, che consente agli utenti di condividere una singola finestra dell'app anziché l'intero schermo del dispositivo, indipendentemente dalla modalità finestra. La condivisione della schermata dell'app esclude la barra di stato, la barra di navigazione, le notifiche e altri elementi della UI di sistema dal display condiviso, anche quando la condivisione della schermata dell'app viene utilizzata per acquisire un'app a schermo intero. Vengono condivisi solo i contenuti dell'app selezionata.
La condivisione dello schermo delle app garantisce la privacy degli utenti, aumenta la produttività 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 di un display del dispositivo o di una finestra dell'app e
proietta l'immagine acquisita su un display virtuale che la visualizza su
un Surface.
Surface fornito dall'applicazione.
L'applicazione fornisce Surface tramite un MediaRecorder,
SurfaceTexture o ImageReader, che utilizza i contenuti del display acquisito e ti consente di gestire le immagini visualizzate su Surface in tempo reale. Puoi salvare le immagini come registrazione o trasmetterle a una TV o a un altro dispositivo.
Display reale
Avvia una sessione di proiezione multimediale ottenendo un token che concede alla tua app la possibilità di acquisire i contenuti del display del dispositivo o della finestra dell'app. Il token
è rappresentato da un'istanza della classe MediaProjection.
Utilizza il metodo getMediaProjection() del servizio di sistema MediaProjectionManager
per creare un'istanza MediaProjection quando avvii una nuova
attività. Avvia l'attività con un intent dal metodo
createScreenCaptureIntent() per specificare un'operazione di acquisizione dello schermo:
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];
ActivityResultLauncherstartMediaProjection = registerForActivityResult( new StartActivityForResult(), result -> { if (result.getResultCode() == Activity.RESULT_OK) { mediaProjection[0] = mediaProjectionManager .getMediaProjection(result.getResultCode(), result.getData()); } } );
startMediaProjection.launch(mediaProjectionManager.createScreenCaptureIntent());
Display virtuale
L'elemento centrale 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 di larghezza e altezza, utilizza le API WindowMetrics introdotte in Android 11 (livello API 30). Per maggiori dettagli, consulta la sezione Dimensioni della proiezione
multimediale.
Surface
Dimensiona la superficie di proiezione multimediale per produrre output nella risoluzione appropriata. Rendi la superficie grande (bassa risoluzione) per il mirroring dello schermo su TV o monitor del computer e piccola (alta risoluzione) per la registrazione del display del dispositivo.
A partire da Android 12L (livello API 32), durante il rendering dei contenuti acquisiti sulla superficie, il sistema ridimensiona i contenuti in modo uniforme, mantenendo le proporzioni, in modo che entrambe le dimensioni dei contenuti (larghezza e altezza) siano uguali o inferiori alle dimensioni corrispondenti della superficie. I contenuti acquisiti vengono poi centrati sulla superficie.
L'approccio di scalabilità di Android 12L migliora la trasmissione dello schermo su televisori e altri display di grandi dimensioni massimizzando le dimensioni dell'immagine della superficie e garantendo le proporzioni corrette.
Autorizzazione per il servizio in primo piano
Se la tua app ha come target Android 14 o versioni successive, il manifest dell'app deve includere una
dichiarazione di autorizzazione 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 multimediale con una chiamata a startForeground().
Se non specifichi il tipo di servizio in primo piano nella chiamata, il tipo viene impostato
per impostazione predefinita su un numero intero bit 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.
Consenso degli utenti
La tua app deve richiedere il consenso dell'utente prima di ogni sessione di proiezione multimediale. Una
sessione è 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 un'eccezione
SecurityException se la tua app esegue una delle seguenti operazioni:
- Passa un'istanza
Intentrestituita dacreateScreenCaptureIntent()agetMediaProjection()più di una volta - Chiama
createVirtualDisplay()più di una volta sulla stessa istanza diMediaProjection
Dimensioni della proiezione multimediale
Una proiezione multimediale può acquisire l'intero display del dispositivo o una finestra dell'app indipendentemente dalla modalità di visualizzazione a finestre.
Dimensione iniziale
Con la proiezione multimediale a schermo intero, l'app deve determinare le dimensioni dello schermo del dispositivo. Nella condivisione della schermata dell'app, la tua app non sarà in grado di determinare le dimensioni del display acquisito finché l'utente non avrà selezionato la regione di acquisizione. Pertanto, la dimensione iniziale di qualsiasi proiezione multimediale è la dimensione 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 di proiezione dei contenuti multimediali è in modalità multi-finestra e occupa solo una parte del display.
Per la compatibilità fino al livello API 14, utilizza il metodo WindowMetricsCalculator
computeMaximumWindowMetrics() della libreria WindowManager
Jetpack.
Chiama il metodo WindowMetrics getBounds() per ottenere la larghezza e l'altezza
del display del dispositivo.
Modifiche alle dimensioni
Le dimensioni della proiezione multimediale possono cambiare quando il dispositivo viene ruotato o quando l'utente seleziona una finestra dell'app come regione di acquisizione nella condivisione della schermata dell'app. La proiezione multimediale potrebbe essere in formato letterbox se i contenuti acquisiti hanno dimensioni diverse rispetto alle metriche massime della finestra ottenute durante la configurazione della proiezione multimediale.
Per garantire che la proiezione multimediale sia allineata con precisione alle dimensioni dei contenuti acquisiti per qualsiasi regione acquisita e in tutte le rotazioni del dispositivo, utilizza il callback onCapturedContentResize() per ridimensionare l'acquisizione. Per saperne di più, consulta la sezione Personalizzazione di seguito.
Personalizzazione
La tua app può personalizzare l'esperienza utente di proiezione multimediale con le seguenti API
MediaProjection.Callback:
onCapturedContentVisibilityChanged(): consente all'app host (l'app che ha avviato la proiezione multimediale) di mostrare o nascondere i contenuti condivisi.Utilizza questo callback per personalizzare la UI della tua app in base alla visibilità della regione acquisita per l'utente. Ad esempio, se la tua app è visibile all'utente e mostra i contenuti acquisiti all'interno della relativa UI e l'app acquisita è visibile anche all'utente (come indicato da questo callback), l'utente vede gli stessi contenuti due volte. Utilizza il callback per aggiornare l'interfaccia utente della tua app in modo da nascondere i contenuti acquisiti e liberare spazio nel layout dell'app per altri contenuti.
onCapturedContentResize(): consente all'app host di modificare le dimensioni della proiezione multimediale sul display virtuale e della proiezione multimedialeSurfacein base alle dimensioni della regione di visualizzazione acquisita.Attivato ogni volta che le dimensioni dei contenuti acquisiti, ovvero una singola finestra dell'app o l'intero display del dispositivo, cambiano (a causa della rotazione del dispositivo o dell'app acquisita che entra in una modalità di visualizzazione a finestre 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 in formato letterbox.
Recupero delle risorse
La tua app deve registrare il callback MediaProjection onStop() per
ricevere una notifica quando la sessione di proiezione multimediale viene interrotta e non è più valida. Quando
la sessione viene interrotta, l'app deve rilasciare le risorse che contiene,
come il display virtuale e la superficie di proiezione. Una sessione di proiezione multimediale interrotta non può più creare un nuovo display virtuale, anche se la tua app non ha mai creato un display virtuale per quella proiezione multimediale.
Il sistema richiama il callback quando la proiezione multimediale termina. Questa interruzione può avvenire per diversi motivi, tra cui:
- l'utente interrompe la sessione utilizzando la UI dell'app o il chip della barra di stato di proiezione dei contenuti multimediali del sistema
- lo schermo è in fase di blocco
- un'altra sessione di proiezione multimediale inizia
- il processo dell'app viene interrotto
Se la tua app non registra il callback, qualsiasi chiamata a createVirtualDisplay()
genera IllegalStateException.
Disattiva
Android 14 o versioni successive attiva la condivisione della schermata dell'app per impostazione predefinita. Ogni sessione di proiezione dei contenuti multimediali offre agli utenti la possibilità di condividere una finestra dell'app o l'intero display.
La tua app può disattivare la condivisione della schermata dell'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() è uguale al comportamento predefinito, ovvero una chiamata a createScreenCaptureIntent().
App ridimensionabili
Rendi sempre ridimensionabili le app di proiezione multimediale
(resizeableActivity="true"). Le app ridimensionabili supportano le modifiche alla Configurazione dispositivo
e la modalità multi-finestra (vedi Supporto della funzione Multi-finestra).
Se la tua app non è ridimensionabile, deve eseguire una query sui limiti di visualizzazione da un contesto finestra e utilizzare getMaximumWindowMetrics() per recuperare 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();
Chip della barra di stato e arresto automatico
Gli exploit di proiezione dello schermo espongono i dati utente privati, come le informazioni finanziarie, perché gli utenti non si rendono conto che lo schermo del loro dispositivo viene condiviso.
Per le app in esecuzione su dispositivi con Android 15 QPR1 o versioni successive, un chip della barra di stato grande e in evidenza avvisa gli utenti di eventuali proiezioni dello schermo in corso. Gli utenti possono toccare il chip per impedire la condivisione, la trasmissione o la registrazione della schermata. Inoltre, la proiezione dello schermo si interrompe automaticamente quando lo schermo del dispositivo è bloccato.
Verifica la disponibilità del chip della barra di stato della proiezione multimediale avviando la condivisione dello schermo, la trasmissione o la registrazione. Il chip dovrebbe apparire nella barra di stato.
Per assicurarti che l'app rilasci le risorse e aggiorni la relativa UI quando la proiezione dello schermo viene interrotta dall'interazione dell'utente con il chip della barra di stato o dall'attivazione della schermata di blocco, procedi nel seguente modo:
Crea un'istanza di
MediaProjection.Callback.Implementa il metodo di callback
onStop(). Il metodo viene chiamato quando la proiezione dello schermo si interrompe. Rilascia tutte le risorse detenute dalla tua app e aggiorna la UI dell'app in base alle necessità.
Per testare il callback, tocca il chip della barra di stato o blocca lo schermo del dispositivo per interrompere
la proiezione dello schermo. Verifica che il metodo onStop() venga chiamato e che la tua app
risponda come previsto.
Risorse aggiuntive
Per ulteriori informazioni sulla proiezione multimediale, vedi Acquisire la riproduzione di video e audio.