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 de bureau.

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 située 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".
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, puis faites-la glisser dans l'interface utilisateur, comme illustré à la figure 2.

Lorsqu'une application est ouverte en mode fenêtrage du 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 et faites-la glisser pour activer le fenêtrage du bureau.

Les utilisateurs peuvent également activer le fenêtrage du bureau à partir du menu qui s'affiche sous la poignée de la fenêtre lorsque vous appuyez ou cliquez dessus, ou à l'aide du raccourci clavier Meta key (Windows, Command, or Search) + Ctrl + Down.

Pour quitter le fenêtrage du bureau, les utilisateurs peuvent fermer toutes les fenêtres actives ou saisir la poignée de la fenêtre en haut d'une fenêtre de bureau et faire glisser l'application en haut de l'écran. Le raccourci clavier Meta + H permet également de quitter le fenêtrage du bureau et d’exécuter à nouveau les applications en plein écran.

Pour revenir au fenêtrage du bureau, appuyez ou cliquez sur la vignette "Espace de bureau" dans l'écran "Récents".

Optimiser la mise en page de votre application pour un environnement semblable à celui d'un ordinateur de bureau

La conception pour une expérience sur ordinateur de bureau peut être très différente de la conception mobile en raison de l'espace d'écran plus important, de la précision de la saisie au clavier et à la souris, et de l'attente d'une productivité élevée.

Jetpack WindowManager fournit une API qui aide les développeurs à décider quand afficher une interface utilisateur de bureau, qui présente généralement une densité d'informations plus élevée, des schémas de navigation différents et des interactions optimisées avec la souris.

lifecycleScope.launch(Dispatchers.Main) {
    lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
        windowInfoTracker.windowEngagementInfo(this@DesktopWindowingActivity)
            .collect { windowEngagementInfo ->
                if(windowEngagementInfo.hasEngagementMode(WindowEngagementInfo.EngagementMode.PRECISE_POINTER)){
                    showDesktopOptimizedUI()
                }else {
                    showTouchOptimizedUI()
                }
        }
    }
}

Pour en savoir plus, consultez la section Concevoir pour un ordinateur de bureau.

Mode de redimensionnement et de compatibilité

En mode fenêtrage du bureau, les applications dont l'orientation est verrouillée sont librement redimensionnables. Cela signifie que même si une activité est verrouillée en mode Portrait, les utilisateurs peuvent toujours redimensionner l'application dans une fenêtre en mode Paysage.

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

L'interface utilisateur des applications déclarées comme non redimensionnables (c'est-à-dire resizeableActivity = false) est mise à l'échelle tout en conservant les mêmes proportions.

Figure 4. : L'interface utilisateur 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 les mêmes proportions. 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 des proportions de l'aperçu ou de l'image capturée, ce qui donne des images étirées, latérales ou à l'envers.

En attendant que les applications soient prêtes à implémenter des viseurs d'appareil photo entièrement adaptatifs, 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 d'appareil photo, consultez la section Mode de compatibilité avec les appareils.

Figure 5. Le viseur de l'appareil photo conserve ses proportions lorsque la fenêtre est redimensionnée.

Marges d'en-tête personnalisables

Toutes les applications exécutées en mode fenêtrage du bureau comportent une barre d'en-tête, même en mode immersif. Vous pouvez personnaliser cette barre pour éviter que le contenu de votre application ne soit masqué et pour dessiner des éléments d'interface utilisateur personnalisés directement dans l'espace d'en-tête.

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.

Implémentation

Pour dessiner du contenu personnalisé dans la barre d'en-tête, vous devez d'abord rendre transparent l'arrière-plan de la barre d'en-tête. Pour cela, utilisez l' APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND avec l' WindowInsetsController.

window.insetsController?.setSystemBarsAppearance(
    WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND,
    WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND
)

Une fois la barre d'en-tête transparente, vous pouvez styliser la zone d'en-tête pour qu'elle corresponde à la conception de votre application. Utilisez WindowInsets.isCaptionBarVisible pour détecter si la barre est présente et appliquer la hauteur ou la marge intérieure appropriée à votre mise en page.

@OptIn(ExperimentalLayoutApi::class)
@Composable
fun CaptionBar() {
    if (WindowInsets.isCaptionBarVisible) {
        Row(
            modifier = Modifier
                .windowInsetsTopHeight(WindowInsets.captionBar)
                .fillMaxWidth()
                .background(if (isSystemInDarkTheme()) Color.White else Color.Black),
            horizontalArrangement = Arrangement.Center,
            verticalAlignment = Alignment.CenterVertically
        ) {
            Text(
                text = "Caption Bar Title",
                style = MaterialTheme.typography.titleMedium,
                modifier = Modifier.padding(4.dp)
            )
        }
    }
}

  • setSystemBarsAppearance(appearance,mask): configure le style visuel des barres système. Le premier paramètre définit les options d'apparence cibles, tandis que le second sert de masque pour contrôler les options spécifiques qui sont modifiées.

  • windowInsetsTopHeight(): définit automatiquement la hauteur de votre composable pour qu'elle corresponde à la barre d'en-tête du système, ce qui permet à votre arrière-plan personnalisé de remplir la zone de légende sans coder en dur les valeurs de pixels.

  • WindowInsets.captionBar : fournit les dimensions des commandes de fenêtrage du bureau (Fermer, Agrandir, etc.), ce qui permet à votre interface utilisateur de se mettre à l'échelle ou de se masquer automatiquement lorsque vous activez ou désactivez le fenêtrage du bureau.

Pour en savoir plus, consultez la section À propos des marges de fenêtre. En plus d'un titre, vous pouvez afficher d'autres éléments d'interface utilisateur dans la barre de légende, tels que des onglets (comme dans Google Chrome), des barres de recherche ou des avatars de profil.

Interface utilisateur

Pour éviter de chevaucher votre interface utilisateur avec les boutons système, Android 15 fournit la WindowInsets#getBoundingRects() méthode. La méthode renvoie une liste d'objets représentant les zones occupées par les éléments système.Rect Tout espace restant dans la barre de légende est une zone de sécurité où vous pouvez placer du contenu personnalisé en toute sécurité.

Basculez l'apparence des éléments de légende du système pour les thèmes clair et sombre à l'aide de APPEARANCE_LIGHT_CAPTION_BARS. Accédez aux marges à l'aide de WindowInsets.Companion.captionBar() dans Compose ou WindowInsets.Type.captionBar() dans Views.

Pour en savoir plus, consultez la section À propos des marges de fenêtre.

Compatibilité avec le multitâche et les instances multiples

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

À partir d'Android 15, vous pouvez utiliser PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI. En définissant cette propriété dans votre AndroidManifest.xml, vous spécifiez que l'interface utilisateur du système doit fournir des options (comme un bouton "Nouvelle fenêtre") pour que l'application soit lancée dans plusieurs instances.

<application>
    <property
        android:name="android.window.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI"
        android:value="true" />
</application>

Remarque : En mode fenêtrage du bureau et dans d'autres environnements multifenêtre, les nouvelles tâches s'ouvrent dans une nouvelle fenêtre. Vérifiez donc le parcours utilisateur chaque fois que votre application démarre plusieurs tâches.

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

En mode multifenêtre, les utilisateurs peuvent démarrer une nouvelle instance d'application en faisant glisser un élément d'interface utilisateur (tel qu'un onglet ou un document) hors de la fenêtre de l'application. Les utilisateurs peuvent également déplacer des éléments entre différentes 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 de bureau.

Transférer des données par glisser-déposer

Pour configurer un composable en tant que source de glissement pour le glisser-déposer multi-instance ce qui permet aux utilisateurs de faire glisser du contenu vers une autre instance de votre application ou de créer une nouvelle instance en déposant du contenu dans une zone vide de l'écran, utilisez le dragAndDropSource modificateur. Dans son lambda, renvoyez DragAndDropTransferData, en transmettant le ClipData qui contient les données à transférer, ainsi que des options pour configurer le comportement multi-instance.

Android 15 introduit deux options clés pour le fenêtrage de style bureau et les interactions multi-instance :

  • DRAG_FLAG_GLOBAL_SAME_APPLICATION: indique qu'une opération de glissement peut traverser les limites de la fenêtre (pour plusieurs instances de la même application). Lorsque startDragAndDrop() est appelé avec cette option définie, seules les fenêtres visibles appartenant à la même application peuvent participer à l'opération de glissement et recevoir le contenu déplacé.

Modifier.dragAndDropSource { _ ->
    DragAndDropTransferData(
        clipData = ClipData.newPlainText("label", "Your data"),
        flags = View.DRAG_FLAG_GLOBAL_SAME_APPLICATION
    )
}

Modifier.dragAndDropSource { _ ->
    val intent = Intent.makeMainActivity(activity.componentName).apply {
        putExtra("EXTRA_ITEM_ID", itemId)
        flags = Intent.FLAG_ACTIVITY_NEW_TASK or
                Intent.FLAG_ACTIVITY_MULTIPLE_TASK or
                Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT
    }

    val pendingIntent = PendingIntent.getActivity(
        activity, 0, intent, PendingIntent.FLAG_IMMUTABLE
    )

    val data = ClipData(
        "Item $itemId",
        arrayOf(ClipDescription.MIMETYPE_TEXT_INTENT),
        ClipData.Item.Builder().setIntentSender(pendingIntent.intentSender).build()
    )

    DragAndDropTransferData(
        clipData = data,
        flags = View.DRAG_FLAG_GLOBAL_SAME_APPLICATION or
                View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG,
    )
}

Recevoir les données transférées

Pour accepter les données d'une autre instance, utilisez le dragAndDropTarget modificateur. Vous devez demander explicitement des autorisations si les données proviennent d'une autre instance ou application.

Modifier.dragAndDropTarget(
    shouldStartDragAndDrop = { event ->
        event.toAndroidDragEvent().clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)
    },
    target = object : DragAndDropTarget {
        override fun onDrop(event: DragAndDropEvent): Boolean {
            requestDragAndDropPermissions(activity, event.toAndroidDragEvent())
            val clipData = event.toAndroidDragEvent().clipData
            val item = clipData?.getItemAt(0)?.text
            if (item != null) {
                // Process the dropped text item here
            }
            return item != null
        }
    }
)

Étapes clés :

  • Filtrer : utilisez shouldStartDragAndDrop pour vérifier si les données entrantes (type MIME) sont compatibles.
  • Autorisations : appelez requestDragAndDropPermissions(event) pour accéder aux données.
  • Gérer : extrayez les données dans le onDrop rappel.

Optimisations supplémentaires

Personnalisez les lancements d'applications et passez des applications du fenêtrage du bureau au plein écran.

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

Toutes les applications, même si elles sont redimensionnables, n'ont pas besoin d'une grande fenêtre pour offrir une valeur ajoutée à l'utilisateur. Vous pouvez utiliser la ActivityOptions#setLaunchBounds() méthode pour spécifier une taille et une position par défaut lors du lancement d'une activité.

Passer en 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.