Menggunakan Library Picture-in-Picture Jetpack

Library Jetpack Picture-in-Picture (PiP) menawarkan solusi yang disederhanakan 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 otomatis menangani perbedaan panggilan API PiP di berbagai versi Android, seperti menggunakan enterPictureInPictureMode sebelum Android 12 dan isAutoEnterEnabled setelahnya, sehingga developer tidak perlu mengelola perbedaan versi.
  • Parameter PiP yang salah: API ini menyediakan solusi terpadu untuk menyetel parameter PiP dengan benar, misalnya setSourceRectHint, untuk membuat animasi yang lancar dan berkualitas tinggi selama pemutaran media.
  • Callback status PiP terpadu: Callback ini menggabungkan onPictureInPictureModeChanged dan onPictureInPictureUiStateChanged ke 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 RemoteActions yang telah ditentukan sebelumnya untuk kasus penggunaan umum, seperti kontrol pemutaran dan tindakan panggilan video.
  • Persiapan untuk masa depan: Fitur PiP lainnya disediakan melalui library Jetpack, sehingga pengguna dapat mengakses fungsi tambahan dengan sedikit 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 Harus Diidentifikasi:

  • onUserLeaveHint
  • setAutoEnterEnabled
  • onPictureInPictureModeChanged
  • onPictureInPictureUiStateChanged
  • setPictureInPictureParams.

2. Konfigurasi AndroidManifest

Pastikan Aktivitas yang memasuki PiP menyatakan dukungan di AndroidManifest.xml dengan configChanges yang diperlukan untuk mencegah mulai 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 mengetahui info 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 sumber.
  • Pemutaran video: VideoPlaybackPictureInPicture; secara otomatis melacak batas tampilan pemutar untuk petunjuk persegi sumber dan memungkinkan pengubahan ukuran yang lancar secara default.

Untuk menggunakan Library Jetpack, ganti implementasi PiP kustom yang ada dengan API Library Jetpack. Kompleksitas dan biaya adopsi akan bervariasi berdasarkan implementasi aplikasi saat ini.

Bagian berikut menjelaskan beberapa kasus penggunaan umum PiP dan langkah-langkah penerapan yang diperlukan:

Aplikasi memberi tahu library tentang status aktif atau tidak aktif navigasi dan menetapkan rasio aspek. Library Jetpack akan menangani sisanya.

Perbedaan utama:

  1. Tidak perlu membedakan masuk otomatis dan masuk lama di sisi aplikasi.
  2. Antarmuka callback gabungan.
  3. Builder PictureInPictureParams baru untuk kompatibilitas mundur.

Panggilan Video

Aplikasi memberi tahu library tentang status aktif atau tidak aktif panggilan dan menetapkan rasio aspek.

Perbedaan utama:

  1. Tidak perlu membedakan masuk otomatis dan masuk lama di sisi aplikasi.
  2. Antarmuka callback gabungan.
  3. Builder PictureInPictureParams baru untuk kompatibilitas mundur.
  4. Ikon tindakan standar untuk panggilan video.

5. Migrasi Kode

  • Logika Entri: Ganti logika khusus API seperti setAutoEnterEnabled untuk Android 12 dan yang lebih tinggi, atau onUserLeaveHint untuk Android 11 dan yang lebih rendah dengan setEnabled. Picu ini setiap kali status kelayakan PiP berubah.
  • Callback: Gabungkan onPictureInPictureModeChanged (penggantian tata letak) dan onPictureInPictureUiStateChanged (animasi/status) ke dalam callback berbasis peristiwa terpadu onPictureInPictureEvent.
  • Tindakan & Parameter: Perbarui parameter menggunakan setActions dan setAspectRatio pada instance template setiap kali parameter tersebut berubah.
  • Penanganan Khusus Video: Untuk aplikasi video, gunakan setPlayerView untuk mengotomatiskan pembaruan petunjuk persegi panjang sumber dan memastikan transisi yang lancar. ` ### 6. Pembersihan

Untuk VideoPlaybackPictureInPicture, panggil close di onDispose atau onDestroy untuk melepaskan resource seperti pelacak tampilan.

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