На этой странице приведены рекомендации по работе с несколькими дисплеями.
Приложения для складных устройств и устройств с несколькими экранами
Как правило, приложения не должны полагаться на статические идентификаторы или логику, которая зависит от некоторых идентификаторов дисплея. В большинстве случаев приложения должны изменять размер и работать на разных дисплеях, а система должна контролировать, где размещать приложения. Например, вы можете создать уникальный интерфейс для складных устройств и запускать специальное приложение на внешнем экране, когда устройство сложено.
В этом случае интерфейс системы или другой системный компонент должен обнаружить складывание, определить, нужно ли выполнить действие, а затем запустить целевое действие и указать идентификатор внешнего дисплея в качестве цели запуска. Приложения не должны обнаруживать это действие или выполнять какие-либо действия в ответ на него, а затем запускаться на определенном экране. Поэтому не думайте, что то, что работает на одном устройстве, будет работать и на других. Другими словами, код, предназначенный для определенных устройств, приводит к фрагментации.
Ограничить доступ к дисплеям
Если конфигурация устройства требует ограничить доступ к одному или нескольким дисплеям, рекомендуем использовать флаг Display#FLAG_PRIVATE, чтобы обозначить дисплей как частный. Это означает, что добавлять контент на экран может только владелец. Если кто-то другой попытается запустить действие или добавить окно, будет вызвано исключение SecurityException.
Если дисплей принадлежит системе, она может добавлять окна и запускать действия.
Кроме того, объекты, размещенные на дисплее, всегда могут получить доступ к этому дисплею. Если владелец запускает действие на экране, то это действие может запускать другие действия на этом экране. Поэтому владелец устройства должен ограничить доступ и разрешить его только проверенным приложениям.
Кроме того, к виртуальным дисплеям применяются дополнительные ограничения, поскольку любое приложение может создать такой дисплей, не показывая его пользователю. Если виртуальный дисплей не принадлежит системе, разрешены только действия с allowEmbedded, а у вызывающего объекта должно быть разрешение ACTIVITY_EMBEDDING.
Дополнительная информация:
ActivityStackSupervisor#isCallerAllowedToLaunchOnDisplayActivityDisplay#isUidPresentDisplayManagerService#isUidPresentOnDisplay
Чтобы условно управлять запуском действий, используйте LaunchParamsController, который перехватывает все запуски действий и позволяет системному компоненту изменять параметры, используемые для запуска. Эта функция доступна в system_server.
Как настроить параметры окон и системные элементы интерфейса
Системные элементы можно настроить для каждого экрана в DisplayWindowSettings. Реализация устройства может предоставлять конфигурацию по умолчанию в /data/system/display_settings.xml.
Это значение определяет, будут ли на экране показываться системные элементы оформления (запускатор, обои, панель навигации и другие окна оформления) и метод ввода.
Подробнее о том, DisplayWindowSettings#shouldShowSystemDecorsLocked и DisplayWindowSettings#shouldShowImeLocked…
Чтобы идентифицировать дисплей, используйте уникальный идентификатор (по умолчанию используется DisplayInfo#uniqueId) или идентификатор физического порта для аппаратных дисплеев (см. DisplayInfo#address).
Например, в следующем примере конфигурации дисплея на симулированном дисплее включены системные украшения и IME:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?> <display-settings> <config identifier="0" /> <display name="overlay:1" shouldShowSystemDecors="true" shouldShowIme="true" /> </display-settings>
В приведенном выше примере для идентификации дисплея в атрибуте name используется значение uniqueId, которое для имитируемого дисплея равно overlay:1.
Для встроенного дисплея значение может быть таким: "local:45354385242535243453".
Вы также можете использовать информацию о порте устройства и задать для свойства identifier="1" значение, соответствующее DisplayWindowSettings#IDENTIFIER_PORT, а затем изменить название, используя формат "port:<port_id>":
<?xmlversion='1.0' encoding='utf-8' standalone='yes' ?> <display-settings> <config identifier="1" /> <display name="port:12345" shouldShowSystemDecors="true" shouldShowIme="true" /> </display-settings>
Подробнее о статичных идентификаторах дисплея…
Дополнительная информация:
Как переключаться между режимами дублирования экрана и ведущего экрана
В Android 17 и более поздних версий DisplayManager использует флаг FLAG_ALLOWS_CONTENT_MODE_SWITCH, чтобы определить, будет ли дисплей переключаться между зеркальным отображением и размещением задач во время выполнения. По умолчанию этот флаг включен для внешних дисплеев и отключен для всех остальных.
Если присутствует FLAG_ALLOWS_CONTENT_MODE_SWITCH, DisplayManager отслеживает безопасный параметр android.provider.Settings.Secure.MIRROR_BUILT_IN_DISPLAY, чтобы определить, нужно ли зеркалировать или размещать задачи. Хотя это логика по умолчанию, OEM-производители могут настроить это поведение.
Показывать топологию и движение указателя
В Android 17 и более поздних версий топология дисплея определяет относительное позиционирование дисплеев и ограничивает перемещение указателя мыши определенным набором дисплеев в топологии.
WindowManager решает включить дисплей в топологию и вызывает DisplayManagerInternal.onDisplayBelongToTopologyChanged. DisplayManager
проверяет DisplayTopologyCoordinator.isDisplayAllowedInTopology, прежде чем добавить дисплей. По умолчанию система добавляет локальные дисплеи, если они могут выполнять задачи.
Если есть несколько общедоступных дисплеев, на которых можно выполнять задачи, решение о том, включать ли дисплей по умолчанию, принимает поставщик логического значения shouldIncludeDefaultDisplayInTopology, который передается в DisplayTopologyCoordinator. Если экран по умолчанию – единственный общедоступный экран, на котором можно размещать задачи, он всегда присутствует в топологии. В AOSP поставщик логических значений возвращает true, только если дисплей по умолчанию поддерживает многооконный режим или если для защищенного параметра Settings.Secure.INCLUDE_DEFAULT_DISPLAY_IN_TOPOLOGY задано значение true.
Приложения запрашивают текущую топологию с помощью DisplayManager.getDisplayTopology и реагируют на изменения топологии, регистрируя прослушиватель с помощью DisplayManager.registerTopologyListener.