diff --git a/example/src/lib.rs b/example/src/lib.rs index c2b0719..9ab3c48 100644 --- a/example/src/lib.rs +++ b/example/src/lib.rs @@ -1,10 +1,10 @@ use android_activity::AndroidApp; -use android_intent::{with_current_env, Action, Extra, Intent}; +use android_intent::{with_current_env, Action, Extra, IntentBuilder}; #[no_mangle] fn android_main(_android_app: AndroidApp) { with_current_env(|env| { - Intent::new(env, Action::Send) + IntentBuilder::new(env, Action::Send) .with_type("text/plain") .with_extra(Extra::Text, "Hello World!") .into_chooser() diff --git a/src/intent.rs b/src/intent.rs index 2f70db5..c57a536 100644 --- a/src/intent.rs +++ b/src/intent.rs @@ -1,28 +1,76 @@ use jni::{ - errors::Error, + errors::Result, objects::{JObject, JString, JValue}, JNIEnv, }; -struct Inner<'vm, 'env> { +/// A messaging object you can use to request an action from another android app component. +#[must_use] +pub struct Intent<'vm, 'env> { env: &'vm mut JNIEnv<'env>, object: JObject<'env>, } -/// A messaging object you can use to request an action from another android app component. +impl<'vm, 'env> Intent<'vm, 'env> { + pub fn from_object(env: &'vm mut JNIEnv<'env>, object: JObject<'env>) -> Self { + Self { env, object } + } + + // TODO: Could also return a borrowed JavaStr so that the caller decides if they just want to read or also allocate + pub fn action(&mut self) -> Result { + let action = self + .env + .call_method(&self.object, "getAction", "()Ljava/lang/String;", &[])? + .l()? + .into(); + + let action = self.env.get_string(&action)?; + Ok(action.into()) + } + + pub fn data_string(&mut self) -> Result { + let data_string = self + .env + .call_method(&self.object, "getDataString", "()Ljava/lang/String;", &[])? + .l()? + .into(); + let data_string = self.env.get_string(&data_string)?; + Ok(data_string.into()) + } + + /// + pub fn string_extra(&mut self, name: &str) -> Result { + let name = self.env.new_string(name)?; + + let extra = self + .env + .call_method( + &self.object, + "getStringExtra", + "(Ljava/lang/String;)Ljava/lang/String;", + &[JValue::Object(&name.into())], + )? + .l()? + .into(); + let extra = self.env.get_string(&extra)?; + Ok(extra.into()) + } +} + +/// A messaging object you can use to request an action from another Android app component. #[must_use] -pub struct Intent<'vm, 'env> { - inner: Result, Error>, +pub struct IntentBuilder<'vm, 'env> { + inner: Result>, } -impl<'vm, 'env> Intent<'vm, 'env> { +impl<'vm, 'env> IntentBuilder<'vm, 'env> { pub fn from_object(env: &'vm mut JNIEnv<'env>, object: JObject<'env>) -> Self { Self { - inner: Ok(Inner { env, object }), + inner: Ok(Intent::from_object(env, object)), } } - fn from_fn(f: impl FnOnce() -> Result, Error>) -> Self { + fn from_fn(f: impl FnOnce() -> Result>) -> Self { let inner = f(); Self { inner } } @@ -39,10 +87,7 @@ impl<'vm, 'env> Intent<'vm, 'env> { &[action_view.borrow()], )?; - Ok(Inner { - env, - object: intent, - }) + Ok(Intent::from_object(env, intent)) }) } @@ -72,7 +117,7 @@ impl<'vm, 'env> Intent<'vm, 'env> { &[JValue::Object(&action_view), uri.borrow()], )?; - Ok(Inner { + Ok(Intent { env, object: intent, }) @@ -81,10 +126,10 @@ impl<'vm, 'env> Intent<'vm, 'env> { /// Set the class name for the intent target. /// ```no_run - /// use android_intent::{Action, Extra, Intent}; + /// use android_intent::{Action, Extra, IntentBuilder}; /// /// # android_intent::with_current_env(|env| { - /// let intent = Intent::new(env, Action::Send) + /// let intent = IntentBuilder::new(env, Action::Send) /// .set_class_name("com.excample", "IntentTarget"); /// # }) /// ``` @@ -110,10 +155,10 @@ impl<'vm, 'env> Intent<'vm, 'env> { /// Add extended data to the intent. /// ```no_run - /// use android_intent::{Action, Extra, Intent}; + /// use android_intent::{Action, Extra, IntentBuilder}; /// /// # android_intent::with_current_env(|env| { - /// let intent = Intent::new(env, Action::Send) + /// let intent = IntentBuilder::new(env, Action::Send) /// .with_extra(Extra::Text, "Hello World!"); /// # }) /// ``` @@ -135,10 +180,10 @@ impl<'vm, 'env> Intent<'vm, 'env> { /// Builds a new [`super::Action::Chooser`] Intent that wraps the given target intent. /// ```no_run - /// use android_intent::{Action, Intent}; + /// use android_intent::{Action, IntentBuilder}; /// /// # android_intent::with_current_env(|env| { - /// let intent = Intent::new(env, Action::Send) + /// let intent = IntentBuilder::new(env, Action::Send) /// .into_chooser(); /// # }) /// ``` @@ -169,10 +214,10 @@ impl<'vm, 'env> Intent<'vm, 'env> { /// Set an explicit MIME data type. /// ```no_run - /// use android_intent::{Action, Intent}; + /// use android_intent::{Action, IntentBuilder}; /// /// # android_intent::with_current_env(|env| { - /// let intent = Intent::new(env, Action::Send) + /// let intent = IntentBuilder::new(env, Action::Send) /// .with_type("text/plain"); /// # }) /// ``` @@ -191,7 +236,7 @@ impl<'vm, 'env> Intent<'vm, 'env> { }) } - pub fn start_activity(self) -> Result<(), Error> { + pub fn start_activity(self) -> Result<()> { let cx = ndk_context::android_context(); let activity = unsafe { JObject::from_raw(cx.context() as jni::sys::jobject) }; @@ -207,10 +252,7 @@ impl<'vm, 'env> Intent<'vm, 'env> { }) } - fn and_then( - mut self, - f: impl FnOnce(Inner<'vm, 'env>) -> Result, Error>, - ) -> Self { + fn and_then(mut self, f: impl FnOnce(Intent<'vm, 'env>) -> Result>) -> Self { self.inner = self.inner.and_then(f); self } diff --git a/src/lib.rs b/src/lib.rs index 397f750..e929fc9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,14 +5,37 @@ mod extra; pub use extra::Extra; mod intent; -pub use intent::Intent; -use jni::{JNIEnv, JavaVM}; +pub use intent::{Intent, IntentBuilder}; +use jni::{errors::Result, objects::JObject, JNIEnv, JavaVM}; /// Run 'f' with the current [`JNIEnv`] from [`ndk_context`]. pub fn with_current_env(f: impl FnOnce(&mut JNIEnv<'_>)) { + // XXX: Pass AndroidActivity? let cx = ndk_context::android_context(); let vm = unsafe { JavaVM::from_raw(cx.vm().cast()) }.unwrap(); let mut env = vm.attach_current_thread().unwrap(); + // TODO: Pass current activity? f(&mut env); } + +/// Provides the intent from [`Activity#getIntent()`]. +/// +/// [`Activity#getIntent()`]: https://developer.android.com/reference/android/app/Activity#getIntent() +pub fn with_current_intent(f: impl FnOnce(Intent<'_, '_>) -> T) -> Result { + // XXX: Pass AndroidActivity? + // XXX: Support onNewIntent() callback with setIntent()? + // https://github.com/rust-mobile/ndk/issues/275 + let cx = ndk_context::android_context(); + let vm = unsafe { JavaVM::from_raw(cx.vm().cast()) }?; + let mut env = vm.attach_current_thread().unwrap(); + let activity = unsafe { JObject::from_raw(cx.context() as jni::sys::jobject) }; + + let object = env + .call_method(activity, "getIntent", "()Landroid/content/Intent;", &[])? + .l()?; + + let intent = Intent::from_object(&mut env, object); + + Ok(f(intent)) +}