Встраивание активности

Встраивание действий оптимизирует приложения на устройствах с большим экраном, разделяя окно задач приложения между двумя действиями или двумя экземплярами одного и того же действия.

Рисунок 1. Приложение настроек с рядом действий.

Если ваше приложение состоит из нескольких действий, внедрение действий позволит вам улучшить взаимодействие с пользователем на планшетах, складных устройствах и устройствах ChromeOS.

Внедрение активности не требует рефакторинга кода. Вы определяете, как ваше приложение отображает свои действия — рядом или в стопке — путем создания файла конфигурации XML или путем выполнения вызовов API Jetpack WindowManager .

Поддержка маленьких экранов поддерживается автоматически. Когда ваше приложение установлено на устройстве с маленьким экраном, действия располагаются одно поверх другого. На больших экранах действия отображаются рядом. Система определяет представление на основе созданной вами конфигурации — логика ветвления не требуется.

Встраивание действий учитывает изменения ориентации устройства и безупречно работает на складных устройствах, складывая и раскладывая действия по мере того, как устройство складывается и раскладывается.

Встраивание действий поддерживается на большинстве устройств с большим экраном под управлением Android 12L (уровень API 32) и выше.

Разделить окно задач

Встраивание действий разделяет окно задач приложения на два контейнера: основной и дополнительный. Контейнеры содержат действия, запущенные из основного действия или из других действий, уже находящихся в контейнерах.

Действия располагаются во вторичном контейнере при их запуске, а вторичный контейнер размещается поверх основного контейнера на маленьких экранах, поэтому расположение действий и обратная навигация соответствуют порядку действий, уже встроенному в ваше приложение.

Внедрение действий позволяет отображать действия различными способами. Ваше приложение может разделить окно задач, одновременно запуская два действия одновременно:

Рисунок 2. Два действия рядом.

Или действие, занимающее все окно задачи, может создать разделение, запустив рядом с ним новое действие:

Рисунок 3. Действие A запускает действие B в стороне.

Действия, которые уже разделены и совместно используют окно задач, могут запускать другие действия следующими способами:

  • В сторону поверх другого действия:

    Рисунок 4. Действие A начинает действие C сбоку от действия B.
  • В сторону и сдвиньте раскол в сторону, скрывая предыдущую основную деятельность:

    Рисунок 5. Действие B запускает действие C в сторону и смещает разделение в сторону.
  • Запустить активность на месте сверху; то есть в том же стеке действий:

    Рисунок 6. Действие B запускает действие C без дополнительных флагов намерения.
  • Запустите полное окно активности в той же задаче:

    Рисунок 7. Действие A или действие B запускает действие C, которое заполняет окно задачи.

Обратная навигация

Различные типы приложений могут иметь разные правила обратной навигации в состоянии разделенного окна задачи в зависимости от зависимостей между действиями или того, как пользователи запускают обратное событие, например:

  • Объединение: если действия связаны и одно не должно отображаться без другого, можно настроить обратную навигацию для завершения обоих.
  • Работа в одиночку: если действия полностью независимы, обратная навигация по действию не влияет на состояние другого действия в окне задачи.

Событие Back отправляется на последнее сфокусированное действие при использовании навигации с помощью кнопок.

Для навигации на основе жестов:

  • Android 14 (уровень API 34) и более ранние версии — событие Back отправляется на действие, в котором произошел жест. Когда пользователи проводят пальцем по левому краю экрана, обратное событие отправляется на действие на левой панели разделенного окна. Когда пользователи проводят пальцем от правой части экрана, обратное событие отправляется на действие на правой панели.

  • Android 15 (уровень API 35) и выше

    • При работе с несколькими действиями в одном приложении жест завершает верхнее действие независимо от направления смахивания, обеспечивая более унифицированный интерфейс.

    • В сценариях, включающих два действия из разных приложений (оверлей), событие Back направляется на последнее действие в фокусе, что соответствует поведению навигации по кнопкам.

Многопанельный макет

Jetpack WindowManager позволяет создавать действия, встраивающие многопанельный макет, на устройствах с большим экраном под управлением Android 12L (уровень API 32) или выше, а также на некоторых устройствах с более ранними версиями платформы. Существующие приложения, основанные на нескольких действиях, а не на фрагментах или макетах на основе представлений, такие как SlidingPaneLayout могут обеспечить улучшенное взаимодействие с пользователем на большом экране без рефакторинга исходного кода.

Одним из распространенных примеров является разделение списка на детали. Чтобы обеспечить качественное представление, система запускает действие списка, а затем приложение сразу же запускает действие детализации. Система перехода ждет, пока будут нарисованы оба действия, а затем отображает их вместе. Для пользователя эти два действия запускаются как одно.

Рисунок 8. Два действия запускаются одновременно в многопанельном макете.

Разделить атрибуты

Вы можете указать, как распределяется окно задачи между разделенными контейнерами и как контейнеры располагаются относительно друг друга.

Для правил, определенных в файле конфигурации XML, установите следующие атрибуты:

  • splitRatio : устанавливает пропорции контейнера. Значение представляет собой число с плавающей запятой в открытом интервале (0,0, 1,0).
  • splitLayoutDirection : определяет расположение разделенных контейнеров относительно друг друга. Ценности включают в себя:
    • ltr : слева направо
    • rtl : справа налево
    • locale : либо ltr , либо rtl определяется настройкой локали.

Примеры см. в разделе конфигурации XML .

Для правил, созданных с использованием API WindowManager, создайте объект SplitAttributes с помощью SplitAttributes.Builder и вызовите следующие методы компоновщика:

  • setSplitType() : устанавливает пропорции разделенных контейнеров. Допустимые аргументы, включая метод SplitAttributes.SplitType.ratio() , см. в разделе SplitAttributes.SplitType
  • setLayoutDirection() : устанавливает макет контейнеров. Возможные значения см. в разделе SplitAttributes.LayoutDirection .

Примеры см. в разделе API WindowManager .

Рисунок 9. Два разделения активности, расположенные слева направо, но с разными коэффициентами разделения.

Заполнители

Действия-заполнители — это пустые второстепенные действия, занимающие область разделения действий. В конечном итоге они предназначены для замены другим действием, содержащим контент. Например, действие-заполнитель может занимать второстепенную сторону разделения действия в макете с подробностями списка до тех пор, пока элемент из списка не будет выбран, после чего действие, содержащее подробную информацию для выбранного элемента списка, заменяет заполнитель.

По умолчанию система отображает заполнители только тогда, когда достаточно места для разделения действий. Заполнители автоматически завершают работу, когда размер экрана изменяется на ширину или высоту, слишком малую для отображения разделения. Если позволяет место, система перезапускает заполнитель с повторно инициализированным состоянием.

Рисунок 10. Складное устройство складывается и раскладывается. Действие заполнителя завершается и создается заново при изменении размера дисплея.

Однако атрибут stickyPlaceholder метода SplitPlaceholderRule или setSticky() метода SplitPlaceholder.Builder может переопределить поведение по умолчанию. Если атрибут или метод задает значение true , система отображает заполнитель как самое верхнее действие в окне задачи, когда размер дисплея изменяется до однопанельного с двухпанельного (пример см. в разделе Конфигурация разделения ). .

Рисунок 11. Складное устройство складывается и раскладывается. Активность заполнителя является липкой.

Изменение размера окна

При изменении конфигурации устройства уменьшите ширину окна задачи, чтобы она не была достаточно большой для многопанельного макета (например, когда складное устройство с большим экраном складывается из размера планшета до размера телефона или размер окна приложения изменяется в многооконном режиме). ), действия, не являющиеся заполнителями, на дополнительной панели окна задач располагаются поверх действий на основной панели.

Действия-заполнители отображаются только в том случае, если ширины дисплея достаточно для разделения. На экранах меньшего размера заполнитель автоматически закрывается. Когда область отображения снова становится достаточно большой, заполнитель создается заново. (См. раздел «Заполнители ».)

Объединение действий возможно, поскольку WindowManager z-упорядочивает действия на вторичной панели над действиями на основной панели.

Несколько действий на дополнительной панели

Действие B запускает действие C на месте без дополнительных флагов намерений:

Разделение действий, содержащее действия A, B и C, причем C расположен поверх B.

в результате получается следующий z-порядок действий в одной задаче:

Вторичная стопка действий, содержащая деятельность C, наложенную поверх операции B. Вторичная стопка наложена поверх основной стопки действий, содержащей операцию A.

Таким образом, в меньшем окне задачи приложение сжимается до одного действия с C на вершине стека:

Небольшое окно, показывающее только действие C.

Возвращаясь назад в меньшем окне, вы перемещаетесь по действиям, расположенным друг над другом.

Если конфигурация окна задачи восстанавливается до большего размера, позволяющего разместить несколько панелей, действия снова отображаются рядом.

Сложные расколы

Действие B запускает действие C в сторону и смещает разделение в сторону:

Окно задач, показывающее действия A и B, затем действия B и C.

В результате получается следующий z-порядок действий в одной задаче:

Действия A, B и C в одном стопке. Действия расположены в следующем порядке сверху вниз: C, B, A.

В меньшем окне задачи приложение сжимается до одного действия с буквой C сверху:

Небольшое окно, показывающее только действие C.

Фиксированная портретная ориентация

Параметр манифеста android:screenOrientation позволяет приложениям ограничивать действия книжной или альбомной ориентацией. Чтобы улучшить взаимодействие с пользователем на устройствах с большим экраном, таких как планшеты и складные устройства, производители устройств (OEM) могут игнорировать запросы на ориентацию экрана и переводить приложение в почтовый ящик в книжной ориентации на альбомных дисплеях или в альбомной ориентации на книжных дисплеях.

Рисунок 12. Действия в почтовом ящике: фиксированная ориентация на альбомном устройстве (слева), фиксированная альбомная ориентация на книжном устройстве (справа).

Аналогичным образом, когда включено встраивание действий, OEM-производители могут настраивать устройства для отображения действий в почтовом ящике с фиксированной книжной ориентацией в альбомной ориентации на больших экранах (ширина ≥ 600 точек). Когда действие с фиксированным портретом запускает второе действие, устройство может отображать два действия рядом на двухпанельном дисплее.

Рисунок 13. Действие A с фиксированным портретом запускает действие B в сторону.

Всегда добавляйте свойство android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED в файл манифеста вашего приложения, чтобы информировать устройства о том, что ваше приложение поддерживает внедрение активности (см. раздел конфигурации разделения ). Устройства, настроенные OEM-производителями, могут затем определить, следует ли использовать фиксированный портретный режим почтового ящика.

Разделенная конфигурация

Правила разделения настраивают разделение активности. Вы определяете правила разделения в файле конфигурации XML или с помощью вызовов API Jetpack WindowManager .

В любом случае ваше приложение должно получить доступ к библиотеке WindowManager и сообщить системе, что приложение реализовало внедрение активности.

Сделайте следующее:

  1. Добавьте последнюю зависимость библиотеки WindowManager в файл build.gradle уровня модуля вашего приложения, например:

    implementation 'androidx.window:window:1.1.0-beta02'

    Библиотека WindowManager предоставляет все компоненты, необходимые для внедрения действий.

  2. Сообщите системе, что в вашем приложении реализовано внедрение активности.

    Добавьте свойство android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED в элемент <application> файла манифеста приложения и установите значение true, например:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android">
        <application>
            <property
                android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED"
                android:value="true" />
        </application>
    </manifest>
    

    В выпуске WindowManager 1.1.0-alpha06 и более поздних версиях разделения внедрения действий отключены, если только свойство не добавлено в манифест и не установлено значение true.

    Кроме того, производители устройств используют этот параметр для включения пользовательских возможностей для приложений, поддерживающих внедрение действий. Например, устройства могут помещать в почтовый ящик действие только в книжной ориентации на альбомных дисплеях, чтобы ориентировать действие для перехода к двухпанельному макету при запуске второго действия (см. Фиксированная книжная ориентация ).

XML-конфигурация

Чтобы создать реализацию внедрения действий на основе XML, выполните следующие шаги:

  1. Создайте файл ресурсов XML, который выполняет следующие действия:

    • Определяет действия, которые разделяют разделение
    • Настраивает параметры разделения
    • Создает заполнитель для вторичного контейнера разделения, когда контент недоступен.
    • Определяет действия, которые никогда не должны быть частью разделения.

    Например:

    <!-- main_split_config.xml -->
    
    <resources
        xmlns:window="http://schemas.android.com/apk/res-auto">
    
        <!-- Define a split for the named activities. -->
        <SplitPairRule
            window:splitRatio="0.33"
            window:splitLayoutDirection="locale"
            window:splitMinWidthDp="840"
            window:splitMaxAspectRatioInPortrait="alwaysAllow"
            window:finishPrimaryWithSecondary="never"
            window:finishSecondaryWithPrimary="always"
            window:clearTop="false">
            <SplitPairFilter
                window:primaryActivityName=".ListActivity"
                window:secondaryActivityName=".DetailActivity"/>
        </SplitPairRule>
    
        <!-- Specify a placeholder for the secondary container when content is
             not available. -->
        <SplitPlaceholderRule
            window:placeholderActivityName=".PlaceholderActivity"
            window:splitRatio="0.33"
            window:splitLayoutDirection="locale"
            window:splitMinWidthDp="840"
            window:splitMaxAspectRatioInPortrait="alwaysAllow"
            window:stickyPlaceholder="false">
            <ActivityFilter
                window:activityName=".ListActivity"/>
        </SplitPlaceholderRule>
    
        <!-- Define activities that should never be part of a split. Note: Takes
             precedence over other split rules for the activity named in the
             rule. -->
        <ActivityRule
            window:alwaysExpand="true">
            <ActivityFilter
                window:activityName=".ExpandedActivity"/>
        </ActivityRule>
    
    </resources>
    
  2. Создайте инициализатор.

    Компонент WindowManager RuleController анализирует файл конфигурации XML и делает правила доступными для системы. Initializer библиотеки запуска Jetpack делает XML-файл доступным для RuleController при запуске приложения, чтобы правила вступили в силу при запуске любых действий.

    Чтобы создать инициализатор, выполните следующие действия:

    1. Добавьте последнюю зависимость библиотеки запуска Jetpack в файл build.gradle уровня модуля, например:

      implementation 'androidx.startup:startup-runtime:1.1.1'

    2. Создайте класс, реализующий интерфейс Initializer .

      Инициализатор делает правила разделения доступными для RuleController , передавая идентификатор файла конфигурации XML ( main_split_config.xml ) методу RuleController.parseRules() .

      Котлин

      class SplitInitializer : Initializer<RuleController> {
      
          override fun create(context: Context): RuleController {
              return RuleController.getInstance(context).apply {
                  setRules(RuleController.parseRules(context, R.xml.main_split_config))
              }
          }
      
          override fun dependencies(): List<Class<out Initializer<*>>> {
              return emptyList()
          }
      }

      Ява

      public class SplitInitializer implements Initializer<RuleController> {
      
           @NonNull
           @Override
           public RuleController create(@NonNull Context context) {
               RuleController ruleController = RuleController.getInstance(context);
               ruleController.setRules(
                   RuleController.parseRules(context, R.xml.main_split_config)
               );
               return ruleController;
           }
      
           @NonNull
           @Override
           public List<Class<? extends Initializer<?>>> dependencies() {
               return Collections.emptyList();
           }
      }
  3. Создайте поставщика контента для определений правил.

    Добавьте androidx.startup.InitializationProvider в файл манифеста приложения в качестве <provider> . Включите ссылку на реализацию инициализатора RuleController SplitInitializer :

    <!-- AndroidManifest.xml -->
    
    <provider android:name="androidx.startup.InitializationProvider"
        android:authorities="${applicationId}.androidx-startup"
        android:exported="false"
        tools:node="merge">
        <!-- Make SplitInitializer discoverable by InitializationProvider. -->
        <meta-data android:name="${applicationId}.SplitInitializer"
            android:value="androidx.startup" />
    </provider>
    

    InitializationProvider обнаруживает и инициализирует SplitInitializer перед вызовом метода onCreate() приложения. В результате правила разделения действуют при запуске основного действия приложения.

API оконного менеджера

Вы можете реализовать внедрение активности программно с помощью нескольких вызовов API. Выполните вызовы метода onCreate() подкласса Application чтобы убедиться в том, что правила действуют до запуска каких-либо действий.

Чтобы программно создать разделение активности, выполните следующие действия:

  1. Создайте правило разделения:

    1. Создайте SplitPairFilter , который идентифицирует действия, которые разделяют разделение:

      Котлин

      val splitPairFilter = SplitPairFilter(
         ComponentName(this, ListActivity::class.java),
         ComponentName(this, DetailActivity::class.java),
         null
      )

      Ява

      SplitPairFilter splitPairFilter = new SplitPairFilter(
         new ComponentName(this, ListActivity.class),
         new ComponentName(this, DetailActivity.class),
         null
      );
    2. Добавьте фильтр в набор фильтров:

      Котлин

      val filterSet = setOf(splitPairFilter)

      Ява

      Set<SplitPairFilter> filterSet = new HashSet<>();
      filterSet.add(splitPairFilter);
    3. Создайте атрибуты макета для разделения:

      Котлин

      val splitAttributes: SplitAttributes = SplitAttributes.Builder()
          .setSplitType(SplitAttributes.SplitType.ratio(0.33f))
          .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
          .build()

      Ява

      final SplitAttributes splitAttributes = new SplitAttributes.Builder()
            .setSplitType(SplitAttributes.SplitType.ratio(0.33f))
            .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
            .build();

      SplitAttributes.Builder создает объект, содержащий атрибуты макета:

      • setSplitType : определяет, как доступная область отображения распределяется по каждому контейнеру активности. Тип разделения соотношения определяет долю доступной области отображения, выделенную основному контейнеру; вторичный контейнер занимает оставшуюся часть доступной области отображения.
      • setLayoutDirection : определяет, как контейнеры действий располагаются относительно друг друга, сначала основной контейнер.
    4. Создайте SplitPairRule :

      Котлин

      val splitPairRule = SplitPairRule.Builder(filterSet)
          .setDefaultSplitAttributes(splitAttributes)
          .setMinWidthDp(840)
          .setMinSmallestWidthDp(600)
          .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
          .setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.NEVER)
          .setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ALWAYS)
          .setClearTop(false)
          .build()

      Ява

      SplitPairRule splitPairRule = new SplitPairRule.Builder(filterSet)
          .setDefaultSplitAttributes(splitAttributes)
          .setMinWidthDp(840)
          .setMinSmallestWidthDp(600)
          .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
          .setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.NEVER)
          .setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ALWAYS)
          .setClearTop(false)
          .build();

      SplitPairRule.Builder создает и настраивает правило:

      • filterSet : Содержит фильтры разделенной пары, которые определяют, когда применять правило, путем определения действий, которые разделяют разделение.
      • setDefaultSplitAttributes : применяет атрибуты макета к правилу.
      • setMinWidthDp : устанавливает минимальную ширину дисплея (в пикселях, не зависящих от плотности, dp), которая обеспечивает разделение.
      • setMinSmallestWidthDp : устанавливает минимальное значение (в dp), которое должен иметь меньший из двух размеров дисплея, чтобы включить разделение независимо от ориентации устройства.
      • setMaxAspectRatioInPortrait : устанавливает максимальное соотношение сторон экрана (высота:ширина) в книжной ориентации, для которого отображаются разделения активности. Если соотношение сторон портретного дисплея превышает максимальное соотношение сторон, разделение отключается независимо от ширины дисплея. Примечание. Значение по умолчанию — 1,4, в результате чего на большинстве планшетов действия занимают все окно задач в книжной ориентации. См. также SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT и setMaxAspectRatioInLandscape . Значение по умолчанию для ландшафта — ALWAYS_ALLOW .
      • setFinishPrimaryWithSecondary : устанавливает, как завершение всех действий во вторичном контейнере влияет на действия в основном контейнере. NEVER указывает, что система не должна завершать основные действия после завершения всех действий во вторичном контейнере (см. Завершение действий ).
      • setFinishSecondaryWithPrimary : устанавливает, как завершение всех действий в основном контейнере влияет на действия во вторичном контейнере. ALWAYS указывает, что система всегда должна завершать действия во вторичном контейнере, когда завершаются все действия в основном контейнере (см. Завершение действий ).
      • setClearTop : указывает, завершаются ли все действия во вторичном контейнере, когда в контейнере запускается новое действие. Значение False указывает, что новые действия располагаются поверх действий, уже находящихся во вторичном контейнере.
    5. Получите одноэлементный экземпляр WindowManager RuleController и добавьте правило:

      Котлин

        val ruleController = RuleController.getInstance(this)
        ruleController.addRule(splitPairRule)
        

      Ява

        RuleController ruleController = RuleController.getInstance(this);
        ruleController.addRule(splitPairRule);
        
  2. Создайте заполнитель для вторичного контейнера, если контент недоступен:

    1. Создайте ActivityFilter , который идентифицирует действие, с которым заполнитель разделяет разделение окна задачи:

      Котлин

      val placeholderActivityFilter = ActivityFilter(
          ComponentName(this, ListActivity::class.java),
          null
      )

      Ява

      ActivityFilter placeholderActivityFilter = new ActivityFilter(
          new ComponentName(this, ListActivity.class),
          null
      );
    2. Добавьте фильтр в набор фильтров:

      Котлин

      val placeholderActivityFilterSet = setOf(placeholderActivityFilter)

      Ява

      Set<ActivityFilter> placeholderActivityFilterSet = new HashSet<>();
      placeholderActivityFilterSet.add(placeholderActivityFilter);
    3. Создайте SplitPlaceholderRule :

      Котлин

      val splitPlaceholderRule = SplitPlaceholderRule.Builder(
            placeholderActivityFilterSet,
            Intent(context, PlaceholderActivity::class.java)
          ).setDefaultSplitAttributes(splitAttributes)
           .setMinWidthDp(840)
           .setMinSmallestWidthDp(600)
           .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
           .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ALWAYS)
           .setSticky(false)
           .build()

      Ява

      SplitPlaceholderRule splitPlaceholderRule = new SplitPlaceholderRule.Builder(
            placeholderActivityFilterSet,
            new Intent(context, PlaceholderActivity.class)
          ).setDefaultSplitAttributes(splitAttributes)
           .setMinWidthDp(840)
           .setMinSmallestWidthDp(600)
           .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
           .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ALWAYS)
           .setSticky(false)
           .build();

      SplitPlaceholderRule.Builder создает и настраивает правило:

      • placeholderActivityFilterSet : содержит фильтры действий, которые определяют, когда применять правило, путем определения действий, с которыми связано действие-заполнитель.
      • Intent : определяет запуск действия-заполнителя.
      • setDefaultSplitAttributes : применяет атрибуты макета к правилу.
      • setMinWidthDp : устанавливает минимальную ширину дисплея (в пикселях, не зависящих от плотности, dp), при которой допускается разделение.
      • setMinSmallestWidthDp : устанавливает минимальное значение (в dp), которое должен иметь меньший из двух размеров дисплея, чтобы можно было разделить независимо от ориентации устройства.
      • setMaxAspectRatioInPortrait : устанавливает максимальное соотношение сторон экрана (высота:ширина) в книжной ориентации, для которого отображаются разделения активности. Примечание. Значение по умолчанию — 1,4, в результате чего на большинстве планшетов действия заполняют окно задачи в книжной ориентации. См. также SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT и setMaxAspectRatioInLandscape . Значение по умолчанию для ландшафта — ALWAYS_ALLOW .
      • setFinishPrimaryWithPlaceholder : устанавливает, как завершение действия-заполнителя влияет на действия в основном контейнере. ВСЕГДА указывает, что система всегда должна завершать действия в основном контейнере после завершения заполнителя (см. Завершение действий ).
      • setSticky : определяет, появляется ли действие-заполнитель поверх стека действий на небольших дисплеях после того, как заполнитель впервые появился в разделении с достаточной минимальной шириной.
    4. Добавьте правило в RuleController WindowManager:

      Котлин

      ruleController.addRule(splitPlaceholderRule)

      Ява

      ruleController.addRule(splitPlaceholderRule);
  3. Укажите действия, которые никогда не должны быть частью разделения:

    1. Создайте ActivityFilter , который идентифицирует действие, которое всегда должно занимать всю область отображения задачи:

      Котлин

      val expandedActivityFilter = ActivityFilter(
        ComponentName(this, ExpandedActivity::class.java),
        null
      )

      Ява

      ActivityFilter expandedActivityFilter = new ActivityFilter(
        new ComponentName(this, ExpandedActivity.class),
        null
      );
    2. Добавьте фильтр в набор фильтров:

      Котлин

      val expandedActivityFilterSet = setOf(expandedActivityFilter)

      Ява

      Set<ActivityFilter> expandedActivityFilterSet = new HashSet<>();
      expandedActivityFilterSet.add(expandedActivityFilter);
    3. Создайте ActivityRule :

      Котлин

      val activityRule = ActivityRule.Builder(expandedActivityFilterSet)
          .setAlwaysExpand(true)
          .build()

      Ява

      ActivityRule activityRule = new ActivityRule.Builder(
          expandedActivityFilterSet
      ).setAlwaysExpand(true)
       .build();

      ActivityRule.Builder создает и настраивает правило:

      • expandedActivityFilterSet : содержит фильтры действий, которые определяют, когда применять правило, путем определения действий, которые вы хотите исключить из разделения.
      • setAlwaysExpand : указывает, должно ли действие заполнять все окно задачи.
    4. Добавьте правило в RuleController WindowManager:

      Котлин

      ruleController.addRule(activityRule)

      Ява

      ruleController.addRule(activityRule);

Межприкладное внедрение

В Android 13 (уровень API 33) и более поздних версиях приложения могут встраивать действия из других приложений. Внедрение действий между приложениями или с использованием нескольких UID обеспечивает визуальную интеграцию действий из нескольких приложений Android. Система отображает активность главного приложения и встроенную активность из другого приложения на экране рядом или сверху и снизу, как при внедрении активности одного приложения.

Например, приложение «Настройки» может встроить действие выбора обоев из приложения WallpaperPicker:

Рис. 14. Приложение «Настройки» (меню слева) со встроенным средством выбора обоев (справа).

Модель доверия

Хост-процессы, встраивающие действия из других приложений, могут переопределить представление внедренных действий, включая размер, положение, обрезку и прозрачность. Вредоносные хосты могут использовать эту возможность, чтобы ввести пользователей в заблуждение и организовать кликджекинг или другие атаки, направленные на исправление пользовательского интерфейса.

Чтобы предотвратить неправомерное использование встраивания действий между приложениями, Android требует, чтобы приложения разрешили встраивание своих действий. Приложения могут обозначать хосты как доверенные или ненадежные.

Доверенные хосты

Чтобы другие приложения могли внедрять и полностью контролировать представление действий из вашего приложения, укажите сертификат SHA-256 ведущего приложения в атрибуте android:knownActivityEmbeddingCerts элементов <activity> или <application> файла манифеста вашего приложения.

Установите значение android:knownActivityEmbeddingCerts в виде строки:

<activity
    android:name=".MyEmbeddableActivity"
    android:knownActivityEmbeddingCerts="@string/known_host_certificate_digest"
    ... />

или, чтобы указать несколько сертификатов, массив строк:

<activity
    android:name=".MyEmbeddableActivity"
    android:knownActivityEmbeddingCerts="@array/known_host_certificate_digests"
    ... />

который ссылается на такой ресурс:

<resources>
    <string-array name="known_host_certificate_digests">
      <item>cert1</item>
      <item>cert2</item>
      ...
    </string-array>
</resources>

Владельцы приложений могут получить дайджест сертификата SHA, запустив задачу Gradle signingReport . Дайджест сертификата представляет собой отпечаток SHA-256 без разделительных двоеточий. Дополнительные сведения см. в разделах «Запуск отчета о подписи» и «Аутентификация клиента» .

Ненадежные хосты

Чтобы разрешить любому приложению встраивать действия вашего приложения и управлять их представлением, укажите атрибут android:allowUntrustedActivityEmbedding в элементах <activity> или <application> манифеста приложения, например:

<activity
    android:name=".MyEmbeddableActivity"
    android:allowUntrustedActivityEmbedding="true"
    ... />

Значением атрибута по умолчанию является false, что предотвращает внедрение активности между приложениями.

Пользовательская аутентификация

Чтобы снизить риски внедрения ненадежных действий, создайте собственный механизм аутентификации, который проверяет личность хоста. Если вы знаете сертификаты хоста, используйте библиотеку androidx.security.app.authenticator для аутентификации. Если хост проходит аутентификацию после внедрения вашей активности, вы можете отобразить фактический контент. Если нет, вы можете сообщить пользователю, что действие не разрешено, и заблокировать контент.

Используйте метод ActivityEmbeddingController#isActivityEmbedded() из библиотеки Jetpack WindowManager, чтобы проверить, внедряет ли хост вашу активность, например:

Котлин

fun isActivityEmbedded(activity: Activity): Boolean {
    return ActivityEmbeddingController.getInstance(this).isActivityEmbedded(activity)
}

Ява

boolean isActivityEmbedded(Activity activity) {
    return ActivityEmbeddingController.getInstance(this).isActivityEmbedded(activity);
}

Ограничение минимального размера

Система Android применяет минимальную высоту и ширину, указанные в элементе <layout> манифеста приложения, ко внедренным действиям. Если приложение не указывает минимальную высоту и ширину, применяются системные значения по умолчанию ( sw220dp ).

Если хост пытается изменить размер встроенного контейнера до размера меньше минимального, встроенный контейнер расширяется, занимая все границы задачи.

<псевдоним активности>

Чтобы внедрение доверенных или ненадежных действий работало с элементом <activity-alias> , к целевому действию, а не к псевдониму, необходимо применить android:knownActivityEmbeddingCerts или android:allowUntrustedActivityEmbedding . Политика, проверяющая безопасность на системном сервере, основана на флагах, установленных на цели, а не на псевдониме.

Хост-приложение

Хост-приложения реализуют встраивание действий между приложениями так же, как они реализуют встраивание действий в одном приложении. Объекты SplitPairRule и SplitPairFilter или ActivityRule и ActivityFilter определяют встроенные действия и разделения окон задач. Правила разделения определяются статически в XML или во время выполнения с использованием вызовов API Jetpack WindowManager.

Если ведущее приложение пытается внедрить действие, которое не включило встраивание между приложениями, действие занимает все границы задачи. В результате хост-приложения должны знать, допускают ли целевые действия встраивание между приложениями.

Если встроенное действие запускает новое действие в той же задаче, и новое действие не включило встраивание между приложениями, действие занимает все границы задачи, а не накладывает действие во внедренном контейнере.

Хост-приложение может без ограничений встраивать свои собственные действия при условии, что действия запускаются в одной и той же задаче.

Примеры разделения

Разделить из полного окна

Рисунок 15. Действие A запускает действие B в стороне.

Рефакторинг не требуется. Вы можете определить конфигурацию разделения статически или во время выполнения, а затем вызвать Context#startActivity() без каких-либо дополнительных параметров.

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

Разделить по умолчанию

Когда целевая страница приложения спроектирована так, чтобы ее можно было разделить на два контейнера на больших экранах, пользовательский опыт будет лучше, когда оба действия создаются и отображаются одновременно. Однако контент может быть недоступен для вторичного контейнера разделения до тех пор, пока пользователь не взаимодействует с действием в основном контейнере (например, пользователь не выбирает элемент в меню навигации). Действие-заполнитель может заполнять пустоту до тех пор, пока содержимое не будет отображаться во вторичном контейнере разделения (см. Заполнители выше).

Рисунок 16. Разделение, созданное путем одновременного открытия двух действий. Одно действие — это заполнитель.

Чтобы создать разделение с заполнителем, создайте заполнитель и свяжите его с основным действием:

<SplitPlaceholderRule
    window:placeholderActivityName=".PlaceholderActivity">
    <ActivityFilter
        window:activityName=".MainActivity"/>
</SplitPlaceholderRule>

Когда приложение получает намерение, целевое действие может отображаться как второстепенная часть разделения действий; например, запрос на показ подробного экрана с информацией об элементе из списка. На небольших дисплеях подробные сведения отображаются в полном окне задачи; на больших устройствах рядом со списком.

Рисунок 17. Подробная информация о ссылках на контент отображается отдельно на маленьком экране, но вместе с активностью списка на большом экране.

Запрос на запуск должен быть перенаправлен на основное действие, а целевое подробное действие должно запускаться в виде разделения. Система автоматически выбирает правильную презентацию — стопкой или рядом — в зависимости от доступной ширины дисплея.

Котлин

override fun onCreate(savedInstanceState Bundle?) {
    . . .
    RuleController.getInstance(this)
        .addRule(SplitPairRule.Builder(filterSet).build())
    startActivity(Intent(this, DetailActivity::class.java))
}

Ява

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    . . .
    RuleController.getInstance(this)
        .addRule(new SplitPairRule.Builder(filterSet).build());
    startActivity(new Intent(this, DetailActivity.class));
}

Назначение глубокой ссылки может быть единственным действием, которое должно быть доступно пользователю в стеке обратной навигации, и вы можете не захотеть отклонять подробное действие и оставлять только основное действие:

Большой дисплей со списком действий и подробными сведениями рядом.           Обратная навигация не позволяет закрыть подробные действия и оставить действия со списком на экране.

Маленький дисплей, отображающий только детализацию. Обратная навигация не позволяет отклонить подробные действия и отобразить действия в списке.

Вместо этого вы можете завершить оба действия одновременно, используя атрибут finishPrimaryWithSecondary :

<SplitPairRule
    window:finishPrimaryWithSecondary="always">
    <SplitPairFilter
        window:primaryActivityName=".ListActivity"
        window:secondaryActivityName=".DetailActivity"/>
</SplitPairRule>

См. раздел Атрибуты конфигурации .

Несколько действий в разделенных контейнерах

Объединение нескольких действий в разделенный контейнер позволяет пользователям получать доступ к более глубокому контенту. Например, при разделении списка на детали пользователю может потребоваться перейти в раздел с подробными сведениями, но оставить основное действие на месте:

Рисунок 18. Действие открыто на вторичной панели окна задачи.

Котлин

class DetailActivity {
    . . .
    fun onOpenSubDetail() {
      startActivity(Intent(this, SubDetailActivity::class.java))
    }
}

Ява

public class DetailActivity {
    . . .
    void onOpenSubDetail() {
        startActivity(new Intent(this, SubDetailActivity.class));
    }
}

Второстепенное действие размещается поверх подробного действия, скрывая его:

Затем пользователь может вернуться на предыдущий уровень детализации, пройдя обратно по стеку:

Рисунок 19. Действие удалено из вершины стека.

Наложение действий друг на друга — это поведение по умолчанию, когда действия запускаются из действия в одном и том же вторичном контейнере. Действия, запущенные из основного контейнера в активном разделении, также попадают во вторичный контейнер на вершине стека действий.

Действия в новой задаче

Когда действия в разделенном окне задачи начинают действия в новой задаче, новая задача отделяется от задачи, включающей разделение, и отображается в полноэкранном режиме. На экране «Последние» отображаются две задачи: задача в сплите и новая задача.

Рисунок 20. Запустите действие C в новой задаче из действия B.

Замена активности

Действия можно заменить в стеке вторичного контейнера; например, когда основное действие используется для навигации верхнего уровня, а вторичное действие — выбранный пункт назначения. Каждый выбор в навигации верхнего уровня должен запускать новое действие во вторичном контейнере и удалять действие или действия, которые там находились ранее.

Рис. 21. Действия навигации верхнего уровня на основной панели заменяют действия назначения на вторичной панели.

Если приложение не завершает действие во вторичном контейнере при изменении выбора навигации, обратная навигация может сбивать с толку, когда разделение свернуто (когда устройство сложено). Например, если у вас есть меню на основной панели, а экраны A и B расположены на дополнительной панели, когда пользователь складывает телефон, B оказывается поверх A, а A — над меню. Когда пользователь возвращается из B, вместо меню появляется A.

В таких случаях экран A необходимо удалить из задней стопки.

Поведение по умолчанию при запуске в сторону нового контейнера поверх существующего разделения — размещение новых вторичных контейнеров сверху и сохранение старых в заднем стеке. Вы можете настроить разделения для очистки предыдущих вторичных контейнеров с помощью clearTop и нормального запуска новых действий.

<SplitPairRule
    window:clearTop="true">
    <SplitPairFilter
        window:primaryActivityName=".Menu"
        window:secondaryActivityName=".ScreenA"/>
    <SplitPairFilter
        window:primaryActivityName=".Menu"
        window:secondaryActivityName=".ScreenB"/>
</SplitPairRule>

Котлин

class MenuActivity {
    . . .
    fun onMenuItemSelected(selectedMenuItem: Int) {
        startActivity(Intent(this, classForItem(selectedMenuItem)))
    }
}

Ява

public class MenuActivity {
    . . .
    void onMenuItemSelected(int selectedMenuItem) {
        startActivity(new Intent(this, classForItem(selectedMenuItem)));
    }
}

Альтернативно можно использовать то же вторичное действие и из основного действия (меню) отправлять новые намерения, которые разрешаются для того же экземпляра, но вызывают обновление состояния или пользовательского интерфейса во вторичном контейнере.

Множественные разделения

Приложения могут обеспечивать многоуровневую глубокую навигацию, запуская дополнительные действия в стороне.

Когда действие во вторичном контейнере запускает новое действие в стороне, новое разделение создается поверх существующего разделения.

Рисунок 22. Действие B запускает действие C в стороне.

Задний стек содержит все действия, которые были открыты ранее, поэтому пользователи могут перейти к разделению A/B после завершения C.

Действия A, B и C в стопке. Действия расположены в следующем порядке сверху вниз: C, B, A.

Чтобы создать новое разделение, запустите новое действие сбоку от существующего вторичного контейнера. Объявите конфигурации для разделения A/B и B/C и запустите действие C обычно из B:

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
    <SplitPairFilter
        window:primaryActivityName=".B"
        window:secondaryActivityName=".C"/>
</SplitPairRule>

Котлин

class B {
    . . .
    fun onOpenC() {
        startActivity(Intent(this, C::class.java))
    }
}

Ява

public class B {
    . . .
    void onOpenC() {
        startActivity(new Intent(this, C.class));
    }
}

Реагировать на изменения состояния разделения

Различные действия в приложении могут иметь элементы пользовательского интерфейса, выполняющие одну и ту же функцию; например, элемент управления, открывающий окно с настройками учетной записи.

Рисунок 23. Различные действия с функционально идентичными элементами пользовательского интерфейса.

Если два действия, у которых есть общий элемент пользовательского интерфейса, разделены, показывать элемент в обоих действиях избыточно и, возможно, сбивает с толку.

Рисунок 24. Дублирующиеся элементы пользовательского интерфейса в разделении активности.

Чтобы узнать, когда действия находятся в разделенном состоянии, проверьте поток SplitController.splitInfoList или зарегистрируйте прослушиватель с помощью SplitControllerCallbackAdapter на предмет изменений в состоянии разделения. Затем соответствующим образом настройте пользовательский интерфейс:

Котлин

val layout = layoutInflater.inflate(R.layout.activity_main, null)
val view = layout.findViewById<View>(R.id.infoButton)
lifecycleScope.launch {
    lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
        splitController.splitInfoList(this@SplitDeviceActivity) // The activity instance.
            .collect { list ->
                view.visibility = if (list.isEmpty()) View.VISIBLE else View.GONE
            }
    }
}

Ява

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    . . .
    new SplitControllerCallbackAdapter(SplitController.getInstance(this))
        .addSplitListener(
            this,
            Runnable::run,
            splitInfoList -> {
                View layout = getLayoutInflater().inflate(R.layout.activity_main, null);
                layout.findViewById(R.id.infoButton).setVisibility(
                    splitInfoList.isEmpty() ? View.VISIBLE : View.GONE);
            });
}

Сопрограммы можно запускать в любом состоянии жизненного цикла, но обычно они запускаются в состоянии STARTED для экономии ресурсов (дополнительную информацию см. в разделе Использование сопрограмм Kotlin с компонентами, учитывающими жизненный цикл ).

Обратные вызовы могут выполняться в любом состоянии жизненного цикла, в том числе при остановке действия. Слушатели обычно должны быть зарегистрированы в onStart() и не зарегистрированы в onStop() .

Полнооконный модальный режим

Некоторые действия блокируют взаимодействие пользователей с приложением до тех пор, пока не будет выполнено определенное действие; например, действие на экране входа в систему, экран подтверждения политики или сообщение об ошибке. Модальные действия не должны появляться в разделении.

Можно заставить действие всегда заполнять окно задачи, используя расширенную конфигурацию:

<ActivityRule
    window:alwaysExpand="true">
    <ActivityFilter
        window:activityName=".FullWidthActivity"/>
</ActivityRule>

Завершить деятельность

Пользователи могут завершить действия по обе стороны от разделения, проведя пальцем от края дисплея:

Рисунок 25. Завершение действия жестом пролистывания B.
Рисунок 26. Завершение действия жестом смахивания A.

Если устройство настроено на использование кнопки «Назад» вместо навигации с помощью жестов, ввод отправляется на сфокусированное действие — действие, которое было затронуто или запущено последним.

Эффект завершения всех действий в контейнере для противоположного контейнера зависит от конфигурации разделения.

Атрибуты конфигурации

Вы можете указать атрибуты правила пары разделения, чтобы настроить, как завершение всех действий на одной стороне разделения влияет на действия на другой стороне разделения. Атрибуты:

  • window:finishPrimaryWithSecondary — Как завершение всех действий во вторичном контейнере влияет на действия в основном контейнере
  • window:finishSecondaryWithPrimary — Как завершение всех действий в основном контейнере влияет на действия во вторичном контейнере

Возможные значения атрибутов включают в себя:

  • always — Всегда завершать действия в связанном контейнере.
  • never — Никогда не завершайте действия в связанном контейнере.
  • adjacent — завершайте действия в связанном контейнере, когда два контейнера отображаются рядом друг с другом, но не когда два контейнера сложены друг в друга.

Например:

<SplitPairRule
    &lt;!-- Do not finish primary container activities when all secondary container activities finish. --&gt;
    window:finishPrimaryWithSecondary="never"
    &lt;!-- Finish secondary container activities when all primary container activities finish. --&gt;
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

Конфигурация по умолчанию

Когда все действия в одном контейнере разделены, оставшийся контейнер занимает все окно:

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

Разделение, содержащее действия A и B. A завершено, и B занимает все окно.

Разделение, содержащее действия A и B. B завершено, и A занимает все окно.

Завершайте дела вместе

Завершите действия в основном контейнере автоматически после завершения всех действий во вторичном контейнере:

<SplitPairRule
    window:finishPrimaryWithSecondary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

Разделение, содержащее действия A и B. B завершено, что также завершает A, оставляя окно задачи пустым.

Разделение, содержащее действия A и B. A завершено, а B в окне задач остается один.

Завершите действия во вторичном контейнере автоматически после завершения всех действий в основном контейнере:

<SplitPairRule
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

Разделение, содержащее действия A и B. A завершено, что также завершает B, оставляя окно задачи пустым.

Разделение, содержащее действия A и B. B завершено, и в окне задачи остается только A.

Завершите действия вместе, когда все действия в основном или вторичном контейнере завершатся:

<SplitPairRule
    window:finishPrimaryWithSecondary="always"
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

Разделение, содержащее действия A и B. A завершено, что также завершает B, оставляя окно задачи пустым.

Разделение, содержащее действия A и B. B завершено, что также завершает A, оставляя окно задачи пустым.

Завершите несколько действий в контейнерах

Если несколько действий объединены в разделенный контейнер, завершение действия в нижней части стека не приводит к автоматическому завершению действий в верхней части.

Например, если во вторичном контейнере находятся два действия, C поверх B:

Вторичный стек действий, содержащий действие C, наложенный поверх B, накладывается поверх основного стека действий, содержащего действие A.

а конфигурация разделения определяется конфигурацией действий A и B:

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

завершение верхнего действия сохраняет разделение.

Разделите действие A в основном контейнере и действия B и C во вторичном, C укладывается поверх B. C завершается, оставляя A и B в разделенном списке действий.

Завершение нижнего (корневого) действия вторичного контейнера не удаляет действия над ним; и поэтому также сохраняет раскол.

Разделите действие A на основной контейнер и действия B и C во вторичный, C укладывается поверх B. B завершается, оставляя A и C в разделенном списке действий.

Любые дополнительные правила совместного завершения действий, например, завершение второстепенного действия основным, также выполняются:

<SplitPairRule
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

Разделите действие A на основной контейнер и действия B и C во вторичный контейнер, C укладывается поверх B. Заканчивается A, а также завершаются B и C.

И когда разделение настроено на одновременное завершение первичного и вторичного:

<SplitPairRule
    window:finishPrimaryWithSecondary="always"
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

Разделите действие A в основном контейнере и действия B и C во вторичном, C укладывается поверх B. C завершается, оставляя A и B в разделенном списке действий.

Разделите действие A в основном контейнере и действия B и C во вторичном, C укладывается поверх B. B завершается, оставляя A и C в разделенном виде деятельности.

Разделите действие A на основной контейнер и действия B и C во вторичный, C накладывается поверх B. Заканчивается A, а также завершаются B и C.

Изменение свойств разделения во время выполнения

Свойства активного и видимого разделения не могут быть изменены. Изменение правил разделения влияет на запуск дополнительных действий и новых контейнеров, но не на существующие и активные разделения.

Чтобы изменить свойства активных сплитов, завершите дополнительное действие или действия в сплите и снова запустите его с новой конфигурацией.

Извлечение действия из разделенного в полное окно

Создайте новую конфигурацию, которая отображает полное окно побочного действия, а затем перезапустите действие с намерением, которое разрешается для того же экземпляра.

Проверьте поддержку разделения во время выполнения

Встраивание действий поддерживается в Android 12L (уровень API 32) и более поздних версиях, но также доступно на некоторых устройствах с более ранними версиями платформы. Чтобы проверить доступность этой функции во время выполнения, используйте свойство SplitController.splitSupportStatus или метод SplitController.getSplitSupportStatus() :

Котлин

if (SplitController.getInstance(this).splitSupportStatus ==
     SplitController.SplitSupportStatus.SPLIT_AVAILABLE) {
     // Device supports split activity features.
}

Ява

if (SplitController.getInstance(this).getSplitSupportStatus() ==
     SplitController.SplitSupportStatus.SPLIT_AVAILABLE) {
     // Device supports split activity features.
}

Если разделение не поддерживается, действия запускаются поверх стека действий (в соответствии с моделью внедрения бездействия).

Запретить переопределение системы

Производители устройств Android (производители оригинального оборудования или OEM-производители) могут реализовать встраивание действий как функцию системы устройства. Система определяет правила разделения для приложений с несколькими действиями, переопределяя поведение окон приложений. Системное переопределение переводит приложения с несколькими действиями в определяемый системой режим внедрения действий.

Внедрение системных действий может улучшить представление приложения с помощью многопанельных макетов, таких как list-detail , без каких-либо изменений в приложении. Однако внедрение действий в систему может также привести к неправильным макетам приложения, ошибкам или конфликтам с внедрением действий, реализованным приложением.

Ваше приложение может запретить или разрешить внедрение системной активности, установив свойство в файле манифеста приложения, например:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application>
        <property
            android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE"
            android:value="true|false" />
    </application>
</manifest>

Имя свойства определяется в объекте WindowProperties Jetpack WindowManager. Установите значение false если ваше приложение реализует внедрение действий или если вы хотите иным образом запретить системе применять свои правила внедрения действий к вашему приложению. Установите значение true , чтобы разрешить системе применять встраивание определяемых системой действий в ваше приложение.

Ограничения, ограничения и предостережения

  • Только ведущее приложение задачи, которое идентифицировано как владелец корневого действия в задаче, может организовывать и внедрять в задачу другие действия. Если действия, поддерживающие внедрение и разделение, выполняются в задаче, принадлежащей другому приложению, то внедрение и разделение не будут работать для этих действий.
  • Действия могут быть организованы только в рамках одной задачи. Запуск действия в новой задаче всегда помещает его в новое расширенное окно за пределами существующих разделений.
  • Только действия одного и того же процесса могут быть организованы и разделены. Обратный вызов SplitInfo сообщает только о действиях, принадлежащих одному и тому же процессу, поскольку невозможно узнать о действиях в разных процессах.
  • Каждое правило парной или единичной активности применяется только к запускам активности, которые происходят после регистрации правила. В настоящее время нет возможности обновить существующие разделения или их визуальные свойства.
  • Конфигурация фильтра разделенной пары должна полностью соответствовать намерениям, используемым при запуске действий. Сопоставление происходит в тот момент, когда новое действие запускается из процесса приложения, поэтому оно может не знать об именах компонентов, которые разрешаются позже в системном процессе при использовании неявных намерений. Если имя компонента неизвестно на момент запуска, вместо него можно использовать подстановочный знак («*/*») и выполнять фильтрацию на основе намеренного действия.
  • В настоящее время невозможно перемещать действия между контейнерами или в разделения и обратно после их создания. Разделения создаются библиотекой WindowManager только при запуске новых действий с соответствующими правилами, а разделения уничтожаются после завершения последнего действия в разделенном контейнере.
  • Действия можно перезапустить при изменении конфигурации, поэтому при создании или удалении разделения и изменении границ действия действие может пройти через полное уничтожение предыдущего экземпляра и создание нового. В результате разработчики приложений должны быть осторожны с такими вещами, как запуск новых действий из обратных вызовов жизненного цикла.
  • Устройства должны включать интерфейс расширений окон для поддержки внедрения действий. Почти все устройства с большим экраном под управлением Android 12L (уровень API 32) или выше включают этот интерфейс. Однако некоторые устройства с большим экраном, которые не способны выполнять несколько действий, не включают интерфейс расширений окон. Если устройство с большим экраном не поддерживает многооконный режим, оно может не поддерживать внедрение активности.

Дополнительные ресурсы

{% дословно %} {% дословно %} {% дословно %} {% дословно %}