La libreria Jetpack Picture in picture (PIP) offre una soluzione semplificata e solida per gli sviluppatori di app per Android per implementare la funzionalità PIP, in particolare per la riproduzione di contenuti multimediali, la comunicazione video e le app di navigazione. Fornendo un'API unificata, la libreria aiuta a eliminare il codice boilerplate, i bug comuni in-app e a migliorare la qualità complessiva dell'esperienza utente PiP.
La libreria Jetpack PiP facilita l'utilizzo delle API PiP esistenti affrontando diverse sfide e incoerenze chiave nell'ecosistema Android:
- Frammentazione del sistema operativo: la libreria gestisce automaticamente le differenze nelle chiamate API PiP tra le varie versioni di Android, ad esempio l'utilizzo di
enterPictureInPictureModeprima di Android 12 eisAutoEnterEnableddopo, in modo che gli sviluppatori non debbano gestire le differenze di versione. - Parametri PiP errati: fornisce una soluzione unificata per impostare correttamente i parametri PiP, ad esempio
setSourceRectHint, per creare animazioni fluide e di alta qualità durante la riproduzione di contenuti multimediali. - Callback dello stato PiP unificati: consolida
onPictureInPictureModeChangedeonPictureInPictureUiStateChangedin un'unica interfaccia di callback unificata (PictureInPictureDelegate.OnPictureInPictureEventListener) per una gestione semplificata dello stato e della UI. - Riduzione del codice boilerplate: la libreria riduce la quantità di codice boilerplate ripetitivo offrendo insiemi predefiniti di
RemoteActionsper casi d'uso comuni, come i controlli di riproduzione e le azioni di videochiamata. - A prova di futuro: altre funzionalità PiP vengono fornite tramite la libreria Jetpack, consentendo agli utenti di accedere a funzionalità aggiuntive con il minimo sforzo.
Workflow di migrazione
Identifica la categoria del caso d'uso dell'app e la logica PiP precedente:
Categorie: riproduzione video, navigazione o videochiamata.
Logica PiP legacy da identificare:
onUserLeaveHintsetAutoEnterEnabledonPictureInPictureModeChangedonPictureInPictureUiStateChangedsetPictureInPictureParams.
2. Configurazione di AndroidManifest
Assicurati che l'attività che entra in PiP dichiari il supporto in AndroidManifest.xml con
il configChanges necessario per evitare riavvii non necessari:
<activity
android:name="VideoActivity" android:supportsPictureInPicture="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">
</activity>
3. Configurazione dell'ambiente
Aggiungi le dipendenze richieste a build.gradle:
dependencies {
implementation("androidx.core:core:1.18.0")
implementation("androidx.activity:activity:1.13.0")
implementation("androidx.core:core-pip:1.0.0-alpha02") }
Utilizza le librerie AndroidX più recenti per le dipendenze e consulta la pagina delle release per informazioni.
4. Selezione e inizializzazione del template
Scegli il modello di implementazione più adatto al caso d'uso dell'app:
- Navigazione e videochiamata:
BasicPictureInPicture; il ridimensionamento continuo non è generalmente supportato e non è necessario un suggerimento per il rettangolo di origine. - Riproduzione video:
VideoPlaybackPictureInPicture; monitora automaticamente i limiti di visualizzazione del player per il suggerimento del rettangolo di origine e attiva il ridimensionamento senza interruzioni per impostazione predefinita.
Per adottare la libreria Jetpack, sostituisci l'implementazione PiP personalizzata esistente con le API della libreria Jetpack. La complessità e il costo dell'adozione variano in base all'implementazione attuale dell'app.
Le sezioni seguenti descrivono alcuni dei casi d'uso tipici di PiP e i passaggi di implementazione necessari:
Navigazione
L'app informa la libreria dello stato attivo o non attivo della navigazione e imposta le proporzioni. La libreria Jetpack si occupa del resto.
Differenze principali:
- Non è necessario distinguere tra l'accesso automatico e l'accesso precedente sul lato app.
- Interfacce di callback consolidate.
- Nuovo builder
PictureInPictureParamsper la compatibilità con le versioni precedenti.
Videochiamata
L'app informa la libreria dello stato attivo o non attivo della chiamata e imposta le proporzioni.
Differenze principali:
- Non è necessario distinguere tra l'accesso automatico e l'accesso precedente sul lato app.
- Interfacce di callback consolidate.
- Nuovo builder
PictureInPictureParamsper la compatibilità con le versioni precedenti. - Icone di azioni standardizzate per le videochiamate.
5. Migrazione del codice
- Logica di ingresso: sostituisci la logica specifica dell'API, ad esempio
setAutoEnterEnabledper Android 12 e versioni successive oonUserLeaveHintper Android 11 e versioni precedenti, consetEnabled. Attiva questo evento ogni volta che lo stato di idoneità alla modalità PIP cambia. - Callback: consolida
onPictureInPictureModeChanged(attivazione/disattivazione del layout) eonPictureInPictureUiStateChanged(animazione/stati) in un callback unificato basato sugli eventionPictureInPictureEvent. - Azioni e parametri: aggiorna i parametri utilizzando
setActionsesetAspectRationell'istanza del template ogni volta che cambiano.
Pattern di implementazione di riferimento
Esempi di implementazioni.
Navigazione e videochiamata
class NavOrVideoCallJpipActivity : ComponentActivity(), PictureInPictureDelegate.OnPictureInPictureEventListener { private lateinit var pictureInPictureImpl: BasicPictureInPicture override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) pictureInPictureImpl = BasicPictureInPicture(this) // BasicPictureInPicture is ideal for Navigation and Video call use cases. pictureInPictureImpl.addOnPictureInPictureEventListener( ContextCompat.getMainExecutor(this), this ) setContent { } } override fun onPictureInPictureEvent( event: PictureInPictureDelegate.Event, config: Configuration? ) { when (event) { PictureInPictureDelegate.Event.ENTERED -> { /* Toggle to PiP layout */ } PictureInPictureDelegate.Event.EXITED -> { /* Toggle to Full-screen layout */ } PictureInPictureDelegate.Event.STASHED -> { /* Optional: PiP is stashed */ } PictureInPictureDelegate.Event.UNSTASHED -> { /* Optional: PiP is unstashed */ } } } }
Riproduzione video
class VideoPlaybackJpipActivity : ComponentActivity(), PictureInPictureDelegate.OnPictureInPictureEventListener { private lateinit var pictureInPictureImpl: VideoPlaybackPictureInPicture override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) pictureInPictureImpl = VideoPlaybackPictureInPicture(this) pictureInPictureImpl.addOnPictureInPictureEventListener( ContextCompat.getMainExecutor(this), this ) setContent { ContentScreen(pictureInPictureImpl) } } override fun onPictureInPictureEvent( event: PictureInPictureDelegate.Event, config: Configuration? ) { when (event) { PictureInPictureDelegate.Event.ENTER_ANIMATION_START -> { /* Hide overlays */ } PictureInPictureDelegate.Event.ENTER_ANIMATION_END -> { /* Animation finished */ } PictureInPictureDelegate.Event.ENTERED -> { /* Switch to PiP layout */ } PictureInPictureDelegate.Event.STASHED -> { /* PiP stashed */ } PictureInPictureDelegate.Event.UNSTASHED -> { /* PiP unstashed */ } PictureInPictureDelegate.Event.EXITED -> { /* Return to full-screen */ } } } @Composable fun ContentScreen(pipController: VideoPlaybackPictureInPicture) { DisposableEffect(pipController) { onDispose { pipController.close() } } } }