From 28b14243570ed81616df74f4e31ebb034f42e857 Mon Sep 17 00:00:00 2001 From: Logan McNaughton <848146+loganmc10@users.noreply.github.com> Date: Wed, 20 May 2026 08:57:56 +0200 Subject: [PATCH] don't hold android app reference (#922) * don't hold android app reference * more * more --- Cargo.lock | 1 + Cargo.toml | 1 + src/lib.rs | 11 ++++++++++- src/ui.rs | 13 +++---------- src/ui/android.rs | 45 ++++++++++++++++++++++----------------------- 5 files changed, 37 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b7c2e232..795ba1e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2249,6 +2249,7 @@ dependencies = [ "futures", "jni 0.22.4", "mimalloc", + "ndk-context", "open", "postcard", "rand 0.10.1", diff --git a/Cargo.toml b/Cargo.toml index 35f9ce35..a89ee1e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,7 @@ open = { version = "5.3", optional = true } [target.'cfg(target_os = "android")'.dependencies] slint = { version = "1.16", default-features = false, features = ["compat-1-2", "std", "backend-android-activity-06", "accessibility"], optional = true } jni = "0.22" +ndk-context = "0.1" [build-dependencies] winresource = "0.1" diff --git a/src/lib.rs b/src/lib.rs index 8b0794fc..598d82c8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,9 @@ mod ui; use clap::Parser; use std::io::Error; +#[cfg(target_os = "android")] +use ui::android; + /// N64 emulator #[derive(Parser, Debug)] #[command(author, version=env!("GIT_DESCRIBE"), about, long_about = None, arg_required_else_help = if cfg!(feature = "gui") { false } else { true })] @@ -308,7 +311,13 @@ pub async fn run() -> std::io::Result<()> { async fn android_main(app: slint::android::AndroidApp) { slint::android::init(app.clone()).unwrap(); - ui::ANDROID_APP.set(app).unwrap(); + if let Err(_) = android::DIRS.set(ui::Dirs { + config_dir: app.internal_data_path().unwrap().join("config"), + data_dir: app.external_data_path().unwrap().join("data"), + cache_dir: app.internal_data_path().unwrap().join("cache"), + }) { + println!("Android dirs already set"); + } let dirs = ui::get_dirs(); diff --git a/src/ui.rs b/src/ui.rs index 2631d4a3..d16065a9 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -17,10 +17,6 @@ pub mod video; #[cfg(feature = "gui")] pub mod vru; -#[cfg(target_os = "android")] -pub static ANDROID_APP: std::sync::OnceLock = - std::sync::OnceLock::new(); - pub static WEB_CLIENT: std::sync::LazyLock = std::sync::LazyLock::new(|| { reqwest::Client::builder() .user_agent(format!( @@ -32,6 +28,7 @@ pub static WEB_CLIENT: std::sync::LazyLock = std::sync::LazyLoc .unwrap() }); +#[derive(Clone)] pub struct Dirs { pub config_dir: std::path::PathBuf, pub data_dir: std::path::PathBuf, @@ -153,12 +150,8 @@ pub fn get_dirs() -> Dirs { #[cfg(target_os = "android")] pub fn get_dirs() -> Dirs { - if let Some(android_app) = ANDROID_APP.get() { - Dirs { - config_dir: android_app.internal_data_path().unwrap().join("config"), - data_dir: android_app.external_data_path().unwrap().join("data"), - cache_dir: android_app.internal_data_path().unwrap().join("cache"), - } + if let Some(android_dirs) = android::DIRS.get() { + android_dirs.clone() } else { panic!("Android app not initialized"); } diff --git a/src/ui/android.rs b/src/ui/android.rs index 88d292ee..9960c1b1 100644 --- a/src/ui/android.rs +++ b/src/ui/android.rs @@ -3,9 +3,10 @@ use jni::refs::Global; use jni::{Env, JavaVM, bind_java_type}; use crate::ui; +pub static DIRS: std::sync::OnceLock = std::sync::OnceLock::new(); bind_java_type! { - AndroidActivity => "android.app.Activity", + AndroidContext => "android.content.Context", type_map = { AndroidIntent => "android.content.Intent", }, @@ -22,12 +23,15 @@ bind_java_type! { fields { #[allow(non_snake_case)] static ACTION_VIEW: JString, + #[allow(non_snake_case)] + static FLAG_ACTIVITY_NEW_TASK: jint, }, constructors { fn new(action: JString), }, methods { fn set_data(uri: AndroidUri) -> AndroidIntent, + fn add_flags(flags: jint) -> AndroidIntent, }, } @@ -68,12 +72,9 @@ pub struct ControllerInfo { /// Lists connected gamepads and joysticks using the Android framework. pub fn list_controllers() -> Vec { - let Some(app) = ui::ANDROID_APP.get() else { - eprintln!("Android app not initialized; cannot list controllers"); - return Vec::new(); - }; + let ctx = ndk_context::android_context(); - let vm = unsafe { JavaVM::from_raw(app.vm_as_ptr().cast()) }; + let vm = unsafe { JavaVM::from_raw(ctx.vm().cast()) }; match vm.attach_current_thread(list_controllers_on_jvm) { Ok(controllers) => controllers, @@ -145,35 +146,33 @@ fn list_controllers_on_jvm(env: &mut Env<'_>) -> jni::errors::Result, - app: &slint::android::AndroidApp, + context: *mut std::ffi::c_void, path: &str, ) -> jni::errors::Result<()> { - let activity_ptr = app.activity_as_ptr().cast(); - let activity = unsafe { env.as_cast_raw::>(&activity_ptr)? }; + let context_ptr = context.cast(); + let context = unsafe { env.as_cast_raw::>(&context_ptr)? }; let uri_string = JString::from_str(env, path.to_string())?; let uri = AndroidUri::parse(env, &uri_string)?; let action_view = AndroidIntent::ACTION_VIEW(env)?; - let intent = AndroidIntent::new(env, &action_view)?; - intent.set_data(env, &uri)?; + let flag = AndroidIntent::FLAG_ACTIVITY_NEW_TASK(env)?; + let intent = AndroidIntent::new(env, &action_view)? + .set_data(env, &uri)? + .add_flags(env, flag)?; - activity.as_ref().start_activity(env, &intent)?; + context.as_ref().start_activity(env, &intent)?; Ok(()) }