Repository navigation
android: Follow the system night mode on the host-driven path - #30
Bombatomica64 wants to merge 1 commit into
Conversation
…#22) Fork main 3d74121 merges the revised android-host-night-mode (c62c318) and android-host-emoji-font (77126f2), which longbridge/gpui-mobile#30 and #29 will carry: night mode is read from the application context, and the emoji font's AssetManager is kept for the life of the process. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
8da05ca to
c62c318
Compare
huacnlee
left a comment
There was a problem hiding this comment.
Thanks for following up on the night-mode note from #29. A few things to fix before merging. These come from reading the diff; I have not checked them on a device.
1. The android-activity path still starts Light, and now disagrees with the platform (jni.rs, InitWindow branch)
Platform::window_appearance() now returns the real night mode, but the android-activity InitWindow path (platform.open_window and the init_window reattach) never calls sync_appearance. On a cold start in dark mode, cx.window_appearance() says Dark while the window's appearance() stays Light until a ConfigChanged, and observe_window_appearance never fires. Before this PR both paths at least agreed on Dark; now they disagree.
2. Sync the initial appearance in the shared code, not only at host.rs call sites (host.rs)
The initial sync is added in three host-only places (new window, reattach, resume). Calling sync_appearance inside AndroidPlatform::open_window (and after init_window) would fix both entry points in one place and resolve item 1.
3. The application context ignores app-local night mode (jni.rs, host_ui_mode)
Night mode is read from getApplicationContext().getResources().getConfiguration(). An app-local setting such as AppCompatDelegate.setDefaultNightMode changes only the Activity's configuration, so an app with an in-app dark-theme toggle would be dark everywhere except the GPUI window. Please read the Activity's Resources instead.
4. configuration_changed depends on the order Android delivers config changes in (host.rs)
Inside onConfigurationChanged, the code reads the application's configuration, assuming the process-level change has already been applied. Process and Activity configuration changes are separate transactions, so this can read a stale uiMode. Passing newConfig.uiMode from the callback into configuration_changed would remove that dependency.
Non-blocking:
window_appearance()now makes several JNI calls on every call on the host path, and builds a newAConfigurationeach time on the android-activity path. Theme code that calls it during render pays that cost every frame. Returning the appearance already stored bysync_appearancewould cost nothing and stay consistent withPlatformWindow::appearance.- The
ConfigurationChangedhandler runs the same process-wide query once per window. Query once before the loop and callset_appearanceon each window. host_ui_modeerrors are swallowed byis_ok_and, so a failed context capture silently means Light forever. Please log a warning.
The host-driven entry point has no AndroidApp, and night mode was only read through its AssetManager, so a host-driven window stayed Light whatever the system used. Platform::window_appearance always answered Dark on either path. Night mode is now read from the application context's Resources when there is no AndroidApp. The first set_host_activity keeps that context for the life of the process. Its configuration follows system-wide changes whatever the Activity in front handles itself, whereas an Activity that handles uiMode keeps a stale configuration in its AssetManager. The host path applies it when a window first opens, when a recreated Activity's surface is attached, on resume, and, for every host's window, from the new host::configuration_changed(), which an Activity that handles uiMode must call from onConfigurationChanged. window_appearance reports the same night mode. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
c62c318 to
ec5c89d
Compare
On the host-driven entry point (
android::host), the GPUI window does not follow the system's dark mode. This PR reads night mode from the host Activity and applies it at the points where it can change.Problem
query_night_mode_via_jni()reads the configuration throughAndroidApp's AssetManager. The host-driven path has noAndroidApp, so the window stayedLightwhatever the system used.Platform::window_appearancealways answeredDark, on either path.Change
AndroidApp, night mode is read from the application context'sgetResources().getConfiguration().uiMode. The firstset_host_activitykeeps that context for the life of the process. Its configuration follows system-wide changes whatever the Activity in front handles itself, whereas an Activity that listsuiModeinconfigChangeskeeps its creation-time configuration in its AssetManager.resumed(the mode may have changed while another app was in front).host::configuration_changed(). An Activity that handlesuiModeitself must call it fromonConfigurationChanged, otherwise its window keeps the old mode until it is resumed. It syncs every host's window, so stacked Activities follow too. LikeMainEvent::ConfigChangedon theandroid-activitypath, it also callsnotify_keyboard_layout_change, so that now runs on every configuration change the host forwards.window_appearancereturns the same value.The
android-activitypath reads night mode as before; onlywindow_appearancechanges there.Screenshots
The system switched to dark while the app was open. Left: without this change, the window stays Light. Right: with it. (OnePlus CPH2581, Android 16, host-driven test app.)
Testing
cargo fmt --all -- --check,cargo clippy --target aarch64-linux-android --lib: no new warnings.uiModeinconfigChanges: cold start in light and in dark; switching to dark while the app is in front (configuration_changed); and switching back to light while it is in the background, then returning to it. In that last case Android deliversonConfigurationChangedjust beforeonResume, so theresumedsync is there for hosts that do not forward configuration changes.Test app: Bombatomica64/Gpui-android (its Diagnostics screen shows the window and platform appearance).
The emoji font on the host path is a separate PR (#29), since it does not depend on this one.
🤖 Generated with Claude Code