1use crate::error::NdkError;
2use serde::{Deserialize, Serialize, Serializer};
3use std::{fs::File, path::Path};
4
5#[derive(Clone, Debug, Deserialize, Serialize)]
7#[serde(rename = "manifest")]
8pub struct AndroidManifest {
9 #[serde(rename(serialize = "xmlns:android"))]
10 #[serde(default = "default_namespace")]
11 ns_android: String,
12 #[serde(default)]
13 pub package: String,
14 #[serde(rename(serialize = "android:sharedUserId"))]
15 pub shared_user_id: Option<String>,
16 #[serde(rename(serialize = "android:versionCode"))]
17 pub version_code: Option<u32>,
18 #[serde(rename(serialize = "android:versionName"))]
19 pub version_name: Option<String>,
20
21 #[serde(rename(serialize = "uses-sdk"))]
22 #[serde(default)]
23 pub sdk: Sdk,
24
25 #[serde(rename(serialize = "uses-feature"))]
26 #[serde(default)]
27 pub uses_feature: Vec<Feature>,
28 #[serde(rename(serialize = "uses-permission"))]
29 #[serde(default)]
30 pub uses_permission: Vec<Permission>,
31
32 #[serde(default)]
33 pub queries: Option<Queries>,
34
35 #[serde(default)]
36 pub application: Application,
37}
38
39impl Default for AndroidManifest {
40 fn default() -> Self {
41 Self {
42 ns_android: default_namespace(),
43 package: Default::default(),
44 shared_user_id: Default::default(),
45 version_code: Default::default(),
46 version_name: Default::default(),
47 sdk: Default::default(),
48 uses_feature: Default::default(),
49 uses_permission: Default::default(),
50 queries: Default::default(),
51 application: Default::default(),
52 }
53 }
54}
55
56impl AndroidManifest {
57 pub fn write_to(&self, dir: &Path) -> Result<(), NdkError> {
58 let file = File::create(dir.join("AndroidManifest.xml"))?;
59 let w = std::io::BufWriter::new(file);
60 quick_xml::se::to_writer(w, &self)?;
61 Ok(())
62 }
63}
64
65#[derive(Clone, Debug, Default, Deserialize, Serialize)]
67pub struct Application {
68 #[serde(rename(serialize = "android:debuggable"))]
69 pub debuggable: Option<bool>,
70 #[serde(rename(serialize = "android:theme"))]
71 pub theme: Option<String>,
72 #[serde(rename(serialize = "android:hasCode"))]
73 #[serde(default)]
74 pub has_code: bool,
75 #[serde(rename(serialize = "android:icon"))]
76 pub icon: Option<String>,
77 #[serde(rename(serialize = "android:label"))]
78 #[serde(default)]
79 pub label: String,
80 #[serde(rename(serialize = "android:extractNativeLibs"))]
81 pub extract_native_libs: Option<bool>,
82 #[serde(rename(serialize = "android:usesCleartextTraffic"))]
83 pub uses_cleartext_traffic: Option<bool>,
84
85 #[serde(rename(serialize = "meta-data"))]
86 #[serde(default)]
87 pub meta_data: Vec<MetaData>,
88 #[serde(default)]
89 pub activity: Activity,
90}
91
92#[derive(Clone, Debug, Deserialize, Serialize)]
94pub struct Activity {
95 #[serde(rename(serialize = "android:configChanges"))]
96 #[serde(default = "default_config_changes")]
97 pub config_changes: Option<String>,
98 #[serde(rename(serialize = "android:label"))]
99 pub label: Option<String>,
100 #[serde(rename(serialize = "android:launchMode"))]
101 pub launch_mode: Option<String>,
102 #[serde(rename(serialize = "android:name"))]
103 #[serde(default = "default_activity_name")]
104 pub name: String,
105 #[serde(rename(serialize = "android:screenOrientation"))]
106 pub orientation: Option<String>,
107 #[serde(rename(serialize = "android:exported"))]
108 pub exported: Option<bool>,
109 #[serde(rename(serialize = "android:resizeableActivity"))]
110 pub resizeable_activity: Option<bool>,
111 #[serde(rename(serialize = "android:alwaysRetainTaskState"))]
112 pub always_retain_task_state: Option<bool>,
113
114 #[serde(rename(serialize = "meta-data"))]
115 #[serde(default)]
116 pub meta_data: Vec<MetaData>,
117 #[serde(rename(serialize = "intent-filter"))]
119 #[serde(default)]
120 pub intent_filter: Vec<IntentFilter>,
121}
122
123impl Default for Activity {
124 fn default() -> Self {
125 Self {
126 config_changes: default_config_changes(),
127 label: None,
128 launch_mode: None,
129 name: default_activity_name(),
130 orientation: None,
131 exported: None,
132 resizeable_activity: None,
133 always_retain_task_state: None,
134 meta_data: Default::default(),
135 intent_filter: Default::default(),
136 }
137 }
138}
139
140#[derive(Clone, Debug, Default, Deserialize, Serialize)]
142pub struct IntentFilter {
143 #[serde(serialize_with = "serialize_actions")]
145 #[serde(rename(serialize = "action"))]
146 #[serde(default)]
147 pub actions: Vec<String>,
148 #[serde(serialize_with = "serialize_catergories")]
150 #[serde(rename(serialize = "category"))]
151 #[serde(default)]
152 pub categories: Vec<String>,
153 #[serde(default)]
154 pub data: Vec<IntentFilterData>,
155}
156
157fn serialize_actions<S>(actions: &[String], serializer: S) -> Result<S::Ok, S::Error>
158where
159 S: Serializer,
160{
161 use serde::ser::SerializeSeq;
162
163 #[derive(Serialize)]
164 struct Action {
165 #[serde(rename = "android:name")]
166 name: String,
167 }
168 let mut seq = serializer.serialize_seq(Some(actions.len()))?;
169 for action in actions {
170 seq.serialize_element(&Action {
171 name: action.clone(),
172 })?;
173 }
174 seq.end()
175}
176
177fn serialize_catergories<S>(categories: &[String], serializer: S) -> Result<S::Ok, S::Error>
178where
179 S: Serializer,
180{
181 use serde::ser::SerializeSeq;
182
183 #[derive(Serialize)]
184 struct Category {
185 #[serde(rename = "android:name")]
186 pub name: String,
187 }
188
189 let mut seq = serializer.serialize_seq(Some(categories.len()))?;
190 for category in categories {
191 seq.serialize_element(&Category {
192 name: category.clone(),
193 })?;
194 }
195 seq.end()
196}
197
198#[derive(Clone, Debug, Default, Deserialize, Serialize)]
200pub struct IntentFilterData {
201 #[serde(rename(serialize = "android:scheme"))]
202 pub scheme: Option<String>,
203 #[serde(rename(serialize = "android:host"))]
204 pub host: Option<String>,
205 #[serde(rename(serialize = "android:port"))]
206 pub port: Option<String>,
207 #[serde(rename(serialize = "android:path"))]
208 pub path: Option<String>,
209 #[serde(rename(serialize = "android:pathPattern"))]
210 pub path_pattern: Option<String>,
211 #[serde(rename(serialize = "android:pathPrefix"))]
212 pub path_prefix: Option<String>,
213 #[serde(rename(serialize = "android:mimeType"))]
214 pub mime_type: Option<String>,
215}
216
217#[derive(Clone, Debug, Default, Deserialize, Serialize)]
219pub struct MetaData {
220 #[serde(rename(serialize = "android:name"))]
221 pub name: String,
222 #[serde(rename(serialize = "android:value"))]
223 pub value: String,
224}
225
226#[derive(Clone, Debug, Default, Deserialize, Serialize)]
228pub struct Feature {
229 #[serde(rename(serialize = "android:name"))]
230 pub name: Option<String>,
231 #[serde(rename(serialize = "android:required"))]
232 pub required: Option<bool>,
233 #[serde(rename(serialize = "android:version"))]
244 pub version: Option<u32>,
245 #[serde(rename(serialize = "android:glEsVersion"))]
246 #[serde(serialize_with = "serialize_opengles_version")]
247 pub opengles_version: Option<(u8, u8)>,
248}
249
250fn serialize_opengles_version<S>(
251 version: &Option<(u8, u8)>,
252 serializer: S,
253) -> Result<S::Ok, S::Error>
254where
255 S: Serializer,
256{
257 match version {
258 Some(version) => {
259 let opengles_version = format!("0x{:04}{:04}", version.0, version.1);
260 serializer.serialize_some(&opengles_version)
261 }
262 None => serializer.serialize_none(),
263 }
264}
265
266#[derive(Clone, Debug, Default, Deserialize, Serialize)]
268pub struct Permission {
269 #[serde(rename(serialize = "android:name"))]
270 pub name: String,
271 #[serde(rename(serialize = "android:maxSdkVersion"))]
272 pub max_sdk_version: Option<u32>,
273}
274
275#[derive(Clone, Debug, Default, Deserialize, Serialize)]
277pub struct Package {
278 #[serde(rename(serialize = "android:name"))]
279 pub name: String,
280}
281
282#[derive(Clone, Debug, Default, Deserialize, Serialize)]
284pub struct QueryProvider {
285 #[serde(rename(serialize = "android:authorities"))]
286 pub authorities: String,
287
288 #[serde(rename(serialize = "android:name"))]
291 pub name: String,
292}
293
294#[derive(Clone, Debug, Default, Deserialize, Serialize)]
296pub struct Queries {
297 #[serde(default)]
298 pub package: Vec<Package>,
299 #[serde(default)]
300 pub intent: Vec<IntentFilter>,
301 #[serde(default)]
302 pub provider: Vec<QueryProvider>,
303}
304
305#[derive(Clone, Debug, Deserialize, Serialize)]
307pub struct Sdk {
308 #[serde(rename(serialize = "android:minSdkVersion"))]
309 pub min_sdk_version: Option<u32>,
310 #[serde(rename(serialize = "android:targetSdkVersion"))]
311 pub target_sdk_version: Option<u32>,
312 #[serde(rename(serialize = "android:maxSdkVersion"))]
313 pub max_sdk_version: Option<u32>,
314}
315
316impl Default for Sdk {
317 fn default() -> Self {
318 Self {
319 min_sdk_version: Some(23),
320 target_sdk_version: None,
321 max_sdk_version: None,
322 }
323 }
324}
325
326fn default_namespace() -> String {
327 "http://schemas.android.com/apk/res/android".to_string()
328}
329
330fn default_activity_name() -> String {
331 "android.app.NativeActivity".to_string()
332}
333
334fn default_config_changes() -> Option<String> {
335 Some("orientation|keyboardHidden|screenSize".to_string())
336}