Google cam kết thúc đẩy công bằng chủng tộc cho Cộng đồng người da đen. Xem cách thực hiện.

Hỗ trợ hiển thị

Các bản 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 cá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 các hoạt động được thay đổi kích thước bao gồm:

  • Một hoạt động có thể có cấu hình khác với ứng dụng hoặc một thành phần không trực quan khác. Một sai lầm phổ biến là đọc các chỉ số hiển thị từ ngữ cảnh ứng dụng. Các giá trị trả về sẽ không được điều chỉnh theo chỉ số khu vực hiển thị mà trong đó một hoạt động được hiển thị.
  • Một hoạt động có thể không xử lý được việc thay đổi kích thước và 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ì những tọa độ liên quan đến vị trí cửa sổ), điều này có thể phá vỡ đầu vào trong nhiều cửa sổ.

Trong Android 7 (trở lên), một ứng dụng có thể được đặt có thể resizeableActivity=false để luôn chạy ở chế độ toàn màn hình. Trong trường hợp này, nền tảng ngăn các hoạt động không thể thay đổi kích thước đi vào màn hình chia 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, thì 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.

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

  • Cấu hình tương tự được áp dụng cho quy trình, quy trình này chứa tất cả các hoạt động và các thành phần không hoạt động.
  • Cấu hình được áp dụng đáp ứng các yêu cầu của CDD đối với 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 một 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 đa 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à quá trình đượ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 cập nhật.

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

Hình này hiển thị 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 diện tích trong khi vẫn giữ nguyên 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 được hiển thị.

Khi không đặt resizeableActivity (hoặc được đặt thành true ), ứng dụng hoàn toàn hỗ trợ 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ệ co 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 độ) được cố định trong cấu hình ghi đè được yêu cầu, vì vậy hoạt động 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, nó sẽ được căn 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à true và SizeCompatModeActivityController (trong Giao diện người dùng hệ thống) nhận lệnh gọi lại để hiển thị quá 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ệ màn hình dài và mỏng cao đế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. Tỷ lệ ứng dụng mẫu đượ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 yêu cầu của Android 9 và thấp hơn (chiều rộng hoặc chiều cao tối thiểu 2,5 inch, tối thiểu 320 DP cho Màn hình rộng smallestScreenWidth ), nhưng chỉ các hoạt động chọn tham gia để hỗ trợ các màn hình nhỏ này mới có thể được đặt ở đó.

Các ứ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 các thuộc tính bố cục hoạt động android:minHeightandroid:minWidth trong AndroidManifest để làm như vậy.

Chính sách hiển thị

Android 10 tách và di chuyển một số chính sách hiển thị nhất định từ việc triển khai WindowManagerPolicy mặc định trong PhoneWindowManager thành 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 điều này sang lớp DisplayPolicy , ngoại trừ theo dõi xoay vòng, đã đượ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
  • Quét quá giá trị
  • Chế độ xoay và xoay người dùng
  • Kích thước cưỡng bức, mật độ và chế độ chia tỷ lệ
  • Chế độ loại bỏ nội dung (khi loại bỏ màn hình)
  • 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 trữ vào phân vùng /data trong display_settings.xml mỗi khi thay đổi cài đặt. Để biết chi tiết, hãy xem DisplayWindowSettings.AtomicFileStorageDisplayWindowSettings#writeSettings() . Các 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 , có thể cần thêm logic để khôi phục tệp nếu bị xóa bằng cách xóa.

Theo mặc định, Android 10 sử dụng DisplayInfo#uniqueId làm số 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 số nhận dạng, có thể được đặt trong DisplayWindowSettings#mIdentifier . Sau mỗi lần ghi, tất cả các cài đặt đều được ghi nên có thể an toàn cập nhật khóa được sử dụng cho mục hiển thị trong bộ nhớ. Để biết chi tiết, hãy xem Số nhận dạng màn hình tĩnh .

Cài đặt vẫn tồn tại 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 các màn hình trong khuôn khổ. 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, một ID khác sẽ dẫn đến.

Nếu một thiết bị có nhiều màn hình khả dụng khi khởi động, các màn hình có thể được gán các số nhận dạng khác nhau, tùy thuộc vào thời gian. Mặc dù Android 9 (trở về trước) bao gồm DisplayInfo#uniqueId , 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 , để đại diện cho 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 số 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 chứa DisplayAddress , một số nhận dạng hiển thị ổn định qua các lần 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 màn hình tĩ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 các nhà sản xuất thiết bị xác định các màn hình trong thiết lập đa màn hình tĩnh và định cấu hình các cài đặt và tính năng hệ thống khác nhau bằng cách sử dụng các số nhận dạng màn hình tĩnh, chẳng hạn như các cổng cho màn hình vật lý. Các phương thức này được ẩn và chỉ được sử dụng trong system_server .

Với ID hiển thị HWC (có thể không rõ ràng 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 một đầu nối vật lý cho đầu ra màn hình, cũng như đốm màu EDID của màn hình. SurfaceFlinger trích xuất thông tin nhà sản xuất hoặc mô hình từ EDID để tạo ID hiển thị 64-bit ổn định được hiển thị với 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ư 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 nhiều nhất hai màn hình vật lý với ID được mã hóa cứng 0 và 1.

Bắt đầu với Android 10, SurfaceFlinger có thể tận dụng API trình soạn nhạc 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 Số nhận dạng hiển thị tĩnh .

Khung công tác 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 nhận được 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ả các màn hình phụ đều được gắn cờ là TYPE_EXTERNAL bất kể loại kết nối. Do đó, các màn hình bên trong bổ sung được coi là 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.

Hạn chế này đã bị loại bỏ trong Android 11 (và cao hơ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 (bên trong so với bên ngoài) là không liên quan. Tuy nhiên, vẫn đúng rằng màn hình chính không thể bị ngắt kết nối và theo đó nó phải là màn hình bên trong trong thực tế. Lưu ý rằng một số điện thoại có thể gập lại 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 được 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 đại diện cho 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, các màn hình được cung 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 ra 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 màn hình, thì SurfaceFlinger phân tích cú pháp cấu trúc EDID và phân bổ ID hiển thị 64-bit ổn định cho màn hình ảo HWC và vật lý. Các ID được thể hiện bằng cách sử dụng loại tùy chọn, trong đó giá trị null thể hiện 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 hỗ trợ HWC, SurfaceFlinger sẽ trở lại hoạt động cũ với nhiều nhất 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 các màn hình riêng lẻ cùng một lúc, Android 10 có thể được định cấu hình để hỗ trợ nhiều cửa sổ tập trung, nhiều nhất là một cửa sổ trên mỗi màn hình. Điều 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ị tại cùng một thời điểm và sử dụng các phương thức nhập hoặc thiết bị khác nhau, chẳng hạn như Android Automotive.

Chúng tôi đặc biệt 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 mối quan tâm bảo mật có thể khiến người dùng tự hỏi 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 an toàn 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 ra 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ó tiêu điể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, vì đầ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 cao nhất (ứng dụng đó được khởi chạy gần đây nhất), bằng cách tạo màn hình ảo ẩn, một ứng dụng độc hại có thể lấy thông tin nhập 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

Vấn đề: Trong Android 9 trở xuống, nhiều nhất một cửa sổ trong hệ thống có tiêu điểm tại một thời điểm.

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 sẽ được lấy nét, hệ thống chỉ cung cấp tiêu điểm cho cửa sổ cao hơn theo thứ tự Z. Hạn chế này được gỡ bỏ đối với các ứng dụng nhắm mục tiêu đến Android 10, tại thời điểm đó, dự kiến ​​rằng chúng có thể hỗ trợ nhiều cửa sổ được tập trung vào đồng thời.

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 toàn cục 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 vào bộ nhớ đệm giá trị. Điều này để chỉ một nguồn, WindowManager, cần theo dõi thứ tự Z của các hoạt động.

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

InputDispatcher hiện có thể có nhiều cửa sổ tập trung (một cửa sổ trên mỗi màn hình). Nếu một sự kiện đầu vào là sự kiện hiển thị cụ thể, thì sự kiện đó được gửi đến cửa sổ có tiêu điểm trong màn hình tương ứng. Nếu không, nó được gửi đến cửa sổ có tiêu điểm trong màn hình được lấy nét, 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 biệt. Xem DisplayContent#mCurrentFocusDisplayContent#mFocusedApp và các 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 .