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