Ofereça suporte a janelas de área de trabalho

Com o modo janela para computador, os usuários podem executar vários apps simultaneamente em janelas redimensionáveis para ter uma experiência versátil, semelhante à de um computador.

Na figura 1, é possível ver a organização da tela com o recurso de janelas do computador ativado. Observações:

  • Os usuários podem executar vários apps lado a lado simultaneamente.
  • A barra de tarefas fica em uma posição fixa na parte de baixo da tela, mostrando os apps em execução. Os usuários podem fixar apps para acesso rápido.
  • Uma nova barra de cabeçalho personalizável decora a parte de cima de cada janela com controles como minimizar e maximizar.
Uma tela de tablet mostrando vários apps em execução em janelas redimensionáveis com uma barra de tarefas na parte de baixo.
Figura 1. Modo Janela para Computador em um tablet.

Por padrão, os apps são abertos em tela cheia nos tablets Android. Para abrir um app no modo janela para computador, pressione e mantenha pressionada a alça da janela na parte de cima da tela e arraste a alça na interface, conforme mostrado na Figura 2.

Quando um app está aberto no janelamento para computadores, outros apps também são abertos em janelas de computador.

Figura 2. Pressione, mantenha pressionado e arraste a alça da janela do app para entrar no modo de janelas da área de trabalho.

Os usuários também podem invocar o redimensionamento de janelas da área de trabalho no menu que aparece abaixo da alça da janela ao tocar ou clicar nela ou usar o atalho de teclado tecla Meta (Windows, Command ou Pesquisa) + Ctrl + Seta para baixo.

Para sair do modo de janelas na área de trabalho, os usuários precisam fechar todas as janelas ativas ou arrastar a alça da janela na parte de cima de uma janela da área de trabalho até a parte de cima da tela. O atalho de teclado Meta + H também sai do modo janela para computador e executa os apps em tela cheia novamente.

Para voltar ao modo de janelas da área de trabalho, toque ou clique no bloco do espaço da área de trabalho na tela "Recentes".

Redimensionamento e modo de compatibilidade

No sistema de janelas para computador, os apps com orientação bloqueada podem ser redimensionados livremente. Isso significa que, mesmo que uma atividade esteja bloqueada na orientação retrato, os usuários ainda podem redimensionar o app para uma janela de orientação paisagem.

Figura 3. Redimensionar a janela de um app restrito ao modo retrato para o modo paisagem.

Os apps declarados como não redimensionáveis (ou seja, resizeableActivity = false) têm a interface dimensionada, mantendo a mesma proporção.

Figura 4. A interface de um app não redimensionável é dimensionada conforme a janela é redimensionada.

Os apps de câmera que bloqueiam a orientação ou são declarados como não redimensionáveis têm um tratamento especial para os visores: a janela é totalmente redimensionável, mas o visor mantém a mesma proporção. Ao presumir que os apps sempre são executados na vertical ou horizontal, eles codificam ou fazem outras suposições que levam a cálculos incorretos da orientação ou proporção da imagem capturada ou de visualização, resultando em imagens esticadas, laterais ou de cabeça para baixo.

Enquanto os apps não estiverem prontos para implementar visores de câmera totalmente responsivos, o tratamento especial vai oferecer uma experiência do usuário mais básica que mitiga os efeitos que suposições erradas podem causar.

Para saber mais sobre o modo de compatibilidade para apps de câmera, consulte Modo de compatibilidade do dispositivo.

Figura 5. O visor da câmera mantém a proporção à medida que a janela é redimensionada.

Encaixes de cabeçalho personalizáveis

Todos os apps executados em janelas de computador têm uma barra de cabeçalho, mesmo no modo imersivo. Verifique se o conteúdo do app não está sendo ocultado pela barra de cabeçalho. A barra de cabeçalho é um tipo de encarte de barra de legenda: WindowInsets.Companion.captionBar(); em visualizações, WindowInsets.Type.captionBar(), que faz parte das barras de sistema.

Saiba mais sobre como processar encartes em Mostrar conteúdo de ponta a ponta no app e processar encartes de janela no Compose.

A barra de cabeçalho também pode ser personalizada. O Android 15 introduziu o tipo de aparência APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND para tornar a barra de cabeçalho transparente e permitir que os apps mostrem conteúdo personalizado dentro dela.

Os apps passam a ser responsáveis por estilizar a parte de cima do conteúdo para parecer a barra de legenda (segundo plano, conteúdo personalizado etc.), com exceção dos elementos de legenda do sistema (botões de fechar e maximizar), que são mostrados pelo sistema na barra de legenda transparente na parte de cima do app.

Os apps podem alternar a aparência dos elementos do sistema dentro da legenda para temas claros e escuros usando APPEARANCE_LIGHT_CAPTION_BARS, de maneira semelhante à alternância da barra de status e da barra de navegação.

O Android 15 também introduziu o método WindowInsets#getBoundingRects() que permite que os apps façam a introspecção dos encartes da barra de legenda com mais detalhes. Os apps podem diferenciar entre áreas em que o sistema desenha elementos do sistema e áreas não utilizadas em que os apps podem colocar conteúdo personalizado sem sobrepor elementos do sistema.

A lista de objetos Rect retornados pela API indica regiões do sistema que devem ser evitadas. O espaço restante (calculado subtraindo os retângulos dos encartes da barra de legenda) é onde o app pode desenhar sem sobrepor elementos do sistema e com a capacidade de receber entrada.

Chrome antes e depois da implementação de cabeçalhos personalizados.
Figura 6. Chrome antes e depois da implementação de cabeçalhos personalizados.

Para definir retângulos de exclusão de gestos do sistema para um cabeçalho personalizado, implemente o seguinte na sua visualização ou elemento combinável:

// In a custom View's onLayout or a similar lifecycle method
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
    super.onLayout(changed, left, top, right, bottom)
    if (changed) {
        // Calculate the height of your custom header
        val customHeaderHeight = 100 // Replace with your actual header height in pixels

        // Create a Rect covering your custom header area
        val exclusionRect = Rect(0, 0, width, customHeaderHeight)

        // Set the exclusion rects for the system
        systemGestureExclusionRects = listOf(exclusionRect)
    }
}

Suporte a multitarefas e várias instâncias

A multitarefa é a base do modo janela para computador, e permitir várias instâncias do seu app pode aumentar muito a produtividade dos usuários.

O Android 15 introduz PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, que os apps podem definir para especificar que a interface do sistema precisa ser mostrada para que o app possa ser iniciado como várias instâncias.

Você pode declarar PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI no AndroidManifest.xml do app dentro da tag <activity>:

<activity
    android:name=".MyActivity"
    android:exported="true"
    android:resizeableActivity="true">
    <meta-data
        android:name="android.window.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI"
        android:value="true" />
</activity>

Gerenciar instâncias de apps com gestos de arrastar

No modo de várias janelas, os usuários podem iniciar uma nova instância de app arrastando um elemento de visualização para fora da janela do app. Os usuários também podem mover elementos entre instâncias do mesmo app.

Figura 7. Arraste uma guia para fora da janela da área de trabalho para iniciar uma nova instância do Chrome.

O Android 15 apresenta duas flags para personalizar o comportamento de arrastar:

  • DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG: indica que uma ação de arrastar sem tratamento precisa ser delegada ao sistema para ser iniciada se nenhuma janela visível processar a ação de soltar. Ao usar essa flag, o caller precisa fornecer ClipData com um ClipData.Item que contenha um IntentSender imutável para uma atividade a ser iniciada (consulte ClipData.Item.Builder#setIntentSender()). O sistema pode iniciar ou não a intent com base em fatores como o tamanho atual da tela ou o modo de janela. Se o sistema não iniciar a intent, ela será cancelada pelo fluxo normal de arrastar.

  • DRAG_FLAG_GLOBAL_SAME_APPLICATION: indica que uma operação de arrastar pode cruzar limites de janela (para várias instâncias do mesmo aplicativo).

    Quando [startDragAndDrop()][20] é chamado com essa flag definida, apenas janelas visíveis pertencentes ao mesmo aplicativo podem participar da operação de arrastar e receber o conteúdo arrastado.

O exemplo a seguir demonstra como usar essas flags com startDragAndDrop():

// Assuming 'view' is the View that initiates the drag
view.setOnLongClickListener {
    // Create an IntentSender for the activity you want to launch
    val launchIntent = Intent(view.context, NewInstanceActivity::class.java)
    val pendingIntent = PendingIntent.getActivity(
        view.context,
        0,
        launchIntent,
        PendingIntent.FLAG_IMMUTABLE // Ensure the PendingIntent is immutable
    )

    // Build the ClipData.Item with the IntentSender
    val item = ClipData.Item.Builder()
        .setIntentSender(pendingIntent.intentSender)
        .build()

    // Create ClipData with a simple description and the item
    val dragData = ClipData(
        ClipDescription("New Instance Drag", arrayOf(ClipDescription.MIMETYPE_TEXT_PLAIN)),
        item
    )

    // Combine the drag flags
    val dragFlags = View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG or
                    View.DRAG_FLAG_GLOBAL_SAME_APPLICATION

    // Start the drag operation
    view.startDragAndDrop(
        dragData,                     // The ClipData to drag
        View.DragShadowBuilder(view), // A visual representation of the dragged item
        null,                         // Local state object (not used here)
        dragFlags                     // The drag flags
    )
    true // Indicate that the long click was consumed
}
Figura 8. Mova uma guia entre duas instâncias do app Chrome.

Otimizações adicionais

Personalize as inicializações de apps e faça a transição de apps do modo janela para computador para tela cheia.

Especificar tamanho e posição padrão

Nem todos os apps, mesmo que redimensionáveis, precisam de uma janela grande para oferecer valor ao usuário. Use o método ActivityOptions#setLaunchBounds() para especificar um tamanho e uma posição padrão quando uma atividade é iniciada.

Confira um exemplo de como definir limites de inicialização para uma atividade:

val options = ActivityOptions.makeBasic()

// Define the desired launch bounds (left, top, right, bottom in pixels)
val launchBounds = Rect(100, 100, 700, 600) // Example: 600x500 window at (100,100)

// Apply the launch bounds to the ActivityOptions
options.setLaunchBounds(launchBounds)

// Start the activity with the specified options
val intent = Intent(this, MyActivity::class.java)
startActivity(intent, options.toBundle())

Entrar no modo de tela cheia no espaço de trabalho

Os apps podem entrar em tela cheia chamando Activity#requestFullScreenMode(). O método mostra o app em tela cheia diretamente do redimensionamento da janela do computador.

Para solicitar o modo de tela cheia de uma atividade, use o seguinte código:

// In an Activity
fun enterFullScreen() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { // Android 15 (U)
        requestFullScreenMode()
    }
}