Le flag de configuration se trouve dans FeatureFlags.java au sein du projet Launcher3 : ```
public static final BooleanFlag TASKBAR_ENABLED = getDebugFlag( "ENABLE_TASKBAR", false, "Enables system Taskbar on large screen devices.");
La classe `DeviceProfile.java` exploite ensuite ce flag pour déterminer si l'appareil est une tablette et si la Taskbar doit être instanciée : ```
isTablet = info.isTablet(windowBounds);
hasTaskbar = isTablet && ApiWrapper.TASKBAR_DRAWN_IN_PROCESS
&& FeatureFlags.TASKBAR_ENABLED.get();
if (hasTaskbar) {
taskbarHeight = res.getDimensionPixelSize(R.dimen.taskbar_size);
}
Détection tablette côté SystemUI
Dans SystemUI, la méthode setupTaskbarIfNeeded() décide s'il faut initialiser ou détruire la Taskbar en fonction de l'état tablette détecté. Lorsque la Taskbar est active, la barre de navigation classique est supprimée : ```
private boolean setupTaskbarIfNeeded() { if (mIsTablet) { removeNavigationBar(mContext.getDisplayId()); mTaskbarDelegate.init(mContext.getDisplayId()); return true; } else { mTaskbarDelegate.destroy(); return false; } }
La variable `mIsTablet` est assignée via : ```
mIsTablet = isTablet(mContext);
La détection repose sur la largeur minimale (smallest width) calculée à partir des dimensions de l'écran, définie dans Utilities.java : ```
@TargetApi(Build.VERSION_CODES.R) public static boolean isTablet(Context context) { WindowManager wm = context.getSystemService(WindowManager.class); Rect screenBounds = wm.getCurrentWindowMetrics().getBounds();
int minDim = Math.min(screenBounds.width(), screenBounds.height());
float smallestWidthDp = dpiFromPx(minDim,
context.getResources().getConfiguration().densityDpi);
return smallestWidthDp >= TABLET_MIN_DPS;
}
### Création conditionnelle de la NavigationBar
Lors de l'appel à `createNavigationBars()`, le système vérifie si la Taskbar a été initialisée. Si c'est le cas, la NavigationBar n'est pas créée sur l'écran par défaut : ```
public void createNavigationBars(boolean includeDefaultDisplay,
RegisterStatusBarResult result) {
updateAccessibilityButtonModeIfNeeded();
boolean skipDefaultNavbar = includeDefaultDisplay
&& setupTaskbarIfNeeded();
Display[] allDisplays = mDisplayManager.getDisplays();
for (Display display : allDisplays) {
if (skipDefaultNavbar || display.getDisplayId() != DEFAULT_DISPLAY) {
createNavigationBar(display, null, result);
}
}
}
Chaîne de démarrage de SystemUI
L'appel à createNavigationBars provient de StatusBar.java : ```
protected void buildNavigationBar(@Nullable RegisterStatusBarResult result) { mNavigationBarController.createNavigationBars(true, result); }
Cette méthode est invoquée à travers la séquence : `start()` → `createAndAddWindows()` → `makeStatusBarView()` → `buildNavigationBar()`. La classe `StatusBar` hérite de `SystemUI`, dont la méthode abstraite `start()` est le point d'entrée : ```
public abstract class SystemUI implements Dumpable {
protected final Context mContext;
public SystemUI(Context context) {
mContext = context;
}
public abstract void start();
}
Déclenchement depuis SystemServer
Le démarrage de SystemUI est lancé par SystemServer.java : ```
t.traceBegin("StartSystemUI"); try { startSystemUi(context, windowManagerF); } catch (Throwable e) { reportWtf("starting System UI", e); } t.traceEnd();
La méthode `startSystemUi()` récupère le composant de service via `PackageManagerInternal` : ```
private static void startSystemUi(Context context, WindowManagerService windowManager) {
PackageManagerInternal pmInternal = LocalServices.getService(PackageManagerInternal.class);
Intent systemUiIntent = new Intent();
systemUiIntent.setComponent(pmInternal.getSystemUiServiceComponent());
systemUiIntent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
context.startServiceAsUser(systemUiIntent, UserHandle.SYSTEM);
windowManager.onSystemUiStarted();
}
Le composant cible est défini dans config.xml du framework : ```
### Initialisation des services SystemUI
`SystemUIService.java` déclenche l'initialisation de tous les composants : ```
@Override
public void onCreate() {
super.onCreate();
((SystemUIApplication) getApplication()).startServicesIfNeeded();
mLogBufferFreezer.attach(mBroadcastDispatcher);
// ... autres initialisations
}
SystemUIApplication récupère la liste des comopsants depuis SystemUIFactory : ```
public void startServicesIfNeeded() { String[] componentNames = SystemUIFactory.getInstance() .getSystemUIServiceComponents(getResources()); startServicesIfNeeded("StartServices", componentNames); }
La liste des services est déclarée dans le fichier de configuration de SystemUI, incluant notamment `StatusBar` : ```
<string-array name="config_systemUIServiceComponents" translatable="false">
<item>com.android.systemui.util.NotificationChannels</item>
<item>com.android.systemui.keyguard.KeyguardViewMediator</item>
<item>com.android.systemui.recents.Recents</item>
<item>com.android.systemui.volume.VolumeUI</item>
<item>com.android.systemui.statusbar.phone.StatusBar</item>
...
</string-array>
Chaque service est instancié puis démarré dans la boucle principale : ```
private void startServicesIfNeeded(String metricsPrefix, String[] services) { if (mServicesStarted) return; mServices = new SystemUI[services.length];
final int count = services.length;
for (int i = 0; i < count; i++) {
String className = services[i];
try {
SystemUI instance = mComponentHelper.resolveSystemUI(className);
if (instance == null) {
Constructor<?> ctor = Class.forName(className)
.getConstructor(Context.class);
instance = (SystemUI) ctor.newInstance(this);
}
mServices[i] = instance;
mServices[i].start();
if (mBootCompleteCache.isBootComplete()) {
mServices[i].onBootCompleted();
}
dumpManager.registerDumpable(
mServices[i].getClass().getName(), mServices[i]);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
mServicesStarted = true;
}
</div>