Aggiungere video utilizzando la funzionalità Picture in picture (PIP)

A partire da Android 8.0 (livello API 26), Android consente l'avvio delle attività in modalità Picture in picture (PIP). PIP è un tipo speciale di modalità multi-finestra usato soprattutto per la riproduzione di video. Consente all'utente di guardare un video in una piccola finestra bloccata in un angolo dello schermo mentre naviga tra le app o sfoglia i contenuti sulla schermata principale.

PIP sfrutta le API multi-finestra rese disponibili in Android 7.0 per fornire la finestra overlay video fissata. Per aggiungere PIP alla tua app, devi registrare le attività che supportano PIP, passare alla modalità PIP per le tue attività, se necessario, e assicurarti che gli elementi UI siano nascosti e che la riproduzione del video continui quando l'attività è in modalità PIP.

La finestra PIP viene visualizzata nel livello più alto dello schermo, in un angolo scelto dal sistema.

PIP è supportato anche sui dispositivi con sistema operativo Android TV compatibili con Android 14 (livello API 34) o versioni successive. Sebbene esistano molte somiglianze, ci sono ulteriori considerazioni quando si utilizza PIP sulla TV.

In che modo gli utenti possono interagire con la finestra PIP

Gli utenti possono trascinare la finestra PIP in un'altra posizione. A partire da Android 12, gli utenti possono anche:

  • Tocca una volta la finestra per visualizzare un pulsante di attivazione/disattivazione a schermo intero, un pulsante di chiusura, un pulsante Impostazioni e le azioni personalizzate fornite dalla tua app (ad esempio, i controlli di riproduzione).

  • Tocca due volte la finestra per passare dalla dimensione PIP attuale a quella massima o minima in PIP. Ad esempio, se tocchi due volte una finestra ingrandita la dimensione PIP viene ridotta al minimo. Viene applicato anche il contrario.

  • Conserva la finestra trascinandola verso il bordo sinistro o destro. Per rimuovere la finestra dall'elenco filtri, tocca la parte visibile della finestra nascosta o trascinala fuori.

  • Ridimensiona la finestra PIP usando le dita per eseguire lo zoom.

L'app controlla quando viene attivata la modalità PIP per l'attività corrente. Ecco alcuni esempi:

  • Un'attività può attivare la modalità PIP quando l'utente tocca il pulsante Home o scorre verso l'alto per andare alla schermata Home. In questo modo, Google Maps continua a mostrare le indicazioni stradali mentre l'utente esegue contemporaneamente un'altra attività.

  • L'app può spostare un video in modalità PIP quando l'utente torna indietro dal video per sfogliare altri contenuti.

  • La tua app può attivare la modalità PIP di un video mentre un utente guarda la fine di un episodio di contenuti. Nella schermata principale vengono mostrate informazioni promozionali o di riepilogo sulla puntata successiva della serie.

  • La tua app può offrire agli utenti un modo per mettere in coda contenuti aggiuntivi mentre guardano un video. La riproduzione del video prosegue in modalità PIP mentre nella schermata principale viene mostrata un'attività di selezione dei contenuti.

Dichiara il supporto dei PIP

Per impostazione predefinita, il sistema non supporta automaticamente PIP per le app. Se vuoi supportare PIP nella tua app, registra la tua attività video nel file manifest impostando android:supportsPictureInPicture su true. Inoltre, specifica che la tua attività gestisce le modifiche alla configurazione del layout in modo che non venga riavviata quando vengono apportate modifiche al layout durante le transizioni dalla modalità PIP.

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

Passa alla modalità PIP della tua attività

A partire da Android 12, puoi passare alla modalità PIP impostando il flag setAutoEnterEnabled su true. Con questa impostazione, un'attività passa automaticamente alla modalità PIP in base alle esigenze senza dover chiamare esplicitamente enterPictureInPictureMode() in onUserLeaveHint. Questo ha l'ulteriore vantaggio di offrire transizioni molto più fluide. Per maggiori dettagli, vedi Rendere più semplici le transizioni alla modalità PIP dalla navigazione tramite gesti.

Se scegli come target Android 11 o versioni precedenti, un'attività deve chiamare enterPictureInPictureMode() per passare alla modalità PIP. Ad esempio, il seguente codice consente di passare alla modalità PIP di un'attività quando l'utente fa clic su un pulsante dedicato nell'interfaccia utente dell'app:

Kotlin

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

Java

@Override
public void onActionClicked(Action action) {
    if (action.getId() == R.id.lb_control_picture_in_picture) {
        getActivity().enterPictureInPictureMode();
        return;
    }
    ...
}

Potresti voler includere una logica che trasferisca un'attività in modalità PIP anziché andare in background. Ad esempio, Google Maps passa alla modalità PIP se l'utente preme il pulsante Home o Recenti mentre l'app naviga. Puoi acquisire questa richiesta eseguendo l'override di onUserLeaveHint():

Kotlin

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

Java

@Override
public void onUserLeaveHint () {
    if (iWantToBeInPipModeNow()) {
        enterPictureInPictureMode();
    }
}

Consigliato: offri agli utenti un'esperienza di transizione PIP ottimale

Android 12 ha aggiunto miglioramenti estetici significativi alle transizioni animate tra finestre PIP e a schermo intero. Ti consigliamo vivamente di implementare tutte le modifiche applicabili, che vengono poi implementate automaticamente su schermi di grandi dimensioni, come pieghevoli e tablet, senza richiedere ulteriori operazioni.

Se la tua app non include aggiornamenti applicabili, le transizioni PIP sono comunque funzionali, ma le animazioni sono meno curate. Ad esempio, il passaggio dalla modalità a schermo intero alla modalità PIP può far scomparire la finestra PIP durante la transizione, per poi riapparire al termine della transizione.

Queste modifiche includono quanto segue.

  • Rendi più fluide le transizioni alla modalità PIP dalla navigazione tramite gesti
  • Imposta un valore sourceRectHint corretto per entrare e uscire dalla modalità PIP
  • Disattivazione del ridimensionamento continuo per i contenuti non video

Fai riferimento all'esempio di Kotlin PictureInPicture per Android come riferimento per consentire un'esperienza di transizione ottimale.

Rendi più fluide le transizioni alla modalità PIP dalla navigazione tramite gesti

A partire da Android 12, il flag setAutoEnterEnabled offre un'animazione molto fluida per passare ai contenuti video in modalità PIP utilizzando la navigazione tramite gesti, ad esempio quando si scorre verso l'alto dalla modalità a schermo intero.

Completa i seguenti passaggi per apportare questa modifica e utilizza questo esempio come riferimento:

  1. Usa setAutoEnterEnabled per creare PictureInPictureParams.Builder:

    Kotlin

    setPictureInPictureParams(PictureInPictureParams.Builder()
        .setAspectRatio(aspectRatio)
        .setSourceRectHint(sourceRectHint)
        .setAutoEnterEnabled(true)
        .build())
    

    Java

    setPictureInPictureParams(new PictureInPictureParams.Builder()
        .setAspectRatio(aspectRatio)
        .setSourceRectHint(sourceRectHint)
        .setAutoEnterEnabled(true)
        .build());
    
  2. Chiama setPictureInPictureParams in anticipo con la versione aggiornata di PictureInPictureParams. L'app non attende il callback onUserLeaveHint (come avrebbe fatto in Android 11).

    Ad esempio, potresti voler chiamare setPictureInPictureParams alla prima riproduzione e a qualsiasi riproduzione successiva se le proporzioni vengono modificate.

  3. Chiama setAutoEnterEnabled(false), ma solo quando è necessario. Ad esempio, probabilmente non vorrai inserire PIP se la riproduzione attuale è in pausa.

Imposta un valore sourceRectHint corretto per entrare e uscire dalla modalità PIP

A partire dall'introduzione di PIP in Android 8.0, setSourceRectHint ha indicato l'area dell'attività visibile dopo la transizione in Picture in picture, ad esempio i limiti della visualizzazione video in un video player.

Con Android 12, il sistema utilizza sourceRectHint per implementare un'animazione molto più fluida sia in modalità PIP sia quando si esce.

Per impostare correttamente sourceRectHint per entrare e uscire dalla modalità PIP:

  1. Costruisci PictureInPictureParams usando i limiti appropriati come sourceRectHint. Consigliamo anche di allegare al video player un listener delle modifiche del layout:

    Kotlin

    val mOnLayoutChangeListener =
    OnLayoutChangeListener { v: View?, oldLeft: Int,
            oldTop: Int, oldRight: Int, oldBottom: Int, newLeft: Int, newTop:
            Int, newRight: Int, newBottom: Int ->
        val sourceRectHint = Rect()
        mYourVideoView.getGlobalVisibleRect(sourceRectHint)
        val builder = PictureInPictureParams.Builder()
            .setSourceRectHint(sourceRectHint)
        setPictureInPictureParams(builder.build())
    }
    
    mYourVideoView.addOnLayoutChangeListener(mOnLayoutChangeListener)
    

    Java

    private final View.OnLayoutChangeListener mOnLayoutChangeListener =
            (v, oldLeft, oldTop, oldRight, oldBottom, newLeft, newTop, newRight,
            newBottom) -> {
        final Rect sourceRectHint = new Rect();
        mYourVideoView.getGlobalVisibleRect(sourceRectHint);
        final PictureInPictureParams.Builder builder =
            new PictureInPictureParams.Builder()
                .setSourceRectHint(sourceRectHint);
        setPictureInPictureParams(builder.build());
    };
    
    mYourVideoView.addOnLayoutChangeListener(mOnLayoutChangeListener);
    
  2. Se necessario, aggiorna sourceRectHint prima che il sistema avvii la transizione di uscita. Quando il sistema sta per uscire dalla modalità PIP, la gerarchia delle visualizzazioni dell'attività viene organizzata in base alla configurazione di destinazione (ad esempio, a schermo intero). L'app può collegare un listener delle modifiche al layout alla vista principale o alla vista di destinazione (ad esempio la vista del video player) per rilevare l'evento e aggiornare sourceRectHint prima dell'inizio dell'animazione.

    Kotlin

    // 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()
            )
        }
    }
    
    

    Java

    // Listener is called right after the user exits PiP but before
    // animating.
    playerView.addOnLayoutChangeListener((v, 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.
            final Rect sourceRectHint = new Rect();
            playerView.getGlobalVisibleRect(sourceRectHint);
            setPictureInPictureParams(
                new PictureInPictureParams.Builder()
                    .setSourceRectHint(sourceRectHint)
                    .build());
        }
    });
    
    

Disattiva il ridimensionamento continuo per i contenuti non video

Android 12 aggiunge il flag setSeamlessResizeEnabled, che fornisce un'animazione di dissolvenza incrociata molto fluida quando si ridimensionano contenuti non video nella finestra PIP. In precedenza, il ridimensionamento di contenuti non video in una finestra PIP poteva creare artefatti visivi fastidiosi.

Per disattivare il ridimensionamento continuo per i contenuti non video:

Kotlin

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

Java

setPictureInPictureParams(new PictureInPictureParams.Builder()
    .setSeamlessResizeEnabled(false)
    .build());

Gestire l'UI durante la modalità PIP

Quando l'attività entra o esce dalla modalità PIP, il sistema chiama Activity.onPictureInPictureModeChanged() o Fragment.onPictureInPictureModeChanged().

Devi eseguire l'override di questi callback per riorganizzare gli elementi dell'interfaccia utente dell'attività. Tieni presente che in modalità PIP la tua attività viene visualizzata in una piccola finestra. Gli utenti non possono interagire con gli elementi UI della tua app in modalità PIP e i dettagli dei piccoli elementi UI potrebbero essere difficili da vedere. Le attività di riproduzione di video con UI minimale forniscono la migliore esperienza utente.

Se l'app deve fornire azioni personalizzate per PIP, consulta Aggiungere controlli in questa pagina. Rimuovi altri elementi UI prima che la tua attività entri in PIP e ripristinali quando l'attività diventa di nuovo a schermo intero:

Kotlin

override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean,
                                           newConfig: Configuration) {
    if (isInPictureInPictureMode) {
        // Hide the full-screen UI (controls, etc.) while in PiP mode.
    } else {
        // Restore the full-screen UI.
    }
}

Java

@Override
public void onPictureInPictureModeChanged (boolean isInPictureInPictureMode, Configuration newConfig) {
    if (isInPictureInPictureMode) {
        // Hide the full-screen UI (controls, etc.) while in PiP mode.
    } else {
        // Restore the full-screen UI.
        ...
    }
}

Aggiungi controlli

La finestra PIP può mostrare i controlli quando l'utente apre il menu della finestra (toccando la finestra su un dispositivo mobile o selezionando il menu dal telecomando della TV).

Se un'app ha una sessione multimediale attiva, vengono visualizzati i controlli di riproduzione, pausa, avanti e precedente.

Puoi anche specificare esplicitamente le azioni personalizzate creando PictureInPictureParams con PictureInPictureParams.Builder.setActions() prima di attivare la modalità PIP e passare i parametri quando entri in modalità PIP utilizzando enterPictureInPictureMode(android.app.PictureInPictureParams) o setPictureInPictureParams(android.app.PictureInPictureParams). Fai attenzione. Se provi ad aggiungere più di getMaxNumPictureInPictureActions(), riceverai solo il numero massimo.

La riproduzione del video continua in modalità PIP

Quando la tua attività passa a PIP, il sistema imposta l'attività nello stato di pausa e chiama il metodo onPause() dell'attività. Se l'attività viene messa in pausa durante il passaggio alla modalità PIP, la riproduzione video non deve essere messa in pausa.

In Android 7.0 e versioni successive, devi mettere in pausa e riprendere la riproduzione dei video quando il sistema chiama onStop() e onStart() per la tua attività. In questo modo puoi evitare di dover controllare se la tua app è in modalità PIP in onPause() e di continuare esplicitamente la riproduzione.

Se non hai impostato il flag setAutoEnterEnabled su true e devi mettere in pausa la riproduzione nell'implementazione di onPause(), controlla la modalità PIP chiamando isInPictureInPictureMode() e gestisci la riproduzione in modo appropriato. Ecco alcuni esempi:

Kotlin

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

Java

@Override
public void onPause() {
    // If called while in PiP mode, do not pause playback
    if (isInPictureInPictureMode()) {
        // Continue playback
        ...
    } else {
        // Use existing playback logic for paused Activity behavior.
        ...
    }
}

Quando l'attività torna alla modalità a schermo intero dalla modalità PIP, il sistema riprende l'attività e chiama il tuo metodo onResume().

Usare una singola attività di riproduzione per PIP

Nella tua app, un utente potrebbe selezionare un nuovo video durante la ricerca di contenuti nella schermata principale, mentre un'attività di riproduzione video è in modalità PIP. Riproduci il nuovo video nell'attività di riproduzione esistente in modalità a schermo intero, invece di avviare una nuova attività che potrebbe confondere l'utente.

Per assicurarti che una singola attività venga utilizzata per le richieste di riproduzione video e attivata o disattivata in base alle esigenze, imposta il valore android:launchMode dell'attività su singleTask nel file manifest:

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

Nella tua attività, sostituisci onNewIntent() e gestisci il nuovo video, interrompendo la riproduzione video esistente se necessario.

best practice

PIP potrebbe essere disattivato sui dispositivi con poca RAM. Prima che l'app utilizzi PIP, assicurati che sia disponibile chiamando il numero hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE).

PIP è destinato ad attività che riproducono video a schermo intero. Quando imposti le tue attività in modalità PIP, evita di mostrare contenuti ad eccezione dei contenuti video. Monitora quando la tua attività entra in modalità PIP e nascondi gli elementi dell'interfaccia utente, come descritto in Gestione dell'interfaccia utente durante PIP.

Quando un'attività è in modalità PIP, per impostazione predefinita non viene attivato l'input. Per ricevere eventi di input in modalità PIP, utilizza MediaSession.setCallback(). Per ulteriori informazioni sull'utilizzo di setCallback(), vedi Visualizzare una scheda Now Playing.

Quando l'app è in modalità PIP, la riproduzione del video nella finestra PIP può causare interferenze audio con un'altra app, ad esempio un'app di lettore musicale o un'app di ricerca vocale. Per evitare che questo accada, richiedi il focus audio quando avvii la riproduzione del video e gestisci le notifiche di modifica dello stato attivo dell'audio, come descritto nella sezione Gestione dello stato attivo audio. Se ricevi una notifica di perdita del focus audio in modalità PIP, metti in pausa o interrompi la riproduzione del video.

Quando l'app sta per attivare PIP, tieni presente che solo l'attività principale entra in modalità Picture in picture. In alcune situazioni, ad esempio sui dispositivi multi-finestra, è possibile che l'attività riportata di seguito venga ora visualizzata e sia nuovamente visibile insieme all'attività PIP. Dovresti gestire questo caso di conseguenza, inclusa l'attività riportata di seguito per ricevere un callback onResume() o onPause(). È anche possibile che l'utente interagisca con l'attività. Ad esempio, se visualizzi un'attività relativa all'elenco dei video e l'attività video in riproduzione in modalità PIP, l'utente potrebbe selezionare un nuovo video dall'elenco e l'attività PIP dovrebbe aggiornarsi di conseguenza.

Codice campione aggiuntivo

Per scaricare un'app di esempio scritta in Android, vedi Esempio di Picture in picture. Per scaricare un'app di esempio scritta in Kotlin, vedi Esempio di PictureInPicture per Android (Kotlin).