Dukungan layar

Pembaruan yang dilakukan pada area khusus tampilan tersebut tercantum di bawah:

Mengubah ukuran aktivitas dan tampilan

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

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

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

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

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

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

Implementasi default menerapkan kebijakan berikut:

Jika suatu aktivitas dideklarasikan tidak kompatibel dengan multi-aplikasi melalui penggunaan atribut android:resizeableActivity dan saat aktivitas tersebut memenuhi salah satu kondisi yang dijelaskan di bawah, saat konfigurasi layar yang diterapkan harus berubah, aktivitas dan proses akan disimpan dengan konfigurasi asli dan pengguna diberi kemampuan untuk meluncurkan kembali proses aplikasi agar menggunakan konfigurasi layar yang diperbarui.

  • Merupakan orientasi tetap melalui penerapan android:screenOrientation
  • Aplikasi memiliki rasio aspek maksimum atau minimum default dengan menargetkan API level atau mendeklarasikan rasio aspek secara eksplisit

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

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

Jika resizeableActivity tidak ditetapkan (atau ditetapkan ke true), aplikasi sepenuhnya mendukung pengubahan ukuran.

Implementasi

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

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

Jika aktivitas SCM menggunakan konfigurasi layar yang berbeda dengan penampungnya (misalnya, ukuran layar diubah, atau aktivitas dipindahkan ke layar lain), ActivityRecord#inSizeCompatMode() bernilai benar dan SizeCompatModeActivityController (di UI Sistem) menerima callback untuk menampilkan tombol mulai ulang proses.

Ukuran dan rasio aspek tampilan

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 ditanganinya.

rasio aplikasi di Android 10

Gambar 1. Contoh rasio aplikasi yang didukung di Android 10

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

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

Menampilkan kebijakan

Android 10 memisahkan dan memindahkan kebijakan tampilan tertentu dari penerapan WindowManagerPolicy default di PhoneWindowManager ke class per tampilan, seperti:

  • Status dan rotasi tampilan
  • Beberapa tombol dan pelacakan peristiwa gerakan
  • UI sistem dan jendela dekorasi

Di Android 9 (dan yang lebih rendah), class PhoneWindowManager menangani kebijakan tampilan, status dan setelan, rotasi, pelacakan frame jendela dekorasi, dan lainnya. Android 10 memindahkan sebagian besar hal ini ke class DisplayPolicy, kecuali untuk pelacakan rotasi, yang telah dipindahkan ke DisplayRotation.

Setelan jendela tampilan

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

  • Mode jendela tampilan default
  • Pemindaian berlebih pada nilai
  • Mode rotasi dan rotasi pengguna
  • Ukuran, kepadatan, dan mode penskalaan yang dipaksakan
  • Mode penghapusan konten (saat tampilan dihapus)
  • Dukungan untuk dekorasi sistem dan IME

Class DisplayWindowSettings berisi setelan untuk opsi ini. Objek tersebut disimpan ke disk dalam partisi /data di display_settings.xml setiap kali setelan diubah. Untuk mengetahui 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 dihapus dengan penghapusan total.

Secara default, Android 10 menggunakan DisplayInfo#uniqueId sebagai ID untuk layar saat mempertahankan setelan. uniqueId harus diisi untuk semua layar. Selain itu, hal ini stabil untuk tampilan fisik dan jaringan. Anda juga dapat menggunakan port tampilan fisik sebagai ID, yang dapat ditetapkan dalam DisplayWindowSettings#mIdentifier. Setelah setiap operasi tulis, semua setelan akan ditulis sehingga aman untuk memperbarui kunci yang digunakan untuk entri tampilan di penyimpanan. Untuk mengetahui detailnya, lihat ID tampilan statis.

Setelan dipertahankan di direktori /data karena alasan historis. Awalnya, setelan ini digunakan untuk mempertahankan setelan yang ditetapkan pengguna, seperti rotasi layar.

ID tampilan statis

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

Jika perangkat memiliki beberapa tampilan yang tersedia dari booting, tampilan tersebut dapat diberi ID yang berbeda, bergantung pada waktunya. Meskipun Android 9 (dan yang lebih lama) menyertakan DisplayInfo#uniqueId, Android 9 (dan yang lebih lama) tidak berisi cukup informasi untuk membedakan antar-layar karena tampilan fisik diidentifikasi sebagai local:0 atau local:1, untuk mewakili layar bawaan dan eksternal.

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

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

Selain update pada uniqueId, DisplayInfo.address berisi DisplayAddress, ID tampilan yang stabil saat reboot. Di Android 10, DisplayAddress mendukung tampilan fisik dan jaringan. DisplayAddress.Physical berisi ID tampilan 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 framework untuk mengidentifikasi layar secara statis. Misalnya, digunakan di DisplayWindowSettings). DisplayAddress.Network berisi alamat MAC dan dapat dibuat dengan DisplayAddress#fromMacAddress().

Penambahan ini memungkinkan produsen perangkat mengidentifikasi layar dalam penyiapan multi-layar statis dan mengonfigurasi berbagai setelan dan fitur sistem menggunakan ID layar statis, seperti port untuk layar fisik. Metode ini disembunyikan dan hanya ditujukan untuk digunakan dalam system_server.

Dengan ID tampilan HWC (yang bisa buram dan tidak selalu stabil), metode ini menampilkan nomor port 8-bit (khusus platform) yang mengidentifikasi konektor fisik untuk output tampilan, serta blob EDID layar. SurfaceFlinger mengekstrak informasi produsen atau model dari EDID untuk menghasilkan ID tampilan 64-bit stabil yang diekspos ke framework. Jika metode ini tidak didukung atau mengalami error, SurfaceFlinger akan beralih kembali ke mode MD lama, dengan DisplayInfo#address adalah null dan DisplayInfo#uniqueId adalah 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 layar

Di Android 9 (dan yang lebih lama), SurfaceFlinger dan DisplayManagerService mengasumsikan adanya maksimal dua layar fisik dengan ID 0 dan 1 hard code.

Mulai Android 10, SurfaceFlinger dapat memanfaatkan Hardware Composer (HWC) API untuk menghasilkan ID tampilan yang stabil, yang memungkinkannya mengelola jumlah tampilan fisik arbitrer. Untuk mempelajari lebih lanjut, lihat ID tampilan statis.

Framework dapat mencari token IBinder untuk layar fisik melalui SurfaceControl#getPhysicalDisplayToken setelah mendapatkan ID layar 64-bit dari SurfaceControl#getPhysicalDisplayIds atau dari peristiwa hotplug DisplayEventReceiver.

Di Android 10 (dan yang lebih lama), tampilan internal utama adalah TYPE_INTERNAL dan semua tampilan sekunder ditandai sebagai TYPE_EXTERNAL, terlepas dari jenis koneksi. Oleh karena itu, tampilan internal tambahan dianggap 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 yang lebih tinggi).

  • Di Android 11, layar pertama yang dilaporkan selama booting adalah layar utama. Jenis koneksi (internal versus eksternal) tidak relevan. Namun, tetap benar bahwa tampilan utama tidak dapat diputus dan mengikuti bahwa tampilan tersebut harus merupakan tampilan internal dalam praktiknya. Perhatikan bahwa beberapa ponsel foldable memiliki beberapa layar internal.
  • Layar sekunder dikategorikan dengan benar sebagai Display.TYPE_INTERNAL atau Display.TYPE_EXTERNAL (sebelumnya dikenal sebagai Display.TYPE_BUILT_IN dan Display.TYPE_HDMI) bergantung pada jenis koneksinya.

Implementasi

Pada Android 9 dan yang lebih lama, layar diidentifikasi berdasarkan ID 32-bit, dengan 0 adalah layar internal, 1 adalah layar eksternal, [2, INT32_MAX] adalah layar virtual HWC, dan -1 mewakili layar yang tidak valid atau layar virtual non-HWC.

Mulai Android 10, layar diberi ID yang stabil dan persisten, yang memungkinkan SurfaceFlinger dan DisplayManagerService melacak lebih dari dua layar dan mengenali tampilan yang sebelumnya terlihat. Jika HWC mendukung IComposerClient.getDisplayIdentificationData dan memberikan data identifikasi tampilan, SurfaceFlinger akan mengurai struktur EDID dan mengalokasikan ID tampilan 64-bit yang stabil untuk tampilan fisik dan virtual HWC. ID dinyatakan menggunakan jenis opsi, dengan nilai null mewakili tampilan yang tidak valid atau tampilan virtual non-HWC. Tanpa dukungan HWC, SurfaceFlinger akan kembali ke perilaku lama dengan maksimal dua layar fisik.

Fokus per layar

Untuk mendukung beberapa sumber input yang menargetkan setiap layar secara bersamaan, Android 10 dapat dikonfigurasi untuk mendukung beberapa jendela yang difokuskan, maksimal satu per tampilan. Hal ini hanya ditujukan untuk jenis perangkat khusus saat beberapa pengguna berinteraksi dengan perangkat yang sama pada waktu yang sama dan menggunakan metode atau perangkat input yang berbeda, seperti Android Automotive.

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

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

Namun, karena input dari keyboard (hardware atau software) hanya dimasukkan ke aktivitas teratas (aplikasi yang baru saja diluncurkan), dengan membuat tampilan virtual tersembunyi, aplikasi berbahaya dapat mengambil input pengguna, bahkan saat menggunakan keyboard software di layar perangkat utama.

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

Kompatibilitas

Masalah: Di Android 9 dan yang lebih rendah, maksimal satu jendela di sistem memiliki fokus pada satu waktu.

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

Implementasi

WindowManagerService#mPerDisplayFocusEnabled mengontrol ketersediaan fitur ini. Di ActivityManager, ActivityDisplay#getFocusedStack() kini digunakan, bukan pelacakan global dalam variabel. ActivityDisplay#getFocusedStack() menentukan fokus berdasarkan urutan Z, bukan menyimpan nilai dalam cache. Hal ini dimaksudkan agar hanya satu sumber, yaitu WindowManager, yang perlu melacak urutan Z aktivitas.

ActivityStackSupervisor#getTopDisplayFocusedStack() menggunakan pendekatan serupa untuk kasus tersebut saat stack yang difokuskan paling atas dalam sistem harus diidentifikasi. Stack dilalui dari atas ke bawah, menelusuri stack pertama yang memenuhi syarat.

InputDispatcher kini dapat memiliki beberapa jendela yang difokuskan (satu per layar). Jika peristiwa input bersifat khusus tampilan, peristiwa tersebut akan dikirim ke jendela yang difokuskan dalam tampilan yang sesuai. Jika tidak, layar akan dikirim ke jendela yang difokuskan di tampilan yang difokuskan, yang merupakan tampilan yang terakhir kali digunakan pengguna.

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

Di WindowManager, jendela yang difokuskan juga dilacak secara terpisah. Lihat DisplayContent#mCurrentFocus dan DisplayContent#mFocusedApp serta penggunaannya masing-masing. Metode pembaruan dan pelacakan fokus terkait telah dipindahkan dari WindowManagerService ke DisplayContent.