From 202a64db45b34dfecc6a36c12646e3bb7392783f Mon Sep 17 00:00:00 2001 From: funman300 Date: Fri, 8 May 2026 19:21:41 -0700 Subject: [PATCH] fix(android): export android_main and gate desktop-only window config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three changes to get the APK past the NativeActivity launch crash: 1. Export `android_main` — NativeActivity dlopen-s libsolitaire_app.so and calls `android_main` as its entry point. Without the symbol the app crashed immediately with UnsatisfiedLinkError. The function sets bevy::android::ANDROID_APP (required by WinitPlugin) then delegates to the existing `run()`. 2. Gate `resize_constraints` to non-Android — on Android max_width and max_height default to 0.0; Bevy's clamp panicked with min=800 > max=0. 3. Gate `apply_smart_default_window_size` to non-Android — the system calls `.clamp(800.0, logical_w)` which panics when the window surface reports zero dimensions during early Android lifecycle events. Window sizing is OS-controlled on Android so the system is irrelevant there. Verified: app boots on x86_64 Android 14 emulator (Pixel_7 AVD, SwiftShader Vulkan), runs for 2+ minutes without crashing. Desktop build: clippy clean, 1282 tests passing. Co-Authored-By: Claude Sonnet 4.6 --- .idea/.gitignore | 3 +++ .idea/Rusty_Solitaire.iml | 9 +++++++++ .idea/markdown.xml | 8 ++++++++ .idea/misc.xml | 6 ++++++ .idea/modules.xml | 8 ++++++++ .idea/vcs.xml | 6 ++++++ solitaire_app/src/lib.rs | 19 +++++++++++++++++++ 7 files changed, 59 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/Rusty_Solitaire.iml create mode 100644 .idea/markdown.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/Rusty_Solitaire.iml b/.idea/Rusty_Solitaire.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/Rusty_Solitaire.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/markdown.xml b/.idea/markdown.xml new file mode 100644 index 0000000..c61ea33 --- /dev/null +++ b/.idea/markdown.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..0280eb4 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..7c487c6 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/solitaire_app/src/lib.rs b/solitaire_app/src/lib.rs index 6115008..1af1bfe 100644 --- a/solitaire_app/src/lib.rs +++ b/solitaire_app/src/lib.rs @@ -116,6 +116,9 @@ pub fn run() { // small enough that a few stray dropped frames from // disabling vsync are imperceptible. present_mode: PresentMode::AutoNoVsync, + // Android windows always fill the screen; max_width/max_height + // default to 0.0, which panics Bevy's clamp when min > max. + #[cfg(not(target_os = "android"))] resize_constraints: bevy::window::WindowResizeConstraints { min_width: 800.0, min_height: 600.0, @@ -195,6 +198,8 @@ pub fn run() { // every fresh launch can flip `disable_smart_default_size` in // Settings to opt out. The flag is checked once at startup; a // mid-session change applies on the next launch. + // Android windows are always full-screen; the OS controls sizing. + #[cfg(not(target_os = "android"))] if !had_saved_geometry && !settings.disable_smart_default_size { app.add_systems(Update, apply_smart_default_window_size); } @@ -335,6 +340,20 @@ fn set_window_icon( *applied = true; } +/// Android entry point called by NativeActivity after dlopen-ing the `.so`. +/// Sets the `AndroidApp` handle that Bevy's winit backend reads before +/// constructing the event loop, then delegates to [`run`]. +/// +/// The `#[bevy_main]` proc-macro would generate the same code but only +/// works on a function named `main`; our shared entry point is `run`, so +/// we emit the equivalent expansion manually. +#[cfg(target_os = "android")] +#[unsafe(no_mangle)] +fn android_main(android_app: bevy::android::android_activity::AndroidApp) { + let _ = bevy::android::ANDROID_APP.set(android_app); + run(); +} + /// Wraps the default panic hook with one that also appends a crash log /// to `/crash.log` (next to `settings.json`). The default hook /// still runs afterwards, so stderr output and debugger integration are