Intégration d'activités

L'intégration d'activités permet d'optimiser une application pour les grands écrans en divisant sa fenêtre de tâches en deux activités ou deux instances d'une même activité.

Figure 1 : Application Paramètres affichant des activités côte à côte.

Si votre application comprend plusieurs activités, l'intégration d'activités vous permet : offrent une expérience utilisateur améliorée sur les tablettes, les appareils pliables et les appareils ChromeOS.

L'intégration d'activités ne nécessite aucune refactorisation du code. Vous déterminez comment votre application affiche ses activités (côte à côte ou empilées) en créant un fichier de configuration XML ou en effectuant des appels d'API Jetpack WindowManager.

Les petits écrans sont gérés automatiquement. Lorsque votre application est installée sur un appareil doté d'un petit écran, les activités sont empilées les unes au-dessus des autres. Sur les grands écrans, les activités sont affichées côte à côte. Le système détermine la présentation en fonction de la configuration que vous avez créée. Aucune logique de ramification n'est nécessaire.

L'intégration d'activités s'adapte aux changements d'orientation de l'appareil et fonctionne parfaitement sur les appareils pliables, empilant et désempilant des activités lorsque l'appareil se plie et se déplie.

L'intégration d'activités est compatible avec la plupart des appareils à grand écran équipés d'Android 12L (niveau d'API 32) ou version ultérieure.

Fractionner la fenêtre de tâches

L'intégration d'activités divise la fenêtre de tâches de l'application en deux conteneurs : un conteneur principal et un conteneur secondaire. Ils hébergent les activités lancées à partir de l'activité principale ou d'autres activités qui se trouvent déjà dans les conteneurs.

Les activités sont empilées dans le conteneur secondaire au fur et à mesure de leur lancement. le conteneur secondaire est empilé sur le conteneur principal sur les petits écrans, Ainsi, l'empilement des activités et le retour arrière sont cohérents avec l'ordre des des activités déjà intégrées à votre application.

L'intégration d'activités vous permet d'afficher les activités de différentes manières. Votre application peut lancer deux activités côte à côte et fractionner ainsi la fenêtre de tâches :

Figure 2 : Deux activités côte à côte

De même, une activité qui occupe la totalité de la fenêtre de tâches peut également lancer une nouvelle activité en parallèle et fractionner l'écran :

Figure 3 : L'activité A lance l'activité B à côté

Les activités partageant une fenêtre de tâches et qui sont déjà sur un écran fractionné peuvent lancer d'autres activités de différentes manières :

  • Sur le côté en haut d'une autre activité :

    Figure 4 : L'activité A ouvre l'activité C sur le côté, au-dessus de l'activité B
  • Sur le côté, en déplaçant horizontalement l'écran fractionné et en masquant ainsi l'activité principale précédente :

    Figure 5 : L'activité B lance l'activité C sur le côté et déplace l'écran fractionné horizontalement
  • La nouvelle activité lancée remplace l'activité lancée précédemment dans la même pile d'activités :

    <ph type="x-smartling-placeholder">
    </ph>
    Figure 6. L'activité B lance l'activité C sans indicateur d'intent supplémentaire
  • L'activité lancée s'affiche dans toute la fenêtre dans la même tâche :

    Image 7. L'activité A ou B lance l'activité C, qui se remplit la fenêtre de tâches.

Navigation vers l'arrière

Différents types d'applications peuvent avoir des règles de navigation arrière différentes lorsque la fenêtre de tâches est fractionnée. Ces règles varient en fonction des dépendances entre les activités ou de la manière dont les utilisateurs déclenchent l'événement "Retour". Voici quelques exemples :

  • Fermeture simultanée des activités : si les activités sont liées et ne peuvent pas s'afficher sans l'autre, la navigation arrière peut être configurée pour les fermer toutes les deux.
  • Fermeture indépendante d'une activité : si les activités sont totalement indépendantes, la navigation arrière au niveau d'une activité n'affecte pas l'état d'une autre activité dans la fenêtre de tâches.

Si vous utilisez un bouton, l'événement "Retour" est envoyé à la dernière activité sélectionnée.

Pour la navigation par gestes :

  • Android 14 (niveau d'API 34) et versions antérieures : l'événement "Retour" est envoyé à l'activité où le geste a eu lieu. Lorsque les utilisateurs balaient l'écran depuis le côté gauche, l'événement "Retour" est envoyé à l'activité dans le volet de gauche de la fenêtre fractionnée. Lorsque les utilisateurs balayent l'écran depuis le côté droit écran, l'événement "Retour" est envoyé à l'activité du volet de droite.

  • Android 15 (niveau d'API 35) ou version ultérieure

    • Lorsque vous travaillez avec plusieurs activités de la même application, le geste met fin à l'activité supérieure, quelle que soit la direction du balayage, ce qui offre une expérience plus unifiée.

    • Dans les scénarios impliquant deux activités provenant de différentes applications (superposition), l'événement "Retour" est dirigé vers la dernière activité sélectionnée, conformément au comportement de la navigation par bouton.

Mise en page à plusieurs volets

Jetpack WindowManager vous permet de créer une mise en page à plusieurs volets pour intégrer les activités sur les appareils à grand écran équipés d'Android 12L (niveau d'API 32) ou version ultérieure, et sur certains appareils dotés d'une version antérieure de la plate-forme. Applications existantes basées sur plusieurs activités plutôt que des fragments ou des mises en page basées sur les vues comme SlidingPaneLayout peut améliorer l'expérience utilisateur sur grand écran sans refactoriser le code source.

Le fractionnement "liste/détail" est un exemple courant. Pour garantir une présentation de qualité, le système lance l'activité "Liste", puis l'application lance immédiatement l'activité "Détail". Le système de transition attend que les deux activités soient dessinées, puis les affiche ensemble. Pour l'utilisateur, les deux les activités sont lancées comme une seule.

Figure 8 : Deux activités ont été lancées simultanément dans une mise en page à plusieurs volets

Attributs de fractionnement

Vous pouvez spécifier les proportions de la fenêtre de tâches entre les conteneurs fractionnés et la disposition des conteneurs les uns par rapport aux autres.

Pour les règles définies dans un fichier de configuration XML, définissez les attributs suivants :

  • splitRatio : définit les proportions des conteneurs. Cette valeur est un nombre à virgule flottante dans l'intervalle ouvert (0,0, 1,0).
  • splitLayoutDirection: spécifie la mise en page des conteneurs fractionnés. les uns par rapport aux autres. Voici quelques valeurs possibles :
    • ltr : de gauche à droite.
    • rtl : de droite à gauche.
    • locale : la valeur ltr ou rtl est déterminée à partir des paramètres régionaux.

Pour obtenir des exemples, consultez la section Configuration XML.

Pour les règles créées à l'aide des API WindowManager, créez un objet SplitAttributes avec SplitAttributes.Builder et appelez les méthodes de compilateur suivantes :

Pour obtenir des exemples, consultez la section API WindowManager.

Figure 9 : Deux fractionnements d'activités avec une orientation de gauche à droite, mais avec différentes proportions

Espaces réservés

Les activités d'un espace réservé sont des activités secondaires vides qui occupent un espace fractionnement des activités. Ils sont destinés à être remplacés par une autre activité comprenant du contenu. Par exemple, une activité d'espace réservé peut occuper le côté secondaire d'un fractionnement d'activité dans une mise en page liste/détails jusqu'à ce qu'un élément de la liste est sélectionnée, puis une activité contenant les détails pour l'élément de liste sélectionné remplace l'espace réservé.

Par défaut, le système n'affiche les espaces réservés que lorsqu'il y a suffisamment d'espace pour un fractionnement d'activité. Les espaces réservés se terminent automatiquement lorsque la largeur ou la hauteur de l'écran devient trop grande ou trop petite pour qu'il soit fractionné. Lorsque l'espace le permet, le système relance l'espace réservé avec un état réinitialisé.

Figure 10. Appareil pliable qui se plie et se déplie. L'activité de l'espace réservé est éliminée, puis relancée lorsque la taille de l'écran change.

Toutefois, l'attribut stickyPlaceholder d'un objet SplitPlaceholderRule ou La méthode setSticky() de SplitPlaceholder.Builder peut remplacer comportement par défaut. Lorsque l'attribut ou la méthode spécifie la valeur true, le affiche l'espace réservé en tant qu'activité de niveau supérieur dans la fenêtre de tâches lorsque l'écran est redimensionné pour présenter un affichage à un volet, au lieu d'un écran à deux volets. (voir la section Configuration de fractionnement pour obtenir un exemple).

Figure 11 : Appareil pliable qui se plie et se déplie. L'activité liée à un espace réservé est persistante.

Changements de taille de la fenêtre

Lorsque la configuration de l'appareil réduit la largeur de la fenêtre de tâches de sorte qu'elle ne soit pas assez grande pour une mise en page à plusieurs volets (par exemple, lorsqu'un appareil pliable à grand écran passe de la taille d'une tablette à celle d'un téléphone ou que la fenêtre de l'application est redimensionnée en mode multifenêtre), les activités autres que les espaces réservés dans le volet secondaire de la fenêtre de tâches sont empilées au-dessus des activités du volet principal.

Les activités d'un espace réservé ne s'affichent que lorsque la largeur de l'écran est suffisante pour le fractionnement. Sur les petits écrans, l'espace réservé est automatiquement fermé. Lorsque redevient suffisamment grande, l'espace réservé est recréé. (voir la section Espaces réservés).

L'empilement d'activités est possible, car WindowManager classe les activités dans leur ordre de plan. dans le volet secondaire au-dessus des activités du volet principal.

Plusieurs activités dans le volet secondaire

L'activité B lance l'activité C sans indicateur d'intent supplémentaire :

Fractionnement contenant les activités A, B et C, avec C empilé sur B.

Les activités sont classées dans cet ordre dans la même tâche :

Pile secondaire contenant l&#39;activité C superposée à B
          La pile secondaire est empilée sur la pile d&#39;activité principale contenant l&#39;activité A.

Par conséquent, dans une fenêtre de tâches de petite taille, l'application se limite à une seule activité et positionne C en haut de la pile :

Fenêtre de petite taille n&#39;affichant que l&#39;activité C.

La navigation arrière dans la fenêtre de petite taille vous permet de parcourir les activités empilées les unes sur les autres.

Si la taille de la fenêtre de tâches est agrandie de sorte à pouvoir accueillir plusieurs volets, les activités s'affichent à nouveau côte à côte.

Fractionnements empilés

L'activité B lance l'activité C sur le côté et déplace l'écran fractionné horizontalement :

Fenêtre de tâches montrant les activités A et B, puis les activités B et C.

Les activités sont classées dans cet ordre dans la même tâche :

Activités A, B et C dans une seule pile. Les activités sont empilées de haut en bas dans l&#39;ordre suivant : C, B, A.

Dans une fenêtre de tâches de petite taille, l'application se limite à une seule activité et positionne C en haut :

Fenêtre de petite taille n&#39;affichant que l&#39;activité C.

Mode portrait fixe

Le paramètre de fichier manifeste android:screenOrientation permet aux applications de contraindre en mode portrait ou paysage. Pour améliorer l'expérience utilisateur sur les grands écrans (tablettes et pliables, par exemple), les fabricants d'appareils (OEM) peuvent ignorer les demandes d'orientation de l'écran et encadrer l'application en mode portrait sur les écrans en mode paysage au format letterbox, ou inversement.

Figure 12 : Activités encadrées au format letterbox : portrait fixe sur l'appareil en mode paysage (à gauche), paysage fixe sur l'appareil en mode portrait (à droite)

De même, lorsque l'intégration d'activités est activée, les OEM peuvent personnaliser les appareils pour Activités en mode portrait fixes au format letterbox en mode paysage sur les grands écrans (largeur ≥ 600 dp). Lorsqu'une activité de portrait fixe démarre une deuxième activité, l'appareil peut afficher les deux activités côte à côte dans une présentation à deux volets.

Figure 13. L'activité A au format portrait fixe lance l'activité B sur le côté

Toujours ajouter android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED au fichier manifeste de votre application pour indiquer aux appareils compatibles avec celle-ci l'intégration d'activités (consultez la section Configuration de fractionnement) ). Les appareils personnalisés par l'OEM déterminent ensuite s'il faut utiliser le format letterbox. les activités en mode portrait fixes.

Configuration de fractionnement

Les règles de fractionnement permettent de configurer les divisions d'activité. Vous définissez les règles de fractionnement dans un fichier de configuration XML ou en effectuant des appels à l'API Jetpack WindowManager.

Dans les deux cas, votre application doit accéder à la bibliothèque WindowManager et informer le système qu'elle a implémenté l'intégration d'activités.

Procédez comme suit :

  1. Ajouter la dernière dépendance de la bibliothèque WindowManager au niveau du module de votre application build.gradle, par exemple:

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

    La bibliothèque WindowManager fournit tous les composants requis pour l'intégration d'activités.

  2. Informez le système que votre application a implémenté l'intégration d'activités.

    Ajoutez la propriété android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED à l'élément <application> du fichier manifeste de l'application, puis définissez la valeur sur "true", par exemple :

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

    À partir de la version 1.1.0-alpha06 de WindowManager, les fractionnements de l'intégration d'activités sont désactivés, sauf si la propriété est ajoutée au fichier manifeste et définie sur "true".

    En outre, les fabricants d'appareils utilisent ce paramètre afin d'activer les fonctionnalités personnalisées pour les applications compatibles avec l'intégration d'activités. Par exemple, les appareils peuvent utiliser le format letterbox pour une activité en mode portrait sur les affichages en mode paysage. L'activité sera ainsi orientée de sorte à passer à une mise en page à deux volets lorsqu'une deuxième activité commencera (voir Mode portrait fixe).

Configuration XML

Pour créer une implémentation XML de l'intégration d'activités, suivez la procédez comme suit:

  1. Créez un fichier de ressources XML qui :

    • définit les activités qui partagent un fractionnement ;
    • Configure les options de fractionnement.
    • Crée un espace réservé pour le conteneur secondaire de la division lorsque le contenu n'est pas disponible
    • Spécifie les activités qui ne doivent jamais faire partie d'un fractionnement.

    Exemple :

    <!-- 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. Créez un initialiseur.

    Le composant RuleController de WindowManager analyse le fichier XML. de configuration Terraform et met les règles à la disposition du système. Un jetpack La bibliothèque Startup Initializer met le fichier XML à la disposition des RuleController au démarrage de l'application afin que les règles s'appliquent activités commencent.

    Pour créer un initialiseur, procédez comme suit :

    1. Ajoutez la dernière dépendance de la bibliothèque Jetpack Startup à votre fichier build.gradle au niveau du module, par exemple :

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

    2. Créez une classe qui implémente l'interface Initializer.

      L'initialiseur met les règles de fractionnement à disposition de RuleController en transmettant l'ID du fichier de configuration XML (main_split_config.xml) à la méthode RuleController.parseRules().

      Kotlin

      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()
          }
      }

      Java

      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. Créez un fournisseur de contenu pour les définitions de règles.

    Ajouter androidx.startup.InitializationProvider au fichier manifeste de votre application en tant que <provider>. Incluez une référence à l'implémentation de l'initialiseur 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 détecte et initialise SplitInitializer avant d'appeler la méthode onCreate() de l'application. Par conséquent, les règles de fractionnement sont appliquées lorsque l'activité principale de l'application commence.

API WindowManager

Vous pouvez implémenter l'intégration d'activités de manière programmatique avec quelques API appels. Effectuez les appels dans la méthode onCreate() d'une sous-classe de Application pour vous assurer que les règles sont en vigueur avant le lancement des activités.

Pour créer un fractionnement des activités de manière programmatique, procédez comme suit :

  1. Créez une règle de fractionnement :

    1. Créer un SplitPairFilter qui identifie les activités qui partagent le fractionnement:

      Kotlin

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

      Java

      SplitPairFilter splitPairFilter = new SplitPairFilter(
         new ComponentName(this, ListActivity.class),
         new ComponentName(this, DetailActivity.class),
         null
      );
    2. Ajoutez le filtre à un ensemble de filtres :

      Kotlin

      val filterSet = setOf(splitPairFilter)

      Java

      Set<SplitPairFilter> filterSet = new HashSet<>();
      filterSet.add(splitPairFilter);
    3. Créez des attributs de mise en page pour le fractionnement :

      Kotlin

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

      Java

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

      SplitAttributes.Builder crée un objet contenant une mise en page. Attributs:

      • setSplitType(): définit la manière dont la zone d'affichage disponible est alloué à chaque conteneur d'activité. Le type de fractionnement des ratios spécifie la proportion de la zone d'affichage disponible allouée conteneur principal ; le conteneur secondaire occupe le reste la zone d'affichage disponible.
      • setLayoutDirection(): spécifie comment les conteneurs d'activité sont disposés les uns par rapport à l’autre, le conteneur principal en premier.
    4. Créez une SplitPairRule :

      Kotlin

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

      Java

      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 crée et configure la règle :

      • filterSet: contient des filtres de paire de fractionnement qui déterminent quand pour appliquer la règle en identifiant les activités qui partagent un fractionnement.
      • setDefaultSplitAttributes(): applique des attributs de mise en page à la d'une règle.
      • setMinWidthDp(): définit la largeur minimale d'affichage (au format pixels indépendants de la densité, dp), qui permettent un fractionnement.
      • setMinSmallestWidthDp() : définit la valeur minimale (en dp) que la plus petite des deux dimensions d'affichage doit avoir pour permettre un fractionnement, quelle que soit l'orientation de l'appareil.
      • setMaxAspectRatioInPortrait(): définit le format d'affichage maximal. format (hauteur:largeur) en mode portrait pour quelle activité les écrans fractionnés s'affichent. Si le format d'un écran en mode portrait dépasse le format maximal, les fractionnements sont désactivés, la largeur de l'écran. Remarque : La valeur par défaut est 1,4. Les activités occupent donc toute la fenêtre de tâches en mode portrait sur la plupart des tablettes. Voir également SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT et setMaxAspectRatioInLandscape(). La valeur par défaut pour le mode paysage est ALWAYS_ALLOW.
      • setFinishPrimaryWithSecondary() : définit l'impact de toutes les activités effectuées dans le conteneur secondaire sur les activités du conteneur principal. NEVER indique que le système ne doit pas terminer les activités principales lorsque toutes les activités l'achèvement du conteneur (voir Arrêter les activités).
      • setFinishSecondaryWithPrimary() : définit l'impact de toutes les activités effectuées dans le conteneur principal sur les activités du conteneur secondaire. ALWAYS indique que le système doit toujours les activités du conteneur secondaire lorsque toutes les activités dans l'état final du conteneur principal (consultez Arrêter les activités).
      • setClearTop(): indique si toutes les activités du conteneurs secondaires sont arrêtées lorsqu'une nouvelle activité est lancée dans le conteneur. La valeur false indique que les nouvelles activités sont empilées sur les activités déjà présentes dans le conteneur secondaire.
    5. Obtenez l'instance du singleton du RuleController WindowManager, puis ajoutez la règle :

      Kotlin

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

      Java

        RuleController ruleController = RuleController.getInstance(this);
        ruleController.addRule(splitPairRule);
        
  2. Créez un espace réservé pour le conteneur secondaire lorsque le contenu n'est pas disponible :

    1. Créez un ActivityFilter qui identifie l'activité à laquelle l'espace réservé partage un fractionnement de la fenêtre de tâches:

      Kotlin

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

      Java

      ActivityFilter placeholderActivityFilter = new ActivityFilter(
          new ComponentName(this, ListActivity.class),
          null
      );
    2. Ajoutez le filtre à un ensemble de filtres :

      Kotlin

      val placeholderActivityFilterSet = setOf(placeholderActivityFilter)

      Java

      Set<ActivityFilter> placeholderActivityFilterSet = new HashSet<>();
      placeholderActivityFilterSet.add(placeholderActivityFilter);
    3. Créez un SplitPlaceholderRule :

      Kotlin

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

      Java

      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 crée et configure la règle :

      • placeholderActivityFilterSet : contient des filtres d'activité qui déterminent quand appliquer la règle en identifiant les activités auxquelles l'activité d'espace réservé est associée.
      • Intent : spécifie le lancement de l'activité d'espace réservé.
      • setDefaultSplitAttributes(): Applique des attributs de mise en page à la règle.
      • setMinWidthDp() : définit la largeur d'affichage minimale (en pixels indépendants de la densité, dp) pour permettre le fractionnement.
      • setMinSmallestWidthDp() : définit la valeur minimale (en dp) que la plus petite des deux dimensions d'affichage doit avoir pour permettre un fractionnement, quelle que soit l'orientation de l'appareil.
      • setMaxAspectRatioInPortrait() : définit le format maximal d'affichage (height:width) en mode portrait pour lequel les fractionnements d'activité seront affichés. Remarque : La valeur par défaut est 1,4. Les activités occupent donc toute la fenêtre de tâches en mode portrait sur la plupart des tablettes. Voir aussi SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT et setMaxAspectRatioInLandscape() La valeur par défaut pour le mode paysage est ALWAYS_ALLOW.
      • setFinishPrimaryWithPlaceholder(): Définit l'impact de la finalisation de l'activité liée à un espace réservé sur les activités dans le conteneur principal. "ALWAYS" indique que le système doit toujours les activités dans le conteneur principal lorsque l'espace réservé se termine (voir Arrêter les activités).
      • setSticky(): détermine si l'activité d'espace réservé apparaît en haut de la pile d'activités sur les petits écrans une fois que est apparu pour la première fois dans un écran fractionné avec un minimum suffisant la largeur.
    4. Ajoutez la règle à la fenêtre RuleController de WindowManager :

      Kotlin

      ruleController.addRule(splitPlaceholderRule)

      Java

      ruleController.addRule(splitPlaceholderRule);
  3. Spécifiez les activités qui ne doivent jamais faire partie d'un fractionnement :

    1. Créez un ActivityFilter qui identifie l'activité qui doit occupent toujours la totalité de la zone d'affichage des tâches:

      Kotlin

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

      Java

      ActivityFilter expandedActivityFilter = new ActivityFilter(
        new ComponentName(this, ExpandedActivity.class),
        null
      );
    2. Ajoutez le filtre à un ensemble de filtres :

      Kotlin

      val expandedActivityFilterSet = setOf(expandedActivityFilter)

      Java

      Set<ActivityFilter> expandedActivityFilterSet = new HashSet<>();
      expandedActivityFilterSet.add(expandedActivityFilter);
    3. Créez une ActivityRule :

      Kotlin

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

      Java

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

      ActivityRule.Builder crée et configure la règle :

      • expandedActivityFilterSet : contient les filtres d'activité qui déterminent quand appliquer la règle en identifiant les activités que vous souhaitez exclure des écrans fractionnés.
      • setAlwaysExpand() : indique si l'activité doit remplir toute la fenêtre de tâches.
    4. Ajoutez la règle à la fenêtre RuleController de WindowManager :

      Kotlin

      ruleController.addRule(activityRule)

      Java

      ruleController.addRule(activityRule);

Intégration entre les applications

Sur Android 13 (niveau d'API 33) ou version ultérieure, les applications peuvent intégrer des activités provenant d'autres applications. L'intégration d'activités entre les applications, également appelée UID, permet de rassembler visuellement les activités de plusieurs applications Android. La système affiche une activité de l'application hôte et une activité intégrée à partir une autre application à l'écran côte à côte ou en haut et en bas, comme dans une application unique l'intégration d'activités.

Par exemple, l'application Paramètres peut intégrer l'activité de sélection du fond d'écran à partir de l'application BackgroundPicker:

Figure 14 : Application Paramètres (menu sur la gauche) avec sélecteur de fond d'écran en tant qu'activité intégrée (à droite)

Modèle de confiance

Les processus hôtes qui intègrent des activités provenant d'autres applications sont en mesure de redéfinir la présentation des activités intégrées, y compris la taille, la position, le recadrage et la transparence. Des hôtes malveillants peuvent utiliser cette fonctionnalité pour induire les utilisateurs en erreur créer des attaques de détournement de clic ou d'autres attaques par redressement de l'interface utilisateur.

Pour éviter l'usage abusif de l'intégration d'activités entre les applications, Android demande aux applications d'autoriser l'intégration de leurs activités. Les applications peuvent désigner des hôtes comme approuvés ou non fiables.

Hôtes approuvés

Pour permettre à d'autres applications d'intégrer et de contrôler entièrement la présentation de votre application, spécifiez le certificat SHA-256 de l'hôte application dans l'attribut android:knownActivityEmbeddingCerts de <activity> ou <application> du fichier manifeste de votre application.

Définissez la valeur de android:knownActivityEmbeddingCerts sous forme de chaîne :

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

ou, pour spécifier plusieurs certificats, un tableau de chaînes :

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

qui fait référence à une ressource semblable à celle-ci :

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

Les propriétaires d'applications peuvent obtenir un condensé de certificat SHA en exécutant Gradle signingReport tâche. Le condensé du certificat correspond à l'empreinte SHA-256, sans les deux-points de séparation. Pour en savoir plus, consultez Générer un rapport de signature et Authentifier votre client.

Hôtes non approuvés

Pour autoriser n'importe quelle application à intégrer les activités de votre application et à contrôler leur présentation, spécifiez l'attribut android:allowUntrustedActivityEmbedding dans les éléments <activity> ou <application> du fichier manifeste de l'application, par exemple :

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

La valeur par défaut de l'attribut est "false", ce qui empêche l'activité entre les applications la représentation vectorielle continue.

Authentification personnalisée

Pour atténuer les risques liés à l'intégration d'activités non approuvées, créez un mécanisme d'authentification personnalisé qui valide l'identité de l'hôte. Si vous connaissez l'hôte de certificats, utilisez la bibliothèque androidx.security.app.authenticator pour s'authentifier. Si l'hôte s'authentifie après l'intégration de votre activité, vous pouvez afficher le contenu réel. Sinon, vous pouvez informer l'utilisateur que l'action n'a pas été autorisée et bloquer le contenu.

Utilisez la méthode ActivityEmbeddingController#isActivityEmbedded() de la bibliothèque Jetpack WindowManager pour vérifier si un hôte intègre votre activité, par exemple :

Kotlin

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

Java

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

Restriction liée à la taille minimale

Le système Android applique la hauteur et la largeur minimales spécifiées dans l'élément <layout> du fichier manifeste de l'application aux activités intégrées. Si une application ne spécifie pas de hauteur ni de largeur minimales, les valeurs par défaut du système s'appliquent (sw220dp).

Si l'hôte tente de redimensionner le conteneur intégré en dessous de la valeur minimale, le conteneur intégré se développe pour occuper toutes les limites de la tâche.

<activity-alias>

Pour que l'intégration d'activités approuvées ou non approuvées fonctionne avec l'élément <activity-alias>, android:knownActivityEmbeddingCerts ou android:allowUntrustedActivityEmbedding doit être appliqué à l'activité cible et non l'alias. La stratégie qui vérifie la sécurité sur le serveur système est en fonction des indicateurs définis sur la cible, et non sur l'alias.

Application hôte

Les applications hôtes implémentent l'intégration d'activités entre les applications de la même manière que l'intégration d'activités dans une seule application. SplitPairRule et Objets SplitPairFilter ou ActivityRule et ActivityFilter spécifier les activités intégrées et le fractionnement de la fenêtre de tâches. Les règles de fractionnement sont définies de manière statique au format XML ou au moment de l'exécution à l'aide d'appels à l'API Jetpack WindowManager.

Si une application hôte tente d'intégrer une activité qui n'a pas activé l'intégration entre les applications, l'activité occupe l'intégralité des limites de la tâche. Par conséquent, les applications hôtes doivent savoir si les activités cibles autorisent l'intégration entre les applications ou non.

Si une activité intégrée lance une nouvelle activité dans la même tâche et que la nouvelle n'a pas activé l'intégration entre les applications, elle occupe le des limites de tâche entières au lieu de superposer l'activité dans le conteneur intégré.

Une application hôte peut intégrer ses propres activités sans restriction, à condition que les activités sont lancées dans la même tâche.

Exemples de fractionnement

Fractionnement de la fenêtre entière

Figure 15 : L'activité A lance l'activité B à côté

Aucune refactorisation n'est nécessaire. Vous pouvez définir la configuration du fractionnement en mode statique ou au moment de l'exécution, puis appeler Context#startActivity() sans paramètres supplémentaires.

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

Fractionnement par défaut

Lorsque la page de destination d'une application est conçue pour être fractionnée en deux conteneurs sur les grands écrans, l'expérience utilisateur est optimale lorsque les deux activités sont créées et présentées simultanément. Toutefois, le contenu destiné au conteneur secondaire du fractionnement n'est pas toujours disponible tant que l'utilisateur n'a pas interagi avec l'activité du conteneur principal (tant qu'il n'a pas sélectionné un élément dans un menu de navigation, par exemple). Une activité d'espace réservé peut remplir le vide jusqu'à ce que le contenu peut être affiché dans le conteneur secondaire du fractionnement (voir les Espaces réservés).

Figure 16 : Fractionnement créé en ouvrant deux activités simultanément. L'une des activités correspond à un espace réservé.

Pour créer un fractionnement avec un espace réservé, créez un espace réservé et associez-le à l'activité principale :

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

Lorsqu'une application reçoit un intent, l'activité cible peut être affichée en tant que partie secondaire d'un fractionnement d'activité (requête d'affichage d'un écran détaillé contenant des informations sur l'élément d'une liste, par exemple). Sur les petits écrans, le détail s'affiche dans la fenêtre de tâches complète ; sur les appareils plus grands, à côté de la liste.

Figure 17 : Activité "Détail" affichée seule sur un petit écran grâce à un lien profond et affichée avec l'activité "Liste" sur un grand écran

La requête de lancement doit être acheminée vers l'activité principale, et l'activité cible destinée aux détails doit être lancée dans un écran fractionné. Le système choisit automatiquement la bonne présentation, empilée ou côte à côte, en fonction des la largeur d'affichage.

Kotlin

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

Java

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

La destination du lien profond peut être la seule activité qui devrait être disponible pour l'utilisateur dans la pile de navigation "Retour", et vous voudrez peut-être éviter de fermer l'activité "Détail" et ne quittez que l'activité principale:

Grand écran avec l&#39;activité &quot;Liste&quot; et l&#39;activité &quot;Détail&quot; côte à côte.
          Navigation arrière qui ne parvient pas à ignorer l&#39;activité &quot;Détail&quot; et à laisser l&#39;activité &quot;Liste&quot; à l&#39;écran.

Petit écran n&#39;affichant que l&#39;activité &quot;Détail&quot;. Navigation arrière qui ne parvient pas à ignorer l&#39;activité &quot;Détail&quot; et à afficher l&#39;activité &quot;Liste&quot; à l&#39;écran.

À la place, vous pouvez arrêter les deux activités simultanément avec l'attribut finishPrimaryWithSecondary :

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

Consultez la section Attributs de configuration.

Plusieurs activités dans des conteneurs fractionnés

L'empilement de plusieurs activités dans un conteneur fractionné permet aux utilisateurs d'accéder à un contenu profond. Par exemple, avec un fractionnement Liste/Détail, l'utilisateur doit parfois accéder à une section de sous-détails tout en conservant l'activité principale :

Figure 18 : Activité ouverte dans le volet secondaire complet de la fenêtre de tâches

Kotlin

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

Java

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

L'activité du sous-détail est placée au-dessus de l'activité du détail et la dissimule :

L'utilisateur peut ensuite revenir au niveau de détail précédent via la navigation arrière dans la pile :

Figure 19 : Activité supprimée du haut de la pile

Les activités sont empilées par défaut lorsqu'elles sont lancées à partir d'une activité qui se trouve dans le même conteneur secondaire. Les activités lancées à partir du conteneur principal dans un fractionnement actif se retrouvent également dans le conteneur secondaire, au-dessus de la pile d'activités.

Activités dans une nouvelle tâche

Lorsque des activités d'une fenêtre de tâches fractionnée déclenchent d'autres activités dans une nouvelle tâche, cette dernière est distincte de la tâche qui inclut le fractionnement et s'affiche en plein écran. L'écran "Recents" (Éléments récents) affiche deux tâches : la tâche sur l'écran fractionné et la nouvelle tâche.

Figure 20 : Lancement de l'activité C dans une nouvelle tâche à partir de l'activité B

Remplacement d'activité

Les activités peuvent être remplacées dans la pile secondaire du conteneur. par exemple, lorsque L'activité principale est utilisée pour la navigation de premier niveau et l'activité secondaire est une destination sélectionnée. Chaque sélection dans la navigation de premier niveau devrait lancer une nouvelle activité dans le conteneur secondaire et supprimer l'activité ou les activités qui s'y trouvaient précédemment.

Figure 21 : L'activité de navigation de premier niveau dans le volet principal remplace les activités de destination dans le volet secondaire

Si l'application ne termine pas l'activité dans le conteneur secondaire au moment Modification de la sélection de la navigation, la navigation vers l'arrière peut prêter à confusion lorsque le fractionnement est réduit (lorsque l'appareil est plié). Par exemple, si vous avez un menu dans volet principal et les écrans A et B empilés dans le volet secondaire, lorsque l'utilisateur plie le téléphone, B est au-dessus de A et A est en haut du menu. Lorsque l'utilisateur revient en arrière à partir de B, A apparaît à la place du menu.

Dans ce cas, l'écran A doit être supprimé de la pile "Retour".

Par défaut, le lancement sur le côté dans un nouveau conteneur au-dessus d'un écran déjà fractionné place les nouveaux conteneurs secondaires au-dessus et conserve les anciens dans la pile "Retour". Vous pouvez configurer les fractionnements pour que les conteneurs secondaires précédents soient effacés avec clearTop et lancer normalement les nouvelles activités.

<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)));
    }
}

Vous pouvez également utiliser la même activité secondaire et, à partir de l'activité principale (menu), envoyer de nouveaux intents qui renvoient à la même instance, mais déclenchent une mise à jour de l'état ou de l'interface utilisateur dans le conteneur secondaire.

Fonctionnement de plusieurs écrans fractionnés

Les applications peuvent fournir une navigation profonde à plusieurs niveaux en lançant des activités supplémentaires sur le côté.

Lorsqu'une activité dans un conteneur secondaire lance une nouvelle activité sur le côté, un écran fractionné est créé au-dessus de l'écran fractionné existant.

Figure 22 : L'activité B lance l'activité C sur le côté

La pile "Retour" contient toutes les activités qui ont été ouvertes afin que les utilisateurs puissent accéder à l'écran fractionné A/B une fois qu'ils auront fermé l'écran C.

Activités A, B et C dans une pile. Les activités sont empilées de haut en bas dans l&#39;ordre suivant : C, B, A.

Pour créer un autre écran fractionné, lancez la nouvelle activité sur le côté du conteneur secondaire existant. Déclarez les configurations correspondant aux écrans fractionnés A/B et B/C, puis lancez l'activité C normalement à partir de 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));
    }
}

Réagir aux changements d'état des écrans fractionnés

Différentes activités d'application peuvent comporter des éléments d'interface utilisateur ayant la même fonction (commande qui ouvre une fenêtre contenant les paramètres du compte, par exemple).

Figure 23 : Activités distinctes avec des éléments d'interface utilisateur identiques du point de vue fonctionnel

Si deux activités qui partagent un élément d'interface utilisateur commun sont fractionnées, il est redondant et parfois déroutant d'afficher cet élément dans les deux activités.

Figure 24 : Éléments d'interface utilisateur en double dans les écrans fractionnés d'une activité

Pour déterminer quand les activités sont dans un écran fractionné, consultez le flux SplitController.splitInfoList ou enregistrez un écouteur avec SplitControllerCallbackAdapter afin de suivre les changements d'état des écrans fractionnés. Ensuite, ajustez l'interface utilisateur en conséquence:

Kotlin

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

Java

@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);
            });
}

Les coroutines peuvent être lancées à n'importe quel état du cycle de vie, mais elles sont généralement lancées à l'état STARTED pour préserver les ressources (voir la section Utiliser des coroutines Kotlin avec des composants tenant compte du cycle de vie pour en savoir plus).

Les rappels peuvent être effectués à n'importe quel état du cycle de vie, y compris lorsqu'une activité est arrêtée. L'enregistrement des écouteurs doit généralement être activé dans onStart() et être annulé dans onStop().

Fenêtre modale plein écran

Certaines activités empêchent les utilisateurs d'interagir avec l'application tant qu'une action spécifique n'a pas été effectuée (par exemple, un écran d'authentification, un écran de confirmation d'un règlement ou un message d'erreur). Les activités modales ne doivent pas s'afficher dans des écrans fractionnés.

Pour forcer une activité à toujours remplir la fenêtre de tâches, utilisez la configuration d'expansion :

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

Arrêter les activités

Les utilisateurs peuvent terminer les activités de chaque côté du fractionnement en balayant l'écran depuis le bord. de l'écran:

Figure 25 : Geste de balayage de l'écran qui met fin à l'activité B
Figure 26 : Geste de balayage de l'écran qui met fin à l'activité A

Si l'appareil est configuré pour utiliser le bouton "Retour" au lieu de la navigation par gestes, l'entrée est envoyée à l'activité sélectionnée, à savoir celle sur laquelle l'utilisateur a appuyé en dernier ou celle qu'il a lancée en dernier.

L'impact de la finalisation de toutes les activités d'un conteneur sur le conteneur opposé dépend de la configuration du fractionnement.

Attributs de configuration

Vous pouvez spécifier des attributs de règle de paire de fractionnement pour configurer le mode de finalisation les activités d'un côté de l'écran fractionné affectent les activités de l'autre côté de la division. Les attributs sont les suivants :

  • window:finishPrimaryWithSecondary : comment terminer toutes les activités dans le conteneur secondaire a une incidence sur les activités du conteneur principal
  • window:finishSecondaryWithPrimary : impact de toutes les activités effectuées dans le conteneur principal sur les activités du conteneur secondaire

Les valeurs possibles des attributs sont les suivantes :

  • always : toujours terminer les activités dans le conteneur associé
  • never : ne jamais terminer les activités dans le conteneur associé
  • adjacent : termine les activités dans le conteneur associé lorsque les deux conteneurs sont affichés côte à côte, mais pas lorsque deux conteneurs sont empilés

Exemple :

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

Configuration par défaut

Lorsque toutes les activités d'un conteneur sont finalisées, le conteneur restant occupe la totalité de la fenêtre :

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

Écran fractionné contenant les activités A et B. L&#39;activité A est arrêtée. En conséquence, l&#39;activité B occupe la fenêtre entière.

Écran fractionné contenant les activités A et B. L&#39;activité B est arrêtée. En conséquence, l&#39;activité A occupe la fenêtre entière.

Mettre fin aux activités simultanément

Mettre fin automatiquement aux activités du conteneur principal lorsque toutes les activités dans la phase finale du conteneur secondaire:

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

Écran fractionné contenant les activités A et B. L&#39;activité B est arrêtée, ce qui met également fin à l&#39;activité A, laissant la fenêtre de tâches vide.

Écran fractionné contenant les activités A et B. L&#39;activité A est arrêtée. L&#39;activité B apparaît seule dans la fenêtre de tâches.

Mettez automatiquement fin aux activités du conteneur secondaire activités de l'arrêt du conteneur principal:

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

Écran fractionné contenant les activités A et B. L&#39;activité A est arrêtée, ce qui met également fin à l&#39;activité B, laissant la fenêtre de tâches vide.

Écran fractionné contenant les activités A et B. L&#39;activité B est arrêtée. L&#39;activité A apparaît seule dans la fenêtre de tâches.

Mettre fin aux activités simultanément lorsque toutes les activités de l'instance principale ou final du conteneur secondaire:

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

Écran fractionné contenant les activités A et B. L&#39;activité A est arrêtée, ce qui met également fin à l&#39;activité B, laissant la fenêtre de tâches vide.

Écran fractionné contenant les activités A et B. L&#39;activité B est arrêtée, ce qui met également fin à l&#39;activité A, laissant la fenêtre de tâches vide.

Mettre fin à plusieurs activités dans les conteneurs

Si plusieurs activités sont empilées dans un conteneur fractionné, l'arrêt d'une activité au bas de la pile ne met pas automatiquement fin aux activités en haut de la pile.

Par exemple, si deux activités se trouvent dans le conteneur secondaire, C au-dessus de B :

La pile secondaire contenant l&#39;activité C empilée sur B, est empilée sur la pile principale contenant l&#39;activité A.

et la configuration du fractionnement est définie par la configuration des activités A et B:

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

L'arrêt de l'activité située en haut de la pile conserve le fractionnement.

Écran fractionné avec l&#39;activité A dans le conteneur principal et les activités B et C dans le conteneur secondaire, avec l&#39;activité C empilée sur B. L&#39;activité C est arrêtée, ce qui laisse A et B dans l&#39;écran fractionné.

L'arrêt de l'activité inférieure (racine) du conteneur secondaire ne supprime pas les activités qui s'y rapportent ; conserve donc également le fractionnement.

Écran fractionné avec l&#39;activité A dans le conteneur principal et les activités B et C dans le conteneur secondaire, avec l&#39;activité C empilée sur B. L&#39;activité B est arrêtée, ce qui laisse A et C dans l&#39;écran fractionné.

Toute règle supplémentaire pour mettre fin à des activités simultanément, comme la fin du l'activité secondaire avec l'activité principale, sont également exécutées:

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

Écran fractionné avec l&#39;activité A dans le conteneur principal et les activités B et C dans le conteneur secondaire, avec l&#39;activité C empilée sur B. L&#39;activité A est arrêtée, ce qui met également fin aux activités B et C.

Et lorsque le fractionnement est configuré pour mettre fin à l'activité principale et à l'activité secondaire simultanément :

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

Écran fractionné avec l&#39;activité A dans le conteneur principal et les activités B et C dans le conteneur secondaire, avec l&#39;activité C empilée sur B. L&#39;activité C est arrêtée, ce qui laisse A et B dans l&#39;écran fractionné.

Écran fractionné avec l&#39;activité A dans le conteneur principal et les activités B et C dans le conteneur secondaire, avec l&#39;activité C empilée sur B. L&#39;activité B est arrêtée, ce qui laisse A et C dans l&#39;écran fractionné.

Écran fractionné avec l&#39;activité A dans le conteneur principal et les activités B et C dans le conteneur secondaire, avec l&#39;activité C empilée sur B. L&#39;activité A est arrêtée, ce qui met également fin aux activités B et C.

Modifier les propriétés de fractionnement au moment de l'exécution

Les propriétés d'un fractionnement actif et visible ne peuvent pas être modifiées. Modifier le paramètre les règles de fractionnement affectent les lancements d'activités supplémentaires et les nouveaux conteneurs, mais pas les fractionnements existants et actifs.

Pour modifier les propriétés des fractionnements actifs, arrêtez les activités secondaires dans l'écran fractionné, puis lancez-les de nouveau sur le côté avec une nouvelle configuration.

Propriétés du fractionnement dynamique

Android 15 (niveau d'API 35) ou version ultérieure compatible avec Jetpack WindowManager 1.4 et les versions supérieures offrent des fonctionnalités dynamiques qui permettent de configurer l'activité divisions de représentation vectorielle continue, y compris:

  • Élargissement des volets : un séparateur interactif et déplaçable permet aux utilisateurs de redimensionner les volets dans une présentation fractionnée.
  • Épinglage d'activité:les utilisateurs peuvent épingler le contenu dans un conteneur isoler la navigation dans le conteneur de la navigation dans l'autre conteneur.
  • Atténuation de la luminosité de la boîte de dialogue en plein écran:lors de l'affichage d'une boîte de dialogue, les applications peuvent spécifier s'il faut assombrir toute la fenêtre de tâches ou uniquement le conteneur qui a ouvert le .

Développement du volet

L'expansion des volets permet aux utilisateurs d'ajuster l'espace d'écran alloué aux deux activités dans une mise en page à deux volets.

Pour personnaliser l'apparence du séparateur de fenêtre et définir la plage de glissement du séparateur, procédez comme suit :

  1. Créer une instance de DividerAttributes

  2. Personnalisez les attributs du séparateur :

    • color : couleur du séparateur de volets déplaçable.

    • widthDp : largeur du séparateur de volets déplaçable. Définissez la valeur sur WIDTH_SYSTEM_DEFAULT pour laisser le système déterminer la largeur du séparateur.

    • Plage de déplacement:le pourcentage minimal de la surface de chaque volet à l'écran occuperont. Peut être compris entre 0,33 et 0,66. Définissez la valeur sur DRAG_RANGE_SYSTEM_DEFAULT pour laisser le système déterminer la plage de glissement.

Kotlin

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

if (WindowSdkExtensions.getInstance().extensionVersion >= 6) {
    splitAttributesBuilder.setDividerAttributes(
      DividerAttributes.DraggableDividerAttributes.Builder()
        .setColor(getColor(context, R.color.divider_color))
        .setWidthDp(4)
        .setDragRange(DividerAttributes.DragRange.DRAG_RANGE_SYSTEM_DEFAULT)
        .build()
    )
}
val splitAttributes: SplitAttributes = splitAttributesBuilder.build()

Java

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

if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 6) {
    splitAttributesBuilder.setDividerAttributes(
      new DividerAttributes.DraggableDividerAttributes.Builder()
        .setColor(ContextCompat.getColor(context, R.color.divider_color))
        .setWidthDp(4)
        .setDragRange(DividerAttributes.DragRange.DRAG_RANGE_SYSTEM_DEFAULT)
        .build()
    );
}
SplitAttributes splitAttributes = splitAttributesBuilder.build();

Épinglage d'activités

L'épinglage d'activité permet aux utilisateurs d'épingler l'une des fenêtres fractionnées afin que l'activité reste telle quelle pendant que les utilisateurs naviguent dans l'autre fenêtre. L'épinglage d'activités offre une expérience multitâche améliorée.

Pour activer l'épinglage d'activité dans votre application, procédez comme suit:

  1. Ajoutez un bouton au fichier de mise en page de l'activité que vous souhaitez épingler, pour exemple, l'activité "Détail" d'une mise en page "Liste et vue détaillée" :

    <androidx.constraintlayout.widget.ConstraintLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/detailActivity"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:background="@color/white"
     tools:context=".DetailActivity">
    
    <TextView
       android:id="@+id/textViewItemDetail"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:textSize="36sp"
       android:textColor="@color/obsidian"
       app:layout_constraintBottom_toTopOf="@id/pinButton"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toTopOf="parent" />
    
    <androidx.appcompat.widget.AppCompatButton
       android:id="@+id/pinButton"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@string/pin_this_activity"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toBottomOf="@id/textViewItemDetail"/>
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    
  2. Dans la méthode onCreate() de l'activité, définissez un écouteur "onclick" sur le bouton:

    Kotlin

    pinButton = findViewById(R.id.pinButton)
    pinButton.setOnClickListener {
        val splitAttributes: SplitAttributes = SplitAttributes.Builder()
            .setSplitType(SplitAttributes.SplitType.ratio(0.66f))
            .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
            .build()
    
        val pinSplitRule = SplitPinRule.Builder()
            .setSticky(true)
            .setDefaultSplitAttributes(splitAttributes)
            .build()
    
        SplitController.getInstance(applicationContext).pinTopActivityStack(taskId, pinSplitRule)
    }

    Java

    Button pinButton = findViewById(R.id.pinButton);
    pinButton.setOnClickListener( (view) => {
        SplitAttributes splitAttributes = new SplitAttributes.Builder()
            .setSplitType(SplitAttributes.SplitType.ratio(0.66f))
            .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
            .build();
    
        SplitPinRule pinSplitRule = new SplitPinRule.Builder()
            .setSticky(true)
            .setDefaultSplitAttributes(splitAttributes)
            .build();
    
        SplitController.getInstance(getApplicationContext()).pinTopActivityStack(getTaskId(), pinSplitRule);
    });

Atténuation en plein écran

Les activités atténuent généralement leur écran pour attirer l'attention sur une boîte de dialogue. Dans l'intégration d'activités, les deux volets de l'écran à double volet doivent s'assombrir, Uniquement le volet contenant l'activité qui a ouvert la boîte de dialogue, pour une UI unifiée expérience.

Avec WindowManager 1.4 ou version ultérieure, la fenêtre de l'application s'assombrit par défaut lorsqu'un (voir EmbeddingConfiguration.DimAreaBehavior.ON_TASK).

Pour assombrir uniquement le conteneur de l'activité qui a ouvert la boîte de dialogue, utilisez EmbeddingConfiguration.DimAreaBehavior.ON_ACTIVITY_STACK

Extraire une activité d'un écran fractionné pour l'afficher dans la fenêtre complète

Créez une configuration qui affichera l'activité secondaire dans une fenêtre complète, puis relancez cette activité avec un intent qui résout la même instance.

Vérifier la prise en charge du fractionnement au moment de l'exécution

L'intégration d'activités est compatible avec Android 12L (niveau d'API 32) ou version ultérieure, mais est également disponible sur certains appareils exécutant des versions antérieures de la plateforme. Pour vérifier la disponibilité de cette fonctionnalité au moment de l'exécution, utilisez la propriété SplitController.splitSupportStatus ou la méthode SplitController.getSplitSupportStatus() :

Kotlin

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

Java

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

Si les fractionnements ne sont pas pris en charge, les activités sont lancées au-dessus de la pile d'activités (en suivant le modèle d'intégration hors activité).

Empêcher le remplacement du système

Les fabricants d'appareils Android (fabricants d'équipement d'origine, ou OEM) peuvent implémenter l'intégration d'activités en tant que fonction du système de l'appareil. Le système spécifie des règles de fractionnement pour les applications à plusieurs activités, ignorant ainsi leur comportement de fenêtrage. Le remplacement du système force les applications à plusieurs activités à utiliser un mode d'intégration d'activités défini par le système.

L'intégration d'activités système peut améliorer la présentation de l'application via plusieurs volets mises en page (list-detail, par exemple), sans aucune modification de l'application. Toutefois, l'intégration d'activités du système peut également provoquer des mises en page d'application incorrectes, des bugs ou est en conflit avec l'intégration d'activités implémentée par l'application.

Votre application peut empêcher ou autoriser l'intégration des activités système en définissant une propriété dans son fichier manifeste, par exemple :

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

Le nom de la propriété est défini dans le WindowProperties de Jetpack WindowManager . Définissez la valeur sur false si votre application implémente l'intégration d'activités. si vous voulez empêcher le système d'appliquer son intégration d'activités à votre application. Définissez la valeur sur true pour permettre au système d'appliquer l'intégration d'activités définie par le système à votre appli.

Limites, restrictions et mises en garde

  • Seule l'application hôte de la tâche, identifiée comme propriétaire de l'activité racine, peut y organiser et y intégrer d'autres activités. Si les activités compatibles avec l'intégration et le fractionnement s'exécutent dans une tâche appartenant à une autre application, ces opérations ne fonctionneront pas.
  • Les activités ne peuvent être organisées que dans une seule tâche. Le lancement d'une activité dans une nouvelle tâche la place toujours dans une nouvelle fenêtre développée en dehors des écrans fractionnés existants.
  • Seules les activités d'un même processus peuvent être organisées et fractionnées. Le rappel SplitInfo ne signale que les activités qui appartiennent au même processus, car il n'existe aucun moyen d'identifier les activités associées à d'autres processus.
  • Chaque règle d'activité unique ou par paire s'applique uniquement aux lancements d'activités qui se produisent après l'enregistrement de la règle. Il n'existe actuellement aucun moyen de mettre à jour les fractionnements actifs ni leurs propriétés visuelles.
  • La configuration du filtre de paire de fractionnement doit correspondre aux intents utilisés lors du lancement complet des activités. La mise en correspondance intervient au moment où une nouvelle activité est lancée à partir du processus d'application. Il est donc possible qu'elle ne connaisse pas les noms de composants résolus ultérieurement dans le processus système lors de l'utilisation d'intents implicites. Si le nom d'un composant n'est pas connu au moment du lancement, une à la place ("*/*") et le filtrage peut être effectué en cas d'action d'intent.
  • Il n'existe actuellement aucun moyen de déplacer des activités entre des conteneurs, ni à l'intérieur de et des écrans fractionnés après leur création. Les écrans fractionnés ne sont créés par la bibliothèque WindowManager que lorsque de nouvelles activités avec des règles associées sont lancées. Les fractionnements sont entièrement éliminés à l'arrêt de la dernière activité dans un écran fractionné.
  • Les activités peuvent être redémarrées lorsque la configuration change. Dès lors, lorsqu'un fractionnement est créé ou supprimé et que les limites de l'activité changent, celle-ci peut subir une destruction complète, puis être créée à nouveau. Par conséquent, les développeurs d'applications doivent prêter attention à des comportements spécifiques tels que le lancement de nouvelles activités à partir de rappels de cycle de vie.
  • Les appareils doivent inclure l'interface des extensions de fenêtre pour permettre l'intégration d'activités. Presque tous les appareils à grand écran équipés d'Android 12L (niveau d'API 32) ou version ultérieure incluent cette interface. Cependant, certains appareils à grand écran qui ne peuvent pas exécuter plusieurs activités, n'incluez pas la fenêtre l'interface des extensions de lieu. Si un appareil à grand écran n'est pas compatible avec le mode multifenêtre, il est possible qu'il ne permette pas l'intégration d'activités.

Ressources supplémentaires