Mendukung jendela desktop

Mode jendela desktop memungkinkan pengguna menjalankan beberapa aplikasi secara bersamaan di jendela aplikasi yang dapat diubah ukurannya untuk pengalaman serbaguna seperti desktop.

Pada gambar 1, Anda dapat melihat organisasi layar dengan pengaktifan mode jendela desktop. Hal-hal yang perlu diperhatikan:

  • Pengguna dapat menjalankan beberapa aplikasi secara berdampingan secara bersamaan.
  • Taskbar berada di posisi tetap di bagian bawah layar yang menampilkan aplikasi yang sedang berjalan. Pengguna dapat menyematkan aplikasi untuk akses cepat.
  • Panel header baru yang dapat disesuaikan menghiasi bagian atas setiap jendela dengan kontrol seperti minimalkan dan maksimalkan.
Gambar 1. Mode jendela desktop di tablet.

Secara default, aplikasi dibuka dalam layar penuh di tablet Android. Untuk meluncurkan aplikasi di mode jendela desktop, tekan lama tuas jendela di bagian atas layar dan tarik tuas di dalam UI, seperti yang terlihat pada gambar 2.

Saat aplikasi terbuka di jendela desktop, aplikasi lain juga akan terbuka di jendela desktop.

Gambar 2. Tekan, tahan, dan tarik tuas jendela aplikasi untuk masuk ke mode jendela desktop.

Pengguna juga dapat memanggil penataan jendela desktop dari menu yang muncul di bawah tuas jendela saat Anda mengetuk atau mengklik tuas atau menggunakan pintasan keyboard Tombol Meta (Windows, Command, atau Penelusuran) + Ctrl + Bawah.

Pengguna keluar dari penataan jendela desktop dengan menutup semua jendela aktif atau dengan menarik tuas jendela di bagian atas jendela desktop dan menarik aplikasi ke bagian atas layar. Pintasan keyboard Meta + H juga akan keluar dari tampilan jendela desktop dan menjalankan aplikasi dalam layar penuh lagi.

Untuk kembali ke penataan jendela desktop, ketuk atau klik kartu ruang desktop di layar Terbaru.

Mode pengubahan ukuran dan kompatibilitas

Dalam mode jendela desktop, aplikasi dengan orientasi terkunci dapat diubah ukurannya secara bebas. Artinya, meskipun aktivitas dikunci ke orientasi potret, pengguna tetap dapat mengubah ukuran aplikasi ke jendela orientasi lanskap.

Gambar 3. Mengubah ukuran jendela aplikasi yang dibatasi untuk potret menjadi lanskap.

Aplikasi yang dinyatakan tidak dapat diubah ukurannya (yaitu, resizeableActivity = false) memiliki UI yang diskalakan dengan tetap mempertahankan rasio aspek yang sama.

Gambar 4. UI aplikasi yang tidak dapat diubah ukurannya akan diskalakan saat jendela diubah ukurannya.

Aplikasi kamera yang mengunci orientasi atau dinyatakan tidak dapat diubah ukurannya memiliki perlakuan khusus untuk jendela bidik kameranya: jendela dapat diubah ukurannya sepenuhnya, tetapi jendela bidik mempertahankan rasio aspek yang sama. Dengan mengasumsikan aplikasi selalu berjalan dalam potret atau lanskap, aplikasi akan meng-hardcode atau membuat asumsi yang menyebabkan salah perhitungan orientasi atau rasio aspek pratinjau atau gambar yang diambil sehingga menghasilkan gambar yang terentang, miring, atau terbalik.

Hingga aplikasi siap menerapkan jendela bidik kamera yang sepenuhnya responsif, perlakuan khusus memberikan pengalaman pengguna yang lebih mendasar yang memitigasi efek yang mungkin disebabkan oleh asumsi yang salah.

Untuk mempelajari lebih lanjut mode kompatibilitas untuk aplikasi kamera, lihat Mode kompatibilitas perangkat.

Gambar 5. Jendela bidik kamera mempertahankan rasio aspeknya saat jendela diubah ukurannya.

Inset header yang dapat disesuaikan

Semua aplikasi yang berjalan dalam jendela desktop memiliki panel header, bahkan dalam mode imersi. Anda dapat menyesuaikan kolom ini untuk mencegah konten aplikasi Anda tertutup dan menggambar elemen UI kustom langsung ke ruang header.

Chrome sebelum dan setelah menerapkan header kustom.
Gambar 6. Chrome sebelum dan setelah menerapkan header kustom.

Implementasi

Untuk menggambar konten kustom di panel header, langkah pertama adalah membuat latar belakang panel header menjadi transparan. Anda dapat melakukannya dengan menggunakan flag APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND dengan WindowInsetsController.

window.insetsController?.setSystemBarsAppearance(
    WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND,
    WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND
)

Setelah kolom header transparan, Anda dapat menata area header agar sesuai dengan desain aplikasi Anda. Gunakan WindowInsets.isCaptionBarVisible untuk mendeteksi apakah panel hadir dan menerapkan tinggi atau padding yang sesuai ke tata letak Anda.

@OptIn(ExperimentalLayoutApi::class)
@Composable
fun CaptionBar() {
    if (WindowInsets.isCaptionBarVisible) {
        Row(
            modifier = Modifier
                .windowInsetsTopHeight(WindowInsets.captionBar)
                .fillMaxWidth()
                .background(if (isSystemInDarkTheme()) Color.White else Color.Black),
            horizontalArrangement = Arrangement.Center,
            verticalAlignment = Alignment.CenterVertically
        ) {
            Text(
                text = "Caption Bar Title",
                style = MaterialTheme.typography.titleMedium,
                modifier = Modifier.padding(4.dp)
            )
        }
    }
}

  • setSystemBarsAppearance(appearance,mask): Mengonfigurasi gaya visual pada kolom sistem. Parameter pertama menentukan flag tampilan target, sedangkan parameter kedua berfungsi sebagai mask untuk mengontrol flag spesifik mana yang diubah.

  • windowInsetsTopHeight(): Otomatis menyetel tinggi Composable agar sesuai dengan kolom header sistem, sehingga membantu latar belakang kustom Anda mengisi area teks tanpa meng-hardcode nilai piksel.

  • WindowInsets.captionBar: Menyediakan dimensi untuk kontrol jendela desktop (Tutup, Maksimalkan, dll.), sehingga UI Anda dapat diskalakan atau disembunyikan secara otomatis saat memasuki atau keluar dari jendela desktop.

Untuk mengetahui informasi selengkapnya, lihat Tentang inset jendela. Selain judul, Anda dapat menampilkan elemen UI lain di kolom teks, seperti tab—seperti di Google Chrome—kolom penelusuran, atau avatar profil.

Antarmuka pengguna

Untuk menghindari tumpang-tindih UI dengan tombol sistem, Android 15 menyediakan metode WindowInsets#getBoundingRects(). Metode ini menampilkan daftar objek Rect yang merepresentasikan area yang ditempati oleh elemen sistem. Ruang yang tersisa di kolom teks adalah area aman tempat Anda dapat menempatkan konten kustom dengan aman.

Alihkan tampilan elemen teks sistem untuk tema terang dan gelap menggunakan APPEARANCE_LIGHT_CAPTION_BARS. Akses inset menggunakan WindowInsets.Companion.captionBar() di Compose, atau WindowInsets.Type.captionBar() di View.

Untuk mengetahui informasi selengkapnya, lihat Tentang inset jendela.

Dukungan multitasking dan multi-instance

Multitasking adalah inti dari mode jendela desktop, dan mengizinkan beberapa instance aplikasi Anda dapat sangat meningkatkan produktivitas pengguna.

Mulai dari Android 15, Anda dapat menggunakan PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI. Dengan menyetel properti ini di AndroidManifest.xml, Anda menentukan bahwa UI sistem harus menyediakan opsi (seperti tombol "Jendela Baru") agar aplikasi diluncurkan dalam beberapa instance.

<application>
    <property
        android:name="android.window.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI"
        android:value="true" />
</application>

Catatan: Dalam penyesuaian jendela desktop dan lingkungan multi-aplikasi lainnya, tugas baru dibuka di jendela baru, jadi periksa kembali perjalanan pengguna setiap kali aplikasi Anda memulai beberapa tugas.

Mengelola instance aplikasi dengan gestur menarik

Dalam mode multi-aplikasi, pengguna dapat memulai instance aplikasi baru dengan menarik elemen UI (seperti tab atau dokumen) keluar dari jendela aplikasi. Pengguna juga dapat memindahkan elemen di antara berbagai instance aplikasi yang sama.

Gambar 7. Mulai instance Chrome baru dengan menarik tab keluar dari jendela desktop.

Mentransfer data dengan menarik lalu melepas

Untuk mengonfigurasi composable sebagai sumber penarikan untuk tarik lalu lepas multi-instance yang memungkinkan pengguna menarik konten ke instance lain aplikasi Anda, atau membuat instance baru dengan melepaskan konten ke area kosong di layar—gunakan pengubah dragAndDropSource. Di lambda-nya, kembalikan DragAndDropTransferData, dengan meneruskan ClipData yang berisi data yang akan ditransfer, dan flag untuk mengonfigurasi perilaku multi-instance.

Android 15 memperkenalkan dua tanda utama untuk mode jendela gaya desktop dan interaksi multi-instance:

  • DRAG_FLAG_GLOBAL_SAME_APPLICATION: Menunjukkan bahwa operasi penarikan dapat melintasi batas jendela (untuk beberapa instance dari aplikasi yang sama). Jika startDragAndDrop() dipanggil dengan setelan tanda ini, hanya jendela yang terlihat milik aplikasi yang sama yang dapat berpartisipasi dalam operasi penarikan dan menerima konten yang ditarik.

Modifier.dragAndDropSource { _ ->
    DragAndDropTransferData(
        clipData = ClipData.newPlainText("label", "Your data"),
        flags = View.DRAG_FLAG_GLOBAL_SAME_APPLICATION
    )
}

Modifier.dragAndDropSource { _ ->
    val intent = Intent.makeMainActivity(activity.componentName).apply {
        putExtra("EXTRA_ITEM_ID", itemId)
        flags = Intent.FLAG_ACTIVITY_NEW_TASK or
                Intent.FLAG_ACTIVITY_MULTIPLE_TASK or
                Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT
    }

    val pendingIntent = PendingIntent.getActivity(
        activity, 0, intent, PendingIntent.FLAG_IMMUTABLE
    )

    val data = ClipData(
        "Item $itemId",
        arrayOf(ClipDescription.MIMETYPE_TEXT_INTENT),
        ClipData.Item.Builder().setIntentSender(pendingIntent.intentSender).build()
    )

    DragAndDropTransferData(
        clipData = data,
        flags = View.DRAG_FLAG_GLOBAL_SAME_APPLICATION or
                View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG,
    )
}

Menerima data yang ditransfer

Untuk menerima data dari instance lain, gunakan pengubah dragAndDropTarget. Anda harus meminta izin secara eksplisit jika data berasal dari instance atau aplikasi yang berbeda.

Modifier.dragAndDropTarget(
    shouldStartDragAndDrop = { event ->
        event.toAndroidDragEvent().clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)
    },
    target = object : DragAndDropTarget {
        override fun onDrop(event: DragAndDropEvent): Boolean {
            requestDragAndDropPermissions(activity, event.toAndroidDragEvent())
            val clipData = event.toAndroidDragEvent().clipData
            val item = clipData?.getItemAt(0)?.text
            if (item != null) {
                // Process the dropped text item here
            }
            return item != null
        }
    }
)

Langkah-langkah utama:

  • Filter: Gunakan shouldStartDragAndDrop untuk memeriksa apakah data yang masuk (jenis MIME) didukung.
  • Izin: Panggil requestDragAndDropPermissions(event) untuk mengakses data.
  • Tangani: Ekstrak data dalam callback onDrop.

Pengoptimalan tambahan

Menyesuaikan peluncuran aplikasi dan mentransisikan aplikasi dari jendela desktop ke layar penuh.

Menentukan ukuran dan posisi default

Tidak semua aplikasi, meskipun dapat diubah ukurannya, memerlukan jendela besar untuk menawarkan nilai pengguna. Anda dapat menggunakan metode ActivityOptions#setLaunchBounds() untuk menentukan ukuran dan posisi default saat aktivitas diluncurkan.

Masuk ke layar penuh dari ruang desktop

Aplikasi dapat ditampilkan dalam layar penuh dengan memanggil Activity#requestFullScreenMode(). Metode menampilkan aplikasi layar penuh langsung dari jendela desktop.