Dukungan Tampilan

Pembaruan yang dilakukan pada area khusus tampilan ini disediakan di bawah ini:

Mengubah ukuran aktivitas dan tampilan

Untuk menunjukkan bahwa aplikasi mungkin tidak mendukung mode multi-jendela atau pengubahan ukuran, aktivitas menggunakan atribut resizeableActivity=false . Masalah umum yang dihadapi aplikasi saat aktivitas diubah ukurannya meliputi:

  • Aktivitas dapat memiliki konfigurasi yang berbeda dari aplikasi atau komponen non-visual lainnya. Kesalahan umum adalah membaca metrik tampilan dari konteks aplikasi. Nilai yang dikembalikan tidak akan disesuaikan dengan metrik area yang terlihat tempat aktivitas ditampilkan.
  • Aktivitas mungkin tidak menangani pengubahan ukuran dan error, menampilkan UI yang terdistorsi, atau kehilangan status karena peluncuran ulang tanpa menyimpan status instance.
  • Aplikasi mungkin mencoba menggunakan koordinat input absolut (bukan yang relatif terhadap posisi jendela), yang dapat merusak input di multi-jendela.

Di Android 7 (dan lebih tinggi), aplikasi dapat diatur resizeableActivity=false untuk selalu berjalan dalam mode layar penuh. Dalam hal ini, platform mencegah aktivitas yang tidak dapat diubah ukurannya masuk ke layar terpisah. Jika pengguna mencoba memanggil aktivitas yang tidak dapat diubah ukurannya dari peluncur saat sudah dalam mode layar terbagi, platform keluar dari mode layar terbagi dan meluncurkan aktivitas yang tidak dapat diubah ukurannya dalam mode layar penuh.

Aplikasi yang secara eksplisit menyetel atribut ini ke false dalam manifes tidak boleh diluncurkan dalam mode multi-jendela, kecuali jika mode kompatibilitas diterapkan:

  • Konfigurasi yang sama diterapkan pada proses, yang berisi semua komponen aktivitas dan non-aktivitas.
  • Konfigurasi yang diterapkan memenuhi persyaratan CDD untuk tampilan yang kompatibel dengan aplikasi.

Di Android 10, platform masih mencegah aktivitas yang tidak dapat diubah ukurannya masuk ke mode layar terbagi, tetapi aktivitas tersebut dapat diskalakan untuk sementara jika aktivitas tersebut telah menyatakan orientasi tetap atau rasio aspek. Jika tidak, aktivitas akan diubah ukurannya untuk memenuhi seluruh layar seperti di Android 9 dan yang lebih rendah.

Implementasi default menerapkan kebijakan berikut:

Saat aktivitas dinyatakan tidak kompatibel dengan multi-jendela melalui penggunaan atribut android:resizeableActivity dan saat aktivitas tersebut memenuhi salah satu kondisi yang dijelaskan di bawah, maka saat konfigurasi layar yang diterapkan harus berubah, aktivitas dan proses disimpan dengan konfigurasi asli dan pengguna diberikan kemampuan untuk meluncurkan kembali proses aplikasi untuk menggunakan konfigurasi layar yang diperbarui.

  • Apakah orientasi tetap melalui aplikasi android:screenOrientation
  • Aplikasi memiliki rasio aspek maksimum atau minimum default dengan menargetkan level API atau menyatakan rasio aspek secara eksplisit

Angka ini menampilkan aktivitas yang tidak dapat diubah ukurannya dengan rasio aspek yang dinyatakan. Saat melipat perangkat, jendela diperkecil agar sesuai dengan area sambil mempertahankan rasio aspek menggunakan kotak huruf yang sesuai. Selain itu, opsi aktivitas mulai ulang diberikan kepada pengguna setiap kali area tampilan untuk aktivitas diubah.

Saat membuka perangkat, konfigurasi, ukuran, dan rasio aspek aktivitas tidak berubah, namun opsi untuk memulai ulang aktivitas ditampilkan.

Ketika resizeableActivity tidak disetel (atau disetel ke true ), aplikasi sepenuhnya mendukung pengubahan ukuran.

Penerapan

Aktivitas yang tidak dapat diubah ukurannya dengan orientasi tetap atau rasio aspek disebut mode kompatibilitas ukuran (SCM) dalam kode. Kondisi didefinisikan dalam ActivityRecord#shouldUseSizeCompatMode() . Saat aktivitas SCM diluncurkan, konfigurasi terkait layar (seperti ukuran atau kepadatan) ditetapkan dalam konfigurasi penggantian yang diminta, sehingga aktivitas tidak lagi bergantung pada konfigurasi tampilan saat ini.

Jika aktivitas SCM tidak dapat memenuhi seluruh layar, aktivitas tersebut akan disejajarkan atas dan dipusatkan secara horizontal. Batas aktivitas dihitung oleh AppWindowToken#calculateCompatBoundsTransformation() .

Saat aktivitas SCM menggunakan konfigurasi layar yang berbeda dari wadahnya (misalnya, tampilan diubah ukurannya, atau aktivitas dipindahkan ke tampilan lain), ActivityRecord#inSizeCompatMode() adalah true dan SizeCompatModeActivityController (di System UI) menerima callback untuk menampilkan proses tombol restart.

Ukuran tampilan dan rasio aspek

Android 10 menyediakan dukungan untuk rasio aspek baru dari rasio tinggi layar panjang dan tipis hingga rasio 1:1. Aplikasi dapat menentukan ApplicationInfo#maxAspectRatio dan ApplicationInfo#minAspectRatio layar yang dapat mereka tangani.

rasio aplikasi di Android 10

Gambar 1. Contoh rasio aplikasi yang didukung di Android 10

Implementasi perangkat dapat memiliki tampilan sekunder dengan ukuran dan resolusi lebih kecil dari yang diperlukan oleh Android 9, dan lebih rendah (minimal lebar atau tinggi 2,5 inci, minimum 320 DP untuk ScreenWidth smallestScreenWidth ), namun hanya aktivitas yang memilih untuk mendukung tampilan kecil ini yang dapat ditempatkan di sana.

Aplikasi dapat ikut serta dengan menyatakan ukuran minimum yang didukung yang lebih kecil dari oe sama dengan ukuran tampilan target. Gunakan atribut tata letak aktivitas android:minHeight dan android:minWidth di AndroidManifest untuk melakukannya.

Kebijakan tampilan

Android 10 memisahkan dan memindahkan kebijakan tampilan tertentu dari implementasi WindowManagerPolicy default di PhoneWindowManager ke kelas per-tampilan, seperti:

  • Status tampilan dan rotasi
  • Beberapa kunci dan pelacakan acara gerak
  • Sistem UI dan jendela dekorasi

Di Android 9 (dan lebih rendah), kelas PhoneWindowManager menangani kebijakan tampilan, status dan pengaturan, rotasi, pelacakan bingkai jendela dekorasi, dan banyak lagi. Android 10 memindahkan sebagian besar ini ke kelas DisplayPolicy , kecuali untuk pelacakan rotasi, yang telah dipindahkan ke DisplayRotation .

Tampilkan pengaturan jendela

Di Android 10, pengaturan windowing per-tampilan yang dapat dikonfigurasi telah diperluas untuk menyertakan:

  • Mode jendela tampilan default
  • Nilai pemindaian berlebih
  • Rotasi pengguna dan mode rotasi
  • Ukuran paksa, kepadatan, dan mode penskalaan
  • Mode penghapusan konten (saat tampilan dihapus)
  • Dukungan untuk dekorasi sistem dan IME

Kelas DisplayWindowSettings berisi pengaturan untuk opsi ini. Mereka bertahan ke disk di /data di display_settings.xml setiap kali pengaturan diubah. Untuk detailnya, lihat DisplayWindowSettings.AtomicFileStorage dan DisplayWindowSettings#writeSettings() . Produsen perangkat dapat memberikan nilai default di display_settings.xml untuk konfigurasi perangkat mereka. Namun, karena file disimpan di /data , logika tambahan mungkin diperlukan untuk memulihkan file jika terhapus dengan wipe.

Secara default, Android 10 menggunakan DisplayInfo#uniqueId sebagai pengenal untuk tampilan saat mempertahankan setelan. uniqueId harus diisi untuk semua tampilan. Selain itu, stabil untuk tampilan fisik dan jaringan. Anda juga dapat menggunakan port tampilan fisik sebagai pengenal, yang dapat diatur di DisplayWindowSettings#mIdentifier . Pada setiap penulisan, semua pengaturan ditulis sehingga aman untuk memperbarui kunci yang digunakan untuk entri tampilan di penyimpanan. Untuk detailnya, lihat Pengidentifikasi tampilan statis .

Pengaturan tetap ada di direktori /data karena alasan historis. Awalnya, mereka digunakan untuk mempertahankan pengaturan yang ditetapkan pengguna, seperti rotasi tampilan.

Pengidentifikasi tampilan statis

Android 9 (dan lebih rendah) tidak menyediakan pengenal yang stabil untuk tampilan dalam kerangka kerja. Saat tampilan ditambahkan ke sistem, Display#mDisplayId atau DisplayInfo#displayId dibuat untuk tampilan tersebut dengan menambahkan penghitung statis. Jika sistem menambahkan dan menghapus tampilan yang sama, ID yang berbeda dihasilkan.

Jika perangkat memiliki beberapa layar yang tersedia sejak boot, layar dapat diberi pengidentifikasi yang berbeda, tergantung pada waktunya. Meskipun Android 9 (dan sebelumnya) menyertakan DisplayInfo#uniqueId , itu tidak berisi informasi yang cukup untuk membedakan antara tampilan karena tampilan fisik diidentifikasi sebagai local:0 atau local:1 , untuk mewakili tampilan internal dan eksternal.

Android 10 mengubah DisplayInfo#uniqueId untuk menambahkan pengenal stabil dan untuk membedakan antara tampilan lokal, jaringan, dan virtual.

Tipe tampilan Format
Lokal
local:<stable-id>
Jaringan
network:<mac-address>
Maya
virtual:<package-name-and-name>

Selain pembaruan uniqueId , DisplayInfo.address berisi DisplayAddress , pengidentifikasi tampilan yang stabil di seluruh reboot. Di Android 10, DisplayAddress mendukung tampilan fisik dan jaringan. DisplayAddress.Physical berisi ID tampilan yang stabil (sama seperti di uniqueId ) dan dapat dibuat dengan DisplayAddress#fromPhysicalDisplayId() .

Android 10 juga menyediakan metode yang mudah untuk mendapatkan informasi port ( Physical#getPort() ). Metode ini dapat digunakan dalam kerangka kerja untuk mengidentifikasi tampilan secara statis. Misalnya, ini digunakan di DisplayWindowSettings ). DisplayAddress.Network berisi alamat MAC dan dapat dibuat dengan DisplayAddress#fromMacAddress() .

Penambahan ini memungkinkan produsen perangkat untuk mengidentifikasi tampilan dalam pengaturan multi-display statis dan untuk mengonfigurasi pengaturan dan fitur sistem yang berbeda menggunakan pengidentifikasi tampilan statis, seperti port untuk tampilan fisik. Metode ini disembunyikan dan dimaksudkan hanya untuk digunakan di dalam system_server .

Dengan ID tampilan HWC (yang bisa buram dan tidak selalu stabil), metode ini mengembalikan nomor port 8-bit (khusus platform) yang mengidentifikasi konektor fisik untuk output tampilan, serta gumpalan EDID tampilan. SurfaceFlinger mengekstrak informasi pabrikan atau model dari EDID untuk menghasilkan ID tampilan 64-bit stabil yang diekspos ke kerangka kerja. Jika metode ini tidak didukung atau error, SurfaceFlinger kembali ke mode MD lama, di mana DisplayInfo#address adalah null dan DisplayInfo#uniqueId di-hard-code, seperti yang dijelaskan di atas.

Untuk memverifikasi bahwa fitur ini didukung, jalankan:

$ dumpsys SurfaceFlinger --display-id
# Example output.
Display 21691504607621632 (HWC display 0): port=0 pnpId=SHP displayName="LQ123P1JX32"
Display 9834494747159041 (HWC display 2): port=1 pnpId=HWP displayName="HP Z24i"
Display 1886279400700944 (HWC display 1): port=2 pnpId=AUS displayName="ASUS MB16AP"

Menggunakan lebih dari dua tampilan

Di Android 9 (dan lebih rendah), SurfaceFlinger dan DisplayManagerService mengasumsikan keberadaan paling banyak dua tampilan fisik dengan ID 0 dan 1.

Dimulai dengan Android 10, SurfaceFlinger dapat memanfaatkan Hardware Composer (HWC) API untuk menghasilkan ID tampilan yang stabil, yang memungkinkannya untuk mengelola sejumlah tampilan fisik yang berubah-ubah. Untuk mempelajari lebih lanjut, lihat Pengidentifikasi tampilan statis .

Kerangka kerja dapat mencari token IBinder untuk tampilan fisik melalui SurfaceControl#getPhysicalDisplayToken setelah memperoleh ID tampilan 64-bit dari SurfaceControl#getPhysicalDisplayIds atau dari acara hotplug DisplayEventReceiver .

Di Android 10 (dan lebih rendah), tampilan internal utama adalah TYPE_INTERNAL dan semua tampilan sekunder ditandai sebagai TYPE_EXTERNAL terlepas dari jenis koneksinya. Oleh karena itu, tampilan internal tambahan diperlakukan sebagai eksternal. Sebagai solusinya, kode khusus perangkat dapat membuat asumsi tentang DisplayAddress.Physical#getPort jika HWC diketahui dan logika alokasi port dapat diprediksi.

Batasan ini dihapus di Android 11 (dan lebih tinggi).

  • Di Android 11, tampilan pertama yang dilaporkan saat booting adalah tampilan utama. Jenis koneksi (internal versus eksternal) tidak relevan. Namun, tetap benar bahwa tampilan utama tidak dapat diputus dan karenanya harus menjadi tampilan internal dalam praktiknya. Perhatikan bahwa beberapa ponsel yang dapat dilipat memiliki beberapa layar internal.
  • Tampilan sekunder dikategorikan dengan benar sebagai Display.TYPE_INTERNAL atau Display.TYPE_EXTERNAL (sebelumnya dikenal sebagai Display.TYPE_BUILT_IN dan Display.TYPE_HDMI , masing-masing) tergantung pada jenis koneksinya.

Penerapan

Di Android 9 dan yang lebih rendah, tampilan diidentifikasi oleh ID 32-bit, di mana 0 adalah tampilan internal, 1 adalah tampilan eksternal, [2, INT32_MAX] adalah tampilan virtual HWC, dan -1 mewakili tampilan yang tidak valid atau non-HWC tampilan maya.

Dimulai dengan Android 10, tampilan diberi ID yang stabil dan persisten, yang memungkinkan SurfaceFlinger dan DisplayManagerService melacak lebih dari dua tampilan dan mengenali tampilan yang terlihat sebelumnya. Jika HWC mendukung IComposerClient.getDisplayIdentificationData dan menyediakan data identifikasi tampilan, SurfaceFlinger mem-parsing struktur EDID dan mengalokasikan ID tampilan 64-bit yang stabil untuk tampilan fisik dan virtual HWC. ID diekspresikan menggunakan tipe opsi, di mana nilai null mewakili tampilan yang tidak valid atau tampilan virtual non-HWC. Tanpa dukungan HWC, SurfaceFlinger kembali ke perilaku lama dengan paling banyak dua tampilan fisik.

Fokus per-tampilan

Untuk mendukung beberapa sumber input yang menargetkan tampilan individual secara bersamaan, Android 10 dapat dikonfigurasi untuk mendukung beberapa jendela terfokus, paling banyak satu per tampilan. Ini hanya ditujukan untuk jenis perangkat khusus ketika beberapa pengguna berinteraksi dengan perangkat yang sama pada waktu yang sama dan menggunakan metode atau perangkat input yang berbeda, seperti Android Automotive.

Sangat disarankan agar fitur ini tidak diaktifkan untuk perangkat biasa, termasuk perangkat multilayar atau yang digunakan untuk pengalaman seperti desktop. Hal ini terutama disebabkan oleh masalah keamanan yang dapat menyebabkan pengguna bertanya-tanya jendela mana yang memiliki fokus input.

Bayangkan pengguna yang memasukkan informasi aman ke bidang input teks, mungkin masuk ke aplikasi perbankan atau memasukkan teks yang berisi informasi sensitif. Aplikasi berbahaya dapat membuat tampilan di luar layar virtual untuk menjalankan aktivitas, juga dengan bidang input teks. Aktivitas yang sah dan berbahaya memiliki fokus dan keduanya menampilkan indikator input aktif (kursor berkedip).

Namun, karena input dari keyboard (perangkat keras atau perangkat lunak) dimasukkan ke aktivitas paling atas saja (aplikasi yang paling baru diluncurkan), dengan membuat tampilan virtual tersembunyi, aplikasi jahat dapat mengambil input pengguna, bahkan saat menggunakan keyboard perangkat lunak. pada layar perangkat utama.

Gunakan com.android.internal.R.bool.config_perDisplayFocusEnabled untuk menyetel fokus per-tampilan.

Kesesuaian

Masalah: Di Android 9 dan yang lebih rendah, paling banyak satu jendela dalam sistem memiliki fokus pada satu waktu.

Solusi: Dalam kasus yang jarang terjadi ketika dua jendela dari proses yang sama akan difokuskan, sistem memberikan fokus hanya ke jendela yang lebih tinggi dalam urutan-Z. Pembatasan ini dihapus untuk aplikasi yang menargetkan Android 10, yang diharapkan dapat mendukung beberapa jendela yang difokuskan secara bersamaan.

Penerapan

WindowManagerService#mPerDisplayFocusEnabled mengontrol ketersediaan fitur ini. Di ActivityManager , ActivityDisplay#getFocusedStack() sekarang digunakan sebagai ganti pelacakan global dalam sebuah variabel. ActivityDisplay#getFocusedStack() menentukan fokus berdasarkan urutan-Z alih-alih menyimpan nilai dalam cache. Ini agar hanya satu sumber, WindowManager, yang perlu melacak urutan-Z aktivitas.

ActivityStackSupervisor#getTopDisplayFocusedStack() mengambil pendekatan serupa untuk kasus-kasus ketika tumpukan fokus paling atas dalam sistem harus diidentifikasi. Tumpukan dilintasi dari atas ke bawah, mencari tumpukan pertama yang memenuhi syarat.

InputDispatcher sekarang dapat memiliki beberapa jendela fokus (satu per tampilan). Jika kejadian input adalah khusus untuk tampilan, maka kejadian tersebut dikirim ke jendela fokus di tampilan yang sesuai. Jika tidak, itu dikirim ke jendela fokus di tampilan fokus, yang merupakan tampilan yang paling baru berinteraksi dengan pengguna.

Lihat InputDispatcher::mFocusedWindowHandlesByDisplay dan InputDispatcher::setFocusedDisplay() . Aplikasi yang difokuskan juga diperbarui secara terpisah di InputManagerService melalui NativeInputManager::setFocusedApplication() .

Di WindowManager , jendela terfokus juga dilacak secara terpisah. Lihat DisplayContent#mCurrentFocus dan DisplayContent#mFocusedApp dan penggunaan masing-masing. Pelacakan fokus terkait dan metode pembaruan telah dipindahkan dari WindowManagerService ke DisplayContent .