diff --git a/platform/android/doc_classes/EditorExportPlatformAndroid.xml b/platform/android/doc_classes/EditorExportPlatformAndroid.xml
index 020e432155d..2fe5539e56d 100644
--- a/platform/android/doc_classes/EditorExportPlatformAndroid.xml
+++ b/platform/android/doc_classes/EditorExportPlatformAndroid.xml
@@ -102,6 +102,9 @@
Foreground layer of the application adaptive icon file. See [url=https://developer.android.com/develop/ui/views/launch/icon_design_adaptive#design-adaptive-icons]Design adaptive icons[/url].
+
+ Monochrome layer of the application adaptive icon file. See [url=https://developer.android.com/develop/ui/views/launch/icon_design_adaptive#design-adaptive-icons]Design adaptive icons[/url].
+
Application icon file. If left empty, it will fallback to [member ProjectSettings.application/config/icon].
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index d5153fb6c04..cecadacf77e 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -214,10 +214,23 @@ static const char *MISMATCHED_VERSIONS_MESSAGE = "Android build version mismatch
static const char *GDEXTENSION_LIBS_PATH = "libs/gdextensionlibs.json";
+// This template string must always match the content of 'platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml'.
+static const String ICON_XML_TEMPLATE =
+ "\n"
+ "\n"
+ " \n"
+ " \n"
+ "%s" // Placeholder for the optional monochrome tag.
+ "";
+
+static const String ICON_XML_PATH = "res/mipmap-anydpi-v26/icon.xml";
+static const String THEMED_ICON_XML_PATH = "res/mipmap-anydpi-v26/themed_icon.xml";
+
static const int icon_densities_count = 6;
static const char *launcher_icon_option = PNAME("launcher_icons/main_192x192");
static const char *launcher_adaptive_icon_foreground_option = PNAME("launcher_icons/adaptive_foreground_432x432");
static const char *launcher_adaptive_icon_background_option = PNAME("launcher_icons/adaptive_background_432x432");
+static const char *launcher_adaptive_icon_monochrome_option = PNAME("launcher_icons/adaptive_monochrome_432x432");
static const LauncherIcon launcher_icons[icon_densities_count] = {
{ "res/mipmap-xxxhdpi-v4/icon.png", 192 },
@@ -246,6 +259,15 @@ static const LauncherIcon launcher_adaptive_icon_backgrounds[icon_densities_coun
{ "res/mipmap/icon_background.png", 432 }
};
+static const LauncherIcon launcher_adaptive_icon_monochromes[icon_densities_count] = {
+ { "res/mipmap-xxxhdpi-v4/icon_monochrome.png", 432 },
+ { "res/mipmap-xxhdpi-v4/icon_monochrome.png", 324 },
+ { "res/mipmap-xhdpi-v4/icon_monochrome.png", 216 },
+ { "res/mipmap-hdpi-v4/icon_monochrome.png", 162 },
+ { "res/mipmap-mdpi-v4/icon_monochrome.png", 108 },
+ { "res/mipmap/icon_monochrome.png", 432 }
+};
+
static const int EXPORT_FORMAT_APK = 0;
static const int EXPORT_FORMAT_AAB = 1;
@@ -1640,12 +1662,13 @@ void EditorExportPlatformAndroid::_process_launcher_icons(const String &p_file_n
}
}
-void EditorExportPlatformAndroid::load_icon_refs(const Ref &p_preset, Ref &icon, Ref &foreground, Ref &background) {
+void EditorExportPlatformAndroid::load_icon_refs(const Ref &p_preset, Ref &icon, Ref &foreground, Ref &background, Ref &monochrome) {
String project_icon_path = GLOBAL_GET("application/config/icon");
icon.instantiate();
foreground.instantiate();
background.instantiate();
+ monochrome.instantiate();
// Regular icon: user selection -> project icon -> default.
String path = static_cast(p_preset->get(launcher_icon_option)).strip_edges();
@@ -1673,14 +1696,24 @@ void EditorExportPlatformAndroid::load_icon_refs(const Ref &
print_verbose("Loading adaptive background icon from " + path);
ImageLoader::load_image(path, background);
}
+
+ // Adaptive monochrome: user selection -> default.
+ path = static_cast(p_preset->get(launcher_adaptive_icon_monochrome_option)).strip_edges();
+ if (!path.is_empty()) {
+ print_verbose("Loading adaptive monochrome icon from " + path);
+ ImageLoader::load_image(path, monochrome);
+ }
}
void EditorExportPlatformAndroid::_copy_icons_to_gradle_project(const Ref &p_preset,
const Ref &p_main_image,
const Ref &p_foreground,
- const Ref &p_background) {
+ const Ref &p_background,
+ const Ref &p_monochrome) {
String gradle_build_dir = ExportTemplateManager::get_android_build_directory(p_preset);
+ String monochrome_tag = "";
+
// Prepare images to be resized for the icons. If some image ends up being uninitialized,
// the default image from the export template will be used.
@@ -1707,7 +1740,19 @@ void EditorExportPlatformAndroid::_copy_icons_to_gradle_project(const Refis_empty()) {
+ print_verbose("Processing launcher adaptive icon p_monochrome for dimension " + itos(launcher_adaptive_icon_monochromes[i].dimensions) + " into " + launcher_adaptive_icon_monochromes[i].export_path);
+ Vector data;
+ _process_launcher_icons(launcher_adaptive_icon_monochromes[i].export_path, p_monochrome,
+ launcher_adaptive_icon_monochromes[i].dimensions, data);
+ store_file_at_path(gradle_build_dir.path_join(launcher_adaptive_icon_monochromes[i].export_path), data);
+ monochrome_tag = " \n";
+ }
}
+
+ // Finalize the icon.xml by formatting the template with the optional monochrome tag.
+ store_string_at_path(gradle_build_dir.path_join(ICON_XML_PATH), vformat(ICON_XML_TEMPLATE, monochrome_tag));
}
Vector EditorExportPlatformAndroid::get_enabled_abis(const Ref &p_preset) {
@@ -1871,6 +1916,7 @@ void EditorExportPlatformAndroid::get_export_options(List *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_icon_option, PROPERTY_HINT_FILE, "*.png"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_foreground_option, PROPERTY_HINT_FILE, "*.png"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_background_option, PROPERTY_HINT_FILE, "*.png"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_monochrome_option, PROPERTY_HINT_FILE, "*.png"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/opengl_debug"), false));
@@ -3012,8 +3058,9 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref main_image;
Ref foreground;
Ref background;
+ Ref monochrome;
- load_icon_refs(p_preset, main_image, foreground, background);
+ load_icon_refs(p_preset, main_image, foreground, background, monochrome);
Vector command_line_flags;
// Write command line flags into the command_line_flags variable.
@@ -3084,7 +3131,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Refget("apk_expansion/public_key");
Vector invalid_abis(enabled_abis);
+
+ //To temporarily store icon xml data.
+ Vector themed_icon_xml_data;
+ int icon_xml_compression_method = -1;
+
while (ret == UNZ_OK) {
//get filename
unz_file_info info;
@@ -3398,6 +3450,20 @@ Error EditorExportPlatformAndroid::export_project_helper(const Refis_empty()) {
+ // Defer processing of icon.xml until after themed_icon.xml is read.
+ icon_xml_compression_method = info.compression_method;
+ skip = true;
+ }
+ }
+
if (file.ends_with(".png") && file.contains("mipmap")) {
for (int i = 0; i < icon_densities_count; ++i) {
if (main_image.is_valid() && !main_image->is_empty()) {
@@ -3415,6 +3481,11 @@ Error EditorExportPlatformAndroid::export_project_helper(const Refis_empty()) {
+ if (file == launcher_adaptive_icon_monochromes[i].export_path) {
+ _process_launcher_icons(file, monochrome, launcher_adaptive_icon_monochromes[i].dimensions, data);
+ }
+ }
}
}
@@ -3462,6 +3533,28 @@ Error EditorExportPlatformAndroid::export_project_helper(const Refis_empty()) {
+ print_line("ADDING: " + ICON_XML_PATH + " (replacing with themed_icon.xml data)");
+
+ const bool uncompressed = icon_xml_compression_method == 0;
+ zip_fileinfo zipfi = get_zip_fileinfo();
+
+ zipOpenNewFileInZip(unaligned_apk,
+ ICON_XML_PATH.utf8().get_data(),
+ &zipfi,
+ nullptr,
+ 0,
+ nullptr,
+ 0,
+ nullptr,
+ uncompressed ? 0 : Z_DEFLATED,
+ Z_DEFAULT_COMPRESSION);
+
+ zipWriteInFileInZip(unaligned_apk, themed_icon_xml_data.ptr(), themed_icon_xml_data.size());
+ zipCloseFileInZip(unaligned_apk);
+ }
+
if (!invalid_abis.is_empty()) {
add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Missing libraries in the export template for the selected architectures: %s. Please build a template with all required libraries, or uncheck the missing architectures in the export preset."), join_abis(invalid_abis, ", ", false)));
CLEANUP_AND_RETURN(ERR_FILE_NOT_FOUND);
diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h
index 97bbd0c7bcc..f09b44f094b 100644
--- a/platform/android/export/export_plugin.h
+++ b/platform/android/export/export_plugin.h
@@ -167,12 +167,13 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
void _process_launcher_icons(const String &p_file_name, const Ref &p_source_image, int dimension, Vector &p_data);
- void load_icon_refs(const Ref &p_preset, Ref &icon, Ref &foreground, Ref &background);
+ void load_icon_refs(const Ref &p_preset, Ref &icon, Ref &foreground, Ref &background, Ref &monochrome);
void _copy_icons_to_gradle_project(const Ref &p_preset,
const Ref &p_main_image,
const Ref &p_foreground,
- const Ref &p_background);
+ const Ref &p_background,
+ const Ref &p_monochrome);
static void _create_editor_debug_keystore_if_needed();
diff --git a/platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml b/platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml
index cfdcca2ab58..0ac805f8f39 100644
--- a/platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml
+++ b/platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml
@@ -1,4 +1,7 @@
+
diff --git a/platform/android/java/lib/res/mipmap-anydpi-v26/themed_icon.xml b/platform/android/java/lib/res/mipmap-anydpi-v26/themed_icon.xml
new file mode 100644
index 00000000000..95457ca7d25
--- /dev/null
+++ b/platform/android/java/lib/res/mipmap-anydpi-v26/themed_icon.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
diff --git a/platform/android/java/lib/res/mipmap/icon_monochrome.png b/platform/android/java/lib/res/mipmap/icon_monochrome.png
new file mode 100644
index 00000000000..28f59ea119f
Binary files /dev/null and b/platform/android/java/lib/res/mipmap/icon_monochrome.png differ