Incorporação de atividades

Mantenha tudo organizado com as coleções Salve e categorize o conteúdo com base nas suas preferências.

A incorporação de atividades otimiza apps em dispositivos de tela grande dividindo a janela de tarefa de um app em duas atividades ou duas instâncias.

Figura 1. App Configurações com atividades lado a lado.

Atualizar as bases de código legadas para oferecer suporte a telas grandes é uma tarefa intensa e demorada. A conversão de apps baseados em atividade para layouts de vários painéis usando fragmentos requer uma refatoração significativa.

A incorporação de atividades exige pouca ou nenhuma refatoração do app. Para determinar como o app mostra as atividades (lado a lado ou empilhadas), crie um arquivo de configuração XML ou faça chamadas da API Jetpack WindowManager.

O suporte a telas pequenas é mantido automaticamente. Quando o app está em um dispositivo com uma tela pequena, as atividades são empilhadas uma sobre a outra. Em telas grandes, as atividades são exibidas lado a lado. O sistema determina a apresentação com base na configuração criada e nenhuma lógica de ramificação é necessária.

A incorporação de atividades oferece suporte a mudanças de orientação do dispositivo e funciona perfeitamente em dispositivos dobráveis, empilhando e desempilhando atividades conforme o dispositivo é dobrado e desdobrado.

O desenvolvimento moderno para Android usa uma arquitetura de atividade única com fragmentos, componentes de navegação e gerenciadores de layout versáteis, como o SlidingPaneLayout.

No entanto, caso seu app seja composto por várias atividades, a incorporação de atividades permite melhorar facilmente a experiência do usuário em tablets, dispositivos dobráveis e ChromeOS.

Dividir janela de tarefas

A incorporação de atividades divide a janela de tarefas do app em dois contêineres: primário e secundário. Os contêineres guardam as atividades iniciadas da atividade principal ou de outras atividades que já estão neles.

As atividades são empilhadas no contêiner secundário conforme são iniciadas, e o contêiner secundário é empilhado sobre o contêiner principal em telas pequenas. Portanto, o empilhamento de atividades e a navegação de retorno são consistentes com a ordem de atividades já integradas ao app.

A incorporação de atividades possibilita mostrar atividades de várias maneiras. O app pode dividir a janela de tarefas iniciando duas atividades lado a lado simultaneamente:

Figura 2. Duas atividades lado a lado.

Ou então, uma atividade que ocupa toda a janela da tarefa pode criar uma divisão ao iniciar uma nova atividade lado a lado:

Figura 3. A atividade A inicia a atividade B ao lado dela.

As atividades que já estão em uma divisão e compartilhando uma janela de tarefas podem iniciar outras atividades das seguintes maneiras:

  • Ao lado, por cima da outra atividade:

    Figura 4. A atividade A inicia a atividade C ao lado e por cima da atividade B.
  • Ao lado e a divisão é movida na lateral, ocultando a atividade principal anterior:

    Figura 5. A atividade B inicia a atividade C ao lado e muda a divisão lateralmente.
  • A atividade é iniciada por cima, ou seja, na mesma pilha de atividades:

    Figura 6. A atividade B inicia a atividade C sem outras sinalizações de intent.
  • A atividade é iniciada em tela cheia na mesma tarefa:

    Figura 7. A atividade A ou a atividade B inicia a atividade C que preenche a janela de tarefas.

Navegação de retorno

Diferentes tipos de aplicativos podem ter diferentes regras de navegação de retorno no estado de janela de uma tarefa dividida, dependendo das dependências entre as atividades ou de como os usuários acionam o evento de retorno, por exemplo:

  • Juntas: se as atividades estão relacionadas e uma delas não é mostrada sem a outra, a navegação de retorno pode ser configurada para concluir as duas.
  • Sozinhas: se as atividades são totalmente independentes, a navegação de retorno em uma atividade não afeta o estado de outra na janela de tarefas.

O evento de retorno é enviado para a última atividade em foco ao usar a navegação por botões. Com a navegação baseada em gestos, o evento "Voltar" é enviado à atividade em que o gesto ocorreu.

Layout com vários painéis

A biblioteca Jetpack WindowManager 1.0 Beta03 possibilita criar um layout de vários painéis com atividades em dispositivos de tela grande com o 12L (API de nível 32) e alguns dispositivos com versões anteriores da plataforma. Os apps já existentes que são baseados em várias atividades em vez de fragmentos ou layouts baseados em visualização, por exemplo, SlidingPaneLayout, podem oferecer uma melhor experiência do usuário na tela grande, sem precisar de refatoração.

Um exemplo comum é uma divisão do painel de detalhes e lista. Para garantir uma apresentação de qualidade, o sistema inicia a atividade de lista e o aplicativo inicia imediatamente a atividade de detalhes. O sistema de transição aguarda até que ambas as atividades sejam desenhadas e as exibe juntas. Para o usuário, as duas atividades são iniciadas como uma só.

Figura 8. Duas atividades foram iniciadas ao mesmo tempo em um layout de vários painéis.

Proporção da divisão

O aplicativo especifica como a proporção da janela de tarefas é aplicada pelo atributo ratio de uma configuração de divisão. Consulte Configuração de divisão abaixo.

Figura 9. Duas divisões de atividade com proporções diferentes.

Marcadores de posição

As atividades de marcador de posição são atividades secundárias vazias que ocupam uma área de divisão de atividade. Elas precisam ser substituídas por outra atividade que inclua conteúdo. Por exemplo, uma atividade de marcador de posição pode ocupar o lado secundário de uma divisão de atividades em um layout de detalhes da lista até um item da lista ser selecionado. Nesse momento, uma atividade contendo as informações detalhadas do item da lista selecionada substitui o marcador.

Os marcadores de posição são mostrados apenas quando há espaço suficiente para uma divisão. Eles são concluídos automaticamente quando o tamanho da tela muda para uma largura muito pequena para exibir uma divisão de atividade, mas são reiniciados automaticamente (com um estado reinicializado) quando o espaço permite.

Figura 10. Dispositivo se dobrando e desdobrando. A atividade do marcador de posição é finalizada e recriada conforme o tamanho da tela muda.

Mudanças no tamanho das janelas

Quando as mudanças na configuração do dispositivo reduzem a largura da janela de tarefas de tal modo que ela não seja grande o suficiente para um layout de vários painéis, as atividades não marcadoras de posição no painel secundário da janela de tarefas são empilhadas sobre as atividades no painel principal. Por exemplo, quando um dispositivo dobrável com tela grande do tamanho de um tablet é dobrado para o tamanho de um smartphone, ou quando a janela do app é redimensionada no modo de várias janelas.

As atividades de marcador de posição são exibidas apenas quando há espaço na tela de largura suficiente para uma divisão. Em telas menores, o marcador é dispensado automaticamente. Quando o espaço na tela se torna grande o suficiente, o marcador é recriado. Consulte Marcadores de posição acima.

O empilhamento da atividade é possível porque WindowManager organiza as atividades no painel secundário na ordem z, acima das atividades no painel principal.

Várias atividades no painel secundário

A atividade B inicia a atividade C sem outras sinalizações de intent:

Divisão de atividade contendo as atividades A, B e C com C empilhada sobre
          B.

Resultando na seguinte ordem z de atividades na mesma tarefa:

Pilha de atividade secundária contendo a atividade C sobre a B.
          A pilha secundária é empilhada sobre a pilha de atividades primária
          que contém a atividade A.

Portanto, em uma janela de tarefas menor, o aplicativo diminui para uma única atividade com C na parte de cima da pilha:

Janela pequena mostrando apenas a atividade C.

Voltar na janela menor é navegar pelas atividades empilhadas umas sobre as outras.

Se a configuração da janela de tarefas for restaurada para um tamanho maior que possa acomodar vários painéis, as atividades são exibidas lado a lado.

Divisões empilhadas

A atividade B inicia a atividade C na lateral e muda a divisão ao lado:

Janela de tarefas mostrando as atividades A e B, depois as atividades B e C.

O resultado é a seguinte ordem z das atividades na mesma tarefa:

As atividades A, B e C em uma única pilha. As atividades são empilhadas
          na seguinte ordem, de cima para baixo: C, B, A.

Em uma janela de tarefas menor, o aplicativo diminui para uma única atividade com C na parte de cima:

Janela pequena mostrando apenas a atividade C.

Configuração de divisões

Contêineres e divisões podem ser criados pela biblioteca WindowManager com base nas regras de divisão. A configuração das regras de divisão envolve várias etapas:

  1. Adicione a dependência da biblioteca WindowManager ao arquivo build.gradle:

    implementation("androidx.window:window:1.0.0-beta03")

  2. Crie um arquivo de recursos para fazer o seguinte:

    • Definir quais atividades precisam ser divididas usando filtros.
    • Configurar as opções de divisão para todas as atividades que compartilham uma divisão.
    • Especificar as atividades que não podem ser colocadas em uma divisão.

    Exemplo:

    <!-- The split configuration for activities. -->
    <resources
        xmlns:window="http://schemas.android.com/apk/res-auto">
    
        <!-- Automatically split the following activity pairs. -->
        <SplitPairRule
            window:splitRatio="0.3"
            window:splitMinWidth="600dp"
            window:finishPrimaryWithSecondary="true"
            window:finishSecondaryWithPrimary="true">
            <SplitPairFilter
                window:primaryActivityName=".SplitActivityList"
                window:secondaryActivityName=".SplitActivityDetail"/>
            <SplitPairFilter
                window:primaryActivityName="*"
                window:secondaryActivityName="*/*"
                window:secondaryActivityAction="android.intent.action.VIEW"/>
        </SplitPairRule>
    
        <!-- Automatically launch a placeholder for the list activity. -->
        <SplitPlaceholderRule
            window:placeholderActivityName=".SplitActivityListPlaceholder"
            window:splitRatio="0.3"
            window:splitMinWidth="600dp">
            <ActivityFilter
                window:activityName=".SplitActivityList"/>
        </SplitPlaceholderRule>
    
    </resources>
    
  3. Informe a biblioteca sobre as regras definidas.

    Neste exemplo, estamos usando a biblioteca Jetpack Startup para executar a inicialização antes que outros componentes do carregamento do app e das atividades sejam iniciados. Para ativar a funcionalidade de inicialização, adicione a dependência da biblioteca ao arquivo do build do app:

    implementation("androidx.startup:startup-runtime:1.1.0")

    E adicione a seguinte entrada ao manifesto do app:

    <!-- AndroidManifest.xml -->
    
    <provider android:name="androidx.startup.InitializationProvider"
        android:authorities="${applicationId}.androidx-startup"
        android:exported="false"
        tools:node="merge">
        <!-- This entry makes ExampleWindowInitializer discoverable. -->
        <meta-data  android:name="androidx.window.sample.embedding.ExampleWindowInitializer"
            android:value="androidx.startup" />
    </provider>
    
  4. Por fim, adicione a implementação da classe inicializadora.

    As regras são definidas fornecendo o ID do arquivo de recursos XML que contém as definições (main_split_config) para SplitController.initialize():

    Kotlin

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

    Java

    class ExampleWindowInitializer extends Initializer<SplitController> {
       @Override
       SplitController create(Context context) {
           SplitController.initialize(context, R.xml.main_split_config);
           return SplitController.getInstance(context);
       }
    
       @Override
       List<Class<? extends Initializer<?>>> dependencies() {
           return emptyList();
       }
    }
    

Exemplos de divisão

Divisão da tela cheia

Figura 11. A atividade A inicia a atividade B ao lado dela.

Nenhuma refatoração é necessária. É possível definir a configuração da divisão de forma estática ou no momento da execução e, em seguida, chamar Context#startActivity() sem outros parâmetros.

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

Dividir por padrão

Quando a página de destino de um aplicativo é projetada para ser dividida em dois contêineres em telas grandes, a experiência do usuário é melhor quando ambas as atividades são criadas e apresentadas de forma simultânea. No entanto, o conteúdo pode não estar disponível para o contêiner secundário da divisão até que o usuário interaja com a atividade no contêiner principal, por exemplo, quando o usuário seleciona um item em um menu de navegação. Uma atividade de marcador de posição pode preencher o espaço vazio até que o conteúdo possa ser exibido no contêiner secundário da divisão. Consulte Marcadores de posição acima.

Figura 12. Divisão criada pela abertura de duas atividades ao mesmo tempo. Uma atividade é um marcador de posição.

Para criar uma divisão com um marcador, crie um marcador e o associe à atividade principal:

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

Quando um app recebe uma intent, a atividade de destino pode ser mostrada como a parte secundária de uma divisão de atividades. Por exemplo, um pedido para mostrar uma tela de detalhes com informações sobre um item de uma lista; Em telas pequenas, os detalhes são mostrados em toda a janela de tarefas. Em dispositivos maiores, ao lado da lista.

Figura 13. A atividade de detalhes de links diretos aparece sozinha em uma tela pequena e junto com uma atividade de lista em uma tela grande.

O pedido de inicialização precisa ser encaminhado para a atividade principal, e a atividade de detalhes de destino precisa ser iniciada em uma divisão. SplitController escolhe automaticamente a apresentação correta (empilhada ou lado a lado) com base na largura de tela disponível.

Kotlin

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

Java

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

O destino do link direto pode ser a única atividade que precisa estar disponível para o usuário na pilha de navegação de retorno e você pode evitar a ação de dispensar a atividade dos detalhes e deixar apenas a atividade principal:

Tela grande com atividade de lista e detalhes lado a lado.
          A navegação de retorno não consegue dispensar a atividade de detalhes e deixar a atividade de lista
          na tela.

Tela pequena apenas com atividade de detalhes. A navegação de retorno não consegue
          dispensar a atividade de detalhes e revelar a atividade da lista.

Em vez disso, é possível finalizar as duas atividades ao mesmo tempo usando o atributo finishPrimaryWithSecondary:

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

Várias atividades em contêineres divididos

O empilhamento de várias atividades em um contêiner dividido permite que os usuários acessem conteúdo localizado em partes diferentes. Por exemplo, com uma divisão dos detalhes da lista, o usuário pode precisar acessar uma seção de subdetalhes, mas manter a atividade principal no lugar:

Figura 14. Atividade aberta no lugar do painel secundário da janela de tarefas.

Kotlin

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

Java

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

A atividade de subdetalhes é posicionada acima da atividade detalhada, que fica ocultada:

O usuário pode voltar ao nível de detalhes anterior navegando de volta pela pilha:

Figura 15. Atividade removida da parte de cima da pilha.

As atividades se empilham umas sobre as outras como comportamento padrão quando são iniciadas de uma atividade no mesmo contêiner secundário. Atividades iniciadas do contêiner principal em uma divisão ativa também acabam no contêiner secundário, na parte de cima da pilha de atividades.

Atividades em uma nova tarefa

Quando as atividades em uma janela de tarefas dividida iniciam atividades em uma nova tarefa, a nova tarefa é separada da tarefa que inclui a divisão e preenche a janela toda. A tela "Recentes" mostra duas tarefas: uma na divisão e a nova tarefa.

Figura 16. A atividade C é iniciada em uma nova tarefa da atividade B.

Substituir atividades

As atividades podem ser substituídas na pilha de contêineres secundária. Por exemplo, quando a atividade principal é usada para a navegação de nível mais alto e a atividade secundária é um destino selecionado. Cada seleção da navegação de nível superior precisa iniciar uma nova atividade no contêiner secundário e remover as atividades que estavam lá anteriormente.

Figura 17. A atividade de navegação de nível mais alto no painel principal substitui as atividades de destino no painel secundário.

Se o app não finaliza a atividade no contêiner secundário quando a seleção de navegação mudar, a navegação de retorno pode ficar confusa quando a divisão é recolhida (quando o dispositivo está dobrado). Por exemplo, se você tiver um menu no painel principal e telas A e B empilhadas no painel secundário, quando o usuário dobrar o smartphone, B vai estar em cima de A e A em cima do menu. Ao navegar de volta de B, o usuário vê A em vez do menu.

Nesses casos, é necessário remover a tela A da backstack.

O comportamento padrão ao iniciar uma atividade na lateral em um novo contêiner acima de uma divisão existente é colocar os novos contêineres secundários na parte de cima e manter os antigos na backstack. É possível configurar as divisões para limpar os contêineres secundários anteriores com clearTop e iniciar novas atividades 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)));
    }
}

Como alternativa, use a mesma atividade secundária e envie novas intents da atividade principal (menu) que sejam resolvidas para a mesma instância, mas acionem uma atualização de estado ou de IU no contêiner secundário.

Várias divisões

Os apps podem oferecer navegação em vários níveis iniciando outras atividades na lateral.

Quando uma atividade em um contêiner secundário inicia uma nova atividade ao lado, uma nova divisão é criada por cima da divisão existente.

Figura 18. A atividade B inicia a atividade C ao lado dela.

A pilha de retorno contém todas as atividades que foram abertas anteriormente para que os usuários possam navegar para a divisão A/B após a finalização de C.

Atividades A, B e C em uma pilha. As atividades são empilhadas na
          seguinte ordem, de cima para baixo: C, B, A.

Para criar uma nova divisão, inicie a nova atividade na lateral pelo contêiner secundário existente. Declare as configurações para as divisões A/B e B/C, e inicie a atividade C normalmente pela 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));
    }
}

Reagir a mudanças de estado da divisão

Atividades diferentes em um app podem ter elementos de IU que executam a mesma função. Por exemplo, um controle que abre uma janela contendo as configurações da conta.

Figura 19. Atividades diferentes com elementos de IU idênticos.

Se duas atividades que têm um elemento de IU em comum estão divididas, é redundante e pode ser confuso mostrar o elemento nas duas atividades.

Figura 20. Elementos de IU duplicados na divisão de atividade.

Para saber quando as atividades estão em uma divisão, registre um listener com o SplitController para mudanças no estado da divisão. Em seguida, ajuste a IU de acordo:

Kotlin

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

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

Java

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

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

É possível fazer callbacks em qualquer estado do ciclo de vida, inclusive quando uma atividade é interrompida. Os listeners geralmente precisam ser registrados em onStart() e não registrados em onStop().

Tela cheia modal

Algumas atividades impedem que os usuários interajam com o aplicativo até que uma ação especificada seja realizada. Por exemplo, uma atividade de tela de login, uma tela de confirmação de política ou uma mensagem de erro. As atividades modais precisam ser impedidas de aparecer em uma divisão.

Uma atividade pode ser forçada a sempre preencher a janela de tarefas usando a configuração de expansão:

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

Finalizar atividades

Os usuários podem finalizar atividades em qualquer lado da divisão deslizando a partir da borda da tela:

Figura 21. Gesto de deslizar para finalizar a atividade B.
Figura 22. Gesto de deslizar para finalizar a atividade A.

Se o dispositivo estiver configurado para usar o botão "Voltar" em vez da navegação por gestos, a entrada é enviada para a atividade em foco, ou seja, a atividade que foi tocada ou iniciada por último.

O resultado da finalização de uma das atividades na divisão depende da configuração da divisão.

Configuração padrão

Quando uma atividade na divisão é concluída, a atividade restante ocupa toda a janela:

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

Divisão contendo as atividades A e B. A atividade A é concluída, deixando B
          ocupar a janela inteira.

Divisão contendo as atividades A e B. A atividade B é concluída, deixando A
          ocupar a janela inteira.

Finalizar atividades em conjunto

Finalize a atividade principal automaticamente quando a atividade secundária for concluída:

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

Divisão contendo as atividades A e B. A conclusão de B, que também
          finaliza A, deixa a janela de tarefas vazia.

Divisão contendo as atividades A e B. A atividade A é concluída, deixando a atividade B sozinha
          na janela de tarefas.

Conclua a atividade secundária automaticamente quando a atividade principal for finalizada:

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

Divisão contendo as atividades A e B. A conclusão de A, que também
          finaliza B, deixa a janela de tarefas vazia.

Divisão contendo as atividades A e B. A atividade B é concluída, deixando a atividade A sozinha
          na janela de tarefas.

Conclua as atividades ao mesmo tempo quando o painel primário ou o secundário são finalizados:

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

Divisão contendo as atividades A e B. A conclusão de A, que também
          finaliza B, deixa a janela de tarefas vazia.

Divisão contendo as atividades A e B. A conclusão de B, que também
          finaliza A, deixa a janela de tarefas vazia.

Finalizar várias atividades em contêineres

Se várias atividades forem empilhadas em um contêiner dividido, finalizar uma atividade na parte de baixo da pilha não conclui automaticamente as atividades na parte de cima.

Por exemplo, se duas atividades estiverem no contêiner secundário, C em cima de B:

A pilha de atividades secundária contendo a atividade C empilhada sobre B
          é colocada sobre a pilha de atividades primária que contém a atividade
          A.

E a configuração da divisão é definida pelas definições das atividades A e B:

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

A conclusão da atividade de cima mantém a divisão.

Divisão com a atividade A no contêiner principal e as atividades B e C no
          secundário, a atividade C empilhada sobre B. C é finalizada, deixando A e B na
          divisão de atividades.

Concluir a atividade de baixo (raiz) do contêiner secundário não remove as atividades acima dele, e assim, retém a divisão.

Divisão com a atividade A no contêiner principal e as atividades B e C no
          secundário, a atividade C empilhada sobre B. A atividade B é concluída e deixa A e C na
          divisão de atividades.

Todas as outras regras para finalizar as atividades em conjunto, por exemplo, a conclusão da atividade secundária com a primária, também são executadas:

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

Divisão com a atividade A no contêiner principal, e as atividades B e C no
          contêiner secundário, a atividade C empilhada sobre B. A é concluída, também
          finalizando B e C.

E quando a divisão estiver configurada para finalizar as partes primária e secundária juntas:

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

Divisão com a atividade A no contêiner principal e as atividades B e C no
          secundário, a atividade C empilhada sobre B. C é finalizada, deixando A e B na
          divisão de atividades.

Divisão com a atividade A no contêiner principal e as atividades B e C no
          secundário, a atividade C empilhada sobre B. A atividade B é concluída e deixa A e C na
          divisão de atividades.

Divisão com a atividade A no contêiner principal e as atividades B e C no
          secundário, a atividade C empilhada sobre B. A é concluída, também finalizando B e
          C.

Mudar propriedades de divisão no momento da execução

Não é possível mudar as propriedades de uma divisão atualmente ativa e visível. Mudar as regras de divisão afeta a inicialização de outras atividades e novos contêineres, mas não divisões existentes e ativas.

Para mudar as propriedades de divisões ativas, conclua a atividade lateral ou as atividades na divisão e reinicie para a lateral novamente com uma nova configuração.

Extrair uma atividade de uma divisão para tela cheia

Crie uma nova configuração que mostre a tela cheia com a atividade lateral e, em seguida, reinicie a atividade com uma intent que é resolvida na mesma instância.

Conferir o suporte para divisão no momento da execução

A incorporação de atividades é um recurso do Android 12L (API de nível 32), mas também está disponível em alguns dispositivos com versões anteriores da plataforma. Para conferir a disponibilidade do recurso no momento da execução, use o método SplitController.isSplitSupported():

Kotlin

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

Java

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

Se não houver suporte para divisões, as atividades serão iniciadas na parte de cima, seguindo o modelo normal.

Limitações, restrições e advertências

  • Apenas o app host da tarefa, que é identificado como o proprietário da atividade raiz, pode organizar e incorporar outras atividades à tarefa. Se as atividades que oferecem suporte a divisões e incorporação forem executadas em uma tarefa que pertença a um aplicativo diferente, as incorporações e divisões não funcionarão para essas atividades.
  • As atividades só podem ser organizadas em uma tarefa. Iniciar uma atividade em uma nova tarefa sempre a coloca em uma nova janela expandida fora das divisões existentes.
  • Apenas atividades no mesmo processo podem ser organizadas e colocadas em uma divisão. O callback SplitInfo só informa atividades que pertencem ao mesmo processo, já que não há como saber sobre atividades em processos diferentes.
  • Cada par ou regra de atividade única se aplica apenas para atividades iniciadas após a regra ser registrada. Atualmente, não há como atualizar as divisões existentes ou as propriedades visuais delas.
  • A configuração do filtro de par dividido precisa corresponder às intents usadas ao iniciar completamente as atividades. A correspondência ocorre no momento em que uma nova atividade é iniciada do processo do aplicativo. Por isso, ela pode não saber sobre os nomes de componentes que são resolvidos mais tarde no processo do sistema ao usar intents implícitas. Se o nome de um componente não for conhecido no momento da inicialização, um caractere curinga pode ser usado (“*/*”) e a filtragem pode ser realizada com base na ação da intent.
  • Atualmente, não há como mover atividades entre contêineres ou para dentro e fora de divisões após serem criadas. As divisões são criadas pela biblioteca WindowManager apenas quando novas atividades com regras correspondentes são iniciadas, e as divisões são destruídas quando a última atividade em um contêiner de divisão é finalizada.
  • As atividades podem ser reiniciadas quando a configuração é alterada. Portanto, quando uma divisão é criada ou removida e os limites de atividades mudam, a atividade pode passar pela destruição completa da instância anterior e criação da nova. Como resultado, os desenvolvedores de apps precisam ter cuidado, por exemplo, com a inicialização de novas atividades usando callbacks do ciclo de vida.

Outros recursos