Incorporamento delle attività

L'incorporamento delle attività ottimizza le app sui dispositivi con schermi di grandi dimensioni suddividendo la finestra delle attività di un'applicazione tra due attività o due istanze della stessa attività.

Figura 1. App Impostazioni con attività affiancate.

Se la tua app consiste in più attività, l'incorporamento delle attività ti consente di offrire un'esperienza utente migliorata su tablet, pieghevoli e dispositivi ChromeOS.

L'incorporamento delle attività non richiede il refactoring del codice. Puoi stabilire il modo in cui l'app visualizza le sue attività, una accanto all'altra o in pila, creando un file di configurazione XML o effettuando chiamate API Jetpack WindowManager.

Il supporto per schermi di piccole dimensioni viene mantenuto automaticamente. Quando la tua app è su un dispositivo con uno schermo piccolo, le attività vengono sovrapposte. Sugli schermi grandi, le attività vengono visualizzate una accanto all'altra. Il sistema determina la presentazione in base alla configurazione che hai creato, senza bisogno di logica di diramazione.

L'incorporamento delle attività supporta i cambiamenti di orientamento del dispositivo e funziona perfettamente sui dispositivi pieghevoli, impilando e rimuovendo le attività quando il dispositivo si piega e si apre.

L'incorporamento delle attività è supportato sulla maggior parte dei dispositivi con schermi di grandi dimensioni con Android 12L (livello API 32) e versioni successive.

Finestra attività divisa

L'incorporamento delle attività suddivide la finestra delle attività dell'app in due container: principale e secondario. I container contengono attività avviate dall'attività principale o da altre attività già presenti.

Le attività vengono raggruppate nel container secondario all'avvio e il container secondario è impilato sopra il container principale su schermi di piccole dimensioni, in modo che l'impilamento delle attività e la navigazione a ritroso siano coerenti con l'ordine delle attività già integrate nell'app.

L'incorporamento delle attività ti consente di visualizzare le attività in diversi modi. La tua app può suddividere la finestra delle attività avviando contemporaneamente due attività affiancate:

Figura 2. Due attività una accanto all'altra.

Oppure, un'attività che occupa l'intera finestra dell'attività può creare una suddivisione avviando una nuova attività accanto a:

Figura 3. L'attività A inizia l'attività B a lato.

Le attività già incluse in una suddivisione e che condividono la finestra di un'attività possono avviare altre attività nei seguenti modi:

  • A lato sopra un'altra attività:

    Figura 4. L'attività A inizia l'attività C a lato sopra l'attività B.
  • Di lato e sposta la divisione lateralmente, nascondendo l'attività principale precedente:

    Figura 5. L'attività B inizia l'attività C di lato e sposta la divisione lateralmente.
  • Avvia un'attività in posizione superiore, ovvero nello stesso stack di attività:

    Figura 6. L'attività B avvia l'attività C senza ulteriori flag di intent.
  • Per avviare un'attività a finestra intera nella stessa attività:

    Figura 7. L'attività A o l'attività B avvia l'attività C che occupa la finestra dell'attività.

Navigazione a ritroso

Diversi tipi di applicazioni possono avere diverse regole di navigazione a ritroso in uno stato di finestra di attività divisa a seconda delle dipendenze tra le attività o del modo in cui gli utenti attivano l'evento Indietro, ad esempio:

  • Integrazione: se le attività sono correlate e non deve essere visualizzata una senza l'altra, la navigazione a ritroso può essere configurata per completare entrambe.
  • Proseguimento da solo: se le attività sono completamente indipendenti, la navigazione a ritroso di un'attività non influisce sullo stato di un'altra attività nella finestra dell'attività.

L'evento Indietro viene inviato all'ultima attività attiva quando utilizzi la navigazione con pulsanti. Con la navigazione basata sui gesti, l'evento Indietro viene inviato all'attività in cui si è verificato il gesto.

Layout a più riquadri

Jetpack WindowManager ti consente di creare un'attività di incorporamento di riquadri a più riquadri su dispositivi con schermi di grandi dimensioni con Android 12L (livello API 32) o versioni successive e su alcuni dispositivi con versioni della piattaforma precedenti. Le app esistenti basate su più attività anziché su frammenti o layout basati sulla visualizzazione, come SlidingPaneLayout, possono offrire una migliore esperienza utente su schermi di grandi dimensioni senza dover ricorrere al refactoring del codice sorgente.

Un esempio comune è la suddivisione elenco-dettagli. Per garantire una presentazione di alta qualità, il sistema avvia l'attività relativa all'elenco e quindi l'applicazione avvia immediatamente l'attività relativa ai dettagli. Il sistema di transizione attende che entrambe le attività vengano tracciate, quindi le visualizza insieme. Per l'utente, le due attività vengono avviate come una sola.

Figura 8. Due attività avviate contemporaneamente in un layout a più riquadri.

Suddividi attributi

Puoi specificare la proporzione della finestra delle attività tra i container suddivisi e la disposizione dei container rispetto agli altri.

Per le regole definite in un file di configurazione XML, imposta i seguenti attributi:

  • splitRatio: imposta le proporzioni del contenitore. Il valore è un numero in virgola mobile nell'intervallo aperto (0,0, 1,0).
  • splitLayoutDirection: specifica il layout dei container suddivisi tra loro. I valori includono:
    • ltr: da sinistra a destra
    • rtl: da destra a sinistra
    • locale: ltr o rtl viene determinato dalle impostazioni internazionali

Consulta la sezione Configurazione XML di seguito per alcuni esempi.

Per le regole create utilizzando le API WindowManager, crea un oggetto SplitAttributes con SplitAttributes.Builder e richiama i seguenti metodi del builder:

Per alcuni esempi, consulta l'API WindowManager di seguito.

Figura 9. Due suddivisioni dell'attività disposte da sinistra a destra, ma con rapporti di suddivisione diversi.

Segnaposto

Le attività segnaposto sono attività secondarie vuote che occupano un'area di una suddivisione attività. Sono destinati a essere sostituiti da un'altra attività che include contenuti. Ad esempio, un'attività segnaposto potrebbe occupare il lato secondario di una suddivisione attività in un layout elenco dettagliato fino a quando non viene selezionato un elemento dell'elenco e a quel punto un'attività contenente le informazioni dettagliate per l'elemento dell'elenco selezionato sostituisce il segnaposto.

Per impostazione predefinita, il sistema visualizza i segnaposto solo quando c'è abbastanza spazio per una suddivisione attività. I segnaposto terminano automaticamente quando le dimensioni di visualizzazione vengono impostate su una larghezza o un'altezza troppo ridotta per visualizzare una suddivisione. Quando lo spazio lo consente, il sistema riavvia il segnaposto con uno stato reinizializzato.

Figura 10. Dispositivo pieghevole che si piega e si apre. L'attività del segnaposto viene completata e ricreata quando cambiano le dimensioni di visualizzazione.

Tuttavia, l'attributo stickyPlaceholder di un metodo SplitPlaceholderRule o setSticky() di SplitPlaceholder.Builder può sostituire il comportamento predefinito. Quando l'attributo o il metodo specifica un valore true, il sistema visualizza il segnaposto come attività di livello più alto nella finestra dell'attività quando la visualizzazione viene ridimensionata a un riquadro singolo da una visualizzazione a due riquadri (vedi Configurazione divisa per un esempio).

Figura 11. Dispositivo pieghevole che si piega e si apre. L'attività del segnaposto è fissa.

Modifiche alle dimensioni delle finestre

Quando le modifiche alla configurazione del dispositivo riducono la larghezza della finestra delle attività in modo che non sia sufficientemente grande per un layout a più riquadri (ad esempio, quando un dispositivo pieghevole con schermo grande passa dalle dimensioni del tablet alle dimensioni del telefono o la finestra dell'app viene ridimensionata in modalità multi-finestra), le attività non segnaposto nel riquadro secondario della finestra delle attività vengono raggruppate sopra le attività nel riquadro principale.

Le attività segnaposto vengono mostrate solo quando la larghezza di visualizzazione è sufficiente per una suddivisione. Su schermi più piccoli, il segnaposto viene ignorato automaticamente. Quando l'area di visualizzazione diventa di nuovo abbastanza grande, il segnaposto viene ricreato. (vedi la sezione Segnaposto sopra).

La sovrapposizione delle attività è possibile perché WindowManager ordina le attività nel riquadro secondario sopra le attività nel riquadro principale.

Più attività nel riquadro secondario

L'attività B avvia l'attività C in loco senza ulteriori flag di intent:

Suddivisione attività contenente le attività A, B e C con C sopra quella B.

generando il seguente ordine z di attività nella stessa attività:

Stack di attività secondarie contenente l'attività C sovrapposta a B.
          Lo stack secondario è sovrapposto allo stack di attività principali
          contenente l'attività A.

Quindi, in una finestra delle attività più piccola, l'applicazione si riduce a una singola attività con C in cima allo stack:

Piccola finestra che mostra solo l'attività C.

Se torni indietro nella finestra più piccola, puoi spostarti tra le attività impilate una sull'altra.

Se la configurazione della finestra delle attività viene ripristinata a una dimensione maggiore che può ospitare più riquadri, le attività vengono visualizzate di nuovo una accanto all'altra.

Suddivisione in pila

L'attività B inizia l'attività C di lato e sposta la divisione lateralmente:

Finestra dell'attività che mostra le attività A e B, poi le attività B e C.

Il risultato è il seguente ordine z di attività nella stessa attività:

Attività A, B e C in un'unica pila. Le attività vengono impilate nel seguente ordine dall'alto verso il basso: C, B, A.

In una finestra dell'attività più piccola, l'applicazione si riduce a una singola attività con C in alto:

Piccola finestra che mostra solo l'attività C.

Orientamento verticale fisso

L'impostazione del manifest android:screenOrientation consente alle app di vincolare le attività all'orientamento verticale oppure orizzontale. Per migliorare l'esperienza utente sui dispositivi con schermi di grandi dimensioni come tablet e pieghevoli, i produttori di dispositivi (OEM) possono ignorare le richieste di orientamento dello schermo e l'orientamento dell'app in formato verticale per i display orizzontali o in orizzontale su display verticali.

Figura 12. Attività in formato Letterbox: verticale su dispositivo orizzontale (sinistra), orizzontale su dispositivo verticale (destra).

Analogamente, quando l'incorporamento delle attività è abilitato, gli OEM possono personalizzare i dispositivi per attività di formato Letterbox e verticale con orientamento orizzontale su schermi di grandi dimensioni (larghezza ≥ 600 dp). Quando un'attività a verticale fisso avvia una seconda attività, il dispositivo può visualizzare le due attività una accanto all'altra in un display a due riquadri.

Figura 13. L'attività A verticale fisso avvia l'attività B di lato.

Aggiungi sempre la proprietà android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED al file manifest dell'app per comunicare ai dispositivi che l'app supporta l'incorporamento dell'attività (consulta la sezione Configurazione di suddivisione di seguito). I dispositivi personalizzati OEM possono quindi stabilire se attivare o meno le attività letterbox fisso e verticale.

Configurazione di suddivisione

Le regole di suddivisione configurano le suddivisioni delle attività. Puoi definire le regole di suddivisione in un file di configurazione XML o effettuando chiamate API WindowManager di Jetpack.

In entrambi i casi, l'app deve accedere alla libreria WindowManager e deve informare il sistema che l'app ha implementato l'incorporamento dell'attività.

Procedi nel seguente modo:

  1. Aggiungi la dipendenza più recente della libreria WindowManager al file build.gradle a livello di modulo della tua app, ad esempio:

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

    La libreria WindowManager fornisce tutti i componenti necessari per l'incorporamento dell'attività.

  2. Comunica al sistema che la tua app ha implementato l'incorporamento dell'attività.

    Aggiungi la proprietà android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED all'elemento <application> del file manifest dell'app e imposta il valore su true, ad esempio:

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

    Nella release WindowManager 1.1.0-alpha06 e successive, le suddivisioni di incorporamento dell'attività sono disabilitate a meno che la proprietà non venga aggiunta al manifest e impostata su true.

    Inoltre, i produttori di dispositivi utilizzano l'impostazione per abilitare funzionalità personalizzate per le app che supportano l'incorporamento dell'attività. Ad esempio, i dispositivi possono applicare un'attività in modalità solo verticale su display orizzontali per orientare l'attività per la transizione a un layout a due riquadri all'inizio di una seconda attività (vedi Orientamento verticale fisso).

Configurazione XML

Per creare un'implementazione basata su XML dell'incorporamento delle attività, completa i seguenti passaggi:

  1. Crea un file di risorse XML con le seguenti operazioni:

    • Definisce le attività che condividono una suddivisione
    • Configura le opzioni di suddivisione
    • Crea un segnaposto per il contenitore secondario della suddivisione quando i contenuti non sono disponibili
    • Specifica le attività che non devono mai far parte di una suddivisione

    Ecco alcuni esempi:

    <!-- 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. Crea un inizializzatore.

    Il componente WindowManager RuleController analizza il file di configurazione XML e rende le regole disponibili al sistema. Una libreria Jetpack Startup Initializer rende disponibile il file XML a RuleController all'avvio dell'app, in modo che le regole vengano applicate all'avvio di un'attività.

    Per creare un inizializzatore, segui questi passaggi:

    1. Aggiungi la dipendenza più recente della libreria Jetpack Startup al file build.gradle a livello di modulo, ad esempio:

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

    2. Crea una classe che implementa l'interfaccia Initializer.

      L'inizializzatore rende le regole di suddivisione disponibili per RuleController passando l'ID del file di configurazione XML (main_split_config.xml) al metodo 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. Crea un fornitore di contenuti per le definizioni delle regole.

    Aggiungi androidx.startup.InitializationProvider al file manifest dell'app come <provider>. Includi un riferimento all'implementazione dell'inizializzatore 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 rileva e inizializza SplitInitializer prima che venga chiamato il metodo onCreate() dell'app. Di conseguenza, le regole di suddivisione diventano effettive all'inizio dell'attività principale dell'app.

API WindowManager

Puoi implementare l'incorporamento dell'attività in modo programmatico con una manciata di chiamate API. Effettua le chiamate nel metodo onCreate() di una sottoclasse di Application per assicurarti che le regole siano effettive prima di avviare qualsiasi attività.

Per creare in modo programmatico una suddivisione attività:

  1. Crea una regola di suddivisione:

    1. Crea un elemento SplitPairFilter che identifichi le attività che condividono la suddivisione:

      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. Per aggiungere il filtro a un insieme di filtri, procedi nel seguente modo:

      Kotlin

      val filterSet = setOf(splitPairFilter)
      

      Java

      Set<SplitPairFilter> filterSet = new HashSet<>();
      filterSet.add(splitPairFilter);
      
    3. Crea attributi di layout per la suddivisione:

      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 crea un oggetto contenente gli attributi di layout:

      • setSplitType: definisce la modalità di allocazione dell'area di visualizzazione disponibile a ciascun contenitore attività. Il tipo di suddivisione rapporto specifica la proporzione dell'area di visualizzazione disponibile assegnata al contenitore principale. Il contenitore secondario occupa la parte restante dell'area di visualizzazione disponibile.
      • setLayoutDirection: specifica in che modo sono disposti i contenitori delle attività l'uno rispetto all'altro, il contenitore principale.
    4. Crea una 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 crea e configura la regola:

      • filterSet: contiene filtri per le coppie di suddivisione che determinano quando applicare la regola identificando le attività che condividono una suddivisione.
      • setDefaultSplitAttributes: applica gli attributi di layout alla regola.
      • setMinWidthDp: imposta la larghezza di visualizzazione minima (in pixel indipendenti dalla densità, dp) che consente una suddivisione.
      • setMinSmallestWidthDp: imposta il valore minimo (in dp) che deve avere la dimensione di visualizzazione minore tra le due per consentire una suddivisione indipendentemente dall'orientamento del dispositivo.
      • setMaxAspectRatioInPortrait: consente di impostare le proporzioni massime del display (altezza:larghezza) con orientamento verticale per le quali vengono visualizzate le suddivisioni delle attività. Se le proporzioni di un display verticale superano quelle massime, le suddivisioni vengono disattivate indipendentemente dalla larghezza del display. Nota:il valore predefinito è 1,4 e, sulla maggior parte dei tablet, le attività occupano l'intera finestra dell'attività con orientamento verticale. Vedi anche SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT e setMaxAspectRatioInLandscape. Il valore predefinito per l'orientamento orizzontale è ALWAYS_ALLOW.
      • setFinishPrimaryWithSecondary: imposta in che modo la fine di tutte le attività nel contenitore secondario influisce sulle attività nel contenitore principale. NEVER indica che il sistema non deve completare le attività principali quando tutte le attività nel contenitore secondario terminano (vedi Completare le attività).
      • setFinishSecondaryWithPrimary: imposta in che modo il completamento di tutte le attività nel contenitore principale influisce sulle attività nel contenitore secondario. ALWAYS indica che il sistema deve sempre terminare le attività nel contenitore secondario al termine di tutte le attività nel contenitore principale (vedi Completare le attività).
      • setClearTop: specifica se tutte le attività nel contenitore secondario vengono terminate quando viene avviata una nuova attività nel contenitore. False indica che le nuove attività vengono sovrapposte alle attività già presenti nel contenitore secondario.
    5. Ottieni l'istanza singleton di WindowManager RuleController e aggiungi la regola:

      Kotlin

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

      Java

      RuleController ruleController = RuleController.getInstance(this);
      ruleController.addRule(splitPairRule);
      
  2. Crea un segnaposto per il contenitore secondario quando i contenuti non sono disponibili:

    1. Crea un elemento ActivityFilter che identifichi l'attività con cui il segnaposto condivide la suddivisione della finestra di un'attività:

      Kotlin

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

      Java

      ActivityFilter placeholderActivityFilter = new ActivityFilter(
          new ComponentName(this, ListActivity.class),
          null
      );
      
    2. Per aggiungere il filtro a un insieme di filtri, procedi nel seguente modo:

      Kotlin

      val placeholderActivityFilterSet = setOf(placeholderActivityFilter)
      

      Java

      Set<ActivityFilter> placeholderActivityFilterSet = new HashSet<>();
      placeholderActivityFilterSet.add(placeholderActivityFilter);
      
    3. Crea una 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 crea e configura la regola:

      • placeholderActivityFilterSet: contiene filtri di attività che determinano quando applicare la regola identificando le attività a cui è associata l'attività segnaposto.
      • Intent: specifica l'avvio dell'attività segnaposto.
      • setDefaultSplitAttributes: applica gli attributi di layout alla regola.
      • setMinWidthDp: imposta la larghezza di visualizzazione minima (in pixel indipendenti dalla densità, dp) che consente una suddivisione.
      • setMinSmallestWidthDp: imposta il valore minimo (in dp) che la dimensione di visualizzazione più piccola deve avere per consentire una suddivisione indipendentemente dall'orientamento del dispositivo.
      • setMaxAspectRatioInPortrait: consente di impostare le proporzioni massime del display (altezza:larghezza) con orientamento verticale per le quali vengono visualizzate le suddivisioni delle attività. Nota:il valore predefinito è 1, 4 e determina che le attività compilano la finestra delle attività con orientamento verticale sulla maggior parte dei tablet. Vedi anche SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT esetMaxAspectRatioInLandscape. Il valore predefinito per l'orientamento orizzontale è ALWAYS_ALLOW.
      • setFinishPrimaryWithPlaceholder: imposta in che modo la fine dell'attività segnaposto influisce sulle attività nel contenitore principale. ALWAYS indica che il sistema deve sempre terminare le attività nel contenitore principale al termine del segnaposto (vedi Termina attività).
      • setSticky: determina se l'attività segnaposto viene visualizzata in cima alla serie di attività su display piccoli dopo che il segnaposto è apparso per la prima volta in una suddivisione con una larghezza minima sufficiente.
    4. Aggiungi la regola a WindowManager RuleController:

      Kotlin

      ruleController.addRule(splitPlaceholderRule)
      

      Java

      ruleController.addRule(splitPlaceholderRule);
      
  3. Specifica le attività che non devono mai far parte di una suddivisione:

    1. Crea un ActivityFilter che identifichi un'attività che deve sempre occupare l'intera area di visualizzazione dell'attività:

      Kotlin

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

      Java

      ActivityFilter expandedActivityFilter = new ActivityFilter(
        new ComponentName(this, ExpandedActivity.class),
        null
      );
      
    2. Per aggiungere il filtro a un insieme di filtri, procedi nel seguente modo:

      Kotlin

      val expandedActivityFilterSet = setOf(expandedActivityFilter)
      

      Java

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

      Kotlin

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

      Java

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

      ActivityRule.Builder crea e configura la regola:

      • expandedActivityFilterSet: contiene filtri di attività che determinano quando applicare la regola identificando le attività da escludere dalle suddivisioni.
      • setAlwaysExpand: specifica se l'attività deve riempire l'intera finestra dell'attività.
    4. Aggiungi la regola a WindowManager RuleController:

      Kotlin

      ruleController.addRule(activityRule)
      

      Java

      ruleController.addRule(activityRule);
      

Incorporamento tra applicazioni

Su Android 13 (livello API 33) e versioni successive, le app possono incorporare attività di altre app. L'incorporamento delle attività tra applicazioni o UID consente l'integrazione visiva delle attività di più applicazioni Android. Il sistema mostra sullo schermo un'attività dell'app host e un'attività incorporata di un'altra app sullo schermo affiancata o in alto e in basso, proprio come nell'incorporamento di attività in una singola app.

Ad esempio, l'app Impostazioni potrebbe incorporare l'attività di selezione dello sfondo dall'app BackgroundPicker:

Figura 14. App Impostazioni (menu a sinistra) con selettore sfondo come attività incorporata (a destra).

Modello di attendibilità

I processi di hosting che incorporano attività di altre app sono in grado di ridefinire la presentazione delle attività incorporate, inclusi dimensioni, posizione, ritaglio e trasparenza. Gli host dannosi possono utilizzare questa funzionalità per ingannare gli utenti e creare attacchi di clickjacking o altri rimedi all'interfaccia utente.

Per evitare l'uso improprio dell'incorporamento di attività tra app, Android richiede alle app di attivare l'opzione per consentire l'incorporamento delle loro attività. Le app possono designare gli host come attendibili o non attendibili.

Host attendibili

Per consentire ad altre applicazioni di incorporare e controllare completamente la presentazione delle attività della tua app, specifica il certificato SHA-256 dell'applicazione host nell'attributo android:knownActivityEmbeddingCerts degli elementi <activity> o <application> del file manifest dell'app.

Imposta il valore di android:knownActivityEmbeddingCerts come stringa:

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

oppure, per specificare più certificati, un array di stringhe:

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

che fa riferimento a una risorsa come la seguente:

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

I proprietari di app possono ottenere un digest del certificato SHA eseguendo l'attività signingReport di Gradle. Il digest del certificato è l'impronta SHA-256 senza i due punti di separazione. Per ulteriori informazioni, consulta le sezioni Eseguire un report sulla firma e Autenticazione del client.

Host non attendibili

Per consentire a un'app di incorporare le sue attività e controllarne la presentazione, specifica l'attributo android:allowUntrustedActivityEmbedding negli elementi <activity> o <application> del file manifest dell'app, ad esempio:

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

Il valore predefinito dell'attributo è false e impedisce l'incorporamento dell'attività tra app.

Autenticazione personalizzata

Per ridurre i rischi dell'incorporamento delle attività non attendibili, crea un meccanismo di autenticazione personalizzato che verifichi l'identità dell'host. Se conosci i certificati host, utilizza la libreria androidx.security.app.authenticator per l'autenticazione. Se l'host esegue l'autenticazione dopo aver incorporato la tua attività, puoi visualizzare i contenuti effettivi. In caso contrario, puoi informare l'utente che l'azione non è stata consentita e bloccare i contenuti.

Utilizza il metodo ActivityEmbeddingController#isActivityEmbedded() della libreria Jetpack WindowManager per verificare se un host sta incorporando la tua attività, ad esempio:

Kotlin

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

Java

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

Limitazione delle dimensioni minime

Il sistema Android applica l'altezza e la larghezza minime specificate nell'elemento <layout> del file manifest dell'app alle attività incorporate. Se un'applicazione non specifica l'altezza e la larghezza minime, si applicano i valori predefiniti di sistema (sw220dp).

Se l'host tenta di ridimensionare il container incorporato a una dimensione inferiore al minimo, il container incorporato si espande in modo da occupare l'intero limite delle attività.

<alias-attività>

Affinché l'incorporamento delle attività attendibili o non attendibili funzioni con l'elemento <activity-alias>, è necessario applicare android:knownActivityEmbeddingCerts o android:allowUntrustedActivityEmbedding all'attività di destinazione anziché all'alias. Il criterio che verifica la sicurezza sul server di sistema si basa sui flag impostati nella destinazione, non sull'alias.

Applicazione host

Le applicazioni host implementano l'incorporamento delle attività tra app nello stesso modo in cui implementano l'incorporamento delle attività singole app. Gli oggetti SplitPairRule e SplitPairFilter o ActivityRule e ActivityFilter specificano le attività incorporate e le suddivisioni delle finestre delle attività. Le regole di suddivisione vengono definite staticamente in XML o in fase di runtime tramite le chiamate API Jetpack WindowManager.

Se un'applicazione host tenta di incorporare un'attività per la quale non è stata attivata l'incorporamento cross-app, l'attività occupa tutti i limiti delle attività. Di conseguenza, le applicazioni host devono sapere se le attività di destinazione consentono l'incorporamento tra app.

Se un'attività incorporata avvia una nuova attività nella stessa attività e per la nuova attività non è stata attivata l'incorporamento cross-app, l'attività occupa tutti i limiti dell'attività anziché sovrapporsi all'attività nel contenitore incorporato.

Un'applicazione host può incorporare le proprie attività senza restrizioni, purché queste vengano avviate nella stessa attività.

Esempi di suddivisione

Dividi dalla finestra intera

Figura 15. L'attività A inizia l'attività B a lato.

Non è richiesto alcun refactoring. Puoi definire la configurazione per la suddivisione in modo statico o in fase di runtime, quindi chiamare Context#startActivity() senza parametri aggiuntivi.

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

Dividi per impostazione predefinita

Quando la pagina di destinazione di un'applicazione è progettata per essere suddivisa in due contenitori su schermi di grandi dimensioni, l'esperienza utente è migliore quando entrambe le attività vengono create e presentate contemporaneamente. Tuttavia, i contenuti potrebbero non essere disponibili per il contenitore secondario della suddivisione finché l'utente non interagisce con l'attività nel contenitore principale (ad esempio, l'utente seleziona un elemento da un menu di navigazione). Un'attività segnaposto può riempire il vuoto finché i contenuti non possono essere visualizzati nel container secondario della suddivisione (vedi segnaposto sopra).

Figura 16. Suddivisione creata aprendo due attività contemporaneamente. Un'attività è un segnaposto.

Per creare una suddivisione con un segnaposto, crea un segnaposto e associalo all'attività principale:

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

Quando un'app riceve un intent, l'attività target può essere mostrata come parte secondaria di una suddivisione dell'attività, ad esempio una richiesta di mostrare una schermata dei dettagli con informazioni su un elemento di un elenco. Sui display piccoli, il dettaglio viene mostrato nell'intera finestra delle attività; sui dispositivi più grandi, accanto all'elenco.

Figura 17. Attività dettagliata dei link diretti mostrata da sola su uno schermo piccolo, ma insieme a un'attività elenco su uno schermo grande.

La richiesta di lancio deve essere indirizzata all'attività principale e l'attività di dettaglio target deve essere lanciata in una suddivisione. Il sistema sceglie automaticamente la presentazione corretta (in pila o affiancata) in base alla larghezza di visualizzazione disponibile.

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 destinazione del link diretto potrebbe essere l'unica attività che dovrebbe essere disponibile per l'utente nello stack di navigazione a ritroso; potresti voler evitare di ignorare l'attività dei dettagli e lasciare solo l'attività principale:

Display grande con attività dell&#39;elenco e dettagli delle attività affiancate.
          La navigazione a ritroso non è in grado di ignorare l&#39;attività dettagliata e lasciare attività dell&#39;elenco sullo schermo.

Piccolo display con solo dettagli attività. La navigazione a ritroso non consente di ignorare l&#39;attività dettagliata e di visualizzare l&#39;attività dell&#39;elenco.

Puoi invece completare entrambe le attività contemporaneamente utilizzando l'attributo finishPrimaryWithSecondary:

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

Consulta la sezione Attributi di configurazione di seguito.

Più attività in contenitori divisi

Sovrapporre più attività in un container suddiviso consente agli utenti di accedere a contenuti profondi. Ad esempio, con una suddivisione elenco-dettagli, l'utente potrebbe dover accedere a una sezione di sottodettagli, ma mantenere attiva l'attività principale:

Figura 18. Attività aperta in loco nel riquadro secondario della finestra dell'attività.

Kotlin

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

Java

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

L'attività dei dettagli secondari viene posizionata sopra l'attività dei dettagli, nascondendola:

L'utente può quindi tornare al livello di dettaglio precedente tornando indietro nello stack:

Figura 19. Attività rimossa dalla parte superiore dell'elenco filtri.

Sovrapporre le attività una sopra l'altra è il comportamento predefinito quando le attività vengono avviate da un'attività nello stesso contenitore secondario. Le attività avviate dal container principale all'interno di un segmento attivo terminano anch'esse nel container secondario in cima allo stack delle attività.

Attività in una nuova attività

Quando le attività in una finestra di attività divisa avviano attività in una nuova attività, quest'ultima è separata dall'attività che include la suddivisione e viene visualizzata a schermo intero. La schermata Recenti mostra due attività: l'attività suddivisa e la nuova.

Figura 20. Inizia l'attività C in una nuova attività dall'attività B.

Sostituzione attività

Le attività possono essere sostituite nello stack di container secondario, ad esempio quando l'attività principale viene utilizzata per la navigazione di primo livello e l'attività secondaria è una destinazione selezionata. Ogni selezione della navigazione di primo livello deve avviare una nuova attività nel contenitore secondario e rimuovere quella o le attività che erano presenti in precedenza.

Figura 21. L'attività di navigazione di livello superiore nel riquadro principale sostituisce le attività di destinazione nel riquadro secondario.

Se l'app non completa l'attività nel contenitore secondario quando la selezione della navigazione cambia, la navigazione a ritroso potrebbe creare confusione quando la suddivisione è compressa (quando il dispositivo è piegato). Ad esempio, se c'è un menu nel riquadro principale e le schermate A e B sono sovrapposte nel riquadro secondario, quando l'utente chiude lo smartphone, B si trova su A e A è sopra il menu. Quando l'utente torna dalla pagina B, al suo posto viene visualizzata la lettera A.

In questi casi, è necessario rimuovere lo schermo A dallo stack posteriore.

Il comportamento predefinito quando si esegue l'avvio in un nuovo container su una suddivisione esistente è quello di collocare i nuovi container secondari in cima e conservare quelli vecchi nel back stack. Puoi configurare le suddivisioni in modo da cancellare i container secondari precedenti con clearTop e avviare normalmente le nuove attività.

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

In alternativa, utilizza la stessa attività secondaria e dall'attività principale (di menu) invia nuovi intent che si risolvono nella stessa istanza, ma attivano un aggiornamento dello stato o dell'interfaccia utente nel contenitore secondario.

Più suddivisioni

Le app possono fornire una navigazione approfondita multilivello avviando attività aggiuntive a lato.

Quando un'attività in un container secondario avvia una nuova attività a lato, viene creata una nuova suddivisione sopra la suddivisione esistente.

Figura 22. L'attività B inizia l'attività C a lato.

Lo stack posteriore contiene tutte le attività che sono state aperte in precedenza, quindi gli utenti possono passare alla suddivisione A/B al termine dell'operazione C.

Attività A, B e C in pila. Le attività vengono impilate nel seguente ordine dall&#39;alto verso il basso: C, B, A.

Per creare una nuova suddivisione, avvia la nuova attività sul lato del container secondario esistente. Dichiara le configurazioni sia per i segmenti A/B e B/C e l'attività di lancio C normalmente da 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));
    }
}

Reagisci alle modifiche di stato di suddivisione

Le diverse attività in un'app possono avere elementi UI che svolgono la stessa funzione; ad esempio, un controllo che apre una finestra contenente le impostazioni dell'account.

Figura 23. Attività diverse con elementi UI funzionalmente identici.

Se due attività che hanno un elemento UI in comune sono suddivise in una suddivisione, è ridondante e potrebbe confondere la visualizzazione dell'elemento in entrambe le attività.

Figura 24. Elementi UI duplicati nella suddivisione attività.

Per sapere quando le attività si trovano in una suddivisione, controlla il flusso SplitController.splitInfoList o registra un listener con SplitControllerCallbackAdapter per rilevare eventuali cambiamenti nello stato di suddivisione. Quindi, modifica l'UI di conseguenza:

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

Le coroutine possono essere lanciate in qualsiasi stato del ciclo di vita, ma in genere vengono lanciate nello stato STARTED per risparmiare risorse (per saperne di più, consulta Utilizzare le coroutine Kotlin con componenti sensibili al ciclo di vita).

I callback possono essere effettuati in qualsiasi stato del ciclo di vita, anche quando un'attività viene interrotta. Generalmente i listener devono essere registrati in onStart() e non registrati in onStop().

modale a schermo intero

Alcune attività impediscono agli utenti di interagire con l'applicazione finché non viene eseguita un'azione specificata; ad esempio, un'attività nella schermata di accesso, una schermata di conferma delle norme o un messaggio di errore. Le attività modali non dovrebbero comparire in una suddivisione.

È possibile forzare un'attività a riempire sempre la finestra dell'attività utilizzando la configurazione di espansione:

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

Termina attività

Gli utenti possono terminare le attività ai lati del segmento scorrendo dal bordo del display:

Figura 25. Gesto di scorrimento per terminare l'attività B.
Figura 26. Gesto di scorrimento per terminare l'attività A.

Se il dispositivo è configurato per l'utilizzo del pulsante Indietro anziché della navigazione tramite gesti, l'input viene inviato all'attività attiva, ovvero l'attività toccata o avviata per ultima.

L'effetto che il completamento di tutte le attività in un contenitore ha sul contenitore opposto dipende dalla configurazione di suddivisione.

Attributi di configurazione

Puoi specificare gli attributi delle regole di coppia di suddivisione per configurare il modo in cui il completamento di tutte le attività su un lato della suddivisione influisca sulle attività sull'altro lato della suddivisione. Gli attributi sono:

  • window:finishPrimaryWithSecondary: come la fine di tutte le attività nel contenitore secondario influisce sulle attività nel contenitore principale
  • window:finishSecondaryWithPrimary: come la fine di tutte le attività nel contenitore principale influisce sulle attività nel contenitore secondario

I valori possibili degli attributi includono:

  • always: completa sempre le attività nel contenitore associato
  • never: non completare mai le attività nel contenitore associato
  • adjacent: completa le attività nel contenitore associato quando i due contenitori sono visualizzati adiacenti, ma non quando sono impilati.

Ecco alcuni esempi:

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

Configurazione predefinita

Quando tutte le attività in un contenitore di una suddivisione vengono terminate, il contenitore rimanente occupa l'intera finestra:

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

Suddivisione contenente le attività A e B. A termina, lasciando B per occupare l&#39;intera finestra.

Suddivisione contenente le attività A e B. B termina, lasciando A per occupare l&#39;intera finestra.

Completa le attività insieme

Completa automaticamente le attività nel contenitore principale al termine di tutte le attività nel contenitore secondario:

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

Suddivisione contenente le attività A e B. B termina e termina anche A, lasciando vuota la finestra dell&#39;attività.

Suddivisione contenente le attività A e B. A termina, lasciando B solo nella finestra dell&#39;attività.

Completa automaticamente le attività nel contenitore secondario al termine di tutte le attività nel contenitore principale:

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

Suddivisione contenente le attività A e B. A termina, che termina anche il B, lasciando vuota la finestra dell&#39;attività.

Suddivisione contenente le attività A e B. B termina, lasciando A da solo nella finestra dell&#39;attività.

Completa le attività insieme quando tutte le attività nel contenitore principale o secondario terminano:

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

Suddivisione contenente le attività A e B. A termina, che termina anche il B, lasciando vuota la finestra dell&#39;attività.

Suddivisione contenente le attività A e B. B termina e termina anche A, lasciando vuota la finestra dell&#39;attività.

Completa più attività nei container

Se più attività sono raggruppate in un contenitore diviso, il completamento di un'attività in fondo alla pila non termina automaticamente le attività nell'elenco superiore.

Ad esempio, se due attività si trovano nel contenitore secondario, C sopra di B:

Lo stack di attività secondarie contenente l&#39;attività C sopra quella B viene sovrapposto allo stack di attività principali che contiene l&#39;attività A.

e la configurazione della suddivisione è definita dalla configurazione delle attività A e B:

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

se finisci l'attività principale, la suddivisione rimane.

Suddividi con l&#39;attività A nel contenitore principale e le attività B e C nel
          secondario, C sovrapposte a B. C termina, lasciando A e B nella suddivisione attività.

Il completamento dell'attività inferiore (principale) del container secondario non rimuove le attività che si trovano sopra il container e quindi conserva anche la suddivisione.

Suddividi con l&#39;attività A nel contenitore principale e le attività B e C nel
          secondario, C sovrapposte a B. termina il B, lasciando A e C nella suddivisione attività.

Vengono eseguite anche eventuali regole aggiuntive per completare le attività insieme, come il completamento dell'attività secondaria con l'attività principale:

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

Suddividi con l&#39;attività A nel contenitore principale e le attività B e C nel
          contenitore secondario, C sovrapposte a B. finisce A e anche
          finire B e C.

E quando la suddivisione è configurata per completare l'operazione principale e secondaria insieme:

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

Suddividi con l&#39;attività A nel contenitore principale e le attività B e C nel
          secondario, C sovrapposte a B. C termina, lasciando A e B nella suddivisione attività.

Suddividi con l&#39;attività A nel contenitore principale e le attività B e C nel
          secondario, C sovrapposte a B. termina il B, lasciando A e C nella suddivisione attività.

Suddividi con l&#39;attività A nel contenitore principale e le attività B e C nel
          secondario, C sovrapposte a B. finisce A, poi finisce B e C.

Modifica le proprietà di suddivisione in fase di esecuzione

Le proprietà di una suddivisione attualmente attiva e visibile non possono essere modificate. La modifica delle regole di suddivisione influisce su ulteriori avvii di attività e nuovi container, ma non sulle suddivisioni esistenti e attive.

Per modificare le proprietà dei segmenti attivi, termina l'attività secondaria o le attività nella suddivisione e avviala di nuovo di lato con una nuova configurazione.

Estrae un'attività da una suddivisione a una finestra intera

Crea una nuova configurazione che mostri l'intera finestra dell'attività secondaria, quindi riavvia l'attività con un intent che si risolve nella stessa istanza.

Verificare il supporto suddiviso in fase di runtime

L'incorporamento delle attività è supportato su Android 12L (livello API 32) e versioni successive, ma è disponibile anche su alcuni dispositivi con versioni della piattaforma precedenti. Per verificare la disponibilità della funzionalità in fase di runtime, utilizza la proprietà SplitController.splitSupportStatus o il metodo 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.
}

Se i segmenti non sono supportati, le attività vengono avviate sopra lo stack delle attività (in base al modello di incorporamento non attività).

Impedisci override del sistema

I produttori di dispositivi Android (produttore di apparecchiature originali o OEM) possono implementare l'incorporamento dell'attività come funzione del sistema dei dispositivi. Il sistema specifica regole di suddivisione per le app multiattività, che sostituiscono il comportamento di windowing delle app. L'override di sistema forza le app multiattività in una modalità di incorporamento dell'attività definita dal sistema.

L'incorporamento dell'attività di sistema può migliorare la presentazione dell'app tramite layout a più riquadri, come list-detail, senza alcuna modifica all'app. Tuttavia, l'incorporamento dell'attività del sistema potrebbe anche causare errori di layout, bug o conflitti con l'incorporamento dell'attività implementato dall'app.

La tua app può impedire o consentire l'incorporamento dell'attività di sistema impostando una proprietà nel file manifest dell'app, ad esempio:

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

Il nome della proprietà è definito nell'oggetto Jetpack WindowManager WindowProperties. Imposta il valore su false se la tua app implementa l'incorporamento delle attività o se vuoi altrimenti impedire al sistema di applicare le regole di incorporamento delle attività all'app. Imposta il valore su true per consentire al sistema di applicare l'incorporamento di attività definito dal sistema alla tua app.

Limitazioni, restrizioni e avvertenze

  • Solo l'app host dell'attività, identificata come proprietaria dell'attività principale nell'attività, può organizzare e incorporare altre attività nell'attività. Se le attività che supportano l'incorporamento e le suddivisioni vengono eseguite in un'attività che appartiene a un'altra applicazione, l'incorporamento e le suddivisioni non funzioneranno per quelle attività.
  • Le attività possono essere organizzate solo all'interno di una singola attività. Quando si avvia un'attività in una nuova attività, questa viene sempre inserita in una nuova finestra espansa al di fuori di eventuali suddivisioni esistenti.
  • Solo le attività nello stesso processo possono essere organizzate e suddivise. Il callback SplitInfo segnala solo le attività che appartengono allo stesso processo, in quanto non è possibile sapere quali sono le attività in processi diversi.
  • Ogni coppia o singola regola di attività si applica solo agli avvii di attività che si verificano dopo la registrazione della regola. Al momento non è possibile aggiornare i segmenti esistenti o le relative proprietà visive.
  • La configurazione del filtro della coppia divisa deve corrispondere agli intent utilizzati durante l'avvio completo delle attività. La corrispondenza avviene nel momento in cui viene avviata una nuova attività dal processo dell'applicazione, quindi potrebbe non essere a conoscenza dei nomi dei componenti che vengono risolti in un secondo momento nel processo di sistema quando si utilizzano intent impliciti. Se il nome di un componente non è noto al momento del lancio, è possibile utilizzare un carattere jolly ("*/*") e l'applicazione di filtri in base all'azione dell'intent.
  • Al momento non è possibile spostare le attività tra i container o all'interno e all'esterno dei segmenti dopo la loro creazione. Le suddivisioni vengono create dalla libreria WindowManager solo quando vengono avviate nuove attività con regole corrispondenti, mentre le suddivisioni vengono eliminate al termine dell'ultima attività in un container di suddivisione.
  • Le attività possono essere riavviate quando la configurazione cambia. Di conseguenza, quando viene creata o rimossa una suddivisione e i limiti delle attività cambiano, l'attività può passare per l'eliminazione completa dell'istanza precedente e per crearne una nuova. Di conseguenza, gli sviluppatori di app devono fare attenzione, ad esempio, al lancio di nuove attività dai callback del ciclo di vita.
  • I dispositivi devono includere l'interfaccia delle estensioni delle finestre per supportare l'incorporamento dell'attività. Quasi tutti i dispositivi con schermi di grandi dimensioni con Android 12L (livello API 32) o versioni successive includono l'interfaccia. Tuttavia, alcuni dispositivi con schermi di grandi dimensioni che non sono in grado di eseguire più attività non includono l'interfaccia delle estensioni delle finestre. Se un dispositivo con schermo grande non supporta la modalità multi-finestra, potrebbe non supportare l'incorporamento dell'attività.

Risorse aggiuntive