1use jni::{
2 errors::Error,
3 objects::{JObject, JString},
4 JNIEnv,
5};
6
7struct Inner<'env> {
8 env: JNIEnv<'env>,
9 object: JObject<'env>,
10}
11
12#[must_use]
14pub struct Intent<'env> {
15 inner: Result<Inner<'env>, Error>,
16}
17
18impl<'env> Intent<'env> {
19 pub fn from_object(env: JNIEnv<'env>, object: JObject<'env>) -> Self {
20 Self {
21 inner: Ok(Inner { env, object }),
22 }
23 }
24
25 fn from_fn(f: impl FnOnce() -> Result<Inner<'env>, Error>) -> Self {
26 let inner = f();
27 Self { inner }
28 }
29
30 pub fn new(env: JNIEnv<'env>, action: impl AsRef<str>) -> Self {
31 Self::from_fn(|| {
32 let intent_class = env.find_class("android/content/Intent")?;
33 let action_view =
34 env.get_static_field(intent_class, action.as_ref(), "Ljava/lang/String;")?;
35
36 let intent =
37 env.new_object(intent_class, "(Ljava/lang/String;)V", &[action_view.into()])?;
38
39 Ok(Inner {
40 env,
41 object: intent,
42 })
43 })
44 }
45
46 pub fn new_with_uri(env: JNIEnv<'env>, action: impl AsRef<str>, uri: impl AsRef<str>) -> Self {
47 Self::from_fn(|| {
48 let url_string = env.new_string(uri)?;
49 let uri_class = env.find_class("android/net/Uri")?;
50 let uri = env.call_static_method(
51 uri_class,
52 "parse",
53 "(Ljava/lang/String;)Landroid/net/Uri;",
54 &[JString::from(url_string).into()],
55 )?;
56
57 let intent_class = env.find_class("android/content/Intent")?;
58 let action_view =
59 env.get_static_field(intent_class, action.as_ref(), "Ljava/lang/String;")?;
60
61 let intent = env.new_object(
62 intent_class,
63 "(Ljava/lang/String;Landroid/net/Uri;)V",
64 &[action_view.into(), uri.into()],
65 )?;
66
67 Ok(Inner {
68 env,
69 object: intent,
70 })
71 })
72 }
73
74 pub fn with_extra(self, key: impl AsRef<str>, value: impl AsRef<str>) -> Self {
84 self.and_then(|inner| {
85 let key = inner.env.new_string(key)?;
86 let value = inner.env.new_string(value)?;
87
88 inner.env.call_method(
89 inner.object,
90 "putExtra",
91 "(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;",
92 &[key.into(), value.into()],
93 )?;
94
95 Ok(inner)
96 })
97 }
98
99 pub fn into_chooser(self) -> Self {
108 self.into_chooser_with_title(None::<&str>)
109 }
110
111 pub fn into_chooser_with_title(self, title: Option<impl AsRef<str>>) -> Self {
112 self.and_then(|mut inner| {
113 let title_value = if let Some(title) = title {
114 let s = inner.env.new_string(title)?;
115 s.into()
116 } else {
117 JObject::null().into()
118 };
119
120 let intent_class = inner.env.find_class("android/content/Intent")?;
121 let intent = inner.env.call_static_method(
122 intent_class,
123 "createChooser",
124 "(Landroid/content/Intent;Ljava/lang/CharSequence;)Landroid/content/Intent;",
125 &[inner.object.into(), title_value],
126 )?;
127
128 inner.object = intent.try_into()?;
129 Ok(inner)
130 })
131 }
132
133 pub fn with_type(self, type_name: impl AsRef<str>) -> Self {
143 self.and_then(|inner| {
144 let jstring = inner.env.new_string(type_name)?;
145
146 inner.env.call_method(
147 inner.object,
148 "setType",
149 "(Ljava/lang/String;)Landroid/content/Intent;",
150 &[jstring.into()],
151 )?;
152
153 Ok(inner)
154 })
155 }
156
157 pub fn start_activity(self) -> Result<(), Error> {
158 let cx = ndk_context::android_context();
159 let activity = unsafe { JObject::from_raw(cx.context() as jni::sys::jobject) };
160
161 self.inner.and_then(|inner| {
162 inner.env.call_method(
163 activity,
164 "startActivity",
165 "(Landroid/content/Intent;)V",
166 &[inner.object.into()],
167 )?;
168
169 Ok(())
170 })
171 }
172
173 fn and_then(mut self, f: impl FnOnce(Inner) -> Result<Inner, Error>) -> Self {
174 self.inner = self.inner.and_then(f);
175 self
176 }
177}