Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[4.3] Cherry-picks for Android Themed Icons (Monochrome) #100066

Merged
merged 2 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@
<member name="launcher_icons/adaptive_foreground_432x432" type="String" setter="" getter="">
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].
</member>
<member name="launcher_icons/adaptive_monochrome_432x432" type="String" setter="" getter="">
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].
</member>
<member name="launcher_icons/main_192x192" type="String" setter="" getter="">
Application icon file. If left empty, it will fallback to [member ProjectSettings.application/config/icon].
</member>
Expand Down
101 changes: 97 additions & 4 deletions platform/android/export/export_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n"
" <background android:drawable=\"@mipmap/icon_background\"/>\n"
" <foreground android:drawable=\"@mipmap/icon_foreground\"/>\n"
"%s" // Placeholder for the optional monochrome tag.
"</adaptive-icon>";

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 },
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -1640,12 +1662,13 @@ void EditorExportPlatformAndroid::_process_launcher_icons(const String &p_file_n
}
}

void EditorExportPlatformAndroid::load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background) {
void EditorExportPlatformAndroid::load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background, Ref<Image> &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<String>(p_preset->get(launcher_icon_option)).strip_edges();
Expand Down Expand Up @@ -1673,14 +1696,24 @@ void EditorExportPlatformAndroid::load_icon_refs(const Ref<EditorExportPreset> &
print_verbose("Loading adaptive background icon from " + path);
ImageLoader::load_image(path, background);
}

// Adaptive monochrome: user selection -> default.
path = static_cast<String>(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<EditorExportPreset> &p_preset,
const Ref<Image> &p_main_image,
const Ref<Image> &p_foreground,
const Ref<Image> &p_background) {
const Ref<Image> &p_background,
const Ref<Image> &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.

Expand All @@ -1707,7 +1740,19 @@ void EditorExportPlatformAndroid::_copy_icons_to_gradle_project(const Ref<Editor
launcher_adaptive_icon_backgrounds[i].dimensions, data);
store_file_at_path(gradle_build_dir.path_join(launcher_adaptive_icon_backgrounds[i].export_path), data);
}

if (p_monochrome.is_valid() && !p_monochrome->is_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<uint8_t> 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 = " <monochrome android:drawable=\"@mipmap/icon_monochrome\"/>\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::ABI> EditorExportPlatformAndroid::get_enabled_abis(const Ref<EditorExportPreset> &p_preset) {
Expand Down Expand Up @@ -1871,6 +1916,7 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *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));

Expand Down Expand Up @@ -3012,8 +3058,9 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
Ref<Image> main_image;
Ref<Image> foreground;
Ref<Image> background;
Ref<Image> monochrome;

load_icon_refs(p_preset, main_image, foreground, background);
load_icon_refs(p_preset, main_image, foreground, background, monochrome);

Vector<uint8_t> command_line_flags;
// Write command line flags into the command_line_flags variable.
Expand Down Expand Up @@ -3084,7 +3131,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Unable to overwrite res/*.xml files with project name."));
}
// Copies the project icon files into the appropriate Gradle project directory.
_copy_icons_to_gradle_project(p_preset, main_image, foreground, background);
_copy_icons_to_gradle_project(p_preset, main_image, foreground, background, monochrome);
// Write an AndroidManifest.xml file into the Gradle project directory.
_write_tmp_manifest(p_preset, p_give_internet, p_debug);

Expand Down Expand Up @@ -3369,6 +3416,11 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
String apk_expansion_pkey = p_preset->get("apk_expansion/public_key");

Vector<ABI> invalid_abis(enabled_abis);

//To temporarily store icon xml data.
Vector<uint8_t> themed_icon_xml_data;
int icon_xml_compression_method = -1;

while (ret == UNZ_OK) {
//get filename
unz_file_info info;
Expand Down Expand Up @@ -3398,6 +3450,20 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
_fix_resources(p_preset, data);
}

if (file == THEMED_ICON_XML_PATH) {
// Store themed_icon.xml data.
themed_icon_xml_data = data;
skip = true;
}

if (file == ICON_XML_PATH) {
if (monochrome.is_valid() && !monochrome->is_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()) {
Expand All @@ -3415,6 +3481,11 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
_process_launcher_icons(file, background, launcher_adaptive_icon_backgrounds[i].dimensions, data);
}
}
if (monochrome.is_valid() && !monochrome->is_empty()) {
if (file == launcher_adaptive_icon_monochromes[i].export_path) {
_process_launcher_icons(file, monochrome, launcher_adaptive_icon_monochromes[i].dimensions, data);
}
}
}
}

Expand Down Expand Up @@ -3462,6 +3533,28 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
ret = unzGoToNextFile(pkg);
}

// Process deferred icon.xml and replace it's data with themed_icon.xml.
if (monochrome.is_valid() && !monochrome->is_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);
Expand Down
5 changes: 3 additions & 2 deletions platform/android/export/export_plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,13 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {

void _process_launcher_icons(const String &p_file_name, const Ref<Image> &p_source_image, int dimension, Vector<uint8_t> &p_data);

void load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background);
void load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background, Ref<Image> &monochrome);

void _copy_icons_to_gradle_project(const Ref<EditorExportPreset> &p_preset,
const Ref<Image> &p_main_image,
const Ref<Image> &p_foreground,
const Ref<Image> &p_background);
const Ref<Image> &p_background,
const Ref<Image> &p_monochrome);

static void _create_editor_debug_keystore_if_needed();

Expand Down
3 changes: 3 additions & 0 deletions platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
WARNING: The content of this file must always match the constant 'platform/android/export/export_plugin.cpp#ICON_XML_TEMPLATE'.
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/icon_background"/>
<foreground android:drawable="@mipmap/icon_foreground"/>
Expand Down
11 changes: 11 additions & 0 deletions platform/android/java/lib/res/mipmap-anydpi-v26/themed_icon.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This file is created to work alongside the icon.xml file.
If the user provides a Monochrome icon in the export settings, its data will be used to overwrite the icon.xml file.
We needed to create this file to get a reference for icon_monochrome.
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/icon_background"/>
<foreground android:drawable="@mipmap/icon_foreground"/>
<monochrome android:drawable="@mipmap/icon_monochrome"/>
</adaptive-icon>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading