hỗ trợ hiển thị

Các cập nhật được thực hiện cho các khu vực dành riêng cho màn hình này được cung cấp bên dưới:

Thay đổi kích thước hoạt động và hiển thị

Để chỉ ra rằng một ứng dụng có thể không hỗ trợ chế độ nhiều cửa sổ hoặc thay đổi kích thước, các hoạt động sử dụng thuộc tính resizeableActivity=false . Các sự cố thường gặp mà ứng dụng gặp phải khi thay đổi kích thước hoạt động bao gồm:

  • Một hoạt động có thể có cấu hình khác với ứng dụng hoặc thành phần không trực quan khác. Một lỗi phổ biến là đọc số liệu hiển thị từ ngữ cảnh ứng dụng. Các giá trị được trả về sẽ không được điều chỉnh theo số liệu vùng hiển thị mà hoạt động được hiển thị trong đó.
  • Một hoạt động có thể không xử lý được việc thay đổi kích thước và gặp sự cố, hiển thị giao diện người dùng bị méo hoặc mất trạng thái do khởi chạy lại mà không lưu trạng thái phiên bản.
  • Một ứng dụng có thể cố gắng sử dụng tọa độ đầu vào tuyệt đối (thay vì tọa độ tương ứng với vị trí cửa sổ), điều này có thể làm hỏng đầu vào trong nhiều cửa sổ.

Trong Android 7 (trở lên), ứng dụng có thể được đặt resizeableActivity=false để luôn chạy ở chế độ toàn màn hình. Trong trường hợp này, nền tảng sẽ ngăn các hoạt động không thể thay đổi kích thước chuyển sang chế độ chia đôi màn hình. Nếu người dùng cố gắng gọi một hoạt động không thể thay đổi kích thước từ trình khởi chạy khi đang ở chế độ chia đôi màn hình, nền tảng sẽ thoát khỏi chế độ chia đôi màn hình và khởi chạy hoạt động không thể thay đổi kích thước ở chế độ toàn màn hình.

Các ứng dụng đặt rõ ràng thuộc tính này thành false trong tệp kê khai không được khởi chạy ở chế độ nhiều cửa sổ, trừ khi áp dụng chế độ tương thích:

  • Cấu hình tương tự được áp dụng cho quy trình, bao gồm tất cả các thành phần hoạt động và không hoạt động.
  • Cấu hình được áp dụng đáp ứng các yêu cầu của CDD dành cho màn hình tương thích với ứng dụng.

Trong Android 10, nền tảng này vẫn ngăn các hoạt động không thể thay đổi kích thước chuyển sang chế độ chia đôi màn hình, nhưng chúng có thể được thu nhỏ tạm thời nếu hoạt động đã khai báo hướng hoặc tỷ lệ khung hình cố định. Nếu không, hoạt động sẽ thay đổi kích thước để lấp đầy toàn bộ màn hình như trong Android 9 trở xuống.

Việc triển khai mặc định áp dụng chính sách sau:

Khi một hoạt động được tuyên bố là không tương thích với nhiều cửa sổ thông qua việc sử dụng thuộc tính android:resizeableActivity và khi hoạt động đó đáp ứng một trong các điều kiện được mô tả bên dưới thì khi cấu hình màn hình được áp dụng phải thay đổi, hoạt động và quy trình đó sẽ được lưu với cấu hình ban đầu và người dùng được cung cấp khả năng khởi chạy lại quy trình ứng dụng để sử dụng cấu hình màn hình đã cập nhật.

  • Định hướng cố định thông qua ứng dụng android:screenOrientation
  • Ứng dụng có tỷ lệ khung hình tối đa hoặc tối thiểu mặc định bằng cách nhắm mục tiêu cấp API hoặc khai báo tỷ lệ khung hình một cách rõ ràng

Hình này hiển thị một hoạt động không thể thay đổi kích thước với tỷ lệ khung hình được khai báo. Khi gập thiết bị, cửa sổ sẽ được thu nhỏ lại để vừa với khu vực trong khi vẫn duy trì tỷ lệ khung hình bằng cách sử dụng hộp thư thích hợp. Ngoài ra, tùy chọn khởi động lại hoạt động được cung cấp cho người dùng mỗi khi khu vực hiển thị cho hoạt động được thay đổi.

Khi mở thiết bị, cấu hình, kích thước và tỷ lệ khung hình của hoạt động không thay đổi nhưng tùy chọn khởi động lại hoạt động sẽ được hiển thị.

Khi resizeableActivity không được đặt (hoặc được đặt thành true ), ứng dụng sẽ hỗ trợ đầy đủ việc thay đổi kích thước.

Thực hiện

Hoạt động không thể thay đổi kích thước với hướng hoặc tỷ lệ khung hình cố định được gọi là chế độ tương thích kích thước (SCM) trong mã. Điều kiện được xác định trong ActivityRecord#shouldUseSizeCompatMode() . Khi một hoạt động SCM được khởi chạy, cấu hình liên quan đến màn hình (chẳng hạn như kích thước hoặc mật độ) sẽ được cố định trong cấu hình ghi đè được yêu cầu, do đó hoạt động này không còn phụ thuộc vào cấu hình hiển thị hiện tại.

Nếu hoạt động SCM không thể lấp đầy toàn bộ màn hình thì nó sẽ được căn chỉnh trên cùng và căn giữa theo chiều ngang. Giới hạn hoạt động được tính toán bởi AppWindowToken#calculateCompatBoundsTransformation() .

Khi một hoạt động SCM sử dụng cấu hình màn hình khác với vùng chứa của nó (ví dụ: màn hình được thay đổi kích thước hoặc hoạt động được chuyển sang màn hình khác), ActivityRecord#inSizeCompatMode() là đúng và SizeCompatModeActivityController (trong Giao diện người dùng hệ thống) nhận được lệnh gọi lại để hiển thị quy trình nút khởi động lại.

Kích thước hiển thị và tỷ lệ khung hình

Android 10 cung cấp hỗ trợ cho các tỷ lệ khung hình mới từ tỷ lệ cao của màn hình dài và mỏng đến tỷ lệ 1:1. Các ứng dụng có thể xác định ApplicationInfo#maxAspectRatioApplicationInfo#minAspectRatio của màn hình mà chúng có thể xử lý.

tỷ lệ ứng dụng trong Android 10

Hình 1. Ví dụ về tỷ lệ ứng dụng được hỗ trợ trong Android 10

Việc triển khai thiết bị có thể có màn hình phụ với kích thước và độ phân giải nhỏ hơn kích thước và độ phân giải mà Android 9 yêu cầu trở xuống (chiều rộng hoặc chiều cao tối thiểu 2,5 inch, tối thiểu 320 DP đối với smallestScreenWidth ), nhưng chỉ những hoạt động chọn tham gia hỗ trợ những màn hình nhỏ này mới có thể được thực hiện được đặt ở đó.

Ứng dụng có thể chọn tham gia bằng cách khai báo kích thước được hỗ trợ tối thiểu nhỏ hơn oe bằng kích thước hiển thị mục tiêu. Sử dụng thuộc tính bố cục hoạt động android:minHeightandroid:minWidth trong AndroidManifest để thực hiện việc này.

Chính sách hiển thị

Android 10 tách biệt và chuyển một số chính sách hiển thị nhất định từ cách triển khai WindowManagerPolicy mặc định trong PhoneWindowManager sang các lớp trên mỗi màn hình, chẳng hạn như:

  • Hiển thị trạng thái và xoay
  • Một số phím và theo dõi sự kiện chuyển động
  • Giao diện người dùng hệ thống và cửa sổ trang trí

Trong Android 9 (trở xuống), lớp PhoneWindowManager xử lý các chính sách hiển thị, trạng thái và cài đặt, xoay, theo dõi khung cửa sổ trang trí, v.v. Android 10 chuyển phần lớn nội dung này sang lớp DisplayPolicy , ngoại trừ tính năng theo dõi xoay đã được chuyển sang DisplayRotation .

Cài đặt cửa sổ hiển thị

Trong Android 10, cài đặt cửa sổ trên mỗi màn hình có thể định cấu hình đã được mở rộng để bao gồm:

  • Chế độ cửa sổ hiển thị mặc định
  • Giá trị quét quá mức
  • Chế độ xoay và xoay người dùng
  • Kích thước, mật độ và chế độ chia tỷ lệ bắt buộc
  • Chế độ xóa nội dung (khi màn hình bị xóa)
  • Hỗ trợ trang trí hệ thống và IME

Lớp DisplayWindowSettings chứa các cài đặt cho các tùy chọn này. Chúng vẫn được lưu vào đĩa trong phân vùng /data trong display_settings.xml mỗi khi cài đặt được thay đổi. Để biết chi tiết, hãy xem DisplayWindowSettings.AtomicFileStorageDisplayWindowSettings#writeSettings() . Nhà sản xuất thiết bị có thể cung cấp các giá trị mặc định trong display_settings.xml cho cấu hình thiết bị của họ. Tuy nhiên, vì tệp được lưu trữ trong /data nên có thể cần logic bổ sung để khôi phục tệp nếu bị xóa bằng thao tác xóa.

Theo mặc định, Android 10 sử dụng DisplayInfo#uniqueId làm giá trị nhận dạng cho màn hình khi duy trì cài đặt. uniqueId phải được điền cho tất cả các màn hình. Ngoài ra, nó ổn định cho màn hình vật lý và mạng. Cũng có thể sử dụng cổng của màn hình vật lý làm mã định danh, cổng này có thể được đặt trong DisplayWindowSettings#mIdentifier . Sau mỗi lần ghi, tất cả cài đặt đều được ghi để việc cập nhật khóa được sử dụng cho mục nhập hiển thị trong bộ nhớ là an toàn. Để biết chi tiết, hãy xem Mã nhận dạng hiển thị tĩnh .

Các cài đặt vẫn được duy trì trong thư mục /data vì lý do lịch sử. Ban đầu, chúng được sử dụng để duy trì các cài đặt do người dùng đặt, chẳng hạn như xoay màn hình.

Số nhận dạng hiển thị tĩnh

Android 9 (trở xuống) không cung cấp số nhận dạng ổn định cho màn hình trong khung. Khi một màn hình được thêm vào hệ thống, Display#mDisplayId hoặc DisplayInfo#displayId được tạo cho màn hình đó bằng cách tăng bộ đếm tĩnh. Nếu hệ thống đã thêm và xóa cùng một màn hình thì sẽ tạo ra một ID khác.

Nếu một thiết bị có sẵn nhiều màn hình khi khởi động, các màn hình đó có thể được gán các mã nhận dạng khác nhau, tùy thuộc vào thời gian. Mặc dù Android 9 (và phiên bản cũ hơn) bao gồm DisplayInfo#uniqueId nhưng nó không chứa đủ thông tin để phân biệt giữa các màn hình vì màn hình vật lý được xác định là local:0 hoặc local:1 , để thể hiện màn hình tích hợp và màn hình bên ngoài.

Android 10 thay đổi DisplayInfo#uniqueId để thêm giá trị nhận dạng ổn định và phân biệt giữa màn hình cục bộ, mạng và màn hình ảo.

Kiểu hiển thị Định dạng
Địa phương
local:<stable-id>
Mạng
network:<mac-address>
Ảo
virtual:<package-name-and-name>

Ngoài các bản cập nhật cho uniqueId , DisplayInfo.address còn chứa DisplayAddress , một mã nhận dạng hiển thị ổn định trong suốt quá trình khởi động lại. Trong Android 10, DisplayAddress hỗ trợ hiển thị vật lý và mạng. DisplayAddress.Physical chứa ID hiển thị ổn định (giống như trong uniqueId ) và có thể được tạo bằng DisplayAddress#fromPhysicalDisplayId() .

Android 10 cũng cung cấp một phương pháp thuận tiện để lấy thông tin cổng ( Physical#getPort() ). Phương pháp này có thể được sử dụng trong khuôn khổ để xác định tĩnh các màn hình. Ví dụ: nó được sử dụng trong DisplayWindowSettings ). DisplayAddress.Network chứa địa chỉ MAC và có thể được tạo bằng DisplayAddress#fromMacAddress() .

Những bổ sung này cho phép nhà sản xuất thiết bị xác định màn hình trong thiết lập nhiều màn hình tĩnh và định cấu hình các tính năng và cài đặt hệ thống khác nhau bằng cách sử dụng mã nhận dạng màn hình tĩnh, chẳng hạn như cổng cho màn hình vật lý. Các phương thức này bị ẩn và chỉ được sử dụng trong system_server .

Với ID hiển thị HWC (có thể mờ và không phải lúc nào cũng ổn định), phương thức này trả về số cổng 8 bit (dành riêng cho nền tảng) xác định đầu nối vật lý cho đầu ra màn hình, cũng như blob EDID của màn hình. SurfaceFlinger trích xuất thông tin về nhà sản xuất hoặc kiểu máy từ EDID để tạo ID hiển thị 64 bit ổn định được hiển thị trên khung. Nếu phương pháp này không được hỗ trợ hoặc xảy ra lỗi, SurfaceFlinger sẽ quay trở lại chế độ MD cũ, trong đó DisplayInfo#address là null và DisplayInfo#uniqueId được mã hóa cứng, như được mô tả ở trên.

Để xác minh rằng tính năng này được hỗ trợ, hãy chạy:

$ 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"

Sử dụng nhiều hơn hai màn hình

Trong Android 9 (trở xuống), SurfaceFlinger và DisplayManagerService giả định sự tồn tại của tối đa hai màn hình vật lý có ID mã hóa cứng là 0 và 1.

Bắt đầu với Android 10, SurfaceFlinger có thể tận dụng API Trình soạn thảo phần cứng (HWC) để tạo ID hiển thị ổn định, cho phép nó quản lý số lượng màn hình vật lý tùy ý. Để tìm hiểu thêm, hãy xem Mã nhận dạng hiển thị tĩnh .

Khung có thể tra cứu mã thông báo IBinder cho màn hình vật lý thông qua SurfaceControl#getPhysicalDisplayToken sau khi lấy ID hiển thị 64-bit từ SurfaceControl#getPhysicalDisplayIds hoặc từ sự kiện cắm nóng DisplayEventReceiver .

Trong Android 10 (trở xuống), màn hình chính bên trong là TYPE_INTERNAL và tất cả màn hình phụ được gắn cờ là TYPE_EXTERNAL bất kể loại kết nối. Do đó, các màn hình bổ sung bên trong được coi là màn hình bên ngoài. Như một giải pháp thay thế, mã dành riêng cho thiết bị có thể đưa ra các giả định về DisplayAddress.Physical#getPort nếu HWC được biết và logic phân bổ cổng có thể dự đoán được.

Giới hạn này được loại bỏ trong Android 11 (trở lên).

  • Trong Android 11, màn hình đầu tiên được báo cáo trong quá trình khởi động là màn hình chính. Loại kết nối (nội bộ và bên ngoài) không liên quan. Tuy nhiên, vẫn đúng là màn hình chính không thể bị ngắt kết nối và trên thực tế, nó phải là màn hình bên trong. Lưu ý rằng một số điện thoại màn hình gập có nhiều màn hình bên trong.
  • Màn hình phụ được phân loại chính xác thành Display.TYPE_INTERNAL hoặc Display.TYPE_EXTERNAL (trước đây gọi là Display.TYPE_BUILT_INDisplay.TYPE_HDMI tương ứng) tùy thuộc vào loại kết nối của chúng.

Thực hiện

Trong Android 9 trở xuống, màn hình được xác định bằng ID 32 bit, trong đó 0 là màn hình bên trong, 1 là màn hình bên ngoài, [2, INT32_MAX] là màn hình ảo HWC và -1 biểu thị màn hình không hợp lệ hoặc không phải HWC hiển thị ảo.

Bắt đầu với Android 10, màn hình được cấp ID ổn định và liên tục, cho phép SurfaceFlinger và DisplayManagerService theo dõi nhiều hơn hai màn hình và nhận dạng các màn hình đã thấy trước đó. Nếu HWC hỗ trợ IComposerClient.getDisplayIdentificationData và cung cấp dữ liệu nhận dạng hiển thị, SurfaceFlinger sẽ phân tích cấu trúc EDID và phân bổ ID hiển thị 64 bit ổn định cho màn hình vật lý và ảo HWC. ID được thể hiện bằng loại tùy chọn, trong đó giá trị null biểu thị màn hình không hợp lệ hoặc màn hình ảo không phải HWC. Nếu không có sự hỗ trợ của HWC, SurfaceFlinger sẽ quay lại hoạt động cũ với tối đa hai màn hình vật lý.

Tiêu điểm trên mỗi màn hình

Để hỗ trợ một số nguồn đầu vào nhắm mục tiêu đến từng màn hình cùng lúc, Android 10 có thể được định cấu hình để hỗ trợ nhiều cửa sổ tập trung, tối đa một cửa sổ cho mỗi màn hình. Tính năng này chỉ dành cho các loại thiết bị đặc biệt khi nhiều người dùng tương tác với cùng một thiết bị cùng lúc và sử dụng các thiết bị hoặc phương thức nhập khác nhau, chẳng hạn như Android Automotive.

Chúng tôi thực sự khuyên bạn không nên bật tính năng này cho các thiết bị thông thường, bao gồm cả thiết bị nhiều màn hình hoặc những thiết bị được sử dụng cho trải nghiệm giống như máy tính để bàn. Điều này chủ yếu là do lo ngại về bảo mật có thể khiến người dùng thắc mắc cửa sổ nào có tiêu điểm đầu vào.

Hãy tưởng tượng người dùng nhập thông tin bảo mật vào trường nhập văn bản, có thể đăng nhập vào ứng dụng ngân hàng hoặc nhập văn bản có chứa thông tin nhạy cảm. Một ứng dụng độc hại có thể tạo một màn hình ảo ngoài màn hình để thực hiện một hoạt động, cũng với trường nhập văn bản. Các hoạt động hợp pháp và độc hại đều có trọng tâm và cả hai đều hiển thị chỉ báo đầu vào đang hoạt động (con trỏ nhấp nháy).

Tuy nhiên, do đầu vào từ bàn phím (phần cứng hoặc phần mềm) chỉ được nhập vào hoạt động trên cùng (ứng dụng được khởi chạy gần đây nhất), bằng cách tạo một màn hình ảo ẩn, một ứng dụng độc hại có thể lấy thông tin đầu vào của người dùng, ngay cả khi sử dụng bàn phím phần mềm. trên màn hình thiết bị chính.

Sử dụng com.android.internal.R.bool.config_perDisplayFocusEnabled để đặt tiêu điểm cho mỗi màn hình.

Khả năng tương thích

Sự cố: Trong Android 9 trở xuống, tại một thời điểm, chỉ có tối đa một cửa sổ trong hệ thống được tập trung.

Giải pháp: Trong trường hợp hiếm hoi khi hai cửa sổ từ cùng một quy trình được tập trung, hệ thống chỉ tập trung vào cửa sổ cao hơn theo thứ tự Z. Hạn chế này được loại bỏ đối với các ứng dụng nhắm mục tiêu Android 10, tại thời điểm đó, người ta mong đợi rằng chúng có thể hỗ trợ nhiều cửa sổ được tập trung vào cùng một lúc.

Thực hiện

WindowManagerService#mPerDisplayFocusEnabled kiểm soát tính khả dụng của tính năng này. Trong ActivityManager , ActivityDisplay#getFocusedStack() hiện được sử dụng thay vì theo dõi chung trong một biến. ActivityDisplay#getFocusedStack() xác định tiêu điểm dựa trên thứ tự Z thay vì lưu giá trị vào bộ nhớ đệm. Điều này là để chỉ có một nguồn, WindowManager, cần theo dõi thứ tự Z của các hoạt động.

ActivityStackSupervisor#getTopDisplayFocusedStack() thực hiện cách tiếp cận tương tự cho những trường hợp khi phải xác định ngăn xếp tập trung cao nhất trong hệ thống. Các ngăn xếp được duyệt từ trên xuống dưới, tìm kiếm ngăn xếp đủ điều kiện đầu tiên.

Hiện tại, InputDispatcher có thể có nhiều cửa sổ tập trung (mỗi cửa sổ một màn hình). Nếu sự kiện đầu vào dành riêng cho màn hình thì sự kiện đó sẽ được gửi đến cửa sổ có tiêu điểm trên màn hình tương ứng. Nếu không, nó sẽ được gửi đến cửa sổ tập trung trong màn hình tập trung, đây là màn hình mà người dùng tương tác gần đây nhất.

Xem InputDispatcher::mFocusedWindowHandlesByDisplayInputDispatcher::setFocusedDisplay() . Các ứng dụng tập trung cũng được cập nhật riêng trong inputManagerService thông qua NativeInputManager::setFocusedApplication() .

Trong WindowManager , các cửa sổ tập trung cũng được theo dõi riêng. Xem DisplayContent#mCurrentFocusDisplayContent#mFocusedApp và cách sử dụng tương ứng. Các phương pháp cập nhật và theo dõi tiêu điểm liên quan đã được chuyển từ WindowManagerService sang DisplayContent .