以下是針對這些特定顯示區域所做的更新:
系統裝飾
Android 10 新增了支援功能,可設定次要螢幕顯示特定系統裝飾,例如桌布、導覽列和啟動器。根據預設,主要螢幕會顯示所有系統裝飾,而次要螢幕則會顯示啟用的裝飾。您可以單獨設定輸入法編輯器 (IME) 支援功能,不必與其他系統修飾項目一起設定。
使用 DisplayWindowSettings#setShouldShowSystemDecorsLocked()
新增系統裝飾的支援功能,以便在特定螢幕上顯示,或在 /data/system/display_settings.xml
中提供預設值。如需範例,請參閱「顯示視窗設定」。
實作
DisplayWindowSettings#setShouldShowSystemDecorsLocked()
也會在 WindowManager#setShouldShowSystemDecors()
中公開,供測試使用。透過意圖觸發此方法的意圖時,系統不會新增先前缺少的裝飾視窗,也不會移除先前提供的裝飾視窗。在大多數情況下,系統裝飾變更的支援功能只有在裝置重新啟動後才會完全生效。
檢查 WindowManager 程式碼集中系統裝飾的支援情形時,通常會執行 DisplayContent#supportsSystemDecorations()
,而在檢查外部服務 (例如系統 UI 是否應顯示導覽列) 時,請使用 WindowManager#shouldShowSystemDecors()
。如要瞭解這項設定控管的內容,請探索這些方法的呼叫點。
系統 UI 裝飾視窗
Android 10 僅為導覽列新增系統裝飾視窗支援功能,因為導覽列對於在活動和應用程式之間導覽至關重要。根據預設,導覽列會顯示「返回」和「主畫面」操作元素。只有在目標螢幕支援系統裝飾時,才會加入這項屬性 (請參閱 DisplayWindowSettings
)。
狀態列 是較複雜的系統視窗,因為它還包含通知欄、快速設定和螢幕鎖定畫面。在 Android 10 中,次要顯示器不支援狀態列。因此,通知、設定和完整鍵盤鎖僅適用於主要螢幕。
次要畫面不支援「Overview/Recents」系統視窗。在 Android 10 中,AOSP 只會在預設螢幕上顯示「近期」畫面,並包含所有螢幕的活動。從「最近」啟動時,系統會預設將位於次要螢幕上的活動移至該螢幕畫面。這種做法有一些已知問題,例如應用程式在其他畫面上顯示時不會立即更新。
實作
如要實作其他系統 UI 功能,裝置製造商應使用單一系統 UI 元件,監聽新增/移除螢幕並呈現適當內容。
支援多螢幕 (MD) 的系統 UI 元件應處理下列情況:
- 啟動時多個螢幕初始化
- 在執行階段新增的螢幕
- 已在執行階段移除螢幕
當系統 UI 偵測到在 WindowManager 之前新增顯示畫面時,就會建立競爭狀態。如要避免這種情況,請在新增螢幕時,實作從 WindowManager 到 System 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
的多螢幕啟動器範例
大多數現有的啟動器不支援多個執行個體,也不針對大螢幕尺寸進行最佳化。此外,使用者通常會期待在次要/外部螢幕上獲得不同的體驗。為提供專屬的第二螢幕活動,Android 10 在意圖篩選器中導入 SECONDARY_HOME
類別。這個活動的執行個體會在支援系統裝飾的所有螢幕上使用,每個螢幕上擁有一個執行個體。
<activity> ... <intent-filter> <category android:name="android.intent.category.SECONDARY_HOME" /> ... </intent-filter> </activity>
活動的啟動模式不得阻止多個執行個體,且能夠適應不同的螢幕大小。啟動模式不能是 singleInstance
或 singleTask
。
實作
在 Android 10 中,RootActivityContainer#startHomeOnDisplay()
會根據啟動主畫面的螢幕,自動選取所需元件和意圖。RootActivityContainer#resolveSecondaryHomeActivity()
包含根據目前選取的啟動器查詢啟動器活動元件的邏輯,而且可以視需要使用系統預設值 (請參閱 ActivityTaskManagerService#getSecondaryHomeIntent()
)。
安全性限制
除了適用於次要螢幕上的活動的限制之外,為了避免惡意應用程式建立啟用系統裝飾的虛擬螢幕,並從介面讀取使用者敏感資訊,啟動器只會顯示在系統擁有的虛擬螢幕上。啟動器不會在非系統虛擬螢幕上顯示內容。
桌布
在 Android 10 (及以上版本) 中,系統支援在次要螢幕上顯示桌布:
圖 2. 內部 (上方) 和外部螢幕 (下方) 上的動態桌布
開發人員可以在 WallpaperInfo
XML 定義中提供 android:supportsMultipleDisplays="true"
,宣告對桌布功能的支援。我們也建議桌布開發人員使用 WallpaperService.Engine#getDisplayContext()
中的螢幕背景載入素材資源。
該架構會為每個螢幕建立一個 WallpaperService.Engine
例項,因此每個引擎都有自己的介面和顯示內容。開發人員需要確保每個引擎都能以不同的影格速率獨立繪製,並遵循 VSYNC。
為個別螢幕選取桌布
Android 10 無法直接支援平台為個別畫面選取桌布,為達成這項目標,您需要使用穩定的顯示裝置 ID,才能保留每個顯示裝置的桌布設定。Display#getDisplayId()
是動態的,因此無法保證實體螢幕在重新啟動後會保留相同的 ID。
不過,Android 10 新增了 DisplayInfo.mAddress
,其中包含實體螢幕的穩定 ID,可用於日後的完整實作。很抱歉,現在已無法為 Android 10 實作邏輯。建議解決方案:
- 使用
WallpaperManager
API 設定桌布。 WallpaperManager
是從Context
物件取得,而每個Context
物件都包含對應顯示畫面 (Context#getDisplay()/getDisplayId()
) 的資訊。因此,您可以從WallpaperManager
例項取得displayId
,而無需新增任何方法。- 在架構端,請使用從
Context
物件取得的displayId
,並將其對應至靜態 ID (例如實體螢幕的連接埠)。使用靜態 ID 保留所選桌布。
這個解決方法會使用現有的桌布挑選器實作項目。如果應用程式是在特定螢幕上開啟,且使用正確的內容,當應用程式呼叫設定桌布時,系統就能自動辨識螢幕。
如果需要為目前螢幕以外的螢幕設定桌布,請為目標螢幕 (Context#createDisplayContext
) 建立新的 Context
物件,然後從該螢幕取得 WallpaperManager
例項。
安全性限制
系統不會在虛擬螢幕上顯示非自有的桌布。 這是因為惡意應用程式可能會建立支援系統裝飾功能的虛擬螢幕,並讀取途徑中的使用者私密資訊 (例如個人相片),造成了一些安全疑慮。
實作
在 Android 10 中,IWallpaperConnection#attachEngine()
和 IWallpaperService#attach()
介面會接受 displayId
參數,以建立個別顯示螢幕連線。WallpaperManagerService.DisplayConnector
會封裝每個螢幕的桌布引擎和連線。在 WindowManager 中,系統會在建構時為每個 DisplayContent
物件建立桌布控制器,而不是為所有螢幕建立單一 WallpaperController
。
部分公開 WallpaperManager
方法實作方式 (例如 WallpaperManager#getDesiredMinimumWidth()
) 已更新為運算作業並提供對應螢幕的資訊。WallpaperInfo#supportsMultipleDisplays()
和對應的資源屬性已新增,讓應用程式開發人員能夠回報哪些桌布可用於多個螢幕。
如果預設螢幕上顯示的桌布服務不支援多螢幕,系統會在次要螢幕上顯示預設桌布。
圖 3. 次要螢幕的桌布備用邏輯