diff --git a/crossbundle/cli/src/cargo_manifest.rs b/crossbundle/cli/src/cargo_manifest.rs index 7f9f357e..9be59205 100644 --- a/crossbundle/cli/src/cargo_manifest.rs +++ b/crossbundle/cli/src/cargo_manifest.rs @@ -1,5 +1,5 @@ use crossbundle_tools::types::{ - android_manifest::{UsesFeature, UsesPermission, UsesPermissionSdk23}, + android_manifest::{Service, UsesFeature, UsesPermission, UsesPermissionSdk23}, *, }; use serde::{Deserialize, Serialize}; @@ -41,6 +41,9 @@ pub struct Metadata { /// Declares a single hardware or software android feature that is used by the application pub android_features: Option>, + /// Android service to place in AndroidManifest.xml. + pub android_service: Option>, + /// Apple build targets. pub apple_build_targets: Option>, /// Apple resources directory path relatively to project path. diff --git a/crossbundle/cli/src/commands/build/android.rs b/crossbundle/cli/src/commands/build/android.rs index 7ed35b0a..c7201c24 100644 --- a/crossbundle/cli/src/commands/build/android.rs +++ b/crossbundle/cli/src/commands/build/android.rs @@ -147,6 +147,7 @@ impl AndroidBuildCommand { config.status("Build finished successfully")?; Ok((android_manifest, sdk, aligned_apk_path)) } + /// Builds AAB with aapt2 tool and signs it with jarsigner pub fn execute_aab( &self, diff --git a/crossbundle/cli/src/commands/build/build_context.rs b/crossbundle/cli/src/commands/build/build_context.rs index 9d2ba1fd..f0041813 100644 --- a/crossbundle/cli/src/commands/build/build_context.rs +++ b/crossbundle/cli/src/commands/build/build_context.rs @@ -2,18 +2,14 @@ use crate::{ cargo_manifest::Metadata, error::{Error, Result}, }; -use cargo::core::Manifest; use crossbundle_tools::{ - commands::{ - android, apple, find_package_cargo_manifest_path, find_workspace_cargo_manifest_path, - parse_manifest, - }, - tools::AndroidSdk, + commands::{android::GenAndroidManifest, *}, + tools::*, types::{ android_manifest::AndroidManifest, apple_bundle::prelude::InfoPlist, AndroidTarget, AppleTarget, }, - utils::Config, + utils::*, }; use std::path::PathBuf; @@ -21,12 +17,13 @@ pub struct BuildContext { pub workspace_manifest_path: PathBuf, pub package_manifest_path: PathBuf, pub project_path: PathBuf, - pub manifest: Manifest, + pub manifest: cargo::core::Manifest, pub metadata: Metadata, pub target_dir: PathBuf, } impl BuildContext { + /// Create new instance of build context pub fn new(config: &Config, target_dir: Option) -> Result { let workspace_manifest_path = find_workspace_cargo_manifest_path(config.current_dir())?; let package_manifest_path = find_package_cargo_manifest_path(config.current_dir())?; @@ -52,6 +49,7 @@ impl BuildContext { }) } + /// Get package name from cargo manifest pub fn package_name(&self) -> String { if let Some(package_name) = self.metadata.android_package_name.clone() { return package_name; @@ -59,10 +57,12 @@ impl BuildContext { self.manifest.summary().name().to_string() } + /// Get package version from cargo manifest pub fn package_version(&self) -> String { self.manifest.summary().version().to_string() } + /// Get target sdk version from cargo manifest pub fn target_sdk_version(&self, sdk: &AndroidSdk) -> u32 { if let Some(target_sdk_version) = self.metadata.target_sdk_version { return target_sdk_version; @@ -70,6 +70,7 @@ impl BuildContext { sdk.default_platform() } + /// Get android build targets from cargo manifest pub fn android_build_targets(&self, build_targets: &Vec) -> Vec { if !build_targets.is_empty() { return build_targets.clone(); @@ -84,20 +85,46 @@ impl BuildContext { vec![AndroidTarget::Aarch64LinuxAndroid] } + /// Get android resources from cargo manifest pub fn android_res(&self) -> Option { self.metadata.android_res.clone() } + /// Get android assets from cargo manifest pub fn android_assets(&self) -> Option { self.metadata.android_assets.clone() } + /// Get android manifest from the path in cargo manifest or generate it with the given configuration pub fn gen_android_manifest( &self, sdk: &AndroidSdk, package_name: &str, debuggable: bool, ) -> Result { + let android_manifest = GenAndroidManifest { + app_id: Some(package_name.to_string()), + package_name: package_name.to_string(), + app_name: self.metadata.app_name.clone(), + version_name: self + .metadata + .version_name + .clone() + .unwrap_or(self.package_version()), + version_code: self.metadata.version_code.clone().unwrap_or(1), + min_sdk_version: self.metadata.min_sdk_version, + target_sdk_version: self + .metadata + .target_sdk_version + .unwrap_or_else(|| sdk.default_platform()), + max_sdk_version: self.metadata.max_sdk_version, + icon: self.metadata.icon.clone(), + debuggable, + permissions_sdk_23: self.metadata.android_permissions_sdk_23.clone(), + permissions: self.metadata.android_permissions.clone(), + features: self.metadata.android_features.clone(), + service: self.metadata.android_service.clone(), + }; if self.metadata.use_android_manifest { let path = self .metadata @@ -106,47 +133,21 @@ impl BuildContext { .unwrap_or_else(|| self.project_path.join("AndroidManifest.xml")); Ok(android::read_android_manifest(&path)?) } else if !self.metadata.use_android_manifest { - let manifest = android::gen_minimal_android_manifest( - self.metadata.android_package_name.clone(), - package_name, - self.metadata.app_name.clone(), - self.metadata - .version_name - .clone() - .unwrap_or(self.package_version()), - self.metadata.version_code.clone(), - self.metadata.min_sdk_version, - self.metadata - .target_sdk_version - .unwrap_or_else(|| sdk.default_platform()), - self.metadata.max_sdk_version, - self.metadata.icon.clone(), - debuggable, - self.metadata.android_permissions_sdk_23.clone(), - self.metadata.android_permissions.clone(), - self.metadata.android_features.clone(), - ); + let manifest = GenAndroidManifest::gen_android_manifest(&android_manifest); Ok(manifest) } else { let target_sdk_version = sdk.default_platform(); - Ok(android::gen_minimal_android_manifest( - None, - package_name, - None, - self.package_version(), - None, - None, + let minimal_android_manifest = GenAndroidManifest { target_sdk_version, - None, - None, - debuggable, - None, - None, - None, - )) + version_name: self.package_version(), + package_name: package_name.to_string(), + ..Default::default() + }; + Ok(minimal_android_manifest.gen_min_android_manifest()) } } + /// Get info plist from the path in cargo manifest or generate it with the given configuration pub fn gen_info_plist(&self, package_name: &String) -> Result { if self.metadata.use_info_plist { let path = self @@ -173,6 +174,7 @@ impl BuildContext { } } + /// Get apple build targets from cargo manifest pub fn apple_build_targets(&self, build_targets: &Vec) -> Vec { if !build_targets.is_empty() { return build_targets.clone(); @@ -187,10 +189,12 @@ impl BuildContext { vec![AppleTarget::X86_64AppleIos] } + /// Get apple resources from cargo manifest pub fn apple_res(&self) -> Option { self.metadata.apple_res.clone() } + /// Get apple assets from cargo manifest pub fn apple_assets(&self) -> Option { self.metadata.apple_assets.clone() } diff --git a/crossbundle/tools/src/commands/android/gen_manifest.rs b/crossbundle/tools/src/commands/android/gen_manifest.rs index 3e1acc4f..4be0300d 100644 --- a/crossbundle/tools/src/commands/android/gen_manifest.rs +++ b/crossbundle/tools/src/commands/android/gen_manifest.rs @@ -1,75 +1,118 @@ use android_manifest::*; -/// Generates minimal [`AndroidManifest`](android_manifest::AndroidManifest) with given -/// changes -pub fn gen_minimal_android_manifest( - app_id: Option, - package_name: &str, - app_name: Option, - version_name: String, - version_code: Option, - min_sdk_version: Option, - target_sdk_version: u32, - max_sdk_version: Option, - icon: Option, - debuggable: bool, - permissions_sdk_23: Option>, - permissions: Option>, - features: Option>, -) -> AndroidManifest { - AndroidManifest { - package: app_id.unwrap_or(format!("com.rust.{}", package_name.replace('-', "_"))), - version_name: Some(version_name), - version_code, - uses_sdk: Some(UsesSdk { - min_sdk_version: Some(min_sdk_version.unwrap_or(9)), - target_sdk_version: Some(target_sdk_version), - max_sdk_version, - }), - uses_permission_sdk_23: permissions_sdk_23.unwrap_or_default(), - uses_permission: permissions.unwrap_or_default(), - uses_feature: features.unwrap_or_default(), - application: Application { - has_code: Some(false), - label: Some(StringResourceOrString::string( - app_name.as_ref().unwrap_or(&package_name.to_owned()), - )), - debuggable: Some(debuggable), - icon: icon.map(|i| MipmapOrDrawableResource::mipmap(&i, None)), - theme: Some(Resource::new_with_package( - "Theme.DeviceDefault.NoActionBar.Fullscreen", - Some("android".to_string()), - )), - activity: vec![Activity { - name: "android.app.NativeActivity".to_string(), - resizeable_activity: Some(true), +#[derive(Default)] +pub struct GenAndroidManifest { + pub app_id: Option, + pub package_name: String, + pub app_name: Option, + pub version_name: String, + pub version_code: u32, + pub min_sdk_version: Option, + pub target_sdk_version: u32, + pub max_sdk_version: Option, + pub icon: Option, + pub debuggable: bool, + pub permissions_sdk_23: Option>, + pub permissions: Option>, + pub features: Option>, + pub service: Option>, +} + +impl GenAndroidManifest { + /// Generates [`AndroidManifest`](android_manifest::AndroidManifest) with + /// given changes + pub fn gen_android_manifest(&self) -> AndroidManifest { + AndroidManifest { + package: self + .app_id + .clone() + .unwrap_or(format!("com.rust.{}", self.package_name.replace('-', "_"))), + version_name: Some(self.version_name.clone()), + version_code: Some(self.version_code), + uses_sdk: Some(UsesSdk { + min_sdk_version: Some(self.min_sdk_version.unwrap_or(9)), + target_sdk_version: Some(self.target_sdk_version), + max_sdk_version: self.max_sdk_version, + }), + uses_permission_sdk_23: self.permissions_sdk_23.clone().unwrap_or_default(), + uses_permission: self.permissions.clone().unwrap_or_default(), + uses_feature: self.features.clone().unwrap_or_default(), + application: Application { + has_code: Some(false), label: Some(StringResourceOrString::string( - app_name.as_ref().unwrap_or(&package_name.to_owned()), + self.app_name + .as_ref() + .unwrap_or(&self.package_name.to_owned()), )), - config_changes: vec![ - ConfigChanges::Orientation, - ConfigChanges::KeyboardHidden, - ConfigChanges::ScreenSize, - ] - .into(), - meta_data: vec![MetaData { - name: Some("android.app.lib_name".to_string()), - value: Some(package_name.replace('-', "_")), - ..Default::default() - }], - intent_filter: vec![IntentFilter { - action: vec![Action { - name: Some("android.intent.action.MAIN".to_string()), + debuggable: Some(self.debuggable), + icon: self + .icon + .clone() + .map(|i| MipmapOrDrawableResource::mipmap(&i, None)), + theme: Some(Resource::new_with_package( + "Theme.DeviceDefault.NoActionBar.Fullscreen", + Some("android".to_string()), + )), + service: self.service.clone().unwrap_or_default(), + activity: vec![Activity { + name: "android.app.NativeActivity".to_string(), + resizeable_activity: Some(true), + label: Some(StringResourceOrString::string( + self.app_name + .as_ref() + .unwrap_or(&self.package_name.to_owned()), + )), + config_changes: vec![ + ConfigChanges::Orientation, + ConfigChanges::KeyboardHidden, + ConfigChanges::ScreenSize, + ] + .into(), + meta_data: vec![MetaData { + name: Some("android.app.lib_name".to_string()), + value: Some(self.package_name.replace('-', "_")), + ..Default::default() }], - category: vec![Category { - name: Some("android.intent.category.LAUNCHER".to_string()), + intent_filter: vec![IntentFilter { + action: vec![Action { + name: Some("android.intent.action.MAIN".to_string()), + }], + category: vec![Category { + name: Some("android.intent.category.LAUNCHER".to_string()), + }], + ..Default::default() }], ..Default::default() }], ..Default::default() - }], + }, + ..Default::default() + } + } + + /// Generate android manifest with minimal required tags. + pub fn gen_min_android_manifest(&self) -> AndroidManifest { + AndroidManifest { + package: self + .app_id + .clone() + .unwrap_or(format!("com.rust.{}", self.package_name.replace('-', "_"))), + version_name: Some(self.version_name.clone()), + version_code: Some(self.version_code), + application: Application { + has_code: Some(false), + label: Some(StringResourceOrString::string( + self.app_name + .as_ref() + .unwrap_or(&self.package_name.to_owned()), + )), + activity: vec![Activity { + name: "android.app.NativeActivity".to_string(), + ..Default::default() + }], + ..Default::default() + }, ..Default::default() - }, - ..Default::default() + } } } diff --git a/crossbundle/tools/src/commands/android/gen_minimal_unsigned_aab.rs b/crossbundle/tools/src/commands/android/gen_minimal_unsigned_aab.rs index b4972f58..5c4d366b 100644 --- a/crossbundle/tools/src/commands/android/gen_minimal_unsigned_aab.rs +++ b/crossbundle/tools/src/commands/android/gen_minimal_unsigned_aab.rs @@ -1,4 +1,4 @@ -use super::gen_minimal_android_manifest; +use super::GenAndroidManifest; use crate::{error::*, tools::AndroidSdk}; use std::path::{Path, PathBuf}; @@ -9,21 +9,14 @@ pub fn gen_minimal_unsigned_aab( target_sdk_version: u32, aab_build_dir: &Path, ) -> Result { - let android_manifest = gen_minimal_android_manifest( - None, - package_name, - None, - "0.0.1".to_string(), - None, - None, - target_sdk_version, - None, - None, - false, - None, - None, - None, - ); + let version_code = 1_u32; + let manifest = GenAndroidManifest { + package_name: String::from(package_name), + version_code, + ..Default::default() + }; + let android_manifest = manifest.gen_min_android_manifest(); + let manifest_path = super::save_android_manifest(aab_build_dir, &android_manifest)?; let apk_path = aab_build_dir.join(format!("{}_module.apk", package_name)); if !aab_build_dir.exists() { diff --git a/crossbundle/tools/tests/aab_full.rs b/crossbundle/tools/tests/aab_full.rs index bb1455ab..89d1afc1 100644 --- a/crossbundle/tools/tests/aab_full.rs +++ b/crossbundle/tools/tests/aab_full.rs @@ -1,7 +1,7 @@ use android_tools::java_tools::{android_dir, AabKey, JarSigner, KeyAlgorithm, Keytool}; use crossbundle_tools::{ commands::{ - android::{self, remove, rust_compile}, + android::{self, remove, rust_compile, GenAndroidManifest}, gen_minimal_project, }, tools::*, @@ -21,6 +21,7 @@ fn test_aab_full() { let sdk = AndroidSdk::from_env().unwrap(); let ndk = AndroidNdk::from_env(Some(sdk.sdk_path())).unwrap(); let target_sdk_version = 30; + let version_code = 1_u32; let profile = Profile::Debug; let build_target = AndroidTarget::Aarch64LinuxAndroid; let bevy_lib_name = format!("lib{}.so", package_name.replace("-", "_")); @@ -45,22 +46,14 @@ fn test_aab_full() { println!("rust was compiled for bevy example"); // Generates manifest - let manifest = android::gen_minimal_android_manifest( - None, - &package_name, - None, - "0.0.1".to_string(), - None, - None, - target_sdk_version, - None, - None, - false, - None, - None, - None, - ); - let manifest_path = android::save_android_manifest(&android_build_dir, &manifest).unwrap(); + let manifest = GenAndroidManifest { + package_name: package_name.clone(), + version_code, + ..Default::default() + }; + let android_manifest = manifest.gen_min_android_manifest(); + let manifest_path = + android::save_android_manifest(&android_build_dir, &android_manifest).unwrap(); assert!(manifest_path.exists()); // Compiles resources diff --git a/crossbundle/tools/tests/aapt2.rs b/crossbundle/tools/tests/aapt2.rs index 011b2911..bbaa4fc9 100644 --- a/crossbundle/tools/tests/aapt2.rs +++ b/crossbundle/tools/tests/aapt2.rs @@ -1,5 +1,5 @@ use crossbundle_tools::{ - commands::android::{gen_minimal_android_manifest, save_android_manifest}, + commands::android::{save_android_manifest, GenAndroidManifest}, tools::AndroidSdk, }; @@ -43,6 +43,7 @@ fn test_aapt2_link() { // Specifies path to needed resources let sdk = AndroidSdk::from_env().unwrap(); + let version_code = 1_u32; let user_dirs = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); let dir = user_dirs.parent().unwrap().parent().unwrap().to_path_buf(); let res_path = dir @@ -62,24 +63,15 @@ fn test_aapt2_link() { assert!(compiled_res.exists()); // Generates minimal android manifest - let manifest = gen_minimal_android_manifest( - None, - "example", - None, - "0.0.1".to_string(), - Some(1), - Some(9), - 30, - None, - None, - false, - None, - None, - None, - ); + let manifest = GenAndroidManifest { + package_name: String::from("example"), + version_code, + ..Default::default() + }; + let android_manifest = manifest.gen_min_android_manifest(); // Saves android manifest into temporary directory - let manifest_path = save_android_manifest(&tempdir, &manifest).unwrap(); + let manifest_path = save_android_manifest(&tempdir, &android_manifest).unwrap(); assert!(manifest_path.exists()); // Link files and generates apk file diff --git a/crossbundle/tools/tests/android_full.rs b/crossbundle/tools/tests/android_full.rs index c19ca408..433659dd 100644 --- a/crossbundle/tools/tests/android_full.rs +++ b/crossbundle/tools/tests/android_full.rs @@ -1,7 +1,7 @@ use android_tools::java_tools::{android_dir, AabKey, KeyAlgorithm, Keytool}; use crossbundle_tools::{ commands::{ - android::{self, remove, rust_compile}, + android::{self, remove, rust_compile, GenAndroidManifest}, gen_minimal_project, }, tools::{AndroidNdk, AndroidSdk}, @@ -21,6 +21,7 @@ fn test_android_full() { let sdk = AndroidSdk::from_env().unwrap(); let ndk = AndroidNdk::from_env(Some(sdk.sdk_path())).unwrap(); let target_sdk_version = 30; + let version_code = 1_u32; let profile = Profile::Release; let build_target = AndroidTarget::Aarch64LinuxAndroid; let quad_lib_name = format!("lib{}.so", quad_package_name.replace("-", "_")); @@ -57,23 +58,14 @@ fn test_android_full() { // Gen android manifest let target_dir = project_path.join("target"); - let manifest = android::gen_minimal_android_manifest( - None, - &quad_package_name, - None, - "0.0.1".to_string(), - None, - None, - target_sdk_version, - None, - None, - true, - None, - None, - None, - ); + let manifest = GenAndroidManifest { + package_name: quad_package_name.clone(), + version_code, + ..Default::default() + }; + let android_manifest = manifest.gen_min_android_manifest(); let apk_build_dir = target_dir.join(&profile).join("apk"); - let manifest_path = android::save_android_manifest(&apk_build_dir, &manifest).unwrap(); + let manifest_path = android::save_android_manifest(&apk_build_dir, &android_manifest).unwrap(); assert!(manifest_path.exists()); // Gen unaligned apk @@ -84,8 +76,8 @@ fn test_android_full() { &manifest_path, None, None, - &manifest.application.label.unwrap().to_string(), - manifest.uses_sdk.unwrap().target_sdk_version.unwrap(), + &quad_package_name, + target_sdk_version, ) .unwrap(); assert!(unaligned_apk_path.exists()); @@ -105,8 +97,13 @@ fn test_android_full() { .unwrap(); // Align apk - let aligned_apk_path = - android::align_apk(&sdk, &unaligned_apk_path, &manifest.package, &apk_build_dir).unwrap(); + let aligned_apk_path = android::align_apk( + &sdk, + &unaligned_apk_path, + &manifest.package_name, + &apk_build_dir, + ) + .unwrap(); assert!(aligned_apk_path.exists()); // Removes old keystore if it exists diff --git a/docs/main-project-configuration.md b/docs/main-project-configuration.md index fec61982..76cdfd0d 100644 --- a/docs/main-project-configuration.md +++ b/docs/main-project-configuration.md @@ -28,6 +28,14 @@ android_res = "res/android" apple_build_targets = ["aarch64-apple-ios", "x86_64-apple-ios"] apple_assets = "assets" apple_res = "res/apple" + +[[package.metadata.android_permissions]] +name = "android.permission.INTERNET" + +[[package.metadata.android_service]] +name = "UpdateService" +intent_filter = [] +meta_data = [] ``` ### ⚙️ Сonfiguration through separate files diff --git a/examples/bevy-2d/Cargo.toml b/examples/bevy-2d/Cargo.toml index 9dc6ca98..a3e0b8b6 100644 --- a/examples/bevy-2d/Cargo.toml +++ b/examples/bevy-2d/Cargo.toml @@ -24,3 +24,11 @@ android_res = "res/android" apple_build_targets = ["aarch64-apple-ios", "x86_64-apple-ios"] apple_assets = "assets" apple_res = "res/apple" + +[[package.metadata.android_permissions]] +name = "android.permission.INTERNET" + +[[package.metadata.android_service]] +name = "UpdateService" +intent_filter = [] +meta_data = []