Library Jetpack Picture-in-Picture (PiP) menawarkan solusi yang efisien dan andal bagi developer aplikasi Android untuk menerapkan fungsi PiP, terutama untuk aplikasi pemutaran media, komunikasi video, dan navigasi. Dengan menyediakan API terpadu, library ini membantu menghilangkan kode boilerplate, bug umum dalam aplikasi, dan meningkatkan kualitas keseluruhan pengalaman pengguna PiP.
Library Jetpack PiP memfasilitasi API PiP yang ada dengan mengatasi beberapa tantangan dan inkonsistensi utama di seluruh ekosistem Android:
- Fragmentasi OS: Library ini secara otomatis menangani perbedaan dalam panggilan API PiP
di berbagai versi Android, seperti menggunakan
enterPictureInPictureModesebelum Android 12 danisAutoEnterEnabledsetelahnya, sehingga developer tidak perlu mengelola perbedaan versi. - Parameter PiP yang salah: Library ini menyediakan solusi terpadu untuk menetapkan parameter PiP dengan benar, misalnya
setSourceRectHint, untuk membuat animasi yang lancar dan berkualitas tinggi selama pemutaran media. - Callback status PiP terpadu: Library ini menggabungkan
onPictureInPictureModeChangeddanonPictureInPictureUiStateChangedke dalam satu antarmuka callback terpadu (PictureInPictureDelegate.OnPictureInPictureEventListener) untuk pengelolaan status dan UI yang disederhanakan. - Pengurangan kode boilerplate: Library ini mengurangi jumlah kode boilerplate yang berulang dengan menawarkan kumpulan
RemoteActionsyang telah ditentukan sebelumnya untuk kasus penggunaan umum, seperti kontrol pemutaran dan tindakan panggilan video. - Ketahanan terhadap perubahan di masa mendatang: Fitur PiP lebih lanjut disediakan melalui library Jetpack, sehingga pengguna dapat mengakses fungsi tambahan dengan upaya minimal atau tanpa upaya.
Alur Kerja Migrasi
Identifikasi kategori kasus penggunaan aplikasi dan logika PiP lama:
Kategori: Pemutaran Video, Navigasi, atau Panggilan Video.
Logika PiP Lama yang Akan Diidentifikasi:
onUserLeaveHintsetAutoEnterEnabledonPictureInPictureModeChangedonPictureInPictureUiStateChangedsetPictureInPictureParams.
2. Konfigurasi AndroidManifest
Pastikan Aktivitas yang memasuki PiP mendeklarasikan dukungan di AndroidManifest.xml dengan configChanges yang diperlukan untuk mencegah memulai ulang yang tidak perlu:
<activity
android:name="VideoActivity" android:supportsPictureInPicture="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">
</activity>
3. Penyiapan Lingkungan
Tambahkan dependensi yang diperlukan ke 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") }
Gunakan library AndroidX terbaru untuk dependensi dan lihat halaman rilis untuk mendapatkan informasi tersebut.
4. Pemilihan dan Inisialisasi Template
Pilih template penerapan yang paling sesuai dengan kasus penggunaan aplikasi:
- Navigasi dan panggilan video:
BasicPictureInPicture; pengubahan ukuran yang lancar biasanya tidak didukung, dan Anda tidak memerlukan petunjuk persegi panjang sumber. - Pemutaran video:
VideoPlaybackPictureInPicture; secara otomatis melacak batas tampilan pemutar untuk petunjuk persegi panjang sumber dan mengaktifkan pengubahan ukuran yang lancar secara default.
Untuk mengadopsi Library Jetpack, ganti penerapan PiP kustom yang ada dengan API Library Jetpack. Kompleksitas dan biaya adopsi akan bervariasi berdasarkan penerapan aplikasi saat ini.
Bagian berikut menjelaskan beberapa kasus penggunaan PiP yang umum dan langkah-langkah penerapan yang diperlukan:
Navigasi
Aplikasi memberi tahu library tentang status aktif atau tidak aktif navigasi dan menetapkan rasio aspek. Library Jetpack akan menangani sisanya.
Perbedaan utama:
- Tidak perlu membedakan entri otomatis dan entri lama di sisi aplikasi.
- Antarmuka callback gabungan.
- Builder
PictureInPictureParamsbaru untuk kompatibilitas mundur.
Panggilan Video
Aplikasi memberi tahu library tentang status aktif atau tidak aktif panggilan dan menetapkan rasio aspek.
Perbedaan utama:
- Tidak perlu membedakan entri otomatis dan entri lama di sisi aplikasi.
- Antarmuka callback gabungan.
- Builder
PictureInPictureParamsbaru untuk kompatibilitas mundur. - Ikon tindakan standar untuk panggilan video.
5. Migrasi Kode
- Logika Entri: Ganti logika khusus API seperti
setAutoEnterEnableduntuk Android 12 dan yang lebih tinggi, atauonUserLeaveHintuntuk Android 11 dan yang lebih rendah dengansetEnabled. Aktifkan ini setiap kali status kelayakan PiP berubah. - Callback: Gabungkan
onPictureInPictureModeChanged(pengalihan tata letak) danonPictureInPictureUiStateChanged(animasi/status) ke dalam callback berbasis peristiwa terpaduonPictureInPictureEvent. - Tindakan & Parameter: Perbarui parameter menggunakan
setActionsdansetAspectRatiopada instance template setiap kali parameter tersebut berubah.
Pola Penerapan Referensi
Contoh penerapan.
Navigasi dan Panggilan Video
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 */ } } } }
Pemutaran 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() } } } }