diff --git a/CHANGELOG.md b/CHANGELOG.md index 97aa3bb1..d46b9c06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## \[0.23.0] + +- Added support to build cargo-mobile2 on Android for use within + termux using the termux feature and updated the android-studio + template to enable use of gradle 9. ## \[0.22.4] - [`74138d4`](https://github.com/tauri-apps/cargo-mobile2/commit/74138d48435492d49d40831e56544abeb56398bd) ([#509](https://github.com/tauri-apps/cargo-mobile2/pull/509) by [@lucasfernog](https://github.com/tauri-apps/cargo-mobile2/../../lucasfernog)) Fixed Android logcat process detection when running an app with `--application-id-suffix`. diff --git a/Cargo.toml b/Cargo.toml index 8de02766..5c7e7caf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,7 @@ brainium = [] rustls = ["ureq/rustls"] native-tls = ["ureq/native-tls"] default = ["cli", "native-tls"] +termux = [] [dependencies] handlebars = "6" diff --git a/README.md b/README.md index b889b4f9..87eaa670 100644 --- a/README.md +++ b/README.md @@ -76,3 +76,21 @@ For fine-grained control of logging, use the `--filter` (or `-f`) option, which the default device logging level set by `-v` or `-vv`. If using the `android_logger` crate to handle Rust log messages, `trace` logs from Rust are mapped to `verbose` logs in Android. + +## Build and use on Android with termux + +Cargo-mobile2 can be build for use within termux on Android to +build Android packages. To compile cargo-mobile2 for termux enable +the termux feature. + +Before using cargo-mobile2 you need to: + +* install rust packages including the android targets (rust-std-aarch64-linux-android, + rust-std-armv7-linux-androideabi, rust-std-i686-linux-android, rust-std-x86-64-linux-android) +* install java & tools like gradle, aapt2 and android-tools packages +* install termux version of android-sdk and android-ndk + +Make sure you set the environment variables `JAVA_HOME` +(eg `$PREFIX/lib/jvm/java-21-openjdk`), `ANDROID_HOME` (location +android sdk: `$PREFIX/opt/android-sdk`) and `NDK_HOME` (location +android ndk: `$PREFIX/opt/android-ndk`). diff --git a/src/android/adb/mod.rs b/src/android/adb/mod.rs index f6dcc014..dc32ad85 100644 --- a/src/android/adb/mod.rs +++ b/src/android/adb/mod.rs @@ -9,6 +9,7 @@ use crate::{env::ExplicitEnv as _, util::cli::Report, DuctExpressionExt}; use std::{ffi::OsString, str, string::FromUtf8Error}; use thiserror::Error; +#[cfg(not(all(feature = "termux", target_os = "android")))] pub fn adb(env: &Env, args: U) -> duct::Expression where U: IntoIterator, @@ -17,6 +18,16 @@ where duct::cmd(env.platform_tools_path().join("adb"), args).vars(env.explicit_env()) } +#[cfg(all(feature = "termux", target_os = "android"))] +pub fn adb(env: &Env, args: U) -> duct::Expression +where + U: IntoIterator, + U::Item: Into, +{ + let prefix = crate::os::prefix().expect("Not running on termux"); + duct::cmd(prefix.join("bin/adb"), args).vars(env.explicit_env()) +} + #[derive(Debug, Error)] pub enum RunCheckedError { #[error(transparent)] diff --git a/src/android/cli.rs b/src/android/cli.rs index 71625670..04a9353b 100644 --- a/src/android/cli.rs +++ b/src/android/cli.rs @@ -231,11 +231,17 @@ impl Exec for Input { } } + #[cfg(not(all(feature = "termux", target_os = "android")))] fn open_in_android_studio(config: &Config, env: &Env) -> Result<(), Error> { os::open_file_with("Android Studio", config.project_dir(), &env.base) .map_err(Error::OpenFailed) } + #[cfg(all(feature = "termux", target_os = "android"))] + fn open_in_android_studio(_config: &Config, _env: &Env) -> Result<(), Error> { + Err(Error::Unsupported) + } + fn get_targets_or_all<'a>(targets: Vec) -> Result>, Error> { if targets.is_empty() { Ok(Target::all().iter().map(|t| t.1).collect()) diff --git a/src/android/env.rs b/src/android/env.rs index c7a423e8..06a40d31 100644 --- a/src/android/env.rs +++ b/src/android/env.rs @@ -124,6 +124,11 @@ impl ExplicitEnv for Env { "NDK_HOME".into(), self.ndk.home().as_os_str().to_os_string(), ); + #[cfg(all(feature = "termux", target_os = "android"))] + envs.insert( + "GRADLE_OPTS".into(), + "-Dorg.gradle.native=false -Djansi.passthrough=true".into(), + ); envs } } diff --git a/src/android/ndk.rs b/src/android/ndk.rs index cfd27069..1adf6f41 100644 --- a/src/android/ndk.rs +++ b/src/android/ndk.rs @@ -39,6 +39,26 @@ pub fn host_tag() -> &'static str { "windows-x86_64" } +#[cfg(all(feature = "termux", target_os = "android", target_arch = "aarch64"))] +pub fn host_tag() -> &'static str { + "linux-aarch64" +} + +#[cfg(all(feature = "termux", target_os = "android", target_arch = "x86_64"))] +pub fn host_tag() -> &'static str { + "linux-x86_64" +} + +#[cfg(all(feature = "termux", target_os = "android", target_arch = "x86"))] +pub fn host_tag() -> &'static str { + "linux-x86" +} + +#[cfg(all(feature = "termux", target_os = "android", target_arch = "arm"))] +pub fn host_tag() -> &'static str { + "linux-arm" +} + #[derive(Clone, Copy, Debug)] pub enum Compiler { Clang, diff --git a/src/android/project.rs b/src/android/project.rs index 777058dc..6bdc7d97 100644 --- a/src/android/project.rs +++ b/src/android/project.rs @@ -180,6 +180,12 @@ pub fn gen( map.insert("has-asset-packs", !asset_packs.is_empty()); map.insert("asset-packs", asset_packs); map.insert("windows", cfg!(windows)); + map.insert( + "termux", + cfg!(all(feature = "termux", target_os = "android")), + ); + #[cfg(all(feature = "termux", target_os = "android"))] + map.insert("termux_prefix", crate::os::prefix().unwrap_or_default()); }, filter.fun(), ) diff --git a/src/init.rs b/src/init.rs index db06a8d8..2467cc3c 100644 --- a/src/init.rs +++ b/src/init.rs @@ -15,6 +15,7 @@ use crate::{ cli::{Report, Reportable, TextWrapper}, }, }; + use std::{ fs, io, path::{Path, PathBuf}, @@ -93,7 +94,7 @@ impl Reportable for Error { pub fn exec( wrapper: &TextWrapper, non_interactive: bool, - skip_dev_tools: bool, + #[allow(unused)] skip_dev_tools: bool, // not used in termux skip_targets_install: bool, #[cfg_attr(not(target_os = "macos"), allow(unused))] reinstall_deps: bool, open_in_editor: bool, @@ -133,6 +134,10 @@ pub fn exec( fs::create_dir_all(&asset_dir) .map_err(|cause| Error::AssetDirCreationFailed { asset_dir, cause })?; } + + #[cfg(all(feature = "termux", target_os = "android"))] + let skip_dev_tools = false; + if !skip_dev_tools && util::command_present("code").map_err(Error::CodeCommandPresentFailed)? { code_command() .before_spawn(move |cmd| { diff --git a/src/os/mod.rs b/src/os/mod.rs index 0cbfb3fb..2e7ea710 100644 --- a/src/os/mod.rs +++ b/src/os/mod.rs @@ -18,7 +18,18 @@ mod windows; #[cfg(windows)] pub use self::windows::*; -#[cfg(not(any(target_os = "macos", target_os = "linux", windows)))] +#[cfg(all(feature = "termux", target_os = "android"))] +mod termux; + +#[cfg(all(feature = "termux", target_os = "android"))] +pub use self::termux::*; + +#[cfg(not(any( + target_os = "macos", + target_os = "linux", + windows, + all(feature = "termux", target_os = "android") +)))] compile_error!("Host platform not yet supported by cargo-mobile2! We'd love if you made a PR to add support for this platform ❤️"); // TODO: we should probably expose common functionality throughout `os` in a diff --git a/src/os/termux/mod.rs b/src/os/termux/mod.rs new file mode 100644 index 00000000..f5e19127 --- /dev/null +++ b/src/os/termux/mod.rs @@ -0,0 +1,153 @@ +use std::ffi::{OsStr, OsString}; +use std::path::{Path, PathBuf}; +use thiserror::Error; + +pub use crate::{ + env::{Env, ExplicitEnv}, + util::ln, +}; + +#[derive(Debug, Error)] +pub enum Error { + #[error("termux is not installed")] + NoTermux, + #[error("termux::API is not installed")] + NoTermuxApi, + #[error("IO error")] + IoError(#[from] std::io::Error), + #[error("Env error")] + VarError(#[from] std::env::VarError), +} + +#[derive(Debug, Error)] +pub enum DetectEditorError { + #[error("No default editor is set: xdg-mime queries for \"text/rust\" and \"text/plain\" both failed")] + NoDefaultEditorSet, +} + +#[derive(Debug, Error)] +pub enum OpenFileError { + #[error("Failed to run {command}: {error}")] + CommandFailed { + command: String, + error: std::io::Error, + }, + #[error("Command parsing failed")] + CommandParsingFailed, + #[error("termux::API is not installed")] + NoTermuxApi, +} + +#[derive(Debug)] +pub struct Application { + exec_command: OsString, + // icon: Option, + // xdg_entry_path: PathBuf, +} + +impl Application { + pub fn detect_editor() -> Result { + let editor = std::env::var("EDITOR").map_err(|_| DetectEditorError::NoDefaultEditorSet)?; + Ok(Self { + exec_command: editor.into(), + // icon: None, + // xdg_entry_path: PathBuf::new(), + }) + } + + pub fn open_file(&self, path: impl AsRef) -> Result<(), OpenFileError> { + let path = path.as_ref(); + duct::cmd(&self.exec_command, path) + .run() + .map(|_| ()) + .map_err(|error| OpenFileError::CommandFailed { + command: self.exec_command.to_string_lossy().to_string(), + error, + }) + } +} + +pub fn open_file_with( + _application: impl AsRef, + path: impl AsRef, + _env: &Env, +) -> Result<(), OpenFileError> { + let _ = termux_api_version().ok_or(OpenFileError::NoTermuxApi)?; + duct::cmd("termux-open", vec![path.as_ref()]) + .run() + .map(|_| ()) + .map_err(|error| OpenFileError::CommandFailed { + command: "termux-open".to_string(), + error, + }) +} + +/// Open file with the $EDITOR (or nano) +pub fn open_with_editor(path: impl AsRef) -> Result { + let editor = std::env::var("EDITOR").unwrap_or_else(|_| "nano".to_string()); + Ok(duct::cmd(editor, path.as_ref()).run()?) +} + +/// Open file with termux-open (requires termux::API) +pub fn open_with_termux(path: impl AsRef) -> Result { + let _ = termux_api_version().ok_or(Error::NoTermuxApi); + Ok(duct::cmd("termux-open", path.as_ref()).run()?) +} + +/// Installs an apk by running termux-open, returns an error +/// when termux::API is not installed or termux-open fails. +pub fn run_apk(apk_path: impl AsRef) -> Result { + open_with_termux(apk_path) +} + +/// Returns the `PathBuf` where termux is installed (eg ``/data/data/com.termux/files/usr``) +pub fn prefix() -> Result { + let prefix = std::env::var("PREFIX")?; + Ok(PathBuf::from(prefix)) +} + +/// Returns `Some(api version)` or None when termux::API version is +/// not available. +pub fn termux_api_version() -> Option { + std::env::var("TERMUX_API_VERSION").ok() +} + +/// Returns `Some(api version)` or None when termux::API version is +/// not available. +pub fn termux_version() -> Option { + std::env::var("TERMUX_VERSION").ok() +} + +pub fn command_path(name: &str) -> std::io::Result { + duct::cmd("sh", ["-c", format!("command -v {name}").as_str()]).run() +} + +pub fn code_command() -> duct::Expression { + let editor = std::env::var("EDITOR").unwrap_or_else(|_| "nano".to_string()); + duct::cmd!(editor) +} + +pub fn replace_path_separator(path: OsString) -> OsString { + path +} + +pub mod consts { + pub const CLANG: &str = "clang"; + pub const CLANGXX: &str = "clang++"; + pub const AR: &str = "ar"; + pub const LD: &str = "ld"; + pub const READELF: &str = "readelf"; + pub const NDK_STACK: &str = "ndk-stack"; +} + +pub mod info { + use super::Error; + use crate::os::Info; + + pub fn check() -> Result { + Ok(Info { + name: "termux".to_string(), + version: super::termux_version().ok_or(Error::NoTermux)?, + }) + } +} diff --git a/src/util/mod.rs b/src/util/mod.rs index caa655c5..fe65cff9 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -52,6 +52,7 @@ pub fn reverse_domain(domain: &str) -> String { domain.split('.').rev().collect::>().join(".") } +#[cfg(not(target_os = "android"))] pub fn rustup_add(triple: &str) -> Result { duct::cmd("rustup", ["target", "add", triple]) .dup_stdio() @@ -59,6 +60,25 @@ pub fn rustup_add(triple: &str) -> Result { .map(|o| o.status) } +#[cfg(target_os = "android")] +pub fn rustup_add(triple: &str) -> Result { + let pkg = match triple { + "aarch64-linux-android" => "rust-std-aarch64-linux-android", + "armv7-linux-androideabi" => "rust-std-armv7-linux-androideabi", + "i686-linux-android" => "rust-std-i686-linux-android", + "wasm32-unknown-unknown" => "rust-std-wasm32-unknown-unknown", + "x86-64-linux-android" => "rust-std-x86-64-linux-android", + _ => { + eprintln!("{triple} is not available for rust on termux"); + return Ok(ExitStatus::default()); + } + }; + duct::cmd("apt", ["install", pkg]) + .dup_stdio() + .run() + .map(|o| o.status) +} + #[derive(Debug, Error)] pub enum HostTargetTripleError { #[error("Failed to detect host target triple: {0}")] @@ -655,25 +675,52 @@ pub fn gradlew( let project_dir = dunce::simplified(&project_dir); let gradlew_p = project_dir.join(gradlew); - if gradlew_p.exists() { - duct::cmd( - gradlew_p, - [OsStr::new("--project-dir"), project_dir.as_ref()], - ) - .vars(env.explicit_env()) - .dup_stdio() + + let mut args: Vec<&OsStr> = vec![OsStr::new("--project-dir"), project_dir.as_ref()]; + #[cfg(feature = "termux")] + { + args.push(OsStr::new("--no-daemon")); + // args.push(OsStr::new("-Dorg.gradle.native=false")); + // args.push(OsStr::new("-Djansi.passthrough=true")); + // args.push(OsStr::new("-g")); + // args.push(OsStr::new("/data/data/com.termux/files/home")); + } + // In Termux, skip the gradlew wrapper script entirely — it tries to download + // a Gradle distribution which fails. Use the system gradle directly instead. + #[cfg(feature = "termux")] + let use_gradlew_script = false; + #[cfg(not(feature = "termux"))] + let use_gradlew_script = gradlew_p.exists(); + + // TOTERMUX + if use_gradlew_script { + duct::cmd(gradlew_p, &args) + .vars(env.explicit_env()) + // .env( + // "GRADLE_OPTS", + // "-Dorg.gradle.native=false -Djansi.passthrough=true", + // ) + .dup_stdio() } else if duct::cmd(gradlew, ["-v"]) .dup_stdio() .run() .map(|o| o.status.success()) .unwrap_or(false) { - duct::cmd(gradlew, [OsStr::new("--project-dir"), project_dir.as_ref()]) + duct::cmd(gradlew, &args) .vars(env.explicit_env()) + // .env( + // "GRADLE_OPTS", + // "-Dorg.gradle.native=false -Djansi.passthrough=true", + // ) .dup_stdio() } else { - duct::cmd(gradle, [OsStr::new("--project-dir"), project_dir.as_ref()]) + duct::cmd(gradle, &args) .vars(env.explicit_env()) + // .env( + // "GRADLE_OPTS", + // "-Dorg.gradle.native=false -Djansi.passthrough=true", + // ) .dup_stdio() } } diff --git a/templates/apps/dioxus/Cargo.toml.hbs b/templates/apps/dioxus/Cargo.toml.hbs index 4c79ca2e..3565ded0 100644 --- a/templates/apps/dioxus/Cargo.toml.hbs +++ b/templates/apps/dioxus/Cargo.toml.hbs @@ -18,7 +18,7 @@ app-dependencies = [ "androidx.appcompat:appcompat:1.6.1", "com.google.android.material:material:1.8.0", ] -project-dependencies = [ "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21" ] +project-dependencies = [ "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.25" ] app-plugins = [ "org.jetbrains.kotlin.android" ] app-permissions = [ "android.permission.INTERNET" ] app-theme-parent = "Theme.MaterialComponents.DayNight.DarkActionBar" @@ -48,4 +48,4 @@ paste = "1.0" env_logger = "0.9.0" [target.'cfg(target_os = "ios")'.dependencies] -core-foundation = "0.10" \ No newline at end of file +core-foundation = "0.10" diff --git a/templates/apps/egui/Cargo.toml.hbs b/templates/apps/egui/Cargo.toml.hbs index 9c49ff9e..0d89bce0 100644 --- a/templates/apps/egui/Cargo.toml.hbs +++ b/templates/apps/egui/Cargo.toml.hbs @@ -16,7 +16,7 @@ path = "gen/bin/desktop.rs" app-dependencies = [ "com.google.android.material:material:1.8.0", ] -project-dependencies = [ "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21" ] +project-dependencies = [ "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.25" ] app-plugins = [ "org.jetbrains.kotlin.android" ] app-theme-parent = "Theme.MaterialComponents.DayNight.DarkActionBar" diff --git a/templates/apps/wry/Cargo.toml.hbs b/templates/apps/wry/Cargo.toml.hbs index d5fa0274..d1778864 100644 --- a/templates/apps/wry/Cargo.toml.hbs +++ b/templates/apps/wry/Cargo.toml.hbs @@ -18,7 +18,7 @@ app-dependencies = [ "androidx.appcompat:appcompat:1.6.1", "com.google.android.material:material:1.8.0", ] -project-dependencies = [ "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21" ] +project-dependencies = [ "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.25" ] app-plugins = [ "org.jetbrains.kotlin.android" ] app-permissions = [ "android.permission.INTERNET" ] app-theme-parent = "Theme.MaterialComponents.DayNight.DarkActionBar" @@ -47,4 +47,4 @@ paste = "1.0" env_logger = "0.9.0" [target.'cfg(target_os = "ios")'.dependencies] -core-foundation = "0.10" \ No newline at end of file +core-foundation = "0.10" diff --git a/templates/platforms/android-studio/app/build.gradle.kts.hbs b/templates/platforms/android-studio/app/build.gradle.kts.hbs index 7c90c790..523f7389 100644 --- a/templates/platforms/android-studio/app/build.gradle.kts.hbs +++ b/templates/platforms/android-studio/app/build.gradle.kts.hbs @@ -38,6 +38,25 @@ android { ) } } + flavorDimensions += "abi" + productFlavors { + create("universal") { + dimension = "abi" + ndk { abiFilters += listOf({{~#each abi-list}}"{{this}}"{{#unless @last}},{{/unless}}{{/each}}) } + } + {{#each arch-list}}create("{{this}}") { dimension = "abi"; ndk { abiFilters.add("{{lookup ../abi-list @index}}") } } + {{/each}} + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + kotlinOptions { + jvmTarget = "11" + } + buildFeatures { + buildConfig = true + } } rust { diff --git a/templates/platforms/android-studio/build.gradle.kts.hbs b/templates/platforms/android-studio/build.gradle.kts.hbs index 16380468..dab62c45 100644 --- a/templates/platforms/android-studio/build.gradle.kts.hbs +++ b/templates/platforms/android-studio/build.gradle.kts.hbs @@ -4,7 +4,7 @@ buildscript { mavenCentral() } dependencies { - classpath("com.android.tools.build:gradle:8.0.0"){{~#each android-project-dependencies}} + classpath("com.android.tools.build:gradle:8.7.3"){{~#each android-project-dependencies}} classpath("{{this}}"){{/each}} } } diff --git a/templates/platforms/android-studio/buildSrc/build.gradle.kts.hbs b/templates/platforms/android-studio/buildSrc/build.gradle.kts.hbs index 099feff7..fab7ecf7 100644 --- a/templates/platforms/android-studio/buildSrc/build.gradle.kts.hbs +++ b/templates/platforms/android-studio/buildSrc/build.gradle.kts.hbs @@ -18,6 +18,5 @@ repositories { dependencies { compileOnly(gradleApi()) - implementation("com.android.tools.build:gradle:8.0.0") } diff --git a/templates/platforms/android-studio/buildSrc/src/main/kotlin/BuildTask.kt.hbs b/templates/platforms/android-studio/buildSrc/src/main/kotlin/BuildTask.kt.hbs index 3122b967..7be826ad 100644 --- a/templates/platforms/android-studio/buildSrc/src/main/kotlin/BuildTask.kt.hbs +++ b/templates/platforms/android-studio/buildSrc/src/main/kotlin/BuildTask.kt.hbs @@ -4,8 +4,12 @@ import org.gradle.api.GradleException import org.gradle.api.logging.LogLevel import org.gradle.api.tasks.Input import org.gradle.api.tasks.TaskAction +import org.gradle.process.ExecOperations +import javax.inject.Inject -open class BuildTask : DefaultTask() { +open class BuildTask @Inject constructor( + private val execOps: ExecOperations +) : DefaultTask() { @Input var rootDirRel: String? = null @Input @@ -19,7 +23,7 @@ open class BuildTask : DefaultTask() { val target = target ?: throw GradleException("target cannot be null") val release = release ?: throw GradleException("release cannot be null") - project.exec { + execOps.exec { workingDir(File(project.projectDir, rootDirRel)) executable("cargo") args(listOf("android", "build")) diff --git a/templates/platforms/android-studio/buildSrc/src/main/kotlin/RustPlugin.kt.hbs b/templates/platforms/android-studio/buildSrc/src/main/kotlin/RustPlugin.kt.hbs index 6f133981..34f6b6c7 100644 --- a/templates/platforms/android-studio/buildSrc/src/main/kotlin/RustPlugin.kt.hbs +++ b/templates/platforms/android-studio/buildSrc/src/main/kotlin/RustPlugin.kt.hbs @@ -1,4 +1,3 @@ -import com.android.build.api.dsl.ApplicationExtension import org.gradle.api.DefaultTask import org.gradle.api.Plugin import org.gradle.api.Project @@ -25,27 +24,6 @@ open class RustPlugin : Plugin { val targetsList = (findProperty("targetList") as? String)?.split(',') ?: listOf({{quote-and-join target-list}}) - extensions.configure { - @Suppress("UnstableApiUsage") - flavorDimensions.add("abi") - productFlavors { - create("universal") { - dimension = "abi" - ndk { - abiFilters += abiList - } - } - defaultArchList.forEachIndexed { index, arch -> - create(arch) { - dimension = "abi" - ndk { - abiFilters.add(defaultAbiList[index]) - } - } - } - } - } - afterEvaluate { for (profile in listOf("debug", "release")) { val profileCapitalized = profile.replaceFirstChar { it.uppercase() } diff --git a/templates/platforms/android-studio/gradle.properties b/templates/platforms/android-studio/gradle.properties.hbs similarity index 89% rename from templates/platforms/android-studio/gradle.properties rename to templates/platforms/android-studio/gradle.properties.hbs index 022338b7..c7b8bf71 100644 --- a/templates/platforms/android-studio/gradle.properties +++ b/templates/platforms/android-studio/gradle.properties.hbs @@ -21,5 +21,8 @@ kotlin.code.style=official # resources declared in the library itself and none from the library's dependencies, # thereby reducing the size of the R class for that library android.nonTransitiveRClass=true -android.defaults.buildfeatures.buildconfig=true -android.nonFinalResIds=false \ No newline at end of file +android.defaults.buildfeatures.buildconfig=false +android.nonFinalResIds=false +{{#if termux}} +android.aapt2FromMavenOverride={{ termux_prefix }}/bin/aapt2 +{{/if}}