Skip to content

DRAFT! Parse Intent from getIntent() and provide extra getters #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: jni-0.21
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions example/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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()
Expand Down
94 changes: 68 additions & 26 deletions src/intent.rs
Original file line number Diff line number Diff line change
@@ -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<String> {
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<String> {
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())
}

/// <https://developer.android.com/reference/android/content/Intent#getStringExtra(java.lang.String)>
pub fn string_extra(&mut self, name: &str) -> Result<String> {
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<Inner<'vm, 'env>, Error>,
pub struct IntentBuilder<'vm, 'env> {
inner: Result<Intent<'vm, 'env>>,
}

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<Inner<'vm, 'env>, Error>) -> Self {
fn from_fn(f: impl FnOnce() -> Result<Intent<'vm, 'env>>) -> Self {
let inner = f();
Self { inner }
}
Expand All @@ -39,10 +87,7 @@ impl<'vm, 'env> Intent<'vm, 'env> {
&[action_view.borrow()],
)?;

Ok(Inner {
env,
object: intent,
})
Ok(Intent::from_object(env, intent))
})
}

Expand Down Expand Up @@ -72,7 +117,7 @@ impl<'vm, 'env> Intent<'vm, 'env> {
&[JValue::Object(&action_view), uri.borrow()],
)?;

Ok(Inner {
Ok(Intent {
env,
object: intent,
})
Expand All @@ -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");
/// # })
/// ```
Expand All @@ -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!");
/// # })
/// ```
Expand All @@ -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();
/// # })
/// ```
Expand Down Expand Up @@ -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");
/// # })
/// ```
Expand All @@ -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) };

Expand All @@ -207,10 +252,7 @@ impl<'vm, 'env> Intent<'vm, 'env> {
})
}

fn and_then(
mut self,
f: impl FnOnce(Inner<'vm, 'env>) -> Result<Inner<'vm, 'env>, Error>,
) -> Self {
fn and_then(mut self, f: impl FnOnce(Intent<'vm, 'env>) -> Result<Intent<'vm, 'env>>) -> Self {
self.inner = self.inner.and_then(f);
self
}
Expand Down
27 changes: 25 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>(f: impl FnOnce(Intent<'_, '_>) -> T) -> Result<T> {
// 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))
}