diff --git a/assets/images/icon.png b/assets/images/icon.png index 527d360c..d00f7831 100644 Binary files a/assets/images/icon.png and b/assets/images/icon.png differ diff --git a/crossbundle/cli/README.md b/crossbundle/cli/README.md index e23087aa..c68ffcf0 100644 --- a/crossbundle/cli/README.md +++ b/crossbundle/cli/README.md @@ -51,11 +51,13 @@ See [installation documentation](https://crossbow.dodorare.com/install/index.htm ## Cargo.toml Metadata syntax ```toml -[[package.metadata.android]] +[[package.metadata]] # Cross-platform user-friendly application name for your app. app_name = "Example" # Cross-platform assets directory path relatively to project path. -assets = "assets" +assets = ["assets"] +# Cross-platform icon path to generate icons for Android and iOS. +icon = "../../assets/images/icon.png" [[package.metadata.android]] # Android application wrapper: supports ndk-glue and sokol @@ -65,9 +67,9 @@ app_name = "Example" # Path to AndroidManifest.xml file manifest_path = "path/to/AndroidManifest.xml" # Android resources directory path relatively to project path. -res = "res/android" +resources = ["res/android"] # Android assets directory path relatively to project path. -assets = "assets" +assets = ["assets"] # Android targets to build on debug or release. debug_build_targets = ["aarch64-linux-android"] release_build_targets = ["aarch64-linux-android"] @@ -103,27 +105,17 @@ authorities = "org.khronos.openxr.runtime_broker;org.khronos.openxr.system_runti # This will be made optional if/when cargo-apk migrates to aapt2. name = "org.khronos.openxr" -# See https://developer.android.com/guide/topics/manifest/uses-feature-element -# -# Note: there can be multiple .uses_feature entries. -[[package.metadata.android.manifest.features]] -name = "android.hardware.vulkan.level" -required = true -version = 1 - # See https://developer.android.com/guide/topics/manifest/meta-data-element [[package.metadata.android.manifest.application.meta_data]] name = "com.oculus.vr.focusaware" value = "true" [package.metadata.apple] -# The user-friendly application name for your app. Displayed in the applications menu -app_name = "Example" # Apple targets to build on debug or release. debug_build_targets = ["aarch64-apple-ios"] release_build_targets = ["aarch64-apple-ios", "x86_64-apple-ios"] # Apple resources directory path relatively to project path. -res = "res/apple" +resources = ["res/apple"] ``` ## CLI options and flags diff --git a/crossbundle/cli/src/commands/build/android.rs b/crossbundle/cli/src/commands/build/android.rs index 2f37d888..70a84047 100644 --- a/crossbundle/cli/src/commands/build/android.rs +++ b/crossbundle/cli/src/commands/build/android.rs @@ -1,9 +1,13 @@ use super::{BuildContext, SharedBuildCommand}; -use crate::error::Result; +use crate::{error::*, types::CrossbowMetadata}; use android_manifest::AndroidManifest; use android_tools::java_tools::{JarSigner, Key}; use clap::Parser; -use crossbundle_tools::{commands::android::*, error::CommandExt, types::*}; +use crossbundle_tools::{ + commands::{android::*, combine_folders}, + error::CommandExt, + types::*, +}; use std::path::{Path, PathBuf}; /// Specifies flags and options needed to build application @@ -96,11 +100,15 @@ impl AndroidBuildCommand { std::env::set_var("ANDROID_SDK_ROOT", sdk.sdk_path()); } + config.status("Preparing resources and assets")?; + let (assets, resources) = + Self::prepare_assets_and_resources(&context.config, &android_build_dir)?; + config.status("Generating gradle project")?; let gradle_project_path = gen_gradle_project( &android_build_dir, - &context.config.get_android_assets(), - &context.config.android.res, + &assets, + &resources, &context.config.android.plugins, )?; @@ -196,6 +204,9 @@ impl AndroidBuildCommand { let manifest = Self::get_android_manifest(context, AndroidStrategy::NativeApk)?; config.status_message("Generating", "AndroidManifest.xml")?; let manifest_path = save_android_manifest(&native_build_dir, &manifest)?; + config.status("Preparing resources and assets")?; + let (assets, resources) = + Self::prepare_assets_and_resources(&context.config, &android_build_dir)?; config.status_message("Compiling", "lib")?; let target_sdk_version = Self::target_sdk_version(&manifest, &sdk); @@ -218,8 +229,8 @@ impl AndroidBuildCommand { &project_path, &native_build_dir, &manifest_path, - &context.config.get_android_assets(), - &context.config.android.res, + &assets, + &resources, &package_name, target_sdk_version, )?; @@ -279,6 +290,9 @@ impl AndroidBuildCommand { let manifest = Self::get_android_manifest(context, AndroidStrategy::NativeAab)?; config.status_message("Generating", "AndroidManifest.xml")?; let manifest_path = save_android_manifest(&native_build_dir, &manifest)?; + config.status("Preparing resources and assets")?; + let (assets, resources) = + Self::prepare_assets_and_resources(&context.config, &android_build_dir)?; config.status_message("Compiling", "lib")?; let target_sdk_version = Self::target_sdk_version(&manifest, &sdk); @@ -297,7 +311,7 @@ impl AndroidBuildCommand { config.status_message("Generating", "proto format APK file")?; - let compiled_res = if let Some(res) = &context.config.android.res { + let compiled_res = if let Some(res) = &resources { let compiled_res_path = native_build_dir.join("compiled_res"); if !compiled_res_path.exists() { std::fs::create_dir_all(&compiled_res_path)?; @@ -316,7 +330,7 @@ impl AndroidBuildCommand { let mut aapt2_link = sdk.aapt2()? .link_compiled_res(compiled_res, &apk_path, &manifest_path); - if let Some(assets) = &context.config.get_android_assets() { + if let Some(assets) = &assets { aapt2_link.assets(assets.clone()) } else { &mut aapt2_link @@ -496,6 +510,7 @@ impl AndroidBuildCommand { sdk.default_platform() } + /// Get min sdk version from cargo manifest pub fn min_sdk_version(android_manifest: &AndroidManifest) -> u32 { android_manifest .uses_sdk @@ -547,4 +562,44 @@ impl AndroidBuildCommand { }); Ok(manifest) } + + /// Prepare assets and resources for the application. + /// + /// Also, this function will generate mipmap icon resources if specified in the + /// CrossbowMetadata config. + pub fn prepare_assets_and_resources( + config: &CrossbowMetadata, + out_dir: &Path, + ) -> Result<(Option, Option)> { + let res = config.get_android_resources(); + let gen_resources = if res.is_empty() && config.icon.is_none() { + None + } else { + let path = out_dir.join("gen_resources"); + std::fs::remove_dir_all(&path).ok(); + combine_folders(res, &path)?; + + if let Some(icon) = &config.icon { + ImageGeneration { + icon_path: icon.to_owned(), + out_icon_name: "ic_launcher.png".to_owned(), + output_path: path.clone(), + force: true, + } + .gen_mipmap_res_from_icon()?; + } + Some(path) + }; + + let assets = config.get_android_assets(); + let gen_assets = if !res.is_empty() { + let path = out_dir.join("gen_assets"); + std::fs::remove_dir_all(&path).ok(); + combine_folders(assets, &path)?; + Some(path) + } else { + None + }; + Ok((gen_assets, gen_resources)) + } } diff --git a/crossbundle/cli/src/commands/build/apple.rs b/crossbundle/cli/src/commands/build/apple.rs index 3011b732..089137ff 100644 --- a/crossbundle/cli/src/commands/build/apple.rs +++ b/crossbundle/cli/src/commands/build/apple.rs @@ -1,8 +1,11 @@ use super::{BuildContext, SharedBuildCommand}; -use crate::error::*; +use crate::{error::*, types::CrossbowMetadata}; use apple_bundle::prelude::InfoPlist; use clap::Parser; -use crossbundle_tools::{commands::apple, types::*}; +use crossbundle_tools::{ + commands::{apple, combine_folders}, + types::*, +}; use std::path::{Path, PathBuf}; #[derive(Parser, Clone, Debug)] @@ -106,31 +109,24 @@ impl IosBuildCommand { )?; let out_dir = context.target_dir.join(rust_triple).join(&profile); let bin_path = out_dir.join(&name); + config.status("Generating app folder")?; let apple_target_dir = &context .target_dir .join("apple") .join(rust_triple) .join(&profile); - let app_path = apple::gen_apple_app_folder( - apple_target_dir, - name, - context - .config - .get_apple_assets() - .as_ref() - .map(|r| project_path.join(r)), - context - .config - .apple - .res - .as_ref() - .map(|r| project_path.join(r)), - )?; + + config.status("Preparing resources and assets")?; + let (assets, resources) = + Self::prepare_assets_and_resources(&context.config, apple_target_dir)?; + + let app_path = apple::gen_apple_app_folder(apple_target_dir, name, assets, resources)?; config.status("Copying binary to app folder")?; std::fs::copy(&bin_path, &app_path.join(&name)).unwrap(); config.status_message("Generating", "Info.plist")?; apple::save_info_plist(&app_path, properties, false).unwrap(); + if self.identity.is_some() { config.status("Starting code signing process")?; apple::copy_profile( @@ -154,6 +150,7 @@ impl IosBuildCommand { apple::codesign(&app_path, true, self.identity.clone(), Some(xcent_path))?; config.status("Code signing process finished")?; } + config.status("Generating ipa file")?; apple::gen_apple_ipa(apple_target_dir, &app_path, name)?; config.status("Build finished successfully")?; @@ -199,4 +196,33 @@ impl IosBuildCommand { }); Ok(info_plist) } + + /// Prepare assets and resources for the application. + pub fn prepare_assets_and_resources( + config: &CrossbowMetadata, + out_dir: &Path, + ) -> Result<(Option, Option)> { + let res = config.get_apple_resources(); + let gen_resources = if res.is_empty() && config.icon.is_none() { + None + } else { + let path = out_dir.join("gen_resources"); + std::fs::remove_dir_all(&path).ok(); + combine_folders(res, &path)?; + + // TODO: Generate icons + Some(path) + }; + + let assets = config.get_apple_assets(); + let gen_assets = if !res.is_empty() { + let path = out_dir.join("gen_assets"); + std::fs::remove_dir_all(&path).ok(); + combine_folders(assets, &path)?; + Some(path) + } else { + None + }; + Ok((gen_assets, gen_resources)) + } } diff --git a/crossbundle/cli/src/types/android_config.rs b/crossbundle/cli/src/types/android_config.rs index 1539c52e..673c56b7 100644 --- a/crossbundle/cli/src/types/android_config.rs +++ b/crossbundle/cli/src/types/android_config.rs @@ -18,11 +18,17 @@ pub struct AndroidConfig { /// **Important:** If this field specified - `manifest` property will be ignored. pub manifest_path: Option, /// Android resources directory path relatively to project path. - pub res: Option, + /// + /// If specified more than one - all resources will be placed into one directory. + #[serde(default)] + pub resources: Vec, /// Custom Android assets directory path relatively to project path. /// + /// If specified more than one - all assets will be placed into one directory. + /// /// **Important:** This property has higher priority than global property. - pub assets: Option, + #[serde(default)] + pub assets: Vec, /// Android debug build targets. #[serde(default)] pub debug_build_targets: Vec, diff --git a/crossbundle/cli/src/types/apple_config.rs b/crossbundle/cli/src/types/apple_config.rs index 062985c3..4e020128 100644 --- a/crossbundle/cli/src/types/apple_config.rs +++ b/crossbundle/cli/src/types/apple_config.rs @@ -12,11 +12,17 @@ pub struct AppleConfig { /// **Important:** If this field specified - `info_plist` property will be ignored. pub info_plist_path: Option, /// Apple `resources` directory path relatively to project path. - pub res: Option, + /// + /// If specified more than one - all resources will be placed into one directory. + #[serde(default)] + pub resources: Vec, /// Custom Apple `assets` directory path relatively to project path. /// + /// If specified more than one - all assets will be placed into one directory. + /// /// **Important:** This property has higher priority than global property. - pub assets: Option, + #[serde(default)] + pub assets: Vec, /// Apple debug build targets. #[serde(default)] pub debug_build_targets: Vec, diff --git a/crossbundle/cli/src/types/mod.rs b/crossbundle/cli/src/types/mod.rs index 1585e142..090436c1 100644 --- a/crossbundle/cli/src/types/mod.rs +++ b/crossbundle/cli/src/types/mod.rs @@ -22,17 +22,22 @@ pub struct CrossbowMetadata { pub app_name: Option, /// Assets directory path relatively to project path. /// + /// If specified more than one - all assets will be placed into one directory. + /// /// **Important:** This property has lower priority than Android or Apple `assets` /// property. - pub assets: Option, + #[serde(default)] + pub assets: Vec, /// Cross-platform permissions for Android and Apple. /// /// **Important:** This property has lower priority than AndroidManifest or Apple /// Info.plist properties. #[serde(default)] pub permissions: Vec, - // TODO: Add `icon` field and icon generation. - // pub icon: Option, + /// Cross-platform icon for Android and Apple. + /// + /// All necessary icons will be automatically generated for Android and iOS. + pub icon: Option, #[cfg(feature = "android")] #[serde(default)] pub android: AndroidConfig, @@ -43,12 +48,30 @@ pub struct CrossbowMetadata { impl CrossbowMetadata { #[cfg(feature = "android")] - pub fn get_android_assets(&self) -> Option { - self.android.assets.clone().or_else(|| self.assets.clone()) + pub fn get_android_assets(&self) -> &[PathBuf] { + if !self.android.assets.is_empty() { + &self.android.assets + } else { + &self.assets + } + } + + #[cfg(feature = "apple")] + pub fn get_apple_assets(&self) -> &[PathBuf] { + if !self.apple.assets.is_empty() { + &self.apple.assets + } else { + &self.assets + } + } + + #[cfg(feature = "android")] + pub fn get_android_resources(&self) -> &[PathBuf] { + &self.android.resources } #[cfg(feature = "apple")] - pub fn get_apple_assets(&self) -> Option { - self.apple.assets.clone().or_else(|| self.assets.clone()) + pub fn get_apple_resources(&self) -> &[PathBuf] { + &self.apple.resources } } diff --git a/crossbundle/tools/Cargo.toml b/crossbundle/tools/Cargo.toml index 004ae1a3..4fb239d3 100644 --- a/crossbundle/tools/Cargo.toml +++ b/crossbundle/tools/Cargo.toml @@ -28,6 +28,7 @@ which = "4.2" tempfile = "3.3" zip = "0.5" zip-extensions = "0.6.1" +image = { version = "0.24.3", default-features = false, features = ["png", "jpeg"] } itertools = "0.10" cargo = "0.63.1" diff --git a/crossbundle/tools/src/commands/android/common/gen_mipmap_res.rs b/crossbundle/tools/src/commands/android/common/gen_mipmap_res.rs new file mode 100644 index 00000000..7bcf8520 --- /dev/null +++ b/crossbundle/tools/src/commands/android/common/gen_mipmap_res.rs @@ -0,0 +1,133 @@ +use crate::error::*; +use image::{DynamicImage, GenericImageView, ImageFormat, Rgba}; +use serde::{Deserialize, Serialize}; +use std::{fs::File, path::PathBuf}; + +#[derive(Debug, Default, Clone, Deserialize, Serialize)] +pub struct ImageGeneration { + /// The path to the source icon will be provided to generate mipmap resources. + pub icon_path: PathBuf, + /// The output name of the icon that will be generated in mipmap resources. + pub out_icon_name: String, + /// Output path to Android resources for generated mipmap resources. + pub output_path: PathBuf, + /// Overwrite android resource directory. + pub force: bool, +} + +impl ImageGeneration { + /// Generate mipmap resources from the icon. Width and height of the icon must be + /// equal. + pub fn gen_mipmap_res_from_icon(&self) -> Result<()> { + let image = image::open(&self.icon_path)?; + let (width, height) = image.dimensions(); + if width != height || height % 2 != 0 { + return Err(Error::WidthAndHeightDifSizes); + } + for (name, size) in get_icon_sizes() { + let scaled = image.thumbnail(size, size); + let img = Self::round_image(&scaled, size); + // TODO: Add shadow. See this: https://github.com/romannurik/AndroidAssetStudio + self.write_image(&name, img)?; + } + Ok(()) + } + + fn round_image(scaled: &DynamicImage, size: u32) -> DynamicImage { + let border = size as f64 / 25.0; + let radius = size as f64 / 2.0; + let cxy = size as f64 / 2.0 - 1.0; + let mut img = scaled.to_rgba8(); + for pix in img.clone().enumerate_pixels() { + let s = (pix.0 as f64 - cxy).hypot(pix.1 as f64 - cxy); + if s > radius - border { + img.put_pixel(pix.0, pix.1, Rgba([0, 0, 0, 0])); + } + } + img.into() + } + + /// Check res directory and then create mipmap resource if it's empty + pub fn write_image(&self, mipmap_name: &str, scaled: DynamicImage) -> Result<()> { + let mipmap_dirs = self.output_path.join(format!("mipmap-{}", mipmap_name)); + if mipmap_dirs.exists() { + if self.force { + std::fs::remove_dir(&mipmap_dirs)?; + std::fs::create_dir_all(&mipmap_dirs)?; + } + return Ok(()); + } else if !mipmap_dirs.exists() { + std::fs::create_dir_all(&mipmap_dirs)?; + } + let mut output = File::create(mipmap_dirs.join(&self.out_icon_name))?; + scaled.write_to(&mut output, ImageFormat::Png)?; + Ok(()) + } +} + +/// Scale image down according to scale ratio. +fn get_icon_sizes() -> Vec<(String, u32)> { + vec![ + (MipmapDpi::Xxxhdpi.to_string(), 192), + (MipmapDpi::Xxhdpi.to_string(), 144), + (MipmapDpi::Xhdpi.to_string(), 96), + (MipmapDpi::Hdpi.to_string(), 72), + (MipmapDpi::Mdpi.to_string(), 48), + (MipmapDpi::Ldpi.to_string(), 36), + ] +} + +pub enum MipmapDpi { + Xxxhdpi, + Xxhdpi, + Xhdpi, + Hdpi, + Mdpi, + Ldpi, +} + +impl ToString for MipmapDpi { + fn to_string(&self) -> String { + match self { + Self::Xxxhdpi => "xxxhdpi".to_string(), + Self::Xxhdpi => "xxhdpi".to_string(), + Self::Xhdpi => "xhdpi".to_string(), + Self::Hdpi => "hdpi".to_string(), + Self::Mdpi => "mdpi".to_string(), + Self::Ldpi => "ldpi".to_string(), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::env::current_dir; + + #[test] + fn test_icon_gen() { + let tempfile = tempfile::tempdir().unwrap(); + let res_dir_path = tempfile.path().to_path_buf(); + let icon_path = current_dir() + .unwrap() + .parent() + .unwrap() + .parent() + .unwrap() + .join("assets") + .join("images") + .join("icon.png"); + let image_generation = ImageGeneration { + icon_path, + out_icon_name: "ic_launcher.png".to_owned(), + output_path: res_dir_path.join("res"), + force: false, + }; + image_generation.gen_mipmap_res_from_icon().unwrap(); + assert!(res_dir_path + .join("res") + .join("mipmap-hdpi") + .join("ic_launcher.png") + .exists()) + } +} diff --git a/crossbundle/tools/src/commands/android/common/mod.rs b/crossbundle/tools/src/commands/android/common/mod.rs index 9fb485e5..105c84c9 100644 --- a/crossbundle/tools/src/commands/android/common/mod.rs +++ b/crossbundle/tools/src/commands/android/common/mod.rs @@ -2,6 +2,7 @@ mod attach_logger; mod detect_abi; mod extract_archive; mod gen_key; +mod gen_mipmap_res; mod helper_functions; mod read_manifest; mod rust_compile; @@ -13,6 +14,7 @@ pub use attach_logger::*; pub use detect_abi::*; pub use extract_archive::*; pub use gen_key::*; +pub use gen_mipmap_res::*; pub use helper_functions::*; pub use read_manifest::*; pub use rust_compile::*; diff --git a/crossbundle/tools/src/commands/apple/read_plist.rs b/crossbundle/tools/src/commands/apple/read_plist.rs index 48a55032..63a2a413 100644 --- a/crossbundle/tools/src/commands/apple/read_plist.rs +++ b/crossbundle/tools/src/commands/apple/read_plist.rs @@ -5,6 +5,9 @@ use std::path::Path; /// Read file and deserializes `Info.plist` into /// [`InfoPlist`](apple_bundle::prelude::InfoPlist). pub fn read_info_plist(path: &Path) -> Result { - apple_bundle::from_file(path) - .map_err(|_| AppleError::FailedToFindInfoPlist(path.to_string_lossy().to_string()).into()) + if !path.exists() { + return Err(AppleError::FailedToFindInfoPlist(path.to_string_lossy().to_string()).into()); + } + let res = apple_bundle::from_file(path)?; + Ok(res) } diff --git a/crossbundle/tools/src/commands/common/combine_folders.rs b/crossbundle/tools/src/commands/common/combine_folders.rs new file mode 100644 index 00000000..d1e8a824 --- /dev/null +++ b/crossbundle/tools/src/commands/common/combine_folders.rs @@ -0,0 +1,22 @@ +use crate::error::Result; +use fs_extra::dir::{copy as copy_dir, CopyOptions}; +use std::fs::create_dir_all; +use std::path::{Path, PathBuf}; + +/// Place all folders' inner files into output directory. +pub fn combine_folders(folder_paths: &[PathBuf], output: &Path) -> Result<()> { + // Create output directory if it doesn't exist. + if !output.exists() { + create_dir_all(output)?; + } + + // Copy options + let mut options = CopyOptions::new(); + options.overwrite = true; + options.content_only = true; + for folder_path in folder_paths { + copy_dir(dunce::simplified(folder_path), output, &options)?; + } + + Ok(()) +} diff --git a/crossbundle/tools/src/commands/common/mod.rs b/crossbundle/tools/src/commands/common/mod.rs index 88d242ed..26e4331a 100644 --- a/crossbundle/tools/src/commands/common/mod.rs +++ b/crossbundle/tools/src/commands/common/mod.rs @@ -1,10 +1,12 @@ //! Common commands used in all platforms. +mod combine_folders; mod create_project; mod find_cargo_manifest_path; mod gen_minimal_project; mod parse_manifest; +pub use combine_folders::*; pub use create_project::*; pub use find_cargo_manifest_path::*; pub use gen_minimal_project::*; diff --git a/crossbundle/tools/src/commands/common/parse_manifest.rs b/crossbundle/tools/src/commands/common/parse_manifest.rs index 188cdf76..637d5c42 100644 --- a/crossbundle/tools/src/commands/common/parse_manifest.rs +++ b/crossbundle/tools/src/commands/common/parse_manifest.rs @@ -1,4 +1,4 @@ -use crate::error::Error; +use crate::error::*; use cargo::{ core::{EitherManifest, Manifest, SourceId}, util::toml::read_manifest, @@ -7,7 +7,7 @@ use cargo::{ use std::path::Path; /// Read manifest files and deserialize it -pub fn parse_manifest(manifest_path: &Path) -> crate::error::Result { +pub fn parse_manifest(manifest_path: &Path) -> Result { let source_id = SourceId::for_path(manifest_path)?; let cargo_config = Config::default()?; let either_manifest = read_manifest(manifest_path, source_id, &cargo_config) diff --git a/crossbundle/tools/src/error.rs b/crossbundle/tools/src/error.rs index c7e9383b..0752a6b6 100644 --- a/crossbundle/tools/src/error.rs +++ b/crossbundle/tools/src/error.rs @@ -99,6 +99,10 @@ pub enum Error { path: PathBuf, cause: std::io::Error, }, + /// Width and height of the icon have different sizes. Choose another image + WidthAndHeightDifSizes, + /// Icons already exist. Use overwrite flag + IconsAlreadyExist, /// Failed to find the manifest in path: {0} FailedToFindManifest(PathBuf), /// Invalid profile: {0} @@ -126,6 +130,8 @@ pub enum Error { /// Android error: {0:?} #[cfg(feature = "android")] Android(#[from] AndroidError), + /// Image crate error: {0:?} + ImageError(#[from] image::ImageError), /// Apple error: {0:?} #[cfg(feature = "apple")] Apple(#[from] AppleError), diff --git a/docs/src/crossbow/configuration.md b/docs/src/crossbow/configuration.md index 8acc74ec..bce4b3af 100644 --- a/docs/src/crossbow/configuration.md +++ b/docs/src/crossbow/configuration.md @@ -14,24 +14,20 @@ authors = ["Example "] edition = "2021" [dependencies] -crossbow = "0.1.0" +crossbow = "0.2.0" -[package.metadata.android] +[package.metadata] app_name = "Game" -target_sdk_version = 30 -icon = "ic_launcher" +assets = ["assets"] +icon = "path/to/icon.png" +[package.metadata.android] release_build_targets = ["aarch64-linux-android"] -assets = "assets" -res = "res/android" +resources = ["res/android"] [package.metadata.apple] -app_name = "Game" -icon = "ic_launcher" - release_build_targets = ["aarch64-apple-ios", "x86_64-apple-ios"] -assets = "assets" -res = "res/apple" +resources = ["res/apple"] ``` ### Сonfiguration through separate files diff --git a/docs/src/tutorials/hello-world.md b/docs/src/tutorials/hello-world.md index 26815842..d06b612a 100644 --- a/docs/src/tutorials/hello-world.md +++ b/docs/src/tutorials/hello-world.md @@ -34,13 +34,9 @@ edition = "2021" [dependencies] crossbundle = "*" -[package.metadata.android] -icon = "ic_launcher" -res = "res/android" - -[package.metadata.apple] -icon = "ic_launcher" -res = "res/apple" +[package.metadata] +app_name = "My Project" +icon = "path/to/icon.png" ``` ```rust diff --git a/examples/bevy-2d/Cargo.toml b/examples/bevy-2d/Cargo.toml index 0fae8c5d..4cff0c8e 100644 --- a/examples/bevy-2d/Cargo.toml +++ b/examples/bevy-2d/Cargo.toml @@ -12,12 +12,12 @@ bevy = { version = "0.7.0", features = ["mp3"] } [package.metadata] app_name = "Bevy 2D" -assets = "../../assets" +assets = ["../../assets"] [package.metadata.android] release_build_targets = ["aarch64-linux-android"] -res = "../../assets/res/android" +resources = ["../../assets/res/android"] [package.metadata.apple] release_build_targets = ["aarch64-apple-ios", "x86_64-apple-ios"] -res = "../../assets/res/apple" +resources = ["../../assets/res/apple"] diff --git a/examples/bevy-explorer/Cargo.toml b/examples/bevy-explorer/Cargo.toml index 9070dfe6..fa8cf422 100644 --- a/examples/bevy-explorer/Cargo.toml +++ b/examples/bevy-explorer/Cargo.toml @@ -16,12 +16,12 @@ jsonrpsee = { version = "0.10.1", features = ["async-client", "client-ws-transpo [package.metadata] app_name = "Bevy Explorer" -assets = "../../assets" +assets = ["../../assets"] [package.metadata.android] release_build_targets = ["aarch64-linux-android"] -res = "../../assets/res/android" +resources = ["../../assets/res/android"] [package.metadata.apple] release_build_targets = ["aarch64-apple-ios", "x86_64-apple-ios"] -res = "../../assets/res/apple" +resources = ["../../assets/res/apple"] diff --git a/examples/macroquad-3d/Cargo.toml b/examples/macroquad-3d/Cargo.toml index b4e794b9..d03b1bde 100644 --- a/examples/macroquad-3d/Cargo.toml +++ b/examples/macroquad-3d/Cargo.toml @@ -11,15 +11,14 @@ anyhow = "1.0" macroquad = "0.3.7" [package.metadata] -app_wrapper = "sokol" app_name = "Macroquad 3D" -assets = "../../assets" +assets = ["../../assets"] +icon = "../../assets/images/icon.png" [package.metadata.android] +app_wrapper = "sokol" manifest_path = "res/AndroidManifest.xml" release_build_targets = ["aarch64-linux-android"] -res = "../../assets/res/android" [package.metadata.apple] release_build_targets = ["aarch64-apple-ios", "x86_64-apple-ios"] -res = "../../assets/res/apple" diff --git a/examples/macroquad-permissions/Cargo.toml b/examples/macroquad-permissions/Cargo.toml index 71fd18dd..f8bac34f 100644 --- a/examples/macroquad-permissions/Cargo.toml +++ b/examples/macroquad-permissions/Cargo.toml @@ -20,8 +20,7 @@ permissions = ["camera", "microphone", "photos", "storage-read"] [package.metadata.android] app_wrapper = "sokol" release_build_targets = ["aarch64-linux-android"] -res = "../../assets/res/android" -# plugins_remote = ["com.crossbow.admob:admob:0.2.0"] +resources = ["../../assets/res/android"] [[package.metadata.android.plugins_local_projects]] include = ":crossbow" @@ -38,6 +37,7 @@ package = "com.crossbow.example.permissions" [package.metadata.android.manifest.uses_sdk] min_sdk_version = 19 target_sdk_version = 30 + [package.metadata.android.manifest.application] icon = "@mipmap/ic_launcher" round_icon = "@mipmap/ic_launcher" @@ -48,7 +48,7 @@ round_icon = "@mipmap/ic_launcher" [package.metadata.apple] release_build_targets = ["aarch64-apple-ios", "x86_64-apple-ios"] -res = "../../assets/res/apple" +resources = ["../../assets/res/apple"] [package.metadata.apple.info_plist] CFBundleIdentifier = "com.crossbow.example.permissions" diff --git a/examples/macroquad-play-billing/Cargo.toml b/examples/macroquad-play-billing/Cargo.toml index 9d6a4426..0fdcf87d 100644 --- a/examples/macroquad-play-billing/Cargo.toml +++ b/examples/macroquad-play-billing/Cargo.toml @@ -19,7 +19,7 @@ app_name = "Play Billing" [package.metadata.android] app_wrapper = "sokol" release_build_targets = ["aarch64-linux-android"] -res = "../../assets/res/android" +resources = ["../../assets/res/android"] [[package.metadata.android.plugins_local_projects]] include = ":crossbow" diff --git a/examples/macroquad-play-games/Cargo.toml b/examples/macroquad-play-games/Cargo.toml index 84cfec10..bf7d328d 100644 --- a/examples/macroquad-play-games/Cargo.toml +++ b/examples/macroquad-play-games/Cargo.toml @@ -19,7 +19,7 @@ app_name = "Play Games" [package.metadata.android] app_wrapper = "sokol" release_build_targets = ["aarch64-linux-android"] -res = "../../assets/res/android" +resources = ["../../assets/res/android"] [[package.metadata.android.plugins_local_projects]] include = ":crossbow" diff --git a/plugins/play-games-services/README.md b/plugins/play-games-services/README.md index 7b665caa..493d7283 100644 --- a/plugins/play-games-services/README.md +++ b/plugins/play-games-services/README.md @@ -51,7 +51,7 @@ Create you resources directory and import it by adding the following in your `Ca ```toml [package.metadata.android] -res = "./res/android" +resources = ["./res/android"] ``` Then create file `./res/android/values/games-ids.xml` in your resources directory with the following content: