don't hold android app reference (#922)

* don't hold android app reference

* more

* more
This commit is contained in:
Logan McNaughton authored and GitHub committed 2026-05-20 06:57:56 +00:00
1 parent e852439b6a
commit 28b1424357
5 files changed
+37 -34

No files matched your search

Generated
+1
View File
@@ -2249,6 +2249,7 @@ dependencies = [
"futures",
"jni 0.22.4",
"mimalloc",
"ndk-context",
"open",
"postcard",
"rand 0.10.1",
+1
View File
@@ -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"
+10 -1
View File
@@ -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();
+3 -10
View File
@@ -17,10 +17,6 @@ pub mod video;
#[cfg(feature = "gui")]
pub mod vru;
#[cfg(target_os = "android")]
pub static ANDROID_APP: std::sync::OnceLock<slint::android::AndroidApp> =
std::sync::OnceLock::new();
pub static WEB_CLIENT: std::sync::LazyLock<reqwest::Client> = std::sync::LazyLock::new(|| {
reqwest::Client::builder()
.user_agent(format!(
@@ -32,6 +28,7 @@ pub static WEB_CLIENT: std::sync::LazyLock<reqwest::Client> = 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");
}
+22 -23
View File
@@ -3,9 +3,10 @@ use jni::refs::Global;
use jni::{Env, JavaVM, bind_java_type};
use crate::ui;
pub static DIRS: std::sync::OnceLock<ui::Dirs> = 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<ControllerInfo> {
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<Vec<Control
/// Opens a URI in the user's default app via [`Intent::ACTION_VIEW`](https://developer.android.com/reference/android/content/Intent#ACTION_VIEW).
pub fn open_uri(path: &str) {
let Some(app) = ui::ANDROID_APP.get() else {
eprintln!("Android app not initialized; cannot open URI");
return;
};
let ctx = ndk_context::android_context();
let path = path.to_string();
app.clone().run_on_java_main_thread(Box::new(move || {
let vm = unsafe { JavaVM::from_raw(app.vm_as_ptr().cast()) };
if let Err(err) = vm.attach_current_thread(|env| open_uri_on_java_main(env, &app, &path)) {
eprintln!("JNI error while opening URI: {err:?}");
}
}));
let vm = unsafe { JavaVM::from_raw(ctx.vm().cast()) };
if let Err(err) = vm.attach_current_thread(|env| open_uri_on_jvm(env, ctx.context(), &path)) {
eprintln!("JNI error while opening URI: {err:?}");
}
}
fn open_uri_on_java_main(
fn open_uri_on_jvm(
env: &mut Env<'_>,
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::<Global<AndroidActivity>>(&activity_ptr)? };
let context_ptr = context.cast();
let context = unsafe { env.as_cast_raw::<Global<AndroidContext>>(&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(())
}