diff --git a/Cargo.toml b/Cargo.toml index 48115ee..432455f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,5 +7,5 @@ license = "MIT" description = "Android intent utilities" [dependencies] -jni = "0.20.0" +jni = "0.21" ndk-context = "0.1.1" diff --git a/example/Cargo.toml b/example/Cargo.toml index c53e914..ae49d7c 100644 --- a/example/Cargo.toml +++ b/example/Cargo.toml @@ -7,9 +7,9 @@ edition = "2021" crate_type=["lib", "cdylib"] [dependencies] -android-activity = { version = "0.4.0", features = ["native-activity"] } +android-activity = { version = "0.6.0", features = ["native-activity"] } android-intent = { path = "../" } -jni = { version = "0.20.0" } +jni = { version = "0.21" } ndk-context = "0.1.1" [package.metadata.android.sdk] diff --git a/example/src/lib.rs b/example/src/lib.rs index df4d24a..1dab2ea 100644 --- a/example/src/lib.rs +++ b/example/src/lib.rs @@ -3,13 +3,12 @@ use android_intent::{with_current_env, Action, Extra, Intent}; #[no_mangle] fn android_main(_android_app: AndroidApp) { - with_current_env(|env| { + let _ = with_current_env(|env| { Intent::new(env, Action::Send) .with_type("text/plain") .with_extra(Extra::Text, "Hello World!") .into_chooser() .start_activity() - .unwrap() }); loop {} diff --git a/src/intent.rs b/src/intent.rs index d4f75c5..b88dc61 100644 --- a/src/intent.rs +++ b/src/intent.rs @@ -1,49 +1,49 @@ use jni::{ - errors::Error, - objects::{JObject, JString}, + objects::{GlobalRef, JObject}, JNIEnv, }; +use crate::{Error, with_current_env}; -struct Inner<'env> { - env: JNIEnv<'env>, - object: JObject<'env>, +struct Inner { + object: GlobalRef, } /// A messaging object you can use to request an action from another android app component. #[must_use] -pub struct Intent<'env> { - inner: Result, Error>, +pub struct Intent { + inner: Result, } -impl<'env> Intent<'env> { - pub fn from_object(env: JNIEnv<'env>, object: JObject<'env>) -> Self { - Self { - inner: Ok(Inner { env, object }), - } +impl Intent { + pub fn from_object(env: &mut JNIEnv, object: JObject) -> Result { + Ok(Self { + inner: Ok(Inner { + object: env.new_global_ref(object)?, + }), + }) } - fn from_fn(f: impl FnOnce() -> Result, Error>) -> Self { + fn from_fn(f: impl FnOnce() -> Result) -> Self { let inner = f(); Self { inner } } - pub fn new(env: JNIEnv<'env>, action: impl AsRef) -> Self { + pub fn new(env: &mut JNIEnv, action: impl AsRef) -> Self { Self::from_fn(|| { let intent_class = env.find_class("android/content/Intent")?; let action_view = - env.get_static_field(intent_class, action.as_ref(), "Ljava/lang/String;")?; + env.get_static_field(&intent_class, action.as_ref(), "Ljava/lang/String;")?; let intent = - env.new_object(intent_class, "(Ljava/lang/String;)V", &[action_view.into()])?; + env.new_object(intent_class, "(Ljava/lang/String;)V", &[(&action_view).into()])?; Ok(Inner { - env, - object: intent, + object: env.new_global_ref(intent)?, }) }) } - pub fn new_with_uri(env: JNIEnv<'env>, action: impl AsRef, uri: impl AsRef) -> Self { + pub fn new_with_uri(env: &mut JNIEnv, action: impl AsRef, uri: impl AsRef) -> Self { Self::from_fn(|| { let url_string = env.new_string(uri)?; let uri_class = env.find_class("android/net/Uri")?; @@ -51,22 +51,21 @@ impl<'env> Intent<'env> { uri_class, "parse", "(Ljava/lang/String;)Landroid/net/Uri;", - &[JString::from(url_string).into()], + &[(&url_string).into()], )?; let intent_class = env.find_class("android/content/Intent")?; let action_view = - env.get_static_field(intent_class, action.as_ref(), "Ljava/lang/String;")?; + env.get_static_field(&intent_class, action.as_ref(), "Ljava/lang/String;")?; let intent = env.new_object( intent_class, "(Ljava/lang/String;Landroid/net/Uri;)V", - &[action_view.into(), uri.into()], + &[(&action_view).into(), (&uri).into()], )?; Ok(Inner { - env, - object: intent, + object: env.new_global_ref(intent)?, }) }) } @@ -76,24 +75,24 @@ impl<'env> Intent<'env> { /// use android_intent::{Action, Extra, Intent}; /// /// # android_intent::with_current_env(|env| { - /// let intent = Intent::new(env, Action::Send); - /// intent.set_class_name("com.excample", "IntentTarget") - /// # }) + /// let intent = Intent::new(env, Action::Send); + /// Ok(intent.set_class_name("com.excample", "IntentTarget")) + /// # }); /// ``` pub fn set_class_name(self, package_name: impl AsRef, class_name: impl AsRef) -> Self { - self.and_then(|inner| { - let package_name = inner.env.new_string(package_name)?; - let class_name = inner.env.new_string(class_name)?; + self.and_then(|inner| { with_current_env(|env| { + let package_name = env.new_string(package_name)?; + let class_name = env.new_string(class_name)?; - inner.env.call_method( - inner.object, + env.call_method( + &inner.object, "setClassName", "(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;", - &[package_name.into(), class_name.into()], + &[(&package_name).into(), (&class_name).into()], )?; Ok(inner) - }) + })}) } /// Add extended data to the intent. @@ -101,24 +100,24 @@ impl<'env> Intent<'env> { /// use android_intent::{Action, Extra, Intent}; /// /// # android_intent::with_current_env(|env| { - /// let intent = Intent::new(env, Action::Send); - /// intent.push_extra(Extra::Text, "Hello World!") - /// # }) + /// let intent = Intent::new(env, Action::Send); + /// Ok(intent.with_extra(Extra::Text, "Hello World!")) + /// # }); /// ``` pub fn with_extra(self, key: impl AsRef, value: impl AsRef) -> Self { - self.and_then(|inner| { - let key = inner.env.new_string(key)?; - let value = inner.env.new_string(value)?; + self.and_then(|inner| { with_current_env(|env| { + let key = env.new_string(key)?; + let value = env.new_string(value)?; - inner.env.call_method( - inner.object, + env.call_method( + &inner.object, "putExtra", "(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;", - &[key.into(), value.into()], + &[(&key).into(), (&value).into()], )?; Ok(inner) - }) + })}) } /// Builds a new [`Action::Chooser`] Intent that wraps the given target intent. @@ -126,33 +125,32 @@ impl<'env> Intent<'env> { /// use android_intent::{Action, Intent}; /// /// # android_intent::with_current_env(|env| { - /// let intent = Intent::new(env, Action::Send).into_chhoser(); - /// # }) + /// Ok(Intent::new(env, Action::Send).into_chooser()) + /// # }); /// ``` pub fn into_chooser(self) -> Self { self.into_chooser_with_title(None::<&str>) } pub fn into_chooser_with_title(self, title: Option>) -> Self { - self.and_then(|mut inner| { + self.and_then(|mut inner| { with_current_env(|env| { let title_value = if let Some(title) = title { - let s = inner.env.new_string(title)?; - s.into() + env.new_string(title)? } else { JObject::null().into() }; - let intent_class = inner.env.find_class("android/content/Intent")?; - let intent = inner.env.call_static_method( + let intent_class = env.find_class("android/content/Intent")?; + let intent = env.call_static_method( intent_class, "createChooser", "(Landroid/content/Intent;Ljava/lang/CharSequence;)Landroid/content/Intent;", - &[inner.object.into(), title_value], + &[(&inner.object).into(), (&title_value).into()], )?; - inner.object = intent.try_into()?; + inner.object = env.new_global_ref(intent.l()?)?; Ok(inner) - }) + })}) } /// Set an explicit MIME data type. @@ -160,39 +158,39 @@ impl<'env> Intent<'env> { /// use android_intent::{Action, Intent}; /// /// # android_intent::with_current_env(|env| { - /// let intent = Intent::new(env, Action::Send); - /// intent.set_type("text/plain"); - /// # }) + /// let intent = Intent::new(env, Action::Send); + /// Ok(intent.with_type("text/plain")) + /// # }); /// ``` pub fn with_type(self, type_name: impl AsRef) -> Self { - self.and_then(|inner| { - let jstring = inner.env.new_string(type_name)?; + self.and_then(|inner| { with_current_env(|env| { + let jstring = env.new_string(type_name)?; - inner.env.call_method( - inner.object, + env.call_method( + &inner.object, "setType", "(Ljava/lang/String;)Landroid/content/Intent;", - &[jstring.into()], + &[(&jstring).into()], )?; Ok(inner) - }) + })}) } pub fn start_activity(self) -> Result<(), Error> { let cx = ndk_context::android_context(); let activity = unsafe { JObject::from_raw(cx.context() as jni::sys::jobject) }; - self.inner.and_then(|inner| { - inner.env.call_method( + self.inner.and_then(|inner| { with_current_env(|env| { + env.call_method( activity, "startActivity", "(Landroid/content/Intent;)V", - &[inner.object.into()], + &[(&inner.object).into()], )?; Ok(()) - }) + })}) } fn and_then(mut self, f: impl FnOnce(Inner) -> Result) -> Self { diff --git a/src/lib.rs b/src/lib.rs index 51cacc0..d8bae2d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,12 +7,16 @@ pub use extra::Extra; mod intent; pub use intent::Intent; use jni::{JNIEnv, JavaVM}; +use jni::errors::Error; /// Run 'f' with the current [`JNIEnv`] from [`ndk_context`]. -pub fn with_current_env(f: impl FnOnce(JNIEnv)) { +pub fn with_current_env(f: F) -> Result +where + F: FnOnce(&mut JNIEnv) -> Result, +{ let cx = ndk_context::android_context(); let vm = unsafe { JavaVM::from_raw(cx.vm().cast()) }.unwrap(); - let env = vm.attach_current_thread().unwrap(); + let mut env = vm.attach_current_thread().unwrap(); - f(env.clone()); + env.with_local_frame(16, |env| f(env)) }