feat(app): Android build target — first working APK at 54 MB
Wires the workspace through `cargo apk build`. After this commit `cargo apk build -p solitaire_app --target x86_64-linux-android` produces a debug-signed APK at `target/debug/apk/solitaire-quest.apk` containing all assets and `lib/x86_64/libsolitaire_app.so` — runnable on the AVD or a physical x86_64 device. The five gating points discovered by iterating compile cycles: 1. solitaire_app split into bin + lib. cargo-apk needs a `cdylib` to bundle as `libmain.so`; pure-bin crates panic with "Bin is not compatible with Cdylib". `src/lib.rs` carries the ECS bootstrap as `pub fn run`; `src/main.rs` is a 3-line shim that delegates for the desktop `cargo run` path. 2. `[package.metadata.android]` pins target SDK 34 / min SDK 26 so cargo-apk doesn't probe for whatever default it ships (which on this machine was an uninstalled API 30). `assets = "../assets"` lets the same asset directory feed both desktop and APK. 3. Workspace `bevy` features add `android-native-activity` (the Bevy-side glue that pairs with cargo-apk's NativeActivity wrapper). The feature is target-gated inside bevy_internal so desktop builds compile it out. 4. `arboard` (clipboard, used by Stats's "Copy share link") has no Android backend — `cargo apk build` fails with E0433 on `platform::Clipboard` if unconditional. Target-gated to `cfg(not(target_os = "android"))`; the system surfaces an informational toast on Android until JNI ClipboardManager is wired in the Phase-Android round. 5. `keyring` + `keyring-core` cannot compile for android — the transitive `rpassword` uses `libc::__errno_location` which bionic doesn't expose. Both crates target-gated; `auth_tokens` ships a stub on Android that returns `KeychainUnavailable` for every call, matching how callers already handle a Linux box without Secret Service. Cosmetic post-pass panic: cargo-apk panics AFTER the APK is signed when it tries to also wrap the bin target. The APK on disk is unaffected. Working around this with `cargo apk build --lib` is the next small step. What's verified: - Desktop `cargo build`, `cargo clippy --workspace --all-targets`, and `cargo test --workspace` all clean. - `cargo apk build -p solitaire_app --target x86_64-linux-android` produces 54 MB debug APK with libsolitaire_app.so + assets. What's NOT yet verified: - Whether the APK actually launches on the AVD / a phone (next step: `adb install` + `adb logcat` against the bevy_test AVD). - Whether `dirs::data_dir()` on Android returns a usable path (sync / persistence will surface this if not). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -317,25 +317,41 @@ fn handle_copy_share_link_button(
|
||||
));
|
||||
return;
|
||||
};
|
||||
match arboard::Clipboard::new() {
|
||||
Ok(mut cb) => match cb.set_text(url.clone()) {
|
||||
Ok(()) => {
|
||||
toast.write(InfoToastEvent(format!("Copied: {url}")));
|
||||
}
|
||||
|
||||
// Desktop: `arboard` writes the URL to the OS clipboard.
|
||||
// Android: `arboard` has no platform backend (would fail to
|
||||
// compile, so the dependency is target-gated in
|
||||
// solitaire_engine/Cargo.toml). The button still spawns and
|
||||
// resolves to a meaningful toast instead — when we wire the
|
||||
// Android Phase, this becomes a JNI call into ClipboardManager.
|
||||
#[cfg(not(target_os = "android"))]
|
||||
{
|
||||
match arboard::Clipboard::new() {
|
||||
Ok(mut cb) => match cb.set_text(url.clone()) {
|
||||
Ok(()) => {
|
||||
toast.write(InfoToastEvent(format!("Copied: {url}")));
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("clipboard write failed: {e}");
|
||||
toast.write(InfoToastEvent(
|
||||
"Couldn't write to clipboard \u{2014} share link wasn't copied.".to_string(),
|
||||
));
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
warn!("clipboard write failed: {e}");
|
||||
warn!("clipboard init failed: {e}");
|
||||
toast.write(InfoToastEvent(
|
||||
"Couldn't write to clipboard \u{2014} share link wasn't copied.".to_string(),
|
||||
"Couldn't reach the clipboard \u{2014} share link wasn't copied.".to_string(),
|
||||
));
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
warn!("clipboard init failed: {e}");
|
||||
toast.write(InfoToastEvent(
|
||||
"Couldn't reach the clipboard \u{2014} share link wasn't copied.".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
#[cfg(target_os = "android")]
|
||||
{
|
||||
toast.write(InfoToastEvent(format!(
|
||||
"Share link: {url}"
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_watch_replay_button(
|
||||
|
||||
Reference in New Issue
Block a user