ndk_build/
manifest.rs

1use crate::error::NdkError;
2use serde::{Deserialize, Serialize, Serializer};
3use std::{fs::File, path::Path};
4
5/// Android [manifest element](https://developer.android.com/guide/topics/manifest/manifest-element), containing an [`Application`] element.
6#[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/// Android [application element](https://developer.android.com/guide/topics/manifest/application-element), containing an [`Activity`] element.
66#[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/// Android [activity element](https://developer.android.com/guide/topics/manifest/activity-element).
93#[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    /// If no `MAIN` action exists in any intent filter, a default `MAIN` filter is serialized by `cargo-apk`.
118    #[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/// Android [intent filter element](https://developer.android.com/guide/topics/manifest/intent-filter-element).
141#[derive(Clone, Debug, Default, Deserialize, Serialize)]
142pub struct IntentFilter {
143    /// Serialize strings wrapped in `<action android:name="..." />`
144    #[serde(serialize_with = "serialize_actions")]
145    #[serde(rename(serialize = "action"))]
146    #[serde(default)]
147    pub actions: Vec<String>,
148    /// Serialize as vector of structs for proper xml formatting
149    #[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/// Android [intent filter data element](https://developer.android.com/guide/topics/manifest/data-element).
199#[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/// Android [meta-data element](https://developer.android.com/guide/topics/manifest/meta-data-element).
218#[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/// Android [uses-feature element](https://developer.android.com/guide/topics/manifest/uses-feature-element).
227#[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    /// The `version` field is currently used for the following features:
234    ///
235    /// - `name="android.hardware.vulkan.compute"`: The minimum level of compute features required. See the [Android documentation](https://developer.android.com/reference/android/content/pm/PackageManager#FEATURE_VULKAN_HARDWARE_COMPUTE)
236    ///   for available levels and the respective Vulkan features required/provided.
237    ///
238    /// - `name="android.hardware.vulkan.level"`: The minimum Vulkan requirements. See the [Android documentation](https://developer.android.com/reference/android/content/pm/PackageManager#FEATURE_VULKAN_HARDWARE_LEVEL)
239    ///   for available levels and the respective Vulkan features required/provided.
240    ///
241    /// - `name="android.hardware.vulkan.version"`: Represents the value of Vulkan's `VkPhysicalDeviceProperties::apiVersion`. See the [Android documentation](https://developer.android.com/reference/android/content/pm/PackageManager#FEATURE_VULKAN_HARDWARE_VERSION)
242    ///    for available levels and the respective Vulkan features required/provided.
243    #[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/// Android [uses-permission element](https://developer.android.com/guide/topics/manifest/uses-permission-element).
267#[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/// Android [package element](https://developer.android.com/guide/topics/manifest/queries-element#package).
276#[derive(Clone, Debug, Default, Deserialize, Serialize)]
277pub struct Package {
278    #[serde(rename(serialize = "android:name"))]
279    pub name: String,
280}
281
282/// Android [provider element](https://developer.android.com/guide/topics/manifest/queries-element#provider).
283#[derive(Clone, Debug, Default, Deserialize, Serialize)]
284pub struct QueryProvider {
285    #[serde(rename(serialize = "android:authorities"))]
286    pub authorities: String,
287
288    // The specs say only an `authorities` attribute is required for providers contained in a `queries` element
289    // however this is required for aapt support and should be made optional if/when cargo-apk migrates to aapt2
290    #[serde(rename(serialize = "android:name"))]
291    pub name: String,
292}
293
294/// Android [queries element](https://developer.android.com/guide/topics/manifest/queries-element).
295#[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/// Android [uses-sdk element](https://developer.android.com/guide/topics/manifest/uses-sdk-element).
306#[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}