Incorporamento delle attività

L'incorporamento delle attività ottimizza le app sui dispositivi con schermi grandi dividendo 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 è composta da 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 determinare in che modo la tua app visualizza le sue attività, affiancate o impilate, creando un file di configurazione XML o effettuando chiamate API Jetpack WindowManager.

Il supporto per gli schermi di piccole dimensioni viene gestito automaticamente. Quando l'app si trova su un dispositivo con uno schermo piccolo, le attività vengono impilate una sull'altra. Su schermi di grandi dimensioni, le attività vengono visualizzate affiancate. Il sistema determina la presentazione in base alla configurazione che hai creato, senza bisogno di una logica di diramazione.

L'incorporamento delle attività si adatta ai cambiamenti di orientamento del dispositivo e funziona perfettamente sui dispositivi pieghevoli, mentre le attività di impilamento e disimpilamento sono disponibili 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: principali e secondari. I container contengono attività avviate dall'attività principale o da altre attività già nei container.

Le attività vengono impilate nel container secondario quando vengono avviate e il container secondario viene impilato sopra il container principale su schermi di piccole dimensioni, in modo che lo stack delle attività e la navigazione a ritroso siano coerenti con l'ordine delle attività già integrate nella tua app.

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

Figura 2. Due attività affiancate.

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

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

Le attività già in una suddivisione e che condividono una finestra delle attività possono avviare altre attività nei seguenti modi:

  • A lato sopra un'altra attività:

    Figura 4. L'attività A inizia l'attività C di lato sull'attività B.
  • Di lato e sposta la divisione nascondendo l'attività principale precedente:

    Figura 5. L'attività B avvia l'attività C di lato e sposta la suddivisione lateralmente.
  • Lancia un'attività nella parte superiore, ovvero nella stessa serie di attività:

    Figura 6. L'attività B avvia l'attività C senza flag per intent aggiuntivi.
  • Avvia una finestra intera dell'attività nella stessa attività:

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

Navigazione a ritroso

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

  • Unione: se le attività sono correlate e una non deve essere mostrata senza l'altra, è possibile configurare la navigazione a ritroso per terminare entrambe.
  • Passaggio autonomo: 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 consente di creare un'attività di incorporamento di un layout multi-riquadro su dispositivi con schermi di grandi dimensioni con Android 12L (livello API 32) o versioni successive e su alcuni dispositivi con versioni precedenti della piattaforma. Le app esistenti basate su più attività piuttosto che su frammenti o layout basati sulle visualizzazioni come SlidingPaneLayout possono fornire un'esperienza utente migliorata su schermi di grandi dimensioni senza dover ricorrere al refactoring del codice sorgente.

Un esempio comune è la suddivisione dei dettagli dell'elenco. Per garantire una presentazione di alta qualità, il sistema avvia l'attività dell'elenco e l'applicazione, quindi, avvia immediatamente l'attività dei 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à sono state avviate contemporaneamente in un layout multi-riquadro.

Attributi suddivisi

Puoi specificare la proporzione della finestra delle attività tra i container divisi e la disposizione dei container l'uno rispetto all'altro.

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 la disposizione dei container suddivisi l'uno rispetto all'altro. 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 chiama i seguenti metodi del builder:

Consulta la sezione API WindowManager di seguito per alcuni esempi.

Figura 9. Due suddivisioni di attività disposte da sinistra a destra ma con proporzioni diverse.

Segnaposto

Le attività segnaposto sono attività secondarie vuote che occupano un'area di una suddivisione attività. In ultima analisi, devono essere sostituite con un'altra attività che include contenuti. Ad esempio, un'attività segnaposto potrebbe occupare il lato secondario di una suddivisione attività in un layout di dettagli elenco fino a quando non viene selezionato un elemento dall'elenco, dopodiché 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'è spazio sufficiente per una suddivisione dell'attività. I segnaposto terminano automaticamente quando le dimensioni di visualizzazione cambiano in larghezza o altezza troppo piccole per poter 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à segnaposto viene completata e ricreata man mano che le dimensioni di visualizzazione cambiano.

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 mostra il segnaposto come attività principale nella finestra dell'attività quando la visualizzazione viene ridimensionata a una visualizzazione a riquadro singolo rispetto a una visualizzazione a due riquadri (per un esempio, consulta Configurazione suddivisa).

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

Modifiche alle dimensioni della finestra

Quando le modifiche alla configurazione del dispositivo riducono la larghezza della finestra dell'attività in modo che non sia abbastanza grande per un layout a più riquadri (ad esempio, quando un dispositivo pieghevole di grandi dimensioni viene piegato da un tablet a uno smartphone o la finestra dell'app viene ridimensionata in modalità multi-finestra), le attività non segnaposto nel riquadro secondario della finestra dell'attività vengono sovrapposte alle attività nel riquadro principale.

Le attività segnaposto vengono visualizzate 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 sufficientemente grande, il segnaposto viene ricreato. Vedi Segnaposto sopra.

L'impilamento 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 senza ulteriori flag di intent:

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

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

Stack di attività secondaria contenente l'attività C sopra B.
          Lo stack secondario è sovrapposto allo stack di attività prmary contenente l'attività A.

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

Piccola finestra che mostra solo l'attività C.

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

Se la configurazione della finestra delle attività viene ripristinata a una dimensione più grande che può ospitare più riquadri, le attività vengono mostrate di nuovo affiancate.

Suddivisioni in pila

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

Finestra delle 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à sono raggruppate nel seguente ordine dall'alto verso il basso: C, B, A.

In una finestra delle 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 manifest android:screenOrientation consente alle app di limitare le attività all'orientamento verticale o 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 utilizzare l'orientamento orizzontale o verticale per l'app sui display orizzontali.

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

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

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

Aggiungi sempre la proprietà android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED al file manifest dell'app per informare i dispositivi che la tua app supporta l'incorporamento delle attività (consulta la sezione Configurazione suddivisa di seguito). I dispositivi personalizzati OEM possono quindi determinare se utilizzare le attività letterbox con verticale fisso.

Configurazione suddivisione

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

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

Segui questi passaggi:

  1. Aggiungi la dipendenza della libreria WindowManager più recente 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 delle attività.

  2. Comunica al sistema che la tua app ha implementato l'incorporamento delle 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 versione 1.1.0-alpha06 e successive di WindowManager, le suddivisioni di incorporamento delle attività vengono disattivate a meno che la proprietà non venga aggiunta al manifest e impostata su true.

    Inoltre, i produttori di dispositivi utilizzano questa impostazione per abilitare funzionalità personalizzate per le app che supportano l'incorporamento delle attività. Ad esempio, i dispositivi possono utilizzare il letterbox un'attività solo verticale sui display orizzontali per orientare l'attività per la transizione a un layout a due riquadri quando inizia una seconda attività (consulta la sezione Orientamento verticale fisso).

Configurazione XML

Per creare un'implementazione basata su XML dell'incorporamento dell'attività, procedi nel seguente modo:

  1. Crea un file di risorsa XML che esegua 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 il contenuto non è disponibile
    • 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 disponibili le regole al sistema. Una libreria Jetpack Startup Initializer rende il file XML disponibile per RuleController all'avvio dell'app, in modo che le regole siano 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 implementi l'interfaccia Initializer.

      L'inizializzazione rende disponibili le regole di suddivisione 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'inizializzazione 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 di chiamare il metodo onCreate() dell'app. Di conseguenza, le regole di suddivisione vengono applicate all'inizio dell'attività principale dell'app.

API WindowManager

Puoi implementare l'incorporamento delle 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 vengano applicate prima del lancio di qualsiasi attività.

Per creare una suddivisione attività in modo programmatico:

  1. Crea una regola di suddivisione:

    1. Crea una 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 attributi di layout:

      • setSplitType: definisce la modalità di allocazione dell'area di visualizzazione disponibile a ciascun contenitore delle attività. Il tipo di suddivisione rapporto specifica la proporzione dell'area di visualizzazione disponibile allocata al contenitore principale; il contenitore secondario occupa la parte rimanente dell'area di visualizzazione disponibile.
      • setLayoutDirection: specifica la disposizione dei contenitori delle attività l'uno rispetto all'altro, a partire dal 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 a coppie divise che determinano quando applicare la regola identificando le attività che condividono una suddivisione.
      • setDefaultSplitAttributes: applica gli attributi di layout alla regola.
      • setMinWidthDp: consente di impostare la larghezza minima della visualizzazione (in pixel indipendenti dalla densità, dp) che consente una suddivisione.
      • setMinSmallestWidthDp: imposta il valore minimo (in dp) che la dimensione più piccola delle due dimensioni di visualizzazione deve avere per attivare una suddivisione indipendentemente dall'orientamento del dispositivo.
      • setMaxAspectRatioInPortrait: imposta le proporzioni massime della visualizzazione (altezza:larghezza) con orientamento verticale per il quale vengono visualizzate le suddivisioni delle attività. Se le proporzioni di una visualizzazione verticale superano quelle massime, le divisioni vengono disattivate indipendentemente dalla larghezza del display. Nota:il valore predefinito è 1, 4 e indica che le attività occupano l'intera finestra delle attività con orientamento verticale sulla maggior parte dei tablet. Vedi anche SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT e setMaxAspectRatioInLandscape. Il valore predefinito per l'orientamento orizzontale è ALWAYS_ALLOW.
      • setFinishPrimaryWithSecondary: imposta in che modo il completamento di tutte le attività nel contenitore secondario influisce sulle attività nel contenitore principale. NEVER indica che il sistema non deve terminare le attività principali quando tutte le attività nel contenitore secondario sono terminate (consulta la sezione Terminare le attività).
      • setFinishSecondaryWithPrimary: consente di impostare 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 quando vengono terminate tutte le attività nel contenitore principale (consulta Terminare le attività).
      • setClearTop: specifica se tutte le attività nel contenitore secondario sono terminate quando viene avviata una nuova attività nel contenitore. False specifica che le nuove attività vengono sovrapposte a quelle già presenti nel contenitore secondario.
    5. Recupera 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 una ActivityFilter che identifichi l'attività con cui il segnaposto condivide una suddivisione della finestra delle 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 minima della visualizzazione (in pixel indipendenti dalla densità, dp) che consente una suddivisione.
      • setMinSmallestWidthDp: consente di impostare il valore minimo (in dp) che la dimensione più piccola delle due dimensioni di visualizzazione deve avere per consentire una suddivisione indipendentemente dall'orientamento del dispositivo.
      • setMaxAspectRatioInPortrait: imposta le proporzioni massime della visualizzazione (altezza:larghezza) con orientamento verticale per il quale vengono visualizzate le suddivisioni delle attività. Nota:il valore predefinito è 1, 4 e indica che le attività riempiono la finestra delle attività con l'orientamento verticale sulla maggior parte dei tablet. Vedi anche SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT e setMaxAspectRatioInLandscape. Il valore predefinito per l'orientamento orizzontale è ALWAYS_ALLOW.
      • setFinishPrimaryWithPlaceholder: consente di impostare in che modo il completamento dell'attività segnaposto influisce sulle attività nel contenitore principale. SEMPRE indica che il sistema deve sempre completare le attività nel contenitore principale al termine del segnaposto (consulta Terminare le attività).
      • setSticky: determina se l'attività segnaposto viene visualizzata sopra l'elenco attività su display piccoli dopo la prima visualizzazione del segnaposto in una suddivisione con 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 le attività di altre app. L'incorporamento delle attività per più applicazioni o UID consente l'integrazione visiva delle attività di più app per Android. Il sistema mostra sullo schermo un'attività dell'app host e un'attività incorporata di un'altra app affiancate o in alto e in basso, proprio come per l'incorporamento dell'attività in una singola app.

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

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

Modello di attendibilità

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

Per impedire l'uso improprio dell'incorporamento delle attività tra app, Android richiede che le app siano attivate 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 delle app possono ottenere una sintesi del certificato SHA eseguendo l'attività signingReport Gradle. Il digest del certificato è l'impronta SHA-256 senza i due punti di separazione. Per maggiori informazioni, vedi Eseguire un report sulla firma e Autenticazione del client.

Host non attendibili

Per consentire a un'app di incorporare le attività dell'app 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, che impedisce l'incorporamento dell'attività tra app.

Autenticazione personalizzata

Per ridurre i rischi di incorporamento di 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 si autentica 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 alle attività incorporate l'altezza e la larghezza minime specificate nell'elemento <layout> del file manifest dell'app. Se un'applicazione non specifica l'altezza e la larghezza minime, vengono applicati i valori predefiniti di sistema (sw220dp).

Se l'host tenta di ridimensionare il contenitore incorporato a una dimensione inferiore al minimo, il contenitore incorporato si espande per occupare tutti i limiti delle attività.

<alias-attività>

Affinché l'incorporamento delle attività attendibili o non attendibili funzioni con l'elemento <activity-alias>, è necessario applicare i 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 sulla destinazione, non sull'alias.

Applicazione host

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

Se un'applicazione host tenta di incorporare un'attività per la quale non è stato attivato l'incorporamento tra 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 tra app, l'attività occupa tutti i limiti dell'attività invece di sovrapporsi all'attività nel contenitore incorporato.

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

Esempi di suddivisione

Dividi dalla finestra intera

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

Non è necessario eseguire il refactoring. Puoi definire la configurazione per la suddivisione in modo statico o in fase di runtime e poi chiamare Context#startActivity() senza parametri aggiuntivi.

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

Suddividi per impostazione predefinita

Se la pagina di destinazione di un'applicazione è progettata per essere suddivisa in due container su schermi di grandi dimensioni, l'esperienza utente è ottimale 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 fino a quando i contenuti non possono essere visualizzati nel contenitore secondario della suddivisione (consulta la sezione Segnaposto sopra).

Figura 16. Suddivisione creata aprendo contemporaneamente due attività. 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 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à dei dettagli dei link diretti mostrata solo su uno schermo piccolo, ma insieme a un'attività dell'elenco su uno schermo di grandi dimensioni.

La richiesta di lancio deve essere indirizzata all'attività principale e l'attività di dettaglio della destinazione 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 posteriore e potresti voler evitare di ignorare l'attività dei dettagli e lasciare solo l'attività principale:

Ampio display con attività elenco e attività dettagli affiancate.
          La navigazione a ritroso non consente di ignorare l&#39;attività dei dettagli e di lasciare sullo schermo l&#39;attività relativa all&#39;elenco.

Piccolo display con solo attività dettagli. La navigazione a ritroso non può ignorare l&#39;attività dei dettagli e mostrare 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 suddivisi

L'impilamento di più attività in un container suddiviso consente agli utenti di accedere a contenuti dettagliati. Ad esempio, con una suddivisione dei dettagli elenco, l'utente potrebbe dover accedere a una sezione dei dettagli secondari, ma mantenere attiva l'attività principale:

Figura 18. Attività aperta nel riquadro secondario della finestra delle 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 accedendo allo stack:

Figura 19. Attività rimossa dalla parte superiore della pila.

Il comportamento predefinito quando le attività vengono avviate da un'attività nello stesso contenitore secondario è l'impilamento delle attività una sopra l'altra. Anche le attività avviate dal container principale all'interno di una suddivisione attiva finiscono nel container secondario in cima allo stack delle attività.

Attività in una nuova attività

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

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

Sostituzione attività

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

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

Se l'app non termina l'attività nel contenitore secondario quando cambia la selezione della navigazione, la navigazione a ritroso potrebbe creare confusione quando la suddivisione è compressa (quando il dispositivo è chiuso). Ad esempio, se è presente un menu nel riquadro principale e le schermate A e B impilate nel riquadro secondario, quando l'utente chiude lo smartphone, B si trova sopra A e A è in cima al menu. Quando l'utente torna indietro da B, al posto del menu compare A.

In questi casi, la schermata A deve essere rimossa dallo stack posteriore.

Il comportamento predefinito quando esegui l'avvio laterale di un nuovo container su una suddivisione esistente prevede l'inserimento dei nuovi container secondari in primo piano e la conservazione di quelli precedenti nello stack precedente. Puoi configurare le suddivisioni in modo da cancellare i container secondari precedenti con clearTop e avviare nuove attività normalmente.

<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 (menu), invia nuovi intent che si risolvono nella stessa istanza, ma attivano un aggiornamento di stato o UI nel container secondario.

Più suddivisioni

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

Quando un'attività in un contenitore secondario avvia una nuova attività laterale, viene creata una nuova suddivisione sopra quella esistente.

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

Lo stack posteriore contiene tutte le attività aperte in precedenza, pertanto gli utenti possono passare alla suddivisione A/B dopo aver terminato C.

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

Per creare una nuova suddivisione, avvia la nuova attività sul lato dal contenitore secondario esistente. Dichiara le configurazioni sia per le sezioni A/B che per quelle B/C e per 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 dello stato suddivise

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 si trovano in una suddivisione, mostrare l'elemento in entrambe le attività può essere ridondante e forse poco chiaro.

Figura 24. Elementi UI duplicati nella suddivisione attività.

Per sapere quando le attività sono in una suddivisione, controlla il flusso SplitController.splitInfoList o registra un listener con SplitControllerCallbackAdapter per le modifiche allo stato di suddivisione. Quindi, modifica l'interfaccia utente 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 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 arrestata. Generalmente i listener devono essere registrati nel dominio onStart() e non registrati in onStop().

modale a finestra intera

Alcune attività impediscono agli utenti di interagire con l'applicazione finché non viene eseguita un'azione specificata; ad esempio, un'attività della schermata di accesso, una schermata di conferma dei criteri o un messaggio di errore. Le attività modali non dovrebbero essere visualizzate 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à su entrambi i 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 utilizzare il pulsante Indietro anziché la navigazione tramite gesti, l'input viene inviato all'attività attiva, ovvero l'attività che è stata toccata o avviata per ultima.

L'effetto del completamento di tutte le attività in un contenitore sul contenitore avversario dipende dalla configurazione della suddivisione.

Attributi di configurazione

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

  • window:finishPrimaryWithSecondary: in che modo la fine di tutte le attività nel contenitore secondario influisce sulle attività nel contenitore principale
  • window:finishSecondaryWithPrimary: in che modo 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 vengono visualizzati uno accanto all'altro, 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 fine intermedia, il contenitore rimanente occupa l'intera finestra:

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

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

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

Completare le attività insieme

Termina automaticamente le attività nel contenitore principale quando tutte le attività nel contenitore secondario sono terminate:

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

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

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

Termina automaticamente le attività nel contenitore secondario quando tutte le attività nel contenitore principale sono terminate:

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

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

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

Completa le attività contemporaneamente quando tutte le attività nel contenitore principale o secondario sono terminate:

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

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

Suddivisione contenente le attività A e B. L&#39;operazione termina B e la 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 all'elenco non comporta il termine automatico delle attività nella parte superiore.

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

Lo stack di attività secondario contenente l&#39;attività C sovrapposto a B
          viene sovrapposto allo stack di attività prmary contenente 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 completi l'attività principale, la suddivisione rimane invariata.

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

Il completamento dell'attività inferiore (principale) del container secondario non rimuove le attività al suo interno e, di conseguenza, conserva anche la suddivisione.

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

Vengono eseguite anche eventuali regole aggiuntive per completare contemporaneamente le attività, ad esempio terminare l'attività secondaria con quella 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 in alto su B. Finitura A, poi finitura B e C.

E quando la suddivisione è configurata per terminare insieme le attività principali e secondarie:

<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 contenitore secondario, C in pila sopra B. C termina, lasciando A e B nella
          suddivisione delle attività.

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

Suddividi con l&#39;attività A nel contenitore principale e le attività B e C nel contenitore secondario, C in pila sopra B. Finitura A, oltre a finiture B e
          C.

Modifica delle proprietà di suddivisione in fase di runtime

Le proprietà di una suddivisione attualmente attiva e visibile non possono essere modificate. La modifica delle regole di suddivisione influisce sull'avvio di attività aggiuntive e sui nuovi container, ma non sulle suddivisioni esistenti e attive.

Per modificare le proprietà dei segmenti attivi, termina l'attività secondaria o le attività nel segmento e avvialo di nuovo a lato con una nuova configurazione.

Estrae un'attività da una suddivisione a finestra intera

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

Verifica la presenza del 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 precedenti della piattaforma. 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 le suddivisioni non sono supportate, le attività vengono avviate sopra lo stack delle attività (seguendo il modello di incorporamento non delle attività).

Evita override del sistema

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

L'incorporamento dell'attività di sistema può migliorare la presentazione dell'app tramite layout multi-riquadro, come list-detail, senza alcuna modifica all'app. Tuttavia, l'incorporamento dell'attività del sistema potrebbe causare anche layout dell'app errati, bug o conflitti con l'incorporamento delle 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 WindowProperties di Jetpack WindowManager. Imposta il valore su false se la tua app implementa l'incorporamento dell'attività o se vuoi impedire altrimenti al sistema di applicare le sue regole di incorporamento dell'attività alla tua app. Imposta il valore su true per consentire al sistema di applicare l'incorporamento dell'attività definita dal sistema alla tua app.

Limitazioni, restrizioni e avvertenze

  • Solo l'app host dell'attività, identificata come proprietaria dell'attività principale, 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 queste attività.
  • Le attività possono essere organizzate in una sola attività. L'avvio di un'attività in una nuova attività viene sempre collocata in una nuova finestra espansa al di fuori di eventuali suddivisioni esistenti.
  • È possibile organizzare e suddividere solo le attività nello stesso processo. Il callback SplitInfo segnala solo le attività che appartengono allo stesso processo, poiché non è in alcun modo possibile conoscere le attività nei diversi processi.
  • Ogni coppia o regola di attività singola si applica solo agli avvii di attività successivi alla registrazione della regola. Al momento non è possibile aggiornare le suddivisioni esistenti o le relative proprietà visive.
  • La configurazione del filtro a coppie divise deve corrispondere agli intent utilizzati quando avvii completamente le attività. La corrispondenza si verifica nel momento in cui viene avviata una nuova attività dal processo di applicazione, quindi potrebbe non conoscere i nomi dei componenti che vengono risolti in seguito durante il processo di sistema quando vengono utilizzati intent impliciti. Se il nome di un componente non è noto al momento del lancio, è possibile utilizzare un carattere jolly ("*/*") e applicare i filtri in base all'azione dell'intent.
  • Al momento non è possibile spostare le attività tra container o all'interno e all'esterno delle suddivisioni dopo la creazione. Le suddivisioni vengono create dalla libreria WindowManager quando vengono avviate nuove attività con regole corrispondenti, mentre le suddivisioni vengono eliminate al termine dell'ultima attività in un container diviso.
  • Le attività possono essere riavviate quando la configurazione cambia, quindi, quando viene creata o rimossa una suddivisione e i limiti delle attività cambiano, l'attività può essere completamente eliminata dall'istanza precedente e creata quella nuova. Di conseguenza, gli sviluppatori di app dovrebbero fare attenzione, ad esempio, al lancio di nuove attività dai callback del ciclo di vita.
  • I dispositivi devono includere l'interfaccia delle estensioni finestra per supportare l'incorporamento delle 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 finestra. Se un dispositivo con schermo grande non supporta la modalità multi-finestra, potrebbe non supportare l'incorporamento delle attività.

Risorse aggiuntive