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 pengaturan layar dengan mode jendela desktop yang diaktifkan. 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 akan terbuka dalam layar penuh di tablet Android. Untuk meluncurkan aplikasi dalam mode jendela desktop, tekan lama tuas jendela di bagian atas layar dan tarik tuas dalam UI, seperti yang terlihat pada gambar 2.

Saat aplikasi dibuka dalam mode jendela desktop, aplikasi lain juga akan dibuka di jendela desktop.

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

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

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

Untuk kembali ke mode jendela desktop, ketuk atau klik ubin ruang desktop di layar Terbaru.

Mengoptimalkan tata letak aplikasi untuk lingkungan seperti desktop

Mendesain untuk pengalaman desktop dapat berbeda secara signifikan dari desain seluler karena ruang layar yang lebih besar, presisi input mouse dan keyboard, serta ekspektasi produktivitas yang tinggi.

Jetpack WindowManager menyediakan API yang tegas untuk membantu developer memutuskan kapan harus menampilkan UI desktop, yang biasanya memiliki kepadatan informasi yang lebih tinggi, pola navigasi yang berbeda, dan interaksi mouse yang dioptimalkan.

lifecycleScope.launch(Dispatchers.Main) {
    lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
        windowInfoTracker.windowEngagementInfo(this@DesktopWindowingActivity)
            .collect { windowEngagementInfo ->
                if(windowEngagementInfo.hasEngagementMode(WindowEngagementInfo.EngagementMode.PRECISE_POINTER)){
                    showDesktopOptimizedUI()
                }else {
                    showTouchOptimizedUI()
                }
        }
    }
}

Untuk mempelajari lebih lanjut, lihat Mendesain untuk desktop.

Mode kompatibilitas dan kemampuan pengubahan ukuran

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

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

Aplikasi yang dideklarasikan sebagai tidak dapat diubah ukurannya (yaitu, resizeableActivity = false) akan diskalakan UI-nya 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 dideklarasikan sebagai 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 mode potret atau lanskap, aplikasi melakukan hardcode atau membuat asumsi yang menyebabkan kesalahan penghitungan orientasi atau rasio aspek pratinjau atau gambar yang diambil, sehingga menghasilkan gambar yang melebar, miring, atau terbalik.

Hingga aplikasi siap menerapkan jendela bidik kamera yang sepenuhnya responsif, perlakuan khusus memberikan pengalaman pengguna yang lebih mendasar yang mengurangi 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 ukuran jendela berubah.

Inset header yang dapat disesuaikan

Semua aplikasi yang berjalan dalam mode jendela desktop memiliki panel header, bahkan dalam mode imersif. Anda dapat menyesuaikan panel ini untuk mencegah konten aplikasi Anda terhalang 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.

Penerapan

Untuk menggambar konten kustom di panel header, langkah pertama adalah membuat latar belakang panel header menjadi transparan. Anda dapat melakukannya 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 panel header transparan, Anda dapat menata gaya area header agar sesuai dengan desain aplikasi Anda. Gunakan WindowInsets.isCaptionBarVisible untuk mendeteksi apakah panel ada 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 kolom sistem. Parameter pertama menentukan flag tampilan target, sedangkan parameter kedua bertindak sebagai mask untuk mengontrol flag tertentu yang diubah.

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

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

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

Antarmuka pengguna

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

Aktifkan/nonaktifkan 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 Android 15, Anda dapat menggunakan PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI. Dengan menetapkan properti ini di AndroidManifest.xml, Anda menentukan bahwa UI sistem harus menyediakan opsi (seperti tombol "Jendela Baru") agar aplikasi dapat diluncurkan dalam beberapa instance.

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

Catatan: Dalam mode jendela desktop dan lingkungan multi-aplikasi lainnya, tugas baru akan terbuka 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 tarik lalu lepas

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

Android 15 memperkenalkan dua flag utama untuk mode jendela bergaya desktop dan interaksi multi-instance:

  • DRAG_FLAG_GLOBAL_SAME_APPLICATION: Menunjukkan bahwa operasi tarik dapat melewati batas jendela (untuk beberapa instance dari aplikasi yang sama). Saat startDragAndDrop() dipanggil dengan flag ini ditetapkan, hanya jendela yang terlihat milik aplikasi yang sama yang dapat berpartisipasi dalam operasi tarik 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 secara eksplisit meminta izin jika data berasal dari instance atau aplikasi lain.

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.
  • Menangani: Ekstrak data dalam callback onDrop.

Pengoptimalan tambahan

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

Menentukan ukuran dan posisi default

Tidak semua aplikasi, meskipun dapat diubah ukurannya, memerlukan jendela besar untuk menawarkan nilai kepada 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 beralih ke layar penuh dengan memanggil Activity#requestFullScreenMode(). Metode ini menampilkan aplikasi dalam layar penuh langsung dari mode jendela desktop.