系統裝飾支持

下面提供了這些特定於顯示的區域所做的更新:

系統裝飾品

Android 10 增加了對配置輔助顯示器的支持,以顯示某些系統裝飾,例如桌布、導覽列和啟動器。預設情況下,主顯示器顯示所有系統裝飾,輔助顯示器顯示那些可選啟用的裝飾。對輸入法編輯器 (IME) 的支援可以與其他系統裝飾分開設定。

使用DisplayWindowSettings#setShouldShowSystemDecorsLocked()添加對特定顯示器上的系統裝飾的支援或在/data/system/display_settings.xml中提供預設值。有關範例,請參閱顯示視窗設定

執行

DisplayWindowSettings#setShouldShowSystemDecorsLocked()也暴露在WindowManager#setShouldShowSystemDecors()中用於測試。觸發此方法以啟用系統裝飾不會添加先前遺失的裝飾窗口,也不會刪除它們(如果以前存在)。在大多數情況下,系統裝飾支援的變更只有在設備重新啟動後才能完全生效。

檢查 WindowManager 程式碼庫中對系統裝飾的支援通常透過DisplayContent#supportsSystemDecorations()進行,而檢查外部服務(例如係統 UI 以檢查是否應顯示導覽列)則使用WindowManager#shouldShowSystemDecors() 。若要了解此設定控制的內容,請探索這些方法的呼叫點。

系統UI裝飾窗口

Android 10添加了對導航列的系統裝飾視窗支持,因為導航列對於在 Activity 和應用程式之間進行導航至關重要。預設情況下,導覽列顯示「返回」和「主頁」功能可供性。僅當目標顯示器支援系統裝飾時才包含此內容(請參閱DisplayWindowSettings )。

狀態列是一個比較複雜的系統窗口,因為它還包含通知欄、快速設定和鎖定畫面。在 Android 10 中,輔助顯示器不支援狀態列。因此,通知、設定和完整的鍵盤保護僅在主顯示幕上可用。

輔助螢幕不支援「概述/最近」系統視窗。在 Android 10 中,AOSP 僅在預設顯示器上顯示“最近”,並包含所有顯示器上的活動。從「最近」啟動時,預設情況下,輔助顯示器上的活動將顯示在該顯示器的前面。此方法存在一些已知問題,例如當應用程式出現在其他螢幕上時不會立即更新。

執行

為了實現額外的系統 UI 功能,裝置製造商應使用單一系統 UI 元件來偵聽顯示器的新增/刪除並呈現適當的內容。

支援多顯示 (MD) 的系統 UI 元件應處理以下情況:

  • 啟動時多顯示器初始化
  • 運行時新增的顯示
  • 顯示在運行時被移除

當系統 UI 在 WindowManager 之前偵測到新增顯示時,它會建立競爭條件。可以透過在新增顯示時實作從 WindowManager 到系統 UI 的自訂回呼來避免這種情況,而不是訂閱 DisplayManager .DisplayListener事件。有關參考實現,請參閱CommandQueue.Callbacks#onDisplayReady以了解導航欄支持,並參閱WallpaperManagerInternal#onDisplayReady以了解壁紙。

此外,Android 10 還提供了以下更新:

  • NavigationBarController類別控制特定於導覽列的所有功能。
  • 若要查看自訂導覽列,請參閱CarStatusBar
  • TYPE_NAVIGATION_BAR不再局限於單一實例,並且可以在每個顯示器上使用。
  • IWindowManager#hasNavigationBar()已更新為僅包含系統 UI 的displayId參數。

啟動器

在 Android 10 中,預設情況下,配置為支援系統裝飾的每個顯示器都有一個專用的主堆疊,用於啟動器活動,類型為WindowConfiguration#ACTIVITY_TYPE_HOME 。每個顯示都使用啟動器活動的單獨實例。

圖 1. platform/development/samples/MultiDisplay的多顯示器啟動器範例

大多數現有啟動器不支援多個實例,且未針對大螢幕尺寸進行最佳化。此外,人們通常期望在輔助/外部顯示器上獲得不同類型的體驗。為了為輔助螢幕提供專用 Activity,Android 10 在 Intent 過濾器中引入了SECONDARY_HOME類別。此活動的實例用於所有支援系統裝飾的顯示器,每個顯示器一個。

<activity>
    ...
    <intent-filter>
        <category android:name="android.intent.category.SECONDARY_HOME" />
        ...
    </intent-filter>
</activity>

該活動必須具有不會阻止多個實例的啟動模式,並且預計能夠適應不同的螢幕尺寸。啟動模式不能是singleInstancesingleTask

執行

在 Android 10 中, RootActivityContainer#startHomeOnDisplay()會根據啟動主畫面的顯示器自動選擇所需的元件和意圖。 RootActivityContainer#resolveSecondaryHomeActivity()包含根據目前選擇的啟動器來尋找啟動器活動元件的邏輯,並且可以根據需要使用系統預設值(請參閱ActivityTaskManagerService#getSecondaryHomeIntent() )。

安全限制

除了適用於輔助顯示器上的活動的限制之外,為了避免惡意應用程式創建啟用了系統裝飾的虛擬顯示器並從表面讀取用戶敏感資訊的可能性,啟動器僅出現在系統擁有的虛擬顯示器上。啟動器不會在非系統虛擬顯示器上顯示內容。

壁紙

在 Android 10(及更高版本)中,輔助顯示器支援桌布:

圖 2.內部(上)和外部顯示器(下)上的動態壁紙

開發人員可以透過在WallpaperInfo XML 定義中提供android:supportsMultipleDisplays="true"來聲明對壁紙功能的支援。壁紙開發人員也應該使用WallpaperService.Engine#getDisplayContext()中的顯示上下文載入資源。

此框架為每個顯示器建立一個WallpaperService.Engine實例,因此每個引擎都有自己的表面和顯示上下文。開發人員需要確保每個引擎都可以以不同的幀速率獨立繪製,並尊重 VSYNC。

為各個螢幕選擇壁紙

Android 10 不提供單一螢幕選擇桌布的直接平台支援。為了實現這一點,需要一個穩定的顯示器標識符來保留每個顯示器的壁紙設定。 Display#getDisplayId()是動態的,因此不能保證實體顯示器在重新啟動後將具有相同的 ID。

不過,Android 10 新增了DisplayInfo.mAddress ,其中包含實體顯示器的穩定標識符,可用於未來的完整實作。不幸的是,現在實現 Android 10 的邏輯為時已晚。建議的解決方案:

  1. 使用WallpaperManager API來設定壁紙。
  2. WallpaperManager是從Context物件取得的,每個Context物件都有對應顯示器的資訊( Context#getDisplay()/getDisplayId() )。因此,您可以從WallpaperManager實例中取得displayId ,而無需新增方法。
  3. 在框架方面,使用從Context物件取得的displayId並將其對應到靜態標識符(例如實體顯示器的連接埠)。使用靜態標識符來保留所選壁紙。

此解決方法使用壁紙選擇器的現有實作。如果它是在特定顯示器上開啟並使用正確的上下文,那麼當它呼叫設定壁紙時,系統可以自動識別顯示器。

如果需要為目前顯示器以外的顯示器設定壁紙,則為目標顯示器建立新的Context物件 ( Context#createDisplayContext ) 並從該顯示器取得WallpaperManager實例。

安全限制

系統不會在不屬於它的虛擬顯示器上顯示壁紙。這是出於安全考慮,惡意應用程式可能會創建啟用系統裝飾支援的虛擬顯示器,並從表面讀取用戶敏感資訊(例如個人照片)。

執行

在 Android 10 中, IWallpaperConnection#attachEngine()IWallpaperService#attach()介面接受displayId參數來建立每個顯示器的連線。 WallpaperManagerService.DisplayConnector封裝了每個顯示器的桌布引擎和連接。在 WindowManager 中,壁紙控制器是在建構時為每個DisplayContent物件建立的,而不是為所有顯示器建立單一WallpaperController

一些公共WallpaperManager方法實作(例如WallpaperManager#getDesiredMinimumWidth() )已更新,以計算並提供相應顯示的資訊。新增了WallpaperInfo#supportsMultipleDisplays()和對應的資源屬性,以便應用程式開發人員可以報告哪些壁紙已準備好用於多個螢幕。

如果預設顯示器上顯示的桌布服務不支援多個顯示器,則系統會在輔助顯示器上顯示預設桌布。

圖 3.輔助顯示器的壁紙回退邏輯