Приложения для складных и многоэкранных устройств
В целом, приложения не должны полагаться на статические идентификаторы или логику, зависящую от идентификаторов дисплея. В большинстве случаев приложения должны изменять размер и работать на разных дисплеях, а система должна контролировать местоположение приложений. Например, для создания нового и уникального пользовательского опыта для складных устройств и запуска специального приложения на внешнем экране при складывании устройства.
В этом случае SystemUI (или другой системный компонент) должен обнаружить область сгиба, определить, целесообразно ли выполнять какое-либо действие, а затем запустить целевое действие, указав в качестве целевого объекта запуска внешний идентификатор дисплея. Приложения не должны обнаруживать это действие или выполнять какие-либо действия в ответ, а затем запускать приложение на конкретном дисплее. Другими словами, не следует предполагать, что то, что работает на одном устройстве, будет работать и на других. Короче говоря, код, специфичный для конкретного устройства, увеличивает фрагментацию.
Ограничить доступ к дисплеям
Если конфигурация устройства требует ограничения доступа к одному или нескольким дисплеям, рекомендуется использовать флаг Display#FLAG_PRIVATE для обозначения таких дисплеев как частных . Это ограничит возможность добавления контента на дисплей для всех, кроме владельца. Любая попытка запустить действие или добавить окно кем-либо, кроме владельца, приведет к исключению SecurityException . Если дисплей принадлежит системе, она может добавлять окна и запускать действия.
Кроме того, объекты, размещенные на экране, всегда могут получить доступ к этому экрану. Если владелец запускает действие на экране, то это действие может запустить другие действия на этом экране. Следовательно, владелец несет ответственность за ограничение доступа и разрешение доступа только доверенным приложениям.
Кроме того, к виртуальным дисплеям добавляются дополнительные ограничения, поскольку любое приложение может создать его, не делая его видимым для пользователя. Если виртуальный дисплей не принадлежит системе, то разрешены только действия с allowEmbedded , а вызывающая сторона должна иметь разрешение ACTIVITY_EMBEDDING .
Для получения более подробной информации см.:
-
ActivityStackSupervisor#isCallerAllowedToLaunchOnDisplay() -
ActivityDisplay#isUidPresent() -
DisplayManagerService#isUidPresentOnDisplay()
Для условного управления запуском действий используйте LaunchParamsController , который перехватывает все запуски действий и позволяет системному компоненту изменять параметры, используемые для запуска. Эта функция доступна в system_server .
Настройка параметров отображения и оформления системы.
Системные настройки оформления можно задать для каждого дисплея в DisplayWindowSettings . Для конкретного устройства можно задать конфигурацию по умолчанию в /data/system/display_settings.xml .
Это значение определяет, будут ли системные элементы оформления (панель запуска, обои, панель навигации и другие окна оформления) и IME отображаться на экране. Для получения подробной информации см. 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>
В приведенном выше примере uniqueId используется для идентификации дисплея в атрибуте name, который для имитируемого дисплея имеет 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 , чтобы определить, следует ли дублировать или размещать задачи. Хотя это логика по умолчанию, производители оборудования могут настроить это поведение.
Отображение топологии и перемещения указателя.
В Android 17 и более поздних версиях топология дисплея определяет относительное положение дисплеев и ограничивает перемещение указателя мыши определенным набором дисплеев в этой топологии.
WindowManager принимает решение о включении дисплея в топологию и вызывает DisplayManagerInternal.onDisplayBelongToTopologyChanged . Перед добавлением дисплея DisplayManager проверяет DisplayTopologyCoordinator.isDisplayAllowedInTopology . По умолчанию, если локальные дисплеи могут размещать задачи, система добавляет их.
Если существует несколько общедоступных дисплеев, которые могут размещать задачи, решение о включении дисплея по умолчанию принимается логическим параметром shouldIncludeDefaultDisplayInTopology , который передается в DisplayTopologyCoordinator . Если дисплей по умолчанию является единственным общедоступным дисплеем, который может размещать задачи, он всегда присутствует в топологии. В AOSP логический параметр возвращает true только в том случае, если дисплей по умолчанию поддерживает оконный режим рабочего стола или если параметр безопасности Settings.Secure.INCLUDE_DEFAULT_DISPLAY_IN_TOPOLOGY имеет значение true .
Apps query the current topology using DisplayManager.getDisplayTopology and react to changes to the topology by registering a listener with DisplayManager.registerTopologyListener .