use jni::{
errors::Error,
objects::{JObject, JString},
JNIEnv,
};
struct Inner<'env> {
env: JNIEnv<'env>,
object: JObject<'env>,
}
#[must_use]
pub struct Intent<'env> {
inner: Result<Inner<'env>, Error>,
}
impl<'env> Intent<'env> {
pub fn from_object(env: JNIEnv<'env>, object: JObject<'env>) -> Self {
Self {
inner: Ok(Inner { env, object }),
}
}
fn from_fn(f: impl FnOnce() -> Result<Inner<'env>, Error>) -> Self {
let inner = f();
Self { inner }
}
pub fn new(env: JNIEnv<'env>, 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;")?;
let intent =
env.new_object(intent_class, "(Ljava/lang/String;)V", &[action_view.into()])?;
Ok(Inner {
env,
object: intent,
})
})
}
pub fn new_with_uri(env: JNIEnv<'env>, 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()],
)?;
let intent_class = env.find_class("android/content/Intent")?;
let action_view =
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()],
)?;
Ok(Inner {
env,
object: intent,
})
})
}
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)?;
inner.env.call_method(
inner.object,
"putExtra",
"(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;",
&[key.into(), value.into()],
)?;
Ok(inner)
})
}
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| {
let title_value = if let Some(title) = title {
let s = inner.env.new_string(title)?;
s.into()
} else {
JObject::null().into()
};
let intent_class = inner.env.find_class("android/content/Intent")?;
let intent = inner.env.call_static_method(
intent_class,
"createChooser",
"(Landroid/content/Intent;Ljava/lang/CharSequence;)Landroid/content/Intent;",
&[inner.object.into(), title_value],
)?;
inner.object = intent.try_into()?;
Ok(inner)
})
}
pub fn with_type(self, type_name: impl AsRef<str>) -> Self {
self.and_then(|inner| {
let jstring = inner.env.new_string(type_name)?;
inner.env.call_method(
inner.object,
"setType",
"(Ljava/lang/String;)Landroid/content/Intent;",
&[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(
activity,
"startActivity",
"(Landroid/content/Intent;)V",
&[inner.object.into()],
)?;
Ok(())
})
}
fn and_then(mut self, f: impl FnOnce(Inner) -> Result<Inner, Error>) -> Self {
self.inner = self.inner.and_then(f);
self
}
}