Skip to content
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

Update to jni 0.21 and android-activity 0.6.0 #3

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ license = "MIT"
description = "Android intent utilities"

[dependencies]
jni = "0.20.0"
jni = "0.21"
ndk-context = "0.1.1"
4 changes: 2 additions & 2 deletions example/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
3 changes: 1 addition & 2 deletions example/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
Expand Down
132 changes: 65 additions & 67 deletions src/intent.rs
Original file line number Diff line number Diff line change
@@ -1,72 +1,71 @@
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<Inner<'env>, Error>,
pub struct Intent {
inner: Result<Inner, Error>,
}

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<Self, Error> {
Ok(Self {
inner: Ok(Inner {
object: env.new_global_ref(object)?,
}),
})
}

fn from_fn(f: impl FnOnce() -> Result<Inner<'env>, Error>) -> Self {
fn from_fn(f: impl FnOnce() -> Result<Inner, Error>) -> Self {
let inner = f();
Self { inner }
}

pub fn new(env: JNIEnv<'env>, action: impl AsRef<str>) -> Self {
pub fn new(env: &mut JNIEnv, action: impl AsRef<str>) -> 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<str>, uri: impl AsRef<str>) -> Self {
pub fn new_with_uri(env: &mut JNIEnv, action: impl AsRef<str>, uri: impl AsRef<str>) -> Self {
Self::from_fn(|| {
let url_string = env.new_string(uri)?;
let uri_class = env.find_class("android/net/Uri")?;
let uri = env.call_static_method(
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)?,
})
})
}
Expand All @@ -76,123 +75,122 @@ 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<str>, class_name: impl AsRef<str>) -> 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.
/// ```no_run
/// 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<str>, value: impl AsRef<str>) -> 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.
/// ```no_run
/// 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<impl AsRef<str>>) -> 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.
/// ```no_run
/// 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<str>) -> 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<Inner, Error>) -> Self {
Expand Down
10 changes: 7 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, T>(f: F) -> Result<T, Error>
where
F: FnOnce(&mut JNIEnv) -> Result<T, Error>,
{
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))
}