Incorporación de actividades

Organiza tus páginas con colecciones Guarda y categoriza el contenido según tus preferencias.

La incorporación de actividades optimiza las apps en dispositivos de pantalla grande mediante la división de la ventana de tareas de una aplicación entre dos actividades o dos instancias de la misma actividad.

Figura 1: App de Configuración con actividades en paralelo.

La actualización de las bases de código heredadas para establecer la compatibilidad con pantallas grandes es una labor abrumadora que lleva mucho tiempo. La conversión de apps basadas en actividades a diseños de varios paneles mediante fragmentos requiere una refactorización considerable.

La incorporación de actividades prácticamente no requiere la refactorización de tu app. Determinarás la manera en que esta mostrará las actividades (una al lado de la otra o apiladas) con la creación de un archivo de configuración XML o llamadas a la API de Jetpack WindowManager.

La compatibilidad con pantallas pequeñas se mantiene automáticamente. Cuando tu app está en un dispositivo con una pantalla pequeña, las actividades se apilan una sobre otra. En pantallas grandes, las actividades se muestran una al lado de la otra. El sistema determina la presentación en función de la configuración que creaste: no se requiere lógica de ramificación.

La incorporación de actividades admite cambios de orientación del dispositivo y funciona a la perfección en dispositivos plegables, en los que se modifica la pila de actividades a medida que el dispositivo se pliega y se despliega.

Modern Android Development usa una arquitectura de actividad única con fragmentos, componentes de navegación y administradores de diseño versátiles, como SlidingPaneLayout.

Sin embargo, si tu app consta de varias actividades, su incorporación te permitirá proporcionar con facilidad una experiencia del usuario mejorada en tablets, plegables y dispositivos ChromeOS.

La incorporación de actividades es compatible con la mayoría de los dispositivos con pantalla grande con Android 12L (nivel de API 32) y versiones posteriores.

Ventana de tareas dividida

La incorporación de actividades divide la ventana de tareas de la app en dos contenedores: uno principal y otro secundario. Estos contenedores incluyen las actividades iniciadas desde la actividad principal o desde otras que ya se encuentran en ellos.

Las actividades se apilan en el contenedor secundario a medida que se inician, y este se apila sobre el principal en pantallas pequeñas, por lo que la pila de actividades y la navegación hacia atrás son coherentes con el orden de las actividades ya integradas en tu app.

La incorporación de actividades te permite mostrarlas de varias formas. Tu app puede dividir la ventana de tareas iniciando dos actividades, una al lado de la otra y de manera simultánea:

Figura 2: Dos actividades en paralelo.

Alternativamente, una actividad que ocupa toda la ventana de tareas puede crear una división mediante el lanzamiento de una nueva actividad junto a ella:

Figura 3: La actividad A inicia la actividad B al costado.

Las actividades que ya están divididas y comparten una ventana de tareas pueden iniciar otras actividades de las siguientes maneras:

  • Al costado y sobre otra actividad:

    Figura 4: La actividad A inicia la actividad C al costado y sobre la actividad B.
  • Al costado y desplazando la división hacia un lado, ocultando la actividad principal anterior:

    Figura 5: La actividad B inicia la actividad C al costado y mueve la división hacia un lado.
  • Iniciando una actividad en el lugar, sobre la otra, es decir, en la misma pila de actividades:

    Figura 6: La actividad B inicia la actividad C sin marcas de intents adicionales.
  • Iniciando una ventana de actividad completa en la misma tarea:

    Figura 7: La actividad A o la actividad B inician la actividad C, que ocupa la ventana de tareas por completo.

Navegación hacia atrás

Los diferentes tipos de aplicaciones pueden tener distintas reglas de navegación hacia atrás en un estado de ventana de tareas dividida según las dependencias entre las actividades o la forma en que los usuarios activan el evento Atrás, por ejemplo:

  • En conjunto: Si las actividades están relacionadas y una no se debería mostrar sin la otra, se puede configurar la navegación hacia atrás para finalizar ambas.
  • Por separado: Si las actividades son completamente independientes, la navegación hacia atrás en una actividad no afecta el estado de otra actividad en la ventana de tareas.

El evento Atrás se envía a la última actividad enfocada cuando se usa la navegación con botones. Con la navegación basada en gestos, el evento Atrás se envía a la actividad donde ocurrió el gesto.

Diseño multipanel

Jetpack WindowManager 1.1.0 Alpha04 te permite compilar un diseño multipanel con actividades en dispositivos de pantalla grande que ejecuten Android 12L (nivel de API 32) y algunos dispositivos con versiones de plataforma anteriores. Las apps existentes que se basan en varias actividades en lugar de fragmentos o diseños basados en vistas, como SlidingPaneLayout, pueden brindar una experiencia mejorada del usuario de pantallas grandes sin una refactorización significativa.

Un ejemplo común es una división de lista y detalles. Para garantizar una presentación de calidad, el sistema inicia la actividad de la lista y, luego, la aplicación inicia de inmediato la actividad de los detalles. El sistema de transición espera hasta que se dibujen ambas actividades y, luego, las muestra juntas. Para el usuario, las dos actividades se inician como una sola.

Figura 8: Dos actividades iniciadas simultáneamente en un diseño multipanel.

Proporciones de división

Tu aplicación puede especificar la proporción de la ventana de tareas mediante el atributo ratio de una configuración de división (consulta la sección Configuración de divisiones que aparece a continuación).

Figura 9: Dos divisiones de actividad con diferentes proporciones de división.

Marcadores de posición

Las actividades de marcadores de posición son actividades secundarias vacías que ocupan una parte de una división de actividad. En definitiva, se las reemplazará por otra actividad que incluya contenido. Por ejemplo, una actividad de marcador de posición puede ocupar el lado secundario de una división de actividad en un diseño de lista y detalles hasta que se seleccione un elemento de la lista, momento en el que una actividad que contiene la información de los detalles del elemento de lista seleccionado reemplaza el marcador de posición.

De forma predeterminada, el sistema muestra marcadores de posición solo cuando hay suficiente espacio para dividir actividades. Los marcadores de posición terminan automáticamente cuando el tamaño de la pantalla cambia a un ancho o una altura demasiado pequeños para mostrar una división. Cuando el espacio lo permite, el sistema reinicia el marcador de posición con un estado de nueva inicialización.

Figura 10: Plegado y desplegado de un dispositivo plegable. La actividad del marcador de posición finaliza y se vuelve a crear cuando cambia el tamaño de la pantalla.

Sin embargo, el atributo stickyPlaceholder de una SplitPlaceholderRule puede anular el comportamiento predeterminado. Si el atributo se establece en verdadero, el sistema muestra el marcador de posición como la actividad más importante de la ventana de tareas cuando se cambia el tamaño de la pantalla de dos paneles a un panel (consulta Configuración de división para ver un ejemplo).

Figura 11: Plegado y desplegado de un dispositivo plegable. La actividad del marcador de posición es fija.

Cambios en el tamaño de la ventana

Cuando los cambios en la configuración del dispositivo reducen el ancho de la ventana de tareas de modo que no sea lo suficientemente grande para un diseño multipanel (por ejemplo, cuando un dispositivo plegable de pantalla grande se pliega y el tamaño de la pantalla cambia de uno de tablet a uno de teléfono, o cuando el tamaño de la ventana de la app cambia al modo multiventana), las actividades sin marcador de posición en el panel secundario de la ventana de tareas se apilan sobre las actividades del panel principal.

Las actividades de marcadores de posición solo se muestran cuando el ancho de la pantalla es el suficiente para realizar una división. En pantallas más pequeñas, el marcador de posición se descarta automáticamente. Cuando el área de visualización vuelve a ser lo bastante grande, se vuelve a crear el marcador de posición. (Consulta la sección Marcadores de posición más arriba).

Es posible apilar las actividades porque WindowManager aplica el orden Z a las actividades del panel secundario sobre las actividades del panel principal.

Varias actividades en el panel secundario

La actividad B inicia la actividad C en el lugar, sin marcas de intents adicionales:

División de actividad que contiene las actividades A, B y C, con C apilada sobre B.

Esto da como resultado el siguiente orden Z de las actividades en la misma tarea:

Pila secundaria de actividades que contiene la actividad C apilada sobre B.
          La pila secundaria se apila sobre la principal que contiene la actividad A.

Por lo tanto, en una ventana de tareas más pequeña, la aplicación se reduce a una sola actividad, con C en la parte superior de la pila:

Ventana pequeña que muestra solo la actividad C.

Si vuelves a la ventana más pequeña, podrás navegar por las actividades apiladas unas sobre otras.

Si se restablece la configuración de la ventana de tareas a un tamaño más grande que admite varios paneles, las actividades se volverán a mostrar una al lado de la otra.

Divisiones apiladas

La actividad B inicia la actividad C al costado y mueve la división hacia un lado.

Ventana de tareas que muestra las actividades A y B, y, luego, las actividades B y C.

El resultado es el siguiente orden Z de las actividades en la misma tarea:

Actividades A, B y C en una sola pila. Las actividades se apilan en el siguiente orden de arriba hacia abajo: C, B y A.

En una ventana de tareas más pequeña, la aplicación se reduce a una sola actividad, con C en la parte superior:

Ventana pequeña que muestra solo la actividad C.

Orientación vertical fija

La configuración del manifiesto android:screenOrientation permite a las apps restringir las actividades a la orientación vertical u horizontal. Para mejorar la experiencia del usuario en dispositivos de pantalla grande, como tablets y dispositivos plegables, los fabricantes de dispositivos (OEMs) pueden ignorar las solicitudes de orientación de la pantalla y colocar la app en formato letterbox en orientación vertical en pantallas horizontales u orientación horizontal en pantallas verticales.

Figura 12. Actividades en formato letterbox: orientación vertical fija en dispositivo horizontal (izquierda) y orientación horizontal fija en dispositivo vertical (derecha)

De manera similar, cuando se habilita la incorporación de actividades, los OEMs pueden personalizar los dispositivos para que realicen actividades con formato letterbox y formato vertical fijo en orientación horizontal en pantallas grandes (ancho superior o igual a 600 dp). Cuando una actividad con formato vertical fijo inicia una segunda actividad, el dispositivo puede mostrar ambas actividades en paralelo en una pantalla con dos paneles.

Figura 13: La actividad con orientación vertical fija A inicia la actividad B al costado.

Agrega siempre la propiedad android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED al archivo de manifiesto de tu app para informar a los dispositivos que tu app admite la incorporación de actividades (consulta la sección Configuración de divisiones a continuación). Los dispositivos personalizados para OEMs pueden determinar si aplican el formato letterbox a las actividades con orientación vertical fija.

Configuración de divisiones

Las divisiones de actividad se configuran con las reglas de división. Puedes definir las reglas de división en un archivo de configuración XML o mediante llamadas a la API de WindowManager de Jetpack.

En cualquier caso, tu app debe acceder a la biblioteca de WindowManager y debe informar al sistema que la app implementó la incorporación de actividades.

Haz lo siguiente:

  1. Agrega la siguiente dependencia de la biblioteca de WindowManager a tu archivo build.gradle:

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

    El componente SplitController de WindowManager administra las divisiones.

  2. Informa al sistema que tu app implementó la incorporación de actividades.

    Agrega la propiedad android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED al elemento <application> del archivo de manifiesto de la app y establece el valor en "true". Por ejemplo:

    <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>
    

    En WindowManager versión 1.1.0-alpha06 y posteriores, se inhabilitaron las divisiones de incorporación de actividades, a menos que se agregue la propiedad al manifiesto y se configure como "true".

    Además, los fabricantes de dispositivos usan el parámetro de configuración para habilitar capacidades personalizadas para las apps que admiten la incorporación de actividades. Por ejemplo, los dispositivos pueden utilizar formato letterbox en una actividad exclusiva del modo vertical en el modo horizontal para la transición a un diseño de panel dual cuando se inicia una segunda actividad.

Configuración de XML

Para crear una implementación basada en XML de incorporación de actividad, completa los siguientes pasos:

  1. Crea un archivo de recursos XML que realice las siguientes acciones:

    • Definir las actividades que comparten una división
    • Configurar las opciones de división
    • Crear un marcador de posición para el contenedor secundario de la división cuando el contenido no está disponible
    • Especificar las actividades que nunca deben ser parte de una división

    Por ejemplo:

    <!-- split_configuration.xml -->
    
    <resources
        xmlns:window="http://schemas.android.com/apk/res-auto">
    
        <!-- Define a split for the named activities. -->
        <SplitPairRule
            window:splitRatio="0.33"
            window:splitMinWidth="840dp"
            window:finishPrimaryWithSecondary="never"
            window:finishSecondaryWithPrimary="always">
            <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:splitMinWidth="840dp"
            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. -->
        <ActivityRule
            window:alwaysExpand="true">
            <ActivityFilter
                window:activityName=".ExpandedActivity"/>
        </ActivityRule>
    
    </resources>
    
  2. Crear un inicializador.

    El componente SplitController de WindowManager administra la división de la actividad según las reglas del archivo de configuración XML. Un Initializer de la biblioteca de Jetpack Startup permite que las reglas de división estén disponibles para SplitController durante el inicio de la app, de modo que SplitController pueda aplicarlas si es necesario a medida que se inician actividades.

    Para crear un inicializador, haz lo siguiente:

    1. Agrega la dependencia de la biblioteca de Jetpack Startup más reciente a tu archivo build.gradle, por ejemplo:

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

    2. Crea una clase que implemente la interfaz Initializer.

      El inicializador pone a disposición las reglas de división para SplitController proporcionando el ID del archivo de recursos XML que contiene las definiciones (split_configuration.xml) al método initialize() del componente.

      Kotlin

      class SplitInitializer : Initializer<SplitController> {
      
       override fun create(context: Context): SplitController {
           SplitController.initialize(context, R.xml.split_configuration)
           return SplitController.getInstance()
       }
      
       override fun dependencies(): List<Class<out Initializer<*>>> {
           return emptyList()
       }
      }
      

      Java

      class SplitInitializer extends Initializer<SplitController> {
      
       @Override
       SplitController create(Context context) {
           SplitController.initialize(context, R.xml.split_configuration);
           return SplitController.getInstance();
       }
      
       @Override
       List<Class<? extends Initializer<?>>> dependencies() {
           return Collections.emptyList();
       }
      }
      
  3. Crea un proveedor de contenido para las definiciones de la regla.

    Agrega androidx.startup.InitializationProvider al archivo de manifiesto de tu app como un <provider>. Incluye una referencia a la implementación de tu inicializador de SplitController, 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 descubre y luego inicializa SplitInitializer antes de que se llame al método onCreate() de la app. Como resultado, las reglas de división están vigentes cuando comienza la actividad principal de la app.

API de WindowManager

Puedes implementar la incorporación de actividades de manera programática con unas pocas llamadas a la API. Realiza las llamadas en el método onCreate() de la actividad principal en la división (la actividad que inicia la división).

Para crear una división de actividad de manera programática, haz lo siguiente:

  1. Crea una regla de división:

    1. Crea un SplitPairFilter que identifique las actividades que comparten la división:

      Kotlin

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

      Java

      SplitPairFilter splitPairFilter = new SplitPairFilter(
       new ComponentName(this, ListActivity.class),
       new ComponentName(this, DetailActivity.class),
       null
      );
      
    2. Agrega el filtro a un conjunto de filtros:

      Kotlin

      val filterSet = setOf(splitPairFilter)
      

      Java

      Set<SplitPairFilter> filterSet = new HashSet<>();
      filterSet.add(splitPairFilter);
      
    3. Crea variables que configuren la división:

      Kotlin

      val minWidth = (800f * resources.displayMetrics.density).toInt()
      val minSmallestWidth = (600f * resources.displayMetrics.density).toInt()
      

      Java

      int minWidth = (int)(800f * getResources().getDisplayMetrics().density);
      int minSmallestWidth = (int)(600f * getResources().getDisplayMetrics().density);
      
      • minWidth: Especifica el ancho mínimo de pantalla (en píxeles) que permite una división.
      • minSmallestWidth: Especifica el valor mínimo (en píxeles) que la más pequeña de las dos dimensiones de pantalla debe tener para permitir una división, independientemente de la orientación del dispositivo.
    4. Compila una SplitPairRule:

      Kotlin

      val splitPairRule = SplitPairRule.Builder(
           filterSet,
           minWidth,
           minSmallestWidth
       ).setSplitRatio(0.33f)
        .setFinishPrimaryWithSecondary(0)
        .setFinishSecondaryWithPrimary(1)
        .setClearTop(false)
        .build()
      

      Java

      SplitPairRule splitPairRule = new SplitPairRule.Builder(
       filterSet,
       minWidth,
       minSmallestWidth
      ).setSplitRatio(0.33f)
      .setFinishPrimaryWithSecondary(0)
      .setFinishSecondaryWithPrimary(1)
      .setClearTop(false)
      .build();
      

      Usa SplitPairRule.Builder para crear la regla y configurar las siguientes propiedades:

      • splitRatio: Es la proporción del área de visualización disponible para el contenedor principal. El contenedor secundario llena el resto del área de visualización disponible.
      • finishPrimaryWithSecondary: Indica cómo el hecho de finalizar todas las actividades del contenedor secundario afecta a las actividades del contenedor principal. 0 = nunca se finalizan las actividades (consulta Finalizar actividades).
      • finishSecondaryWithPrimary: Indica cómo el hecho de finalizar todas las actividades del contenedor principal afecta a las actividades del contenedor secundario. 1 = siempre se finalizan las actividades (consulta Finalizar actividades).
      • clearTop: Determina si todas las actividades del contenedor secundario finalizan cuando se inicia una nueva actividad en el contenedor.
    5. Obtén una instancia del SplitController de WindowManager y registra la regla:

      Kotlin

      val splitController = SplitController.getInstance()
      splitController.registerRule(splitPairRule)
      

      Java

      SplitController splitController = SplitController.getInstance();
      splitController.registerRule(splitPairRule);
      
  2. Crea un marcador de posición para el contenedor secundario cuando el contenido no esté disponible:

    1. Crea un ActivityFilter que identifique la actividad con la que el marcador de posición comparte la ventana de tareas dividida:

      Kotlin

      val activityFilter = ActivityFilter(
        ComponentName(applicationContext, ListActivity::class.java),
        null
      )
      

      Java

      ActivityFilter activityFilter = new ActivityFilter(
        new ComponentName(this, ListActivity.class),
        null
      );
      
    2. Agrega el filtro a un conjunto de filtros:

      Kotlin

      val activityFilterSet = setOf(activityFilter)
      

      Java

      Set<ActivityFilter> activityFilterSet = new HashSet<>();
      activityFilterSet.add(activityFilter);
      
    3. Crear un elemento SplitPlaceholderRule:

      Kotlin

      val splitPlaceholderRule = SplitPlaceholderRule.Builder(
            activityFilterSet,
            Intent(applicationContext, PlaceholderActivity::class.java),
            minWidth,
            minSmallestWidth
        ).setSplitRatio(0.33f)
         .setFinishPrimaryWithPlaceholder(1)
         .build()
      

      Java

      SplitPlaceholderRule splitPlaceholderRule = new SplitPlaceholderRule.Builder(
        activityFilterSet,
        new Intent(this, PlaceholderActivity.class),
        minWidth,
        minSmallestWidth
      ).setSplitRatio(0.33f)
      .setFinishPrimaryWithPlaceholder(1)
      .build();
      

      Usa SplitPlaceholderRule.Builder para crear la regla y configurar las siguientes propiedades:

      • splitRatio: Es la proporción del área de visualización disponible para el contenedor principal. La actividad del marcador de posición ocupa el resto del área de visualización disponible.
      • finishPrimaryWithPlaceholder: Indica cómo el hecho de finalizar la actividad del marcador de posición afecta a las actividades del contenedor principal. 1 = siempre se finalizan las actividades (consulta Finalizar actividades).
    4. Registra la regla con el SplitController de WindowManager:

      Kotlin

      splitController.registerRule(splitPlaceholderRule)
      

      Java

      splitController.registerRule(splitPlaceholderRule);
      
  3. Especifica las actividades que nunca deben ser parte de una división:

    1. Crea un ActivityFilter que identifique una actividad que siempre debe ocupar toda el área de visualización de la tarea:

      Kotlin

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

      Java

      ActivityFilter expandedActivityFilter = new ActivityFilter(
         new ComponentName(this, ExpandedActivity.class),
         null
      );
      
    2. Agrega el filtro a un conjunto de filtros:

      Kotlin

      val expandedfilterSet = setOf(expandedActivityFilter)
      

      Java

      Set<ActivityFilter> expandedfilterSet = new HashSet<>();
      expandedfilterSet.add(expandedActivityFilter);
      
    3. Crea una ActivityRule:

      Kotlin

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

      Java

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

      Usa ActivityRule.Builder para crear la regla y configurar las siguientes propiedades:

      • alwaysExpand: Indica si la actividad debe llenar toda la ventana de tareas.
    4. Registra la regla con el SplitController de WindowManager:

      Kotlin

      splitController.registerRule(activityRule)
      

      Java

      splitController.registerRule(activityRule);
      

Incorporación en varias aplicaciones

En Android 13 (nivel de API 33) y versiones posteriores, las apps pueden incorporar actividades de otras apps. La incorporación de actividades entre apps o UID permite la integración visual de actividades de varias aplicaciones para Android. El sistema muestra una actividad de la app host y una actividad incorporada de otra app en pantalla lado a lado o en las partes superior e inferior como en la incorporación de actividades de una app individual.

Por ejemplo, la app de Configuración podría incorporar la actividad del selector de fondo de pantalla de la app de WallpaperPicker:

Figura 14: App de Configuración (menú de la izquierda) con el selector de fondo de pantalla como actividad incorporada (derecha)

Modelo de confianza

Los procesos de host que incorporan actividades de otras apps pueden redefinir la presentación de las actividades incorporadas, incluidos el tamaño, la posición, el recorte y la transparencia. Los hosts maliciosos pueden usar esta función para engañar a los usuarios y crear una captura de clic u otros ataques de compensación de IU.

Para evitar el uso inadecuado de la incorporación de actividades entre apps, Android requiere que las apps acepten habilitar la incorporación de sus actividades. Las apps pueden designar hosts como confiables o no confiables.

Hosts confiables

Para permitir que otras aplicaciones incorporen y controlen por completo la presentación de actividades desde tu app, especifica el certificado SHA-1 de la aplicación host en el atributo android:knownActivityEmbeddingCerts de <activity> o los elementos <application> del archivo de manifiesto de tu app.

Configura el valor de android:knownActivityEmbeddingCerts como una cadena:

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

O, para especificar varios certificados, un array de cadenas:

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

Que hace referencia a un recurso como el siguiente:

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

Para obtener un resumen de certificados SHA, los propietarios de la app pueden ejecutar la tarea signingReport de Gradle. El resumen del certificado es la huella digital SHA-1 sin los dos puntos separados. Para obtener más información, consulta Cómo ejecutar un informe de firma y Autenticación de tu cliente.

Hosts no confiables

Para permitir que cualquier app incorpore las actividades de tu app y controle su presentación, especifica el atributo android:allowUntrustedActivityEmbedding en los elementos <activity> o <application> del manifiesto de la app, por ejemplo:

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

El valor predeterminado del atributo es "false", lo cual impide que se incorpore la actividad en diferentes apps.

Autenticación personalizada

Para mitigar los riesgos de incorporación de actividad no confiable, crea un mecanismo de autenticación personalizado que verifique la identidad del host. Si conoces los certificados de host, usa la biblioteca androidx.security.app.authenticator para la autenticación. Si el host hace la autenticación después de incorporar tu actividad, puedes mostrar el contenido real. De lo contrario, puedes informarle al usuario que no se permitió la acción y bloquear el contenido.

Usa el método SplitController#isActivityEmbedded() de la biblioteca de WindowManager de Jetpack para verificar si un host incorpora tu actividad, por ejemplo:

Kotlin

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

Java

public static boolean isActivityEmbedded(@NonNull Activity activity) {
    return SplitController.getInstance().isActivityEmbedded(activity);
}

Restricción de tamaño mínimo

El sistema Android aplica la altura y el ancho mínimos especificados en el elemento <layout> del manifiesto de la app a las actividades incorporadas. Si una aplicación no especifica la altura y el ancho mínimos, se aplicarán los valores predeterminados del sistema (sw220dp).

Si el host intenta cambiar el tamaño del contenedor incorporado a un tamaño inferior al mínimo, el contenedor incorporado se expande para ocupar todos los límites de la tarea.

<activity-alias>

Para que la incorporación de actividades confiables o no confiables funcione con el elemento <activity-alias>, se deben aplicar android:knownActivityEmbeddingCerts o android:allowUntrustedActivityEmbedding a la actividad objetivo, en lugar del alias. La política que verifica la seguridad en el servidor del sistema se basa en las marcas establecidas en el objetivo, no en el alias.

Aplicación de host

Las aplicaciones de host implementan la incorporación de actividades entre apps de la misma manera en que implementan la incorporación de actividades en una sola app. Los objetos SplitPairRule y SplitPairFilter o ActivityRule y ActivityFilter especifican actividades incorporadas y divisiones de ventanas de tareas. Las reglas de división se definen de manera estática en XML o en el tiempo de ejecución mediante las llamadas a la API de WindowManager de Jetpack.

Si una aplicación de host intenta incorporar una actividad que no habilitó la incorporación entre apps, la actividad ocupará todos los límites de la tarea. Como resultado, las aplicaciones de host deben saber si las actividades de destino permiten la incorporación entre apps.

Si una actividad incorporada comienza una nueva actividad en la misma tarea y no se habilitó la incorporación entre apps, la actividad ocupará todos los límites de la tarea en lugar de superponerse en el contenedor incorporado.

Una aplicación de host puede incorporar sus propias actividades sin restricciones, siempre que las actividades se inicien en la misma tarea.

Ejemplos de divisiones

División desde la ventana completa

Figura 15: La actividad A inicia la actividad B al costado.

No es necesario refactorizar. Puedes definir la configuración de la división de manera estática o en el tiempo de ejecución y, luego, llamar a Context#startActivity() sin ningún parámetro adicional.

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

División predeterminada

Cuando la página de destino de una aplicación está diseñada para dividirse en dos contenedores en pantallas grandes, la experiencia del usuario resulta óptima si ambas actividades se crean y se presentan de forma simultánea. Sin embargo, es posible que el contenido no esté disponible para el contenedor secundario de la división hasta que el usuario interactúe con la actividad en el contenedor principal (por ejemplo, cuando el usuario seleccione un elemento de un menú de navegación). Una actividad de marcador de posición puede llenar el vacío hasta que el contenido se pueda mostrar en el contenedor secundario de la división (consulta la sección Marcadores de posición más arriba).

Figura 16: División que se crea mediante la apertura simultánea de dos actividades. Una actividad es un marcador de posición.

Para crear una división con un marcador de posición, crea un marcador de posición y asócialo con la actividad principal:

<SplitPlaceholderRule
    window:placeholderIntentName=".Placeholder">
    <ActivityFilter
        window:activityName=".Main"/>
</SplitPlaceholderRule>

Cuando una app recibe un intent, la actividad objetivo se puede mostrar como la parte secundaria de una división de actividad (por ejemplo, una solicitud para mostrar una pantalla de detalles con información sobre un elemento de una lista). En pantallas pequeñas, el detalle se muestra en toda la ventana de tareas. En dispositivos más grandes, se muestra junto a la lista.

Figura 17: Actividad de detalles de vínculos directos que se muestra sola en una pantalla pequeña, pero junto con una actividad de lista en una pantalla grande.

La solicitud de inicio se debe enrutar a la actividad principal, y la actividad objetivo de detalles debe iniciarse en una división. SplitController elige automáticamente la presentación correcta (apilada o en paralelo) según el ancho de la pantalla disponible.

Kotlin

override fun onCreate(savedInstanceState Bundle?) {
    …
    splitController.registerRule(SplitPairRule(newFilters))
    startActivity(Intent(this, DetailActivity::class.java))
}

Java

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    …
    splitController.registerRule(new SplitPairRule(newFilters));
    startActivity(new Intent(this, DetailActivity.class));
}

El destino del vínculo directo puede ser la única actividad que debe estar disponible para el usuario en la pila de navegación hacia atrás, y te recomendamos que evites descartar la actividad de detalles y dejar solo la actividad principal:

Pantalla grande con la actividad de lista y la actividad de detalles en paralelo.
          La navegación hacia atrás no puede descartar la actividad de detalles y dejar la actividad de lista en pantalla.

Pantalla pequeña solo con la actividad de detalles. La navegación hacia atrás no puede descartar la actividad de detalles y mostrar la actividad de lista.

En su lugar, puedes finalizar ambas actividades al mismo tiempo mediante el atributo finishPrimaryWithSecondary:

<SplitPairRule
    window:finishPrimaryWithSecondary="always">
    <SplitPairFilter
        window:primaryActivityName=".List"
        window:secondaryActivityName=".Detail"/>
</SplitPairRule>

Consulta Atributos de configuración a continuación.

Varias actividades en contenedores divididos

Apilar varias actividades en un contenedor dividido permite a los usuarios acceder a contenido específico. Por ejemplo, con una división de lista y detalles, es posible que el usuario necesite ir a una sección de detalles secundarios, pero mantener la actividad principal en su lugar:

Figura 18: Actividad abierta en el lugar, en el panel secundario de la ventana de tareas.

Kotlin

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

Java

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

La actividad de detalles secundarios se coloca sobre la actividad de detalles, y la oculta:

Luego, el usuario puede regresar al nivel anterior de detalles navegando hacia atrás en la pila:

Figura 19: Se quitó la actividad de la parte superior de la pila.

El apilado de actividades unas sobre otras es el comportamiento predeterminado cuando estas se inician desde una que se encuentra en el mismo contenedor secundario. Las actividades iniciadas desde el contenedor principal dentro de una división activa también terminan en el contenedor secundario en la parte superior de la pila de actividades.

Actividades en una tarea nueva

Cuando las actividades en una ventana dividida de tareas inician actividades en una tarea nueva, esta última es independiente de la que incluye la división y se muestra en una ventana completa. En la pantalla Recents, se muestran dos tareas: la que está en la división y la nueva.

Figura 20: Se inicia la actividad C en una tarea nueva desde la actividad B.

Reemplazo de actividades

Las actividades se pueden reemplazar en la pila del contenedor secundario (por ejemplo, cuando se usa la actividad principal para la navegación de nivel superior y la actividad secundaria es un destino seleccionado). Cada selección de la navegación de nivel superior debe iniciar una actividad nueva en el contenedor secundario y quitar las actividades que antes estaban allí.

Figura 21: La actividad de navegación de nivel superior del panel principal reemplaza las actividades de destino en el panel secundario.

La navegación hacia atrás puede resultar confusa cuando se contrae la división (cuando se pliega el dispositivo) si la app no finaliza la actividad en el contenedor secundario cuando cambia la selección de navegación. Por ejemplo, si tienes un menú en el panel principal y las pantallas A y B apiladas en el panel secundario, cuando el usuario pliegue el teléfono, B estará sobre A, y A estará sobre el menú. Cuando el usuario navegue hacia atrás desde B, aparecerá A en lugar del menú.

En esos casos, se debe quitar la pantalla A de la pila de actividades.

El comportamiento predeterminado cuando se realiza un inicio lateral en un contenedor nuevo sobre una división existente es colocar los nuevos contenedores secundarios en la parte superior y conservar los antiguos en la pila de actividades. Puedes configurar las divisiones de modo que se borren los contenedores secundarios anteriores mediante clearTop y que se inicien las actividades nuevas con normalidad.

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

Kotlin

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

Java

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

De forma alternativa, usa la misma actividad secundaria y, desde la actividad principal (el menú), envía nuevos intents que se resuelvan en la misma instancia, pero que activen un estado o una actualización de la IU en el contenedor secundario.

Divisiones múltiples

Las apps pueden proporcionar navegación profunda de varios niveles iniciando actividades adicionales a un lado.

Cuando una actividad en un contenedor secundario inicia una actividad nueva a un costado, se crea una división nueva sobre la existente.

Figura 22: La actividad B inicia la actividad C al costado.

La pila de actividades contiene todas las actividades que se abrieron antes, por lo que los usuarios pueden navegar a la división A/B después de finalizar C.

Actividades A, B y C en una pila. Las actividades se apilan en el siguiente orden de arriba hacia abajo: C, B y A.

Para crear una nueva división, inicia la actividad nueva al lado del contenedor secundario existente. Declara las configuraciones para las divisiones A/B y B/C, e inicia la actividad C normalmente desde B:

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

Kotlin

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

Java

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

Cómo reaccionar a los cambios de estado de divisiones

Las diferentes actividades de una app pueden tener elementos de la IU que realizan la misma función (por ejemplo, un control que abre una ventana que contiene la configuración de la cuenta).

Figura 23: Diferentes actividades con elementos de la IU funcionalmente idénticos.

Si dos actividades que tienen un elemento de la IU en común están en una división, mostrar el elemento en ambas actividades resultará redundante y podría generar confusión.

Figura 24: Elementos de la IU duplicados en la división de actividad.

A fin de saber cuándo las actividades se encuentran en una división, registra un objeto de escucha con SplitController para los cambios en el estado de la división. Luego, ajusta la IU según corresponda:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    splitController
        .addSplitListener(this, mainThreadExecutor, SplitInfoChangeCallback())
}

inner class SplitInfoChangeCallback : Consumer<List<SplitInfo>> {
    override fun accept(splitInfoList: List<SplitInfo>) {
        findViewById<View>(R.id.infoButton).visibility =
            if (!splitInfoList.isEmpty()) View.GONE else View.VISIBLE
    }
}

Java

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    splitController
        .addSplitListener(this, mainThreadExecutor, SplitInfoChangeCallback());
}

class SplitInfoChangeCallback extends Consumer<List<SplitInfo>> {
    public void accept(List<SplitInfo> splitInfoList) {
        findViewById<View>(R.id.infoButton).visibility =
            !splitInfoList.isEmpty()) ? View.GONE : View.VISIBLE;
    }
}

Las devoluciones de llamada se pueden realizar en cualquier estado del ciclo de vida, incluso cuando se detiene una actividad. Por lo general, los objetos de escucha deben estar registrados en onStart() y no deben registrarse en onStop().

Ventana modal completa

Algunas actividades bloquean a los usuarios para que no interactúen con la aplicación hasta que se realice una acción especificada (por ejemplo, una actividad de la pantalla de acceso, la pantalla de confirmación de la política o un mensaje de error). Se debe evitar que las actividades modales aparezcan en una división.

Una actividad puede verse forzada a llenar siempre la ventana de tareas mediante la configuración de expansión:

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

Cómo finalizar actividades

Los usuarios pueden finalizar las actividades en cualquiera de los lados de la división si deslizan el dedo desde el borde de la pantalla:

Figura 25. Gesto de deslizar el dedo para finalizar la actividad B.
Figura 26. Gesto de deslizar el dedo para finalizar la actividad A.

Si el dispositivo está configurado a fin de usar el botón Atrás en lugar de la navegación por gestos, la entrada se envía a la actividad enfocada, es decir, aquella que se tocó o se inició por última vez.

El resultado de finalizar una de las actividades de la división depende de la configuración de esta última.

Atributos de configuración

Puedes especificar atributos de reglas de vinculación de divisiones para configurar cómo el hecho de finalizar todas las actividades en un lado de la división afecta las actividades del otro lado. Los atributos son los siguientes:

  • window:finishPrimaryWithSecondary: Indica cómo el hecho de finalizar las actividades del contenedor secundario afecta las actividades del contenedor principal.
  • window:finishSecondaryWithPrimary: Indica cómo el hecho de finalizar las actividades del contenedor principal afecta las actividades del contenedor secundario.

Entre los valores posibles de los atributos, se incluyen los siguientes:

  • always: Siempre finaliza las actividades en el contenedor asociado.
  • never: Nunca finaliza las actividades en el contenedor asociado.
  • adjacent: Finaliza las actividades en el contenedor asociado cuando ambos contenedores se muestran uno al lado del otro, pero no cuando están apilados.

Por ejemplo:

<SplitPairRule
    <!-- Do not finish the primary activity when the secondary activity finishes. -->
    window:finishPrimaryWithSecondary="never"
    <!-- Finish the secondary activity whenever the primary activity finishes. -->
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

Configuración predeterminada

Cuando finaliza una actividad de la división, la actividad restante ocupa toda la ventana:

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

División que contiene las actividades A y B. A finalizó, y toda la ventana queda ocupada por B.

División que contiene las actividades A y B. B finalizó y toda la ventana queda ocupada por A.

Cómo finalizar actividades al mismo tiempo

La actividad principal finaliza automáticamente cuando lo haga la actividad secundaria:

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

División que contiene las actividades A y B. B finalizó, lo que también finalizó A, y la ventana de tareas queda vacía.

División que contiene las actividades A y B. A finalizó, y solo B queda en la ventana de tareas.

Finaliza la actividad secundaria automáticamente cuando lo haga la actividad principal:

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

División que contiene las actividades A y B. A finalizó, lo que también finalizó B, y la ventana de tareas queda vacía.

División que contiene las actividades A y B. B finalizó, y solo A queda en la ventana de tareas.

Finaliza las actividades al mismo tiempo cuando se complete la principal o la secundaria:

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

División que contiene las actividades A y B. A finalizó, lo que también finalizó B, y la ventana de tareas queda vacía.

División que contiene las actividades A y B. B finalizó, lo que también finalizó A, y la ventana de tareas queda vacía.

Cómo finalizar varias actividades en contenedores

Si se apilan varias actividades en un contenedor dividido, finalizar una de las que esté en la parte inferior de la pila no finalizará automáticamente aquellas que se encuentren en la parte superior.

Por ejemplo, si dos actividades están en el contenedor secundario, y C está sobre B:

La pila de actividades secundaria que contiene la actividad C sobre B se apila sobre la principal que contiene la actividad A.

La configuración de la división se define según la configuración de las actividades A y B:

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

Cuando termina la actividad superior, se conserva la división.

División con la actividad A en el contenedor principal y las actividades B y C en el secundario, con C apilada sobre B. C finaliza y deja A y B en la división de actividad.

Finalizar la actividad inferior (raíz) del contenedor secundario no quita las actividades sobre ella. De esta manera, se conserva la división.

División con la actividad A en el contenedor principal y las actividades B y C en el secundario, con C apilada sobre B. B finaliza y deja A y C en la división de actividad.

También se ejecutan las reglas adicionales a los efectos de finalizar actividades al mismo tiempo, como finalizar la actividad secundaria con la principal:

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

División con la actividad A en el contenedor principal y las actividades B y C en el secundario, con C apilada sobre B. A finaliza, lo que también finaliza B y C.

Cuando la división se configura para finalizar la actividad principal y la secundaria al mismo tiempo, se ve lo siguiente:

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

División con la actividad A en el contenedor principal y las actividades B y C en el secundario, con C apilada sobre B. C finaliza y deja A y B en la división de actividad.

División con la actividad A en el contenedor principal y las actividades B y C en el secundario, con C apilada sobre B. B finaliza y deja A y C en la división de actividad.

División con la actividad A en el contenedor principal y las actividades B y C en el secundario, con C apilada sobre B. A finaliza, lo que también finaliza B y C.

Cómo cambiar las propiedades de las divisiones durante el tiempo de ejecución

No se pueden cambiar las propiedades de una división que se encuentre visible y activa. El cambio en las reglas de división afecta los inicios de actividades adicionales y los contenedores nuevos, pero no las divisiones existentes y activas.

Para cambiar las propiedades de las divisiones activas, finaliza las actividades laterales o presentes en la división y vuelve a iniciarlas al lado con una configuración nueva.

Cómo extraer una actividad de una división a una ventana completa

Crea una nueva configuración que muestre la ventana completa de la actividad lateral y, luego, reinicia la actividad con un intent que se resuelva en la misma instancia.

Cómo comprobar la compatibilidad con las divisiones durante el tiempo de ejecución

La incorporación de actividades es compatible con Android 12L (nivel de API 32) y versiones posteriores, pero también puede estar disponible en algunos dispositivos con versiones anteriores de la plataforma. Para comprobar la disponibilidad de la función en el tiempo de ejecución, usa el método SplitController.isSplitSupported():

Kotlin

val splitController = SplitController.Companion.getInstance()
if (splitController.isSplitSupported()) {
    // Device supports split activity features.
}

Java

SplitController splitController = SplitController.Companion.getInstance();
if (splitController.isSplitSupported()) {
  // Device supports split activity features.
}

Si no se admiten las divisiones, las actividades se iniciarán en la parte superior (como en el modelo de incorporación sin actividad).

Evita la anulación del sistema

Los fabricantes de dispositivos Android (fabricantes de equipos originales o OEMs) pueden implementar la incorporación de actividades como una función del sistema del dispositivo. El sistema especifica las reglas de división para las apps de varias actividades, lo que anula el comportamiento del sistema de ventanas de las apps. La anulación del sistema fuerza las apps de varias actividades a un modo de incorporación de actividad definido por el sistema.

La incorporación de la actividad del sistema puede mejorar la presentación de la app a través de diseños de varios paneles, como list-detail, sin ningún cambio en la app. Sin embargo, la incorporación de la actividad del sistema también puede causar errores de diseño, otros errores o conflictos con la incorporación de actividades que implementa la app.

Para evitar o permitir la incorporación de actividades del sistema, configura una propiedad en el archivo de manifiesto de la app, por ejemplo:

<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>

El nombre de la propiedad se define en el objeto WindowProperties de Jetpack WindowManager. Establece el valor en false si tu app implementa la incorporación de actividades o si quieres que el sistema no aplique sus reglas de incorporación de actividades a tu app. Establece el valor en true para permitir que el sistema aplique la incorporación de actividad definida por el sistema a tu app.

Limitaciones, restricciones y advertencias

  • Solo la app host de la tarea, que se identifica como propietaria de la actividad raíz de esta, puede organizar e incorporar otras actividades en la tarea. Si las actividades que admiten incorporación y divisiones se ejecutan en una tarea que pertenece a una aplicación diferente, la incorporación y las divisiones no funcionarán para esas actividades.
  • Las actividades solo se pueden organizar dentro de una misma tarea. Iniciar una actividad en una tarea nueva siempre la coloca en una ventana expandida nueva fuera de cualquier división existente.
  • Solo se pueden organizar y dividir las actividades que formen parte de un mismo proceso. La devolución de llamada SplitInfo solo informa actividades que pertenecen al mismo proceso, ya que no hay manera de conocer aquellas que existan en diferentes procesos.
  • Cada regla de actividad individual o de vinculación se aplica solo a los inicios de actividad que ocurran después de que se registre la regla. Por el momento, no hay forma de actualizar las divisiones existentes ni sus propiedades visuales.
  • La configuración del filtro de vinculación de divisiones debe coincidir con los intents que se usan cuando se inician actividades por completo. La coincidencia se produce cuando se inicia una actividad nueva desde el proceso de la aplicación, por lo que es posible que no conozcas los nombres de los componentes que se resuelven más adelante en el proceso del sistema cuando se usan intents implícitos. Si no se conoce el nombre de un componente al momento del inicio, se puede usar un comodín ("*/*") y realizar el filtrado según la acción de intent.
  • Por el momento, no hay forma de mover las actividades entre contenedores ni dentro o fuera de las divisiones después de crearlas. La biblioteca WindowManager solo crea las divisiones cuando se inician actividades nuevas con reglas que coincidan, y las divisiones se destruyen cuando finaliza la última actividad de un contenedor de divisiones.
  • Las actividades pueden volver a iniciarse cuando cambia la configuración, de modo que, cuando se crea o quita una división y cambian los límites de la actividad, esta puede pasar por la destrucción completa de la instancia anterior y la creación de la nueva. Como resultado, los desarrolladores de apps deberían tener cuidado con cuestiones como el inicio de actividades nuevas a partir de devoluciones de llamada de ciclo de vida.
  • Los dispositivos deben incluir la interfaz de extensiones de ventana para admitir la incorporación de actividades. Casi todos los dispositivos de pantalla grande con Android 12L (nivel de API 32) o versiones posteriores incluyen la interfaz. Sin embargo, algunos dispositivos de pantalla grande que no pueden ejecutar varias actividades no incluyen la interfaz de extensiones de ventana. Si un dispositivo de pantalla grande no admite el modo multiventana, es posible que no admita la incorporación de actividades.

Recursos adicionales