mirror of
https://github.com/gopher64/gopher64.git
synced 2026-07-11 01:25:20 +02:00
Handle intent results (#932)
* Handle intent results * more * more * more * more
This commit is contained in:
@@ -4,6 +4,11 @@ import android.app.NativeActivity
|
||||
import android.content.Intent
|
||||
|
||||
class SlintActivity : NativeActivity() {
|
||||
companion object {
|
||||
init {
|
||||
System.loadLibrary("gopher64")
|
||||
}
|
||||
}
|
||||
private external fun nativeOnActivityResult(requestCode: Int, resultCode: Int, data: Intent?)
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
|
||||
+133
-6
@@ -1,12 +1,27 @@
|
||||
use jni::objects::JString;
|
||||
use jni::objects::{JClass, JObject, JString};
|
||||
use jni::refs::Global;
|
||||
use jni::{Env, JavaVM, bind_java_type};
|
||||
use jni::sys::jint;
|
||||
use jni::{Env, EnvUnowned, JavaVM, bind_java_type};
|
||||
|
||||
use crate::ui;
|
||||
|
||||
pub const REQUEST_SELECT_ROM: jint = 1;
|
||||
|
||||
pub static ANDROID_APP: std::sync::Mutex<Option<slint::android::AndroidApp>> =
|
||||
std::sync::Mutex::new(None);
|
||||
|
||||
pub static SELECT_ROM_TX: std::sync::Mutex<
|
||||
Option<tokio::sync::oneshot::Sender<Option<std::path::PathBuf>>>,
|
||||
> = std::sync::Mutex::new(None);
|
||||
|
||||
bind_java_type! {
|
||||
DocumentsContract => "android.provider.DocumentsContract",
|
||||
fields {
|
||||
#[allow(non_snake_case)]
|
||||
static EXTRA_INITIAL_URI: JString,
|
||||
},
|
||||
}
|
||||
|
||||
bind_java_type! {
|
||||
AndroidActivity => "android.app.Activity",
|
||||
type_map = {
|
||||
@@ -14,6 +29,11 @@ bind_java_type! {
|
||||
},
|
||||
methods {
|
||||
fn start_activity(intent: AndroidIntent) -> (),
|
||||
fn start_activity_for_result(intent: AndroidIntent, request_code: jint) -> (),
|
||||
},
|
||||
fields {
|
||||
#[allow(non_snake_case)]
|
||||
static RESULT_OK: jint,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -26,6 +46,10 @@ bind_java_type! {
|
||||
#[allow(non_snake_case)]
|
||||
static ACTION_VIEW: JString,
|
||||
#[allow(non_snake_case)]
|
||||
static ACTION_OPEN_DOCUMENT: JString,
|
||||
#[allow(non_snake_case)]
|
||||
static CATEGORY_OPENABLE: JString,
|
||||
#[allow(non_snake_case)]
|
||||
static FLAG_ACTIVITY_NEW_TASK: jint,
|
||||
},
|
||||
constructors {
|
||||
@@ -33,7 +57,11 @@ bind_java_type! {
|
||||
},
|
||||
methods {
|
||||
fn set_data(uri: AndroidUri) -> AndroidIntent,
|
||||
fn set_type(r#type: JString) -> AndroidIntent,
|
||||
fn add_category(category: JString) -> AndroidIntent,
|
||||
fn add_flags(flags: jint) -> AndroidIntent,
|
||||
fn get_data() -> AndroidUri,
|
||||
fn put_extra(extra: JString, value: JString) -> AndroidIntent,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -41,6 +69,7 @@ bind_java_type! {
|
||||
AndroidUri => "android.net.Uri",
|
||||
methods {
|
||||
static fn parse(uri_string: JString) -> AndroidUri,
|
||||
fn to_string() -> JString,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -192,16 +221,61 @@ fn open_uri_on_jvm(env: &mut Env<'_>, path: &str) -> jni::errors::Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn select_rom(_rom_dir: slint::SharedString) -> Option<std::path::PathBuf> {
|
||||
None
|
||||
pub async fn select_rom(rom_dir: slint::SharedString) -> Option<std::path::PathBuf> {
|
||||
let vm = if let Ok(app) = ANDROID_APP.lock()
|
||||
&& let Some(app) = app.as_ref()
|
||||
{
|
||||
unsafe { JavaVM::from_raw(app.vm_as_ptr().cast()) }
|
||||
} else {
|
||||
eprintln!("Android app not initialized");
|
||||
return None;
|
||||
};
|
||||
if let Err(err) = vm.attach_current_thread(|env| select_rom_on_jvm(env, rom_dir.to_string())) {
|
||||
eprintln!("JNI error while opening URI: {err:?}");
|
||||
return None;
|
||||
}
|
||||
let (tx, rx) = tokio::sync::oneshot::channel::<Option<std::path::PathBuf>>();
|
||||
if let Ok(mut tx_lock) = SELECT_ROM_TX.lock() {
|
||||
tx_lock.replace(tx);
|
||||
} else {
|
||||
eprintln!("Error locking SELECT_ROM_TX");
|
||||
return None;
|
||||
}
|
||||
rx.await.unwrap_or(None)
|
||||
}
|
||||
|
||||
pub async fn select_gb_rom(_player: i32) -> Option<std::path::PathBuf> {
|
||||
None
|
||||
select_rom(slint::SharedString::new()).await
|
||||
}
|
||||
|
||||
pub async fn select_gb_ram(_player: i32) -> Option<std::path::PathBuf> {
|
||||
None
|
||||
select_rom(slint::SharedString::new()).await
|
||||
}
|
||||
|
||||
fn select_rom_on_jvm(env: &mut Env<'_>, rom_dir: String) -> jni::errors::Result<()> {
|
||||
if let Ok(app) = ANDROID_APP.lock()
|
||||
&& let Some(app) = app.as_ref()
|
||||
{
|
||||
let raw_activity_global = app.activity_as_ptr() as jni::sys::jobject;
|
||||
let activity = unsafe { env.as_cast_raw::<Global<AndroidActivity>>(&raw_activity_global)? };
|
||||
|
||||
let action = AndroidIntent::ACTION_OPEN_DOCUMENT(env)?;
|
||||
let category = AndroidIntent::CATEGORY_OPENABLE(env)?;
|
||||
let mime_type = JString::from_str(env, "*/*")?;
|
||||
let mut intent = AndroidIntent::new(env, &action)?
|
||||
.set_type(env, &mime_type)?
|
||||
.add_category(env, &category)?;
|
||||
if !rom_dir.is_empty() {
|
||||
let start_dir = JString::from_str(env, rom_dir)?;
|
||||
let extra_initial_uri = DocumentsContract::EXTRA_INITIAL_URI(env)?;
|
||||
intent = intent.put_extra(env, &extra_initial_uri, &start_dir)?;
|
||||
}
|
||||
|
||||
activity.start_activity_for_result(env, &intent, REQUEST_SELECT_ROM)?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(jni::errors::Error::UninitializedJavaVM)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_dirs() -> ui::Dirs {
|
||||
@@ -217,3 +291,56 @@ pub fn get_dirs() -> ui::Dirs {
|
||||
panic!("Android app not initialized");
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "system" fn Java_io_github_gopher64_gopher64_SlintActivity_nativeOnActivityResult<
|
||||
'caller,
|
||||
>(
|
||||
mut unowned_env: EnvUnowned<'caller>,
|
||||
_class: JClass<'caller>,
|
||||
request_code: jint,
|
||||
result_code: jint,
|
||||
intent_data: JObject<'caller>,
|
||||
) {
|
||||
let outcome = unowned_env.with_env(|env| -> Result<_, jni::errors::Error> {
|
||||
if result_code != AndroidActivity::RESULT_OK(env)? {
|
||||
if let Ok(mut tx_lock) = SELECT_ROM_TX.lock()
|
||||
&& let Some(tx) = tx_lock.take()
|
||||
{
|
||||
let _ = tx.send(None);
|
||||
}
|
||||
return Ok(()); // user cancelled
|
||||
}
|
||||
if intent_data.is_null() {
|
||||
if let Ok(mut tx_lock) = SELECT_ROM_TX.lock()
|
||||
&& let Some(tx) = tx_lock.take()
|
||||
{
|
||||
let _ = tx.send(None);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
let result_intent = unsafe { env.as_cast_raw::<AndroidIntent>(&intent_data)? };
|
||||
match request_code {
|
||||
REQUEST_SELECT_ROM => {
|
||||
let uri = result_intent.as_ref().get_data(env)?;
|
||||
if uri.is_null() {
|
||||
if let Ok(mut tx_lock) = SELECT_ROM_TX.lock()
|
||||
&& let Some(tx) = tx_lock.take()
|
||||
{
|
||||
let _ = tx.send(None);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
let path = uri.to_string(env)?;
|
||||
if let Ok(mut tx_lock) = SELECT_ROM_TX.lock()
|
||||
&& let Some(tx) = tx_lock.take()
|
||||
{
|
||||
let _ = tx.send(Some(std::path::PathBuf::from(path.to_string())));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
outcome.resolve::<jni::errors::ThrowRuntimeExAndDefault>()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user