
Migrer vers Android API 35 : guide rapide et minimaliste
Vous devez migrer votre application Android vers l’API 35 ? Ce guide vous explique comment réussir cette mise à jour avec un minimum d’efforts, tout en respectant les exigences Google Play et en garantissant une expérience utilisateur acceptable sur les appareils modernes.
Depuis juillet 2025, Google impose aux développeurs de cibler l’API Android 15 (API 35) ou plus pour publier ou mettre à jour leurs applications. Si, comme moi, vous maintenez une application gratuite et stable, il est essentiel d’optimiser votre temps et d’éviter une refonte complète.
L’arrivée des écrans edge-to-edge, des coins arrondis et des encoches (notch) sur les appareils Android récents pose de nouveaux défis : chevauchement de la status bar, Floating Action Buttons (FAB) difficiles d’accès, contenu masqué par la navigation bar… Ce guide vous aide à adapter rapidement votre interface sans tout réécrire.
Caractéristiques de l’application Aesthetics Workout
Dans le cadre de cet article, je vous propose une étude de cas sur l’application Aesthetics Workout, qui est mature, stable et que je maintiens depuis plusieurs années.
Aesthetics Workout est une application Android de fitness qui permet de suivre ses entraînements, planifier des séances et visualiser ses progrès. Lancée en 2018, elle repose sur Material Design 1.0, et après tant d’années, l’application est stable mais peu adaptée aux exigences modernes.
Au fil des années, j’ai intégré les packages Androidx et Material Components pour assurer la compatibilité, mais j’ai choisi de conserver le design d’origine.
Configurer correctement votre émulateur Android API 35 ou plus
Avant de commencer les tests, il est primordial de configurer un émulateur Android qui reflète les spécificités des appareils modernes pour lesquels les contraintes edge-to-edge sont les plus fortes.
Pour cela, vous avez deux options :
- Créer un émulateur Android 15 ou plus récent avec un skin adapté aux écrans arrondis et aux encoches comme le Pixel 7 pro,
- Activer les encoches dans les paramètres des « options pour les développeurs ».
Problèmes rencontrés avec la migration Android API 35
Les premiers tests sur l’émulateur Android 16 (API 36) révèlent plusieurs limites du kit graphique historique qui ne sont pas compatibles avec les exigences modernes :
- Les boutons flottants d’action sont difficiles à atteindre à cause de la navigation bar.
- Les encoches (notch) des caméras frontales masquent des éléments de l’interface.
- L’app bar est chevauchée par la status bar, rendant l’affichage confus.
Pourquoi la refonte complète n’est pas réaliste ?
La documentation officielle Android recommande une refonte de l’interface pour gérer l’edge-to-edge et les écrans arrondis. Mais avec des dizaines d’activités et de fragments, l’effort serait disproportionné pour une application 100 % gratuite et sans revenus publicitaires.
Pour respecter l’API cible Android 15 et offrir une expérience utilisateur correcte, il faudrait prendre en compte les spécificités des nouveaux appareils à plusieurs niveaux :
- Ajouter un padding au dessus de l’app bar pour éviter le chevauchement.
- Repositionner les FAB pour les rendre accessibles.
- Adapter les RecyclerView pour que le contenu ne soit pas masqué par la navigation bar ou l’encoche.
- Ajuster les BottomSheet pour garantir leur visibilité.
Diagnostic préliminaire
Pour évaluer les modifications nécessaires, j’ai parcouru l’ensemble de l’application et testé la solution la plus simple : ajouter un padding sur le conteneur principal de chaque activité grâce à la méthode setOnApplyWindowInsetsListener
. En utilisant ce callback, je demande au système de déterminer les marges internes nécessaires en prenant en compte les encoches WindowInsets.Type.displayCutout()
et les barres système WindowInsets.Type.systemBars()
.

Comme vous le voyez sur l’image précédente, je me retrouve avec une barre blanche en haut et en bas de l’écran lorsque l’écran est en mode portrait, et un espace blanc à gauche et à droite lorsque l’écran est en mode paysage. Ce n’est pas idéal, mais cela permet de résoudre la majorité des problèmes de chevauchement et de visibilité :
- Les FAB sont complètement visibles et accessibles.
- L’app bar est positionnée sous la status bar.
Pour ces activités, il me restera à trouver une solution pour gérer les espacements blancs pour rendre l’interface plus esthétique.
Malheureusement, cette solution ne fonctionne pas pour l’activité la plus complexe (celle de du suivi de l’entraînement en temps réel), car l’interface est trop chargée, et le padding engendre des problèmes avec le BottomSheetBehavior
. Même en appliquant l’attribut fitSystemWindows="true"
, les composants ne s’ajustent pas correctement.
Pour cette dernière, il faudra envisager une refonte partielle de l’interface graphique sans dégrader l’expérience utilisateur.
Appliquer un padding et un background dynamique aux conteneurs roots
Après le diagnostic préliminaire, j’ai constaté qu’en appliquant un padding sur le conteneur root, je règle 95% des problèmes d’accessibilité et de chevauchement des composants graphiques. Il est donc probable que cette solution suffise également pour la majorité de vos activités. Je vous propose donc de découvrir en détail comment procéder.
Étape 1 : Ajouter un padding au conteneur root
Cette première étape est simple et rapide. Elle est d’ailleurs recommandée par la documentation officielle Android. Il suffit d’ajouter un listener pour les WindowInsets
dans le conteneur root de chaque activité.
Pour cela, j’ai créer une classe utilitaire EdgeToEdgeUtil
qui applique la configuration commune à toutes les activités. Voici le code :
package com.aesthetics.workout.core.graphics.ui;
import android.app.Activity;
import android.view.View;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsCompat;
public class EdgeToEdgeUtil {
/**
* Configure activity to have same appearance of non edge to edge device.
*
* @param activity The current activity.
* @param rootViewId The root view id.
*/
public static void configureEdgeToEdgeForStandardActivity(Activity activity, int rootViewId) {
// Setup fitSystemWindows to false
WindowCompat.setDecorFitsSystemWindows(activity.getWindow(), false);
// Apply padding to root view
View rootView = activity.findViewById(rootViewId);
ViewCompat.setOnApplyWindowInsetsListener(
rootView,
(v, insets) -> {
// Get system bar inserts (status bar and navigation bar)
Insets inset = insets.getInsets(
WindowInsetsCompat.Type.systemBars()
| WindowInsetsCompat.Type.displayCutout()
); // <1>
// Apply padding to root view
v.setPadding(inset.left, inset.top, inset.right, inset.bottom);
// Return CONSUMED to prevent child view to manage insets
return insets;
}
);
}
}
On récupère les _window insets_ pour les barres système et les encoches grâce à la combinaison des deux flags.
Étape 2 : Ajouter un background dynamique
Un background dynamique pour reproduire le rendu classique
En appliquant un padding, selon la configuration de l’appareil, vous obtiendrez un espace blanc ou noir (selon le thème) au niveau des bordures de l’écran lorsque l’écran est en mode portrait ou paysage (en haut et en bas, ou à gauche et à droite selon l’orientation de l’écran). Pour éviter cet effet inesthétique, la solution la plus simple que j’ai trouvé consiste à appliquer un background dynamique.
Dans la plupart des cas, le conteneur root de vos activités est un CoordinatorLayout
ou ConstraintLayout
. Il contient généralement un AppBarLayout
en haut, éventuellement un BottomNavigationView
en bas, et un composant central (FrameLayout
, RecyclerView
, etc.). En définissant un background dynamique sur le conteneur root, il est possible d’attribuer une couleur spécifique dans les zones de padding, tout en conservant la couleur principale du thème sur le contenu central.
Cette approche permet de régler les effets indésirables du padding et de retrouver un rendu conforme aux standards Material Design 1 :
- Une status bar légèrement plus sombre que l’app bar.
- Une navigation bar adaptée (blanche ou noire selon l’appareil).
C’est d’ailleurs le rendu que j’avais sur l’émulateur Android 14 en ciblant l’API 34 (voir l’image suivante).

Créer un background dynamique
L’objectif est de créer un background dynamique qui utilise la couleur de fond @color/colorPrimaryDark
pour la zone supérieure (status bar) et @android:color/white
ou @android:color/black
pour les zones latérales et inférieures. Pour cela, la solution la plus souple consiste à générer un BitmapDrawable
créé dynamiquement à partir des informations de l’écran, des window insets et des couleurs du thème.
package com.aesthetics.workout.core.graphics.ui;
import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.DisplayMetrics;
import android.view.View;
+import androidx.core.content.ContextCompat;
import androidx.core.graphics.Insets;
// ...
+import com.aesthetics.workout.R;
+
public class EdgeToEdgeUtil {
+ public static Drawable getBackgroundDrawable(Activity activity, Insets insets) {
+ // Compute screen size
+ DisplayMetrics dm = new DisplayMetrics();
+ activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
+
+ // Create bitmap
+ Bitmap bitmap = Bitmap.createBitmap(
+ dm.widthPixels,
+ dm.heightPixels,
+ Bitmap.Config.ARGB_8888
+ );
+ Canvas canvas = new Canvas(bitmap);
+
+ // Draw background
+ canvas.drawColor(Color.TRANSPARENT);
+
+ Paint paint = new Paint();
+ paint.setStyle(Paint.Style.FILL);
+
+ // Draw black rectangles
+ // - left
+ paint.setColor(Color.BLACK);
+ canvas.drawRect(0, 0, insets.left, dm.heightPixels, paint);
+ // - right
+ canvas.drawRect(
+ dm.widthPixels - insets.right, 0, dm.widthPixels, dm.heightPixels, paint
+ );
+ // - bottom
+ canvas.drawRect(
+ insets.left, dm.heightPixels - insets.bottom, dm.widthPixels - insets.right, dm.heightPixels, paint
+ );
+
+ // Draw top
+ paint.setColor(ContextCompat.getColor(activity, R.color.colorPrimaryDark));
+ canvas.drawRect(insets.left, 0, dm.widthPixels - insets.right, insets.top, paint);
+
+ return new BitmapDrawable(activity.getResources(), bitmap);
+ }
+
/**
* Configure activity to have same appearance of non edge to edge device.
// ...
Lorsque vous appliquez ce drawable comme background du conteneur root, seules les zones de padding seront colorées, tandis que le contenu central conservera la couleur principale du thème (centre transparent). Cela permet d’obtenir un rendu graphique conforme aux précédentes versions.
Appliquer le background dynamique
Pour appliquer ce background dynamique, nous devons obligatoirement le faire via le code Java, car les XML de layout attendent un drawable statique. J’ai donc choisi de l’intégré à la classe utilitaire EdgeToEdgeUtil
que j’ai créée précédemment. Voici comment procéder :
// ...
// Apply padding to root view
v.setPadding(inset.left, inset.top, inset.right, inset.bottom);
+ // Configure background on root view to simulate hold device style using dynamic background
+ ViewCompat.setBackground(rootView, getBackgroundDrawable(activity, inset));
+
// Return CONSUMED to prevent child view to manage insets
return insets;
}
// ...
Les mauvaises surprises de la migration vers Android API 36
Comme mentionné précédemment, j’ai choisi de cibler l’API 36 pour anticiper les futures exigences de Google Play. Cependant, cette migration a révélé un problème inattendu : la fin du support du onBackPressed()
sur les appareils utilisant la nouvelle API de navigation.
Le système de navigation a évolué pour supporter les gestes de navigation modernes, ce qui a rendu l’implémentation de la navigation arrière plus complexe. En effet, le comportement par défaut de mon émulateur Android 16 ne permet plus d’utiliser onBackPressed()
pour gérer la navigation arrière.
Heureusement, il existe une solution simple pour contourner ce problème. Il suffit d’appeler l’ancienne méthode onBackPressed()
dans le callback getOnBackInvokedDispatcher().registerOnBackInvokedCallback
pour les appareils API 33 et supérieurs. Voici le code source que j’ai ajouté :
// ...
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
OnBackInvokedDispatcher.PRIORITY_DEFAULT,
this::onBackPressed
);
}
// ...
Conclusion
Migrer une application Android mature vers l’API 35 peut sembler intimidant face aux nouvelles exigences edge-to-edge et aux écrans arrondis. Pourtant, comme nous l’avons vu, il est souvent possible de résoudre les problèmes avec des solutions simples : l’ajout de padding et d’un background dynamique permet de garantir l’accessibilité et la compatibilité sans devoir reprendre toutes les Activity
.
Chaque application ayant ses spécificités, il est probable que vous rencontriez d’autres défis ou trouviez des astuces complémentaires. N’hésitez pas à partager vos retours d’expérience, vos questions ou vos solutions dans les commentaires. Je suis curieux de découvrir comment vous avez abordé la migration de votre application vers l’API 35 ou 36.