Compatibilidad con la renderización en ventanas de escritorio

El modo de ventanas de escritorio permite que los usuarios ejecuten varias apps simultáneamente en ventanas de apps redimensionables para brindar una experiencia versátil similar a la de una computadora de escritorio.

En la figura 1, puedes ver la organización de la pantalla con la función de ventanas de escritorio habilitada. Información que debes tener en cuenta:

  • Los usuarios pueden ejecutar varias apps una al lado de la otra de forma simultánea.
  • La barra de tareas se encuentra en una posición fija en la parte inferior de la pantalla y muestra las apps en ejecución. Los usuarios pueden fijar apps para acceder a ellas rápidamente.
  • La nueva barra de encabezado personalizable decora la parte superior de cada ventana con controles como minimizar y maximizar.
Una pantalla de tablet que muestra varias apps que se ejecutan en ventanas redimensionables con una barra de tareas en la parte inferior.
Figura 1: Modo de ventanas de escritorio en una tablet

De forma predeterminada, las apps se abren en pantalla completa en las tablets Android. Para iniciar una app en ventanas de escritorio, mantén presionado el controlador de la ventana en la parte superior de la pantalla y arrástralo dentro de la IU, como se muestra en la figura 2.

Cuando una app se abre en la renderización en ventanas de escritorio, las demás apps también se abren en ventanas de escritorio.

Figura 2: Presiona, mantén presionado y arrastra el controlador de la ventana de la app para ingresar a la ventana de escritorio.

Los usuarios también pueden invocar la función de ventanas de escritorio desde el menú que aparece debajo del controlador de la ventana cuando presionan o hacen clic en el controlador, o bien usan la combinación de teclas tecla Meta (Windows, Comando o Búsqueda) + Ctrl + flecha hacia abajo.

Para salir del modo de ventanas de escritorio, los usuarios deben cerrar todas las ventanas activas o tomar el controlador de la ventana en la parte superior de una ventana de escritorio y arrastrar la app a la parte superior de la pantalla. La combinación de teclas Meta + H también cierra las ventanas de escritorio y vuelve a ejecutar las apps en pantalla completa.

Para volver a la ventana de escritorio, presiona o haz clic en el mosaico del espacio de escritorio en la pantalla Recientes.

Modo de compatibilidad y cambio de tamaño

En el sistema de ventanas de escritorio, las apps con orientación bloqueada se pueden redimensionar libremente. Esto significa que, incluso si una actividad está bloqueada en orientación vertical, los usuarios pueden cambiar el tamaño de la app a una ventana de orientación horizontal.

Figura 3: Cambiar el tamaño de la ventana de una app solo con orientación vertical a horizontal

Las apps declaradas como no redimensionables (es decir, resizeableActivity = false) tienen su IU ajustada mientras mantienen la misma relación de aspecto.

Figura 4: La IU de una app que no cambia de tamaño se ajusta a medida que cambia el tamaño de la ventana.

Las apps de cámara que bloquean la orientación o se declaran como no redimensionables tienen un tratamiento especial para sus visores de cámara: la ventana es completamente redimensionable, pero el visor mantiene la misma relación de aspecto. Si se supone que las apps siempre se ejecutan en orientación vertical u horizontal, las apps codifican de forma rígida o, de otro modo, realizan suposiciones que conducen a cálculos incorrectos de la orientación o la relación de aspecto de la vista previa o la imagen capturada, lo que genera imágenes estiradas, laterales o invertidas.

Hasta que las apps estén listas para implementar visores de cámara completamente responsivos, el tratamiento especial proporcionará una experiencia del usuario más básica que mitigue los efectos que puedan causar las suposiciones incorrectas.

Para obtener más información sobre el modo de compatibilidad de las apps de cámara, consulta Modo de compatibilidad del dispositivo.

Figura 5. El visor de la cámara conserva su relación de aspecto a medida que se cambia el tamaño de la ventana.

Inserciones de encabezado personalizables

Todas las apps que se ejecutan en ventanas de escritorio tienen una barra de encabezado, incluso en el modo envolvente. Verifica que la barra de encabezado no oculte el contenido de tu app. La barra de encabezado es un tipo de inserción de barra de subtítulos: WindowInsets.Companion.captionBar(); en las vistas, WindowInsets.Type.captionBar(), que forma parte de las barras del sistema.

Puedes obtener más información para controlar las inserciones en Cómo mostrar el contenido de borde a borde en tu app y controlar las inserciones de ventana en Compose.

La barra de encabezado también se puede personalizar. Android 15 introdujo el tipo de apariencia APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND para que la barra de encabezado sea transparente y permita que las apps dibujen contenido personalizado dentro de ella.

Luego, las apps se vuelven responsables de aplicar el diseño a la parte superior de su contenido para que se vea como la barra de leyendas (fondo, contenido personalizado, etcétera), con la excepción de los elementos de leyendas del sistema (botones de cerrar y maximizar), que el sistema dibuja en la barra de leyendas transparente en la parte superior de la app.

Las apps pueden activar o desactivar la apariencia de los elementos del sistema dentro del subtítulo para los temas claro y oscuro con APPEARANCE_LIGHT_CAPTION_BARS, de manera similar a como se activa o desactiva la barra de estado y la barra de navegación.

Android 15 también introdujo el método WindowInsets#getBoundingRects(), que permite que las apps inspeccionen las inserciones de la barra de leyendas con más detalle. Las apps pueden diferenciar entre las áreas en las que el sistema dibuja elementos del sistema y las áreas no utilizadas en las que las apps pueden colocar contenido personalizado sin superponer elementos del sistema.

La lista de objetos Rect que devuelve la API indica las regiones del sistema que se deben evitar. El espacio restante (que se calcula restando los rectángulos de los Insets de la barra de título) es donde la app puede dibujar sin superponerse con los elementos del sistema y con la capacidad de recibir entrada.

Chrome antes y después de implementar encabezados personalizados.
Figura 6: Chrome antes y después de implementar encabezados personalizados.

Para establecer rectángulos de exclusión de gestos del sistema para un encabezado personalizado, implementa lo siguiente en tu vista o elemento componible:

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

Compatibilidad con instancias y tareas múltiples

La multitarea es la base de las ventanas de escritorio, y permitir varias instancias de tu app puede aumentar considerablemente la productividad de los usuarios.

Android 15 introduce PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, que las apps pueden establecer para especificar que se debe mostrar la IU del sistema para que la app permita que se inicie como varias instancias.

Puedes declarar PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI en el AndroidManifest.xml de tu app dentro de la etiqueta <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>

Administra instancias de apps con gestos de arrastre

En el modo multiventana, los usuarios pueden iniciar una nueva instancia de la app arrastrando un elemento de vista fuera de la ventana de la app. Los usuarios también pueden mover elementos entre instancias de la misma app.

Figura 7: Arrastra una pestaña fuera de la ventana del escritorio para iniciar una nueva instancia de Chrome.

Android 15 introduce dos marcas para personalizar el comportamiento de arrastre:

  • DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG: Indica que un arrastre no controlado se debe delegar al sistema para que se inicie si ninguna ventana visible controla la acción de soltar. Cuando se usa esta marca, el llamador debe proporcionar ClipData con un ClipData.Item que contenga un IntentSender inmutable para una actividad que se lanzará (consulta ClipData.Item.Builder#setIntentSender()). El sistema puede lanzar el intent o no según factores como el tamaño de pantalla o el modo de ventanas actuales. Si el sistema no inicia el intent, este se cancela a través del flujo de arrastre normal.

  • DRAG_FLAG_GLOBAL_SAME_APPLICATION: Indica que una operación de arrastre puede cruzar los límites de la ventana (para varias instancias de la misma aplicación).

    Cuando se llama a [startDragAndDrop()][20] con este parámetro de configuración, solo las ventanas visibles que pertenecen a la misma aplicación pueden participar en la operación de arrastre y recibir el contenido arrastrado.

En el siguiente ejemplo, se muestra cómo usar estas marcas con 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: Mover una pestaña entre dos instancias de la app de Chrome

Optimizaciones adicionales

Personaliza los inicios de apps y la transición de apps de ventanas en computadoras a pantalla completa.

Cómo especificar el tamaño y la posición predeterminados

No todas las apps, incluso si se pueden cambiar de tamaño, necesitan una ventana grande para ofrecer valor al usuario. Puedes usar el método ActivityOptions#setLaunchBounds() para especificar un tamaño y una posición predeterminados cuando se inicia una actividad.

A continuación, se muestra un ejemplo de cómo establecer límites de inicio para una actividad:

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

Cómo ingresar a pantalla completa desde el espacio de trabajo

Las apps pueden pasar a pantalla completa llamando a Activity#requestFullScreenMode(). El método muestra la app en pantalla completa directamente desde la ventana del escritorio.

Para solicitar el modo de pantalla completa desde una actividad, usa el siguiente código:

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