Améliorez l'expérience photo sur les appareils pliables

1. Avant de commencer

Pourquoi choisir les appareils pliables ?

Les appareils pliables constituent des innovations qu'on ne voit qu'une fois par génération. Ils offrent des expériences exceptionnelles et des occasions uniques d'enchanter vos utilisateurs grâce à des fonctionnalités différenciées comme l'interface utilisateur à plat pour une utilisation mains libres.

Prérequis

  • Connaissances de base sur le développement des applications Android
  • Connaissances de base sur le framework d'injection de dépendances avec Hilt

Objectifs de l'atelier

Dans cet atelier de programmation, vous allez créer une application d'appareil photo dont les mises en page sont optimisées pour les appareils pliables.

Capture d'écran de l'application en cours d'exécution

Vous commencez avec une application d'appareil photo basique qui ne réagit pas aux positions de l'appareil ou qui n'exploite pas la caméra arrière améliorée pour prendre des selfies de meilleure qualité. Vous modifiez le code source de façon à déplacer l'aperçu vers le plus petit écran quand l'appareil est déplié et à obtenir une réaction quand le téléphone est défini en mode sur table.

Bien que l'application d'appareil photo soit le cas d'utilisation le plus pratique pour cette API, les deux fonctionnalités que vous allez apprendre dans cet atelier de programmation peuvent être appliquées à n'importe quelle application.

Points abordés

  • Utiliser Jetpack WindowManager pour obtenir une réaction aux changements de position
  • Déplacer votre application vers l'écran le plus petit d'un appareil pliable

Ce dont vous aurez besoin

  • Une version récente d'Android Studio
  • Un appareil pliable ou un émulateur d'appareil pliable

2. Configuration

Obtenir le code de démarrage

  1. Si Git est installé, vous pouvez simplement exécuter la commande ci-dessous. Pour vérifier si Git est installé, saisissez git --version dans le terminal ou la ligne de commande, et vérifiez qu'il s'exécute correctement.
git clone https://github.com/android/large-screen-codelabs.git
  1. Facultatif : Si vous n'avez pas accès à Git, cliquez sur le bouton ci-dessous pour télécharger l'ensemble du code de cet atelier de programmation : .

Ouvrir le premier module

  • Dans Android Studio, ouvrez le premier module sous /step1.

Capture d'écran d'Android Studio montrant le code en lien avec cet atelier de programmation

Si vous êtes invité à utiliser la dernière version de Gradle, continuez et mettez-le à jour.

3. Exécuter et observer

  1. Exécutez le code sur le module step1.

Comme vous pouvez le voir, il s'agit d'une application d'appareil photo simple. Vous pouvez alterner caméra frontale et caméra arrière, et ajuster le format. Toutefois, le premier bouton en partant de la gauche n'a aucun effet, mais il va être le point d'entrée du mode Selfie avec la caméra arrière.

Capture d'écran de l'application avec mise en surbrillance de l'icône du mode Selfie avec la caméra arrière

  1. Placez maintenant l'appareil dans une position semi-ouverte, dans laquelle la charnière n'est pas complètement à plat ou fermée, mais forme un angle de 90 degrés.

Comme vous pouvez le voir, l'application ne répond pas aux différentes positions de l'appareil et la mise en page ne change donc pas. La charnière reste ainsi au milieu du viseur.

4. Découvrir Jetpack WindowManager

La bibliothèque Jetpack WindowManager aide les développeurs d'applications à créer des expériences optimisées pour les appareils pliables. Elle contient la classe FoldingFeature, qui décrit le pli d'un écran flexible ou la charnière entre deux écrans physiquement distincts. Son API permet d'accéder à des informations importantes concernant l'appareil :

La classe FoldingFeature contient des informations supplémentaires, comme occlusionType() ou isSeparating(), mais nous ne les étudierons pas en détail dans cet atelier de programmation.

À partir de la version 1.1.0-beta01, la bibliothèque utilise l'API WindowAreaController, qui active le mode d'affichage arrière afin de déplacer la fenêtre actuelle vers l'écran aligné avec la caméra arrière, ce qui est idéal pour prendre des selfies avec la caméra arrière et pour de nombreux autres cas d'utilisation !

Ajouter des dépendances

  • Pour utiliser Jetpack WindowManager dans votre application, vous devez ajouter les dépendances suivantes au fichier build.gradle au niveau du module :

step1/build.gradle

def work_version = '1.1.0-beta01'
implementation "androidx.window:window:$work_version"
implementation "androidx.window:window-java:$work_version"
implementation "androidx.window:window-core:$work_version"

Vous pouvez maintenant accéder aux classes FoldingFeature et WindowAreaController dans votre application. Elles vous permettent de créer une expérience photo exceptionnelle sur les appareils pliables !

5. Implémenter le mode Selfie avec la caméra arrière

Commencez par le mode d'affichage arrière. L'API qui permet ce mode est WindowAreaControllerJavaAdapter, qui requiert un Executor et renvoie une WindowAreaSession qui stocke l'état actuel. Cette WindowAreaSession doit être conservée quand votre Activity est détruite et recréée. Vous devez donc la stocker dans un ViewModel pour la stocker en sécurité en cas de modifications de configuration.

  1. Déclarez ces variables dans la propriété MainActivity :

step1/MainActivity.kt

private lateinit var windowAreaController: WindowAreaControllerJavaAdapter
private lateinit var displayExecutor: Executor
  1. Initialisez-les ensuite dans la méthode onCreate() :

step1/MainActivity.kt

windowInfoTracker = WindowInfoTracker.getOrCreate(this)
displayExecutor = ContextCompat.getMainExecutor(this)
windowAreaController = WindowAreaControllerJavaAdapter(WindowAreaController.getOrCreate())

Votre Activity est maintenant prête à déplacer le contenu vers l'écran plus petit, mais vous devez stocker la session.

  1. Pour stocker la session, ouvrez CameraViewModel et déclarez cette variable à l'intérieur :

step1/CameraViewModel.kt

var rearDisplaySession: WindowAreaSession? = null
        private set

rearDisplaySession doit être une variable, car elle change chaque fois que vous en créez une. Toutefois, vous devez veiller à ce qu'elle ne puisse pas être mise à jour de l'extérieur puisque vous allez créer une méthode qui la met à jour en cas de besoin.

  1. Copiez ce code dans CameraViewModel :

step1/CameraViewModel.kt

fun updateSession(newSession: WindowAreaSession? = null) {
        rearDisplaySession = newSession
}

Cette méthode est appelée chaque fois que votre code doit mettre à jour la session. Il est important de l'encapsuler dans un point d'accès unique.

L'API Rear Display repose sur une approche d'écouteur : quand vous demandez à déplacer le contenu vers l'écran le plus petit, vous ouvrez une session qui est renvoyée via la méthode onSessionStarted() de l'écouteur. Quand vous souhaitez revenir à l'écran intérieur (plus grand), vous fermez la session et vous obtenez une confirmation dans la méthode onSessionEnded(). Ces méthodes vous permettent de mettre à jour la session rearDisplaySession dans CameraViewModel. Pour créer cet écouteur, vous devez implémenter l'interface WindowAreaSessionCallback.

  1. Modifiez la déclaration MainActivity de sorte qu'elle implémente l'interface WindowAreaSessionCallback :

step1/MainActivity.kt

class MainActivity : AppCompatActivity(), WindowAreaSessionCallback

Implémentez maintenant les méthodes onSessionStarted et onSessionEnded dans MainActivity. Pour la première, enregistrez la session WindowAreaSession et, dans la deuxième, réinitialisez-la à la valeur null. Cela est particulièrement important puisque la présence de la session WindowAreaSession vous permet de déterminer si vous devez lancer une session ou en fermer une.

step1/MainActivity.kt

override fun onSessionEnded() {
    viewModel.updateSession(null)
}

override fun onSessionStarted(session: WindowAreaSession) {
    viewModel.updateSession(session)
}
  1. Dans le fichier MainActivity.kt, créez la dernière partie de code nécessaire au fonctionnement de cette API :

step1/MainActivity.kt

private fun startRearDisplayMode() {
   if (viewModel.rearDisplaySession != null) {
      viewModel.rearDisplaySession?.close()
   } else {
      windowAreaController.startRearDisplayModeSession(
         this,
         displayExecutor,
         this
      )
   }
}

Comme indiqué plus tôt, pour savoir quelle mesure prendre, vous devez vérifier la présence de rearDisplaySession dans CameraViewModel. Si elle n'a pas la valeur null, la session est déjà en cours et elle se ferme. En revanche, si elle a la valeur null, utilisez windowAreaController pour lancer une nouvelle session. L'objet Activity est alors transmis deux fois. La première occurrence sert de Context et la deuxième d'écouteur WindowAreaSessionCallback.

  1. Créez et exécutez l'application. Si vous dépliez ensuite votre appareil et que vous appuyez sur le bouton d'affichage arrière, un message tel que celui-ci s'affiche :

Capture d'écran de la requête d'un utilisateur montrant que le mode d'affichage arrière est lancé.

  1. Cliquez sur Changer d'écran maintenant et constatez le déplacement de votre contenu vers l'écran extérieur !

6. Implémenter le mode sur table

Il est maintenant temps de rendre votre application pliable : déplacez votre contenu sur le côté ou au-dessus de la charnière de l'appareil en fonction de l'orientation du pli. Pour ce faire, vous allez écrire du code dans l'objet FoldingStateActor pour que votre code soit distinct de l'objet Activity dans un souci de lisibilité.

L'élément principal de cette API est l'interface WindowInfoTracker, qui est créée à l'aide d'une méthode statique nécessitant un objet Activity :

step1/CameraCodelabDependencies.kt

@Provides
fun provideWindowInfoTracker(activity: Activity) =
        WindowInfoTracker.getOrCreate(activity)

Vous n'avez pas besoin d'écrire ce code puisqu'il est déjà présent, mais il est utile de comprendre la conception de l'interface WindowInfoTracker.

  1. Écoutez les modifications des fenêtres dans la méthode onResume() de votre Activity :

step1/MainActivity.kt

lifecycleScope.launch {
    foldingStateActor.checkFoldingState(
         this@MainActivity,
         binding.viewFinder
    )
}
  1. Ouvrez maintenant le fichier FoldingStateActor puisqu'il est temps de remplir la méthode checkFoldingState().

Comme vous l'avez déjà vu, elle s'exécute lors de la phase RESUMED de votre Activity et elle utilise l'interface WindowInfoTracker pour écouter les modifications de mise en page.

step1/FoldingStateActor.kt

windowInfoTracker.windowLayoutInfo(activity)
      .collect { newLayoutInfo ->
         activeWindowLayoutInfo = newLayoutInfo
         updateLayoutByFoldingState(cameraViewfinder)
      }

À l'aide de l'interface WindowInfoTracker, vous pouvez appeler windowLayoutInfo() pour collecter un Flow de WindowLayoutInfo contenant toutes les informations disponibles dans l'objet DisplayFeature.

La dernière étape consiste à coder la réaction à ces modifications et à déplacer le contenu en conséquence. Vous devez le faire dans la méthode updateLayoutByFoldingState(), une étape à la fois.

  1. Veillez à ce que activityLayoutInfo contienne quelques propriétés DisplayFeature, dont au moins une propriété FoldingFeature. Dans le cas contraire, ne faites rien :

step1/FoldingStateActor.kt

val foldingFeature = activeWindowLayoutInfo?.displayFeatures
            ?.firstOrNull { it is FoldingFeature } as FoldingFeature?
            ?: return
  1. Calculez la position du pli pour vous assurer que la position de l'appareil modifie votre mise en page et qu'elle ne se trouve pas en dehors des limites de votre hiérarchie :

step1/FoldingStateActor.kt

val foldPosition = FoldableUtils.getFeaturePositionInViewRect(
            foldingFeature,
            cameraViewfinder.parent as View
        ) ?: return

Maintenant que vous êtes sûr que l'objet FoldingFeature modifie votre mise en page, vous devez déplacer votre contenu.

  1. Vérifiez que l'objet FoldingFeature a la valeur HALF_OPEN. Autrement, restaurez simplement la position de votre contenu. Si la valeur est HALF_OPEN, effectuez une autre vérification et agissez selon l'orientation du pli :

step1/FoldingStateActor.kt

if (foldingFeature.state == FoldingFeature.State.HALF_OPENED) {
    when (foldingFeature.orientation) {
        FoldingFeature.Orientation.VERTICAL -> {
            cameraViewfinder.moveToRightOf(foldPosition)
        }
        FoldingFeature.Orientation.HORIZONTAL -> {
            cameraViewfinder.moveToTopOf(foldPosition)
        }
    }
} else {
    cameraViewfinder.restore()
}

Si le pli a la valeur VERTICAL, déplacez votre contenu vers la droite. Autrement, placez-le au-dessus de la position du pli.

  1. Créez et exécutez votre application, puis dépliez votre appareil et placez-le en mode sur table pour voir le contenu se déplacer en conséquence !

7. Félicitations

Dans cet atelier de programmation, vous avez découvert les caractéristiques des appareils pliables, les changements de position et l'API Rear Display.

Complément d'informations

Référence