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 principalmente 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 Picture in picture alla tua app, devi registrare le tue attività che supportano questa funzionalità, passare alla modalità PIP in base alle tue esigenze e assicurarti che gli elementi UI siano nascosti e che la riproduzione del video continui quando l'attività è in questa modalità.

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

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 azioni personalizzate fornite dalla tua app (ad esempio i controlli di riproduzione).

  • Tocca due volte la finestra per passare dalla dimensione PIP attuale alla dimensione massima o minima per il PIP; ad esempio, toccando due volte una finestra ingrandita la riduce al minimo; anche il contrario è vero.

  • Nascondere la finestra trascinandola verso il bordo sinistro o destro. Per rimuovere l'elenco dalla finestra, tocca la parte visibile della finestra nascosta o trascinala fuori.

  • Ridimensiona la finestra PIP usando lo zoom pizzicando lo schermo.

L'app controlla quando l'attività corrente entra in modalità PIP. Ecco alcuni esempi:

  • Un'attività può attivare la modalità PIP quando l'utente tocca il pulsante Home o scorre in alto fino 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ò attivare la modalità PIP quando l'utente torna indietro dal video per sfogliare altri contenuti.

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

  • L'app può fornire agli utenti un modo per mettere in coda altri contenuti mentre guardano un video. La riproduzione del video continua in modalità PIP mentre la schermata principale visualizza un'attività di selezione dei contenuti.

Dichiara il supporto di PIP

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

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

Imposta la tua attività su PIP

A partire da Android 12, puoi impostare la tua attività in modalità PIP impostando il flag setAutoEnterEnabled su true. Con questa impostazione, un'attività passa automaticamente alla modalità PIP secondo necessità senza dover chiamare esplicitamente enterPictureInPictureMode() in onUserLeaveHint. Questo offre il vantaggio aggiuntivo di fornire transizioni molto più fluide. Per maggiori dettagli, consulta Rendere più fluide le transizioni alla modalità PIP dalla navigazione tramite gesti.

Se hai scelto come target Android 11 o versioni precedenti, un'attività deve chiamare enterPictureInPictureMode() per passare alla modalità PIP. Ad esempio, il seguente codice imposta un'attività in modalità PIP 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 imposti 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 è in uso. Puoi acquisire questo caso eseguendo l'override di onUserLeaveHint():

Kotlin

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

Java

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

Opzione consigliata: offri agli utenti un'esperienza di transizione PIP elegante

Android 12 ha aggiunto miglioramenti estetici significativi alle transizioni animate tra le finestre PIP e a schermo intero. Consigliamo vivamente di implementare tutte le modifiche applicabili. Dopo averle apportate, queste modifiche vengono automaticamente scalate su schermi di grandi dimensioni, come pieghevoli e tablet, senza alcuna ulteriore operazione.

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

Queste modifiche comportano quanto segue.

  • Semplificare le transizioni alla modalità PIP dalla navigazione tramite gesti
  • Impostazione di un valore sourceRectHint corretto per attivare e uscire dalla modalità PIP
  • Disattivazione del ridimensionamento continuo per i contenuti non video

Fai riferimento all'esempio di Android Kotlin PictureInPicture come riferimento per avere un'esperienza di transizione fluida.

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

A partire da Android 12, il flag setAutoEnterEnabled offre un'animazione molto più fluida per il passaggio ai contenuti video in modalità Picture in picture utilizzando la navigazione tramite gesti, ad esempio quando si scorre verso la schermata Home fino alla schermata Home.

Completa i seguenti passaggi per apportare questa modifica e fai riferimento a 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 PictureInPictureParams aggiornata. L'app non attende il callback onUserLeaveHint (come avrebbe fatto in Android 11).

    Ad esempio, se le proporzioni cambiano, potresti voler chiamare setPictureInPictureParams durante la prima riproduzione e qualsiasi successiva.

  3. Chiama setAutoEnterEnabled(false), ma solo se necessario. Ad esempio, probabilmente non vorrai inserire la modalità 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 alla modalità Picture in picture, ad esempio i limiti della visualizzazione del video in un video player.

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

Per impostare correttamente sourceRectHint per l'ingresso e l'uscita dalla modalità PIP:

  1. Costruisci PictureInPictureParams utilizzando i limiti corretti come sourceRectHint. Ti consigliamo anche di collegare 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 inizi la transizione di uscita. Quando il sistema sta per uscire dalla modalità PIP, la gerarchia di visualizzazione dell'attività viene strutturata in base alla configurazione di destinazione (ad esempio, schermo intero). L'app può collegare un listener di modifiche del layout alla propria vista principale o a quella di destinazione (ad esempio la visualizzazione del video player) per rilevare l'evento e aggiornare il valore 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 più fluida durante il ridimensionamento di contenuti non video nella finestra PIP. In precedenza, il ridimensionamento di contenuti non video in una finestra PIP poteva creare artefatti visivi scioccanti.

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 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 tracciare nuovamente gli elementi UI 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 quando è in modalità PIP e potrebbe essere difficile vedere i dettagli di piccoli elementi dell'interfaccia utente. Le attività di riproduzione video con UI minima offrono la migliore esperienza utente.

Se la tua app deve fornire azioni personalizzate per PIP, consulta Aggiungere controlli in questa pagina. Rimuovi altri elementi dell'interfaccia utente prima che l'attività entri in PIP e ripristinali quando l'attività torna 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 relativo menu (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 superare i parametri quando entri in questa modalità utilizzando enterPictureInPictureMode(android.app.PictureInPictureParams) o setPictureInPictureParams(android.app.PictureInPictureParams). Fai attenzione. Se provi ad aggiungere più di getMaxNumPictureInPictureActions(), otterrai solo il numero massimo.

Proseguimento della riproduzione del video in modalità PIP

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

In Android 7.0 e versioni successive, devi mettere in pausa e riprendere la riproduzione del video quando il sistema chiama i dispositivi onStop() e onStart() dell'attività. In questo modo, non dovrai controllare se la tua app è in modalità PIP in onPause() e continuare esplicitamente la riproduzione.

Se non hai impostato il flag setAutoEnterEnabled su true e devi mettere in pausa la riproduzione nell'implementazione onPause(), verifica 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 la tua attività disattiva la modalità PIP e torna alla modalità a schermo intero, il sistema riprende la tua attività e richiama il tuo metodo onResume().

Usare una singola attività di riproduzione per PIP

Nella tua app, un utente può selezionare un nuovo video quando sfoglia i contenuti sulla 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, anziché avviare una nuova attività che potrebbe confondere l'utente.

Per assicurarti che una singola attività venga utilizzata per le richieste di riproduzione video e venga attivata o disattivata la modalità PIP 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à, esegui l'override di onNewIntent() e gestisci il nuovo video, interrompendo la riproduzione di quelli esistenti, se necessario.

Best practice

La funzionalità PIP potrebbe essere disattivata sui dispositivi con RAM insufficiente. Prima che la tua app utilizzi PIP, verifica che sia disponibile chiamando il numero hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE).

PIP è destinato alle attività che prevedono la riproduzione di video a schermo intero. Quando attivi la modalità PIP, non mostrare nulla, 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 la modalità PIP.

Quando un'attività è in modalità PIP, per impostazione predefinita non viene focalizzata sull'input. Per ricevere eventi di input in modalità PIP, usa MediaSession.setCallback(). Per ulteriori informazioni sull'uso di setCallback(), consulta la sezione Visualizzare una scheda Now Playing.

Quando l'app è in modalità PIP, la riproduzione video nella finestra PIP può causare interferenze audio con un'altra app, ad esempio un'app per lettore musicale o un'app di ricerca vocale. Per evitare che ciò accada, richiedi lo stato attivo dell'audio quando inizi a riprodurre il video e gestisci le notifiche relative alla modifica della messa a fuoco audio, come descritto in Gestire la messa a fuoco dell'audio. Se ricevi una notifica relativa alla perdita del focus audio in modalità PIP, metti in pausa o interrompi la riproduzione del video.

Quando la tua app sta per entrare in Picture in picture, tieni presente che solo l'attività principale entra in modalità Picture in picture. In alcune situazioni, ad esempio su dispositivi multi-finestra, è possibile che l'attività indicata di seguito venga ora mostrata e diventi di nuovo visibile insieme all'attività PIP. Devi gestire il caso di conseguenza, inclusa l'attività riportata di seguito come ottenere un callback onResume() o onPause(). È anche possibile che l'utente possa interagire con l'attività. Ad esempio, se visualizzi un'attività di elenco di video e l'attività di riproduzione video in modalità PIP, l'utente può 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 Android PictureInPicture Sample (Kotlin).