Prendre en charge le fenêtrage de bureau

Le fenêtrage du bureau permet aux utilisateurs d'exécuter plusieurs applications simultanément dans des fenêtres d'application redimensionnables pour une expérience polyvalente semblable à celle d'un ordinateur.

La figure 1 illustre l'organisation de l'écran lorsque le fenêtrage du bureau est activé. Points à noter :

  • Les utilisateurs peuvent exécuter plusieurs applications côte à côte simultanément.
  • La barre des tâches est en position fixe en bas de l'écran et affiche les applications en cours d'exécution. Les utilisateurs peuvent épingler des applications pour y accéder rapidement.
  • Une nouvelle barre d'en-tête personnalisable décore le haut de chaque fenêtre avec des commandes telles que "Réduire" et "Agrandir".
Écran de tablette affichant plusieurs applications exécutées dans des fenêtres redimensionnables avec une barre des tâches en bas.
Figure 1 : Fenêtrage du bureau sur une tablette.

Par défaut, les applications s'ouvrent en plein écran sur les tablettes Android. Pour lancer une application en mode fenêtrage du bureau, appuyez de manière prolongée sur la poignée de la fenêtre en haut de l'écran et faites-la glisser dans l'UI, comme illustré sur la figure 2.

Lorsqu'une application est ouverte dans le fenêtrage de bureau, les autres applications s'ouvrent également dans des fenêtres de bureau.

Figure 2 : Appuyez de manière prolongée sur la poignée de la fenêtre de l'application, puis faites-la glisser pour accéder à la gestion des fenêtres sur le bureau.

Les utilisateurs peuvent également appeler le fenêtrage du bureau à partir du menu qui s'affiche sous le handle de la fenêtre lorsqu'ils appuient ou cliquent sur le handle, ou en utilisant le raccourci clavier Touche Meta (Windows, Commande ou Recherche) + Ctrl + Flèche vers le bas.

Les utilisateurs quittent le mode fenêtré sur ordinateur en fermant toutes les fenêtres actives ou en saisissant le handle de fenêtre en haut d'une fenêtre de bureau et en faisant glisser l'application vers le haut de l'écran. Le raccourci clavier Meta+H permet également de quitter le mode fenêtré et d'exécuter à nouveau les applications en plein écran.

Pour revenir à l'affichage des fenêtres sur le bureau, appuyez ou cliquez sur la vignette de l'espace de bureau dans l'écran "Récents".

Redimensionnement et mode de compatibilité

Dans le fenêtrage de bureau, les applications dont l'orientation est verrouillée sont librement redimensionnables. Cela signifie que même si une activité est verrouillée en orientation portrait, les utilisateurs peuvent toujours redimensionner l'application en une fenêtre d'orientation paysage.

Figure 3 : Redimensionnement de la fenêtre d'une application limitée au mode portrait en mode paysage.

Les applications déclarées comme non redimensionnables (c'est-à-dire resizeableActivity = false) voient leur UI mise à l'échelle tout en conservant les mêmes proportions.

Figure 4 : L'UI d'une application non redimensionnable est mise à l'échelle lorsque la fenêtre est redimensionnée.

Les applications d'appareil photo qui verrouillent l'orientation ou sont déclarées comme non redimensionnables bénéficient d'un traitement spécial pour leur viseur : la fenêtre est entièrement redimensionnable, mais le viseur conserve le même format. En supposant que les applications s'exécutent toujours en mode Portrait ou Paysage, elles codent en dur ou font des hypothèses qui entraînent des erreurs de calcul de l'orientation ou du format de l'image prévisualisée ou capturée, ce qui donne des images étirées, de travers ou à l'envers.

Tant que les applications ne sont pas prêtes à implémenter des viseurs d'appareil photo entièrement responsifs, le traitement spécial offre une expérience utilisateur plus basique qui atténue les effets que de mauvaises hypothèses peuvent entraîner.

Pour en savoir plus sur le mode de compatibilité des applications de caméras, consultez Mode de compatibilité avec les appareils.

Figure 5 : Le viseur de la caméra conserve ses proportions lorsque la fenêtre est redimensionnée.

Encarts d'en-tête personnalisables

Toutes les applications s'exécutant dans un système de fenêtres de bureau disposent d'une barre d'en-tête, même en mode immersif. Vérifiez que le contenu de votre application n'est pas masqué par la barre d'en-tête. La barre d'en-tête est un type d'encart de barre de légende : WindowInsets.Companion.captionBar(); dans les vues, WindowInsets.Type.captionBar(), qui fait partie des barres système.

Pour en savoir plus sur la gestion des encarts, consultez Afficher le contenu bord à bord dans votre application et gérer les encarts de fenêtre dans Compose.

La barre d'en-tête est également personnalisable. Android 15 a introduit le type d'apparence APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND pour rendre la barre d'en-tête transparente et permettre aux applications de dessiner du contenu personnalisé à l'intérieur de la barre d'en-tête.

Les applications sont alors responsables de la mise en forme de la partie supérieure de leur contenu pour qu'elle ressemble à la barre de légende (arrière-plan, contenu personnalisé, etc.), à l'exception des éléments de légende système (boutons de fermeture et d'agrandissement), qui sont dessinés par le système sur la barre de légende transparente au-dessus de l'application.

Les applications peuvent activer ou désactiver l'apparence des éléments système dans la légende pour les thèmes clair et sombre à l'aide de APPEARANCE_LIGHT_CAPTION_BARS, de la même manière que la barre d'état et la barre de navigation.

Android 15 a également introduit la méthode WindowInsets#getBoundingRects(), qui permet aux applications d'inspecter plus en détail les encarts de la barre de légende. Les applications peuvent faire la différence entre les zones où le système dessine des éléments système et les zones inutilisées où les applications peuvent placer du contenu personnalisé sans chevaucher les éléments système.

La liste des objets Rect renvoyée par l'API indique les régions système à éviter. L'espace restant (calculé en soustrayant les rectangles des Insets de la barre de légende) est l'endroit où l'application peut dessiner sans chevaucher les éléments système et avec la possibilité de recevoir des entrées.

Chrome avant et après l'implémentation d'en-têtes personnalisés.
Figure 6. Chrome avant et après l'implémentation d'en-têtes personnalisés.

Pour définir des rectangles d'exclusion des gestes système pour un en-tête personnalisé, implémentez ce qui suit dans votre vue ou composable :

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

Compatibilité avec le multitâche et les instances multiples

Le multitâche est au cœur du fenêtrage sur ordinateur. Autoriser plusieurs instances de votre application peut considérablement augmenter la productivité des utilisateurs.

Android 15 introduit PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, que les applications peuvent définir pour spécifier que l'UI système doit être affichée pour que l'application puisse être lancée en tant qu'instances multiples.

Vous pouvez déclarer PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI dans le AndroidManifest.xml de votre application au sein du 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>

Gérer les instances d'application avec des gestes de déplacement

En mode multifenêtre, les utilisateurs peuvent démarrer une nouvelle instance d'application en faisant glisser un élément de vue hors de la fenêtre de l'application. Les utilisateurs peuvent également déplacer des éléments entre des instances de la même application.

Figure 7 : Démarrez une nouvelle instance de Chrome en faisant glisser un onglet hors de la fenêtre du bureau.

Android 15 introduit deux indicateurs permettant de personnaliser le comportement de déplacement :

  • DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG : indique qu'un déplacement non géré doit être délégué au système pour être démarré si aucune fenêtre visible ne gère le dépôt. Lorsque cet indicateur est utilisé, l'appelant doit fournir ClipData avec un ClipData.Item contenant un IntentSender immuable pour une activité à lancer (voir ClipData.Item.Builder#setIntentSender()). Le système peut lancer ou non l'intention en fonction de facteurs tels que la taille de l'écran ou le mode de fenêtrage actuels. Si le système ne lance pas l'intent, celui-ci est annulé à l'aide du flux de déplacement normal.

  • DRAG_FLAG_GLOBAL_SAME_APPLICATION : indique qu'une opération de déplacement peut traverser les limites de la fenêtre (pour plusieurs instances de la même application).

    Lorsque [startDragAndDrop()][20] est appelé avec ce flag défini, seules les fenêtres visibles appartenant à la même application peuvent participer à l'opération de déplacement et recevoir le contenu déplacé.

L'exemple suivant montre comment utiliser ces indicateurs avec 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
}
Figure 8. Déplacer un onglet entre deux instances de l'application Chrome.

Optimisations supplémentaires

Personnalisez le lancement des applications et passez du mode fenêtrage du bureau au plein écran.

Spécifier la taille et la position par défaut

Toutes les applications, même redimensionnables, n'ont pas besoin d'une grande fenêtre pour offrir de la valeur aux utilisateurs. Vous pouvez utiliser la méthode ActivityOptions#setLaunchBounds() pour spécifier une taille et une position par défaut lorsqu'une activité est lancée.

Voici un exemple de définition des limites de lancement pour une activité :

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

Passer en mode plein écran depuis l'espace de bureau

Les applications peuvent passer en plein écran en appelant Activity#requestFullScreenMode(). La méthode affiche l'application en plein écran directement à partir du fenêtrage du bureau.

Pour demander le mode plein écran à partir d'une activité, utilisez le code suivant :

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