Skip to content

Commit 3bf1176

Browse files
authored
Merge pull request #100066 from syntaxerror247/themed-icons
[4.3] Cherry-picks for Android Themed Icons (Monochrome)
2 parents b51be50 + e42c255 commit 3bf1176

File tree

6 files changed

+117
-6
lines changed

6 files changed

+117
-6
lines changed

platform/android/doc_classes/EditorExportPlatformAndroid.xml

+3
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@
102102
<member name="launcher_icons/adaptive_foreground_432x432" type="String" setter="" getter="">
103103
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].
104104
</member>
105+
<member name="launcher_icons/adaptive_monochrome_432x432" type="String" setter="" getter="">
106+
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].
107+
</member>
105108
<member name="launcher_icons/main_192x192" type="String" setter="" getter="">
106109
Application icon file. If left empty, it will fallback to [member ProjectSettings.application/config/icon].
107110
</member>

platform/android/export/export_plugin.cpp

+97-4
Original file line numberDiff line numberDiff line change
@@ -214,10 +214,23 @@ static const char *MISMATCHED_VERSIONS_MESSAGE = "Android build version mismatch
214214

215215
static const char *GDEXTENSION_LIBS_PATH = "libs/gdextensionlibs.json";
216216

217+
// This template string must always match the content of 'platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml'.
218+
static const String ICON_XML_TEMPLATE =
219+
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
220+
"<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n"
221+
" <background android:drawable=\"@mipmap/icon_background\"/>\n"
222+
" <foreground android:drawable=\"@mipmap/icon_foreground\"/>\n"
223+
"%s" // Placeholder for the optional monochrome tag.
224+
"</adaptive-icon>";
225+
226+
static const String ICON_XML_PATH = "res/mipmap-anydpi-v26/icon.xml";
227+
static const String THEMED_ICON_XML_PATH = "res/mipmap-anydpi-v26/themed_icon.xml";
228+
217229
static const int icon_densities_count = 6;
218230
static const char *launcher_icon_option = PNAME("launcher_icons/main_192x192");
219231
static const char *launcher_adaptive_icon_foreground_option = PNAME("launcher_icons/adaptive_foreground_432x432");
220232
static const char *launcher_adaptive_icon_background_option = PNAME("launcher_icons/adaptive_background_432x432");
233+
static const char *launcher_adaptive_icon_monochrome_option = PNAME("launcher_icons/adaptive_monochrome_432x432");
221234

222235
static const LauncherIcon launcher_icons[icon_densities_count] = {
223236
{ "res/mipmap-xxxhdpi-v4/icon.png", 192 },
@@ -246,6 +259,15 @@ static const LauncherIcon launcher_adaptive_icon_backgrounds[icon_densities_coun
246259
{ "res/mipmap/icon_background.png", 432 }
247260
};
248261

262+
static const LauncherIcon launcher_adaptive_icon_monochromes[icon_densities_count] = {
263+
{ "res/mipmap-xxxhdpi-v4/icon_monochrome.png", 432 },
264+
{ "res/mipmap-xxhdpi-v4/icon_monochrome.png", 324 },
265+
{ "res/mipmap-xhdpi-v4/icon_monochrome.png", 216 },
266+
{ "res/mipmap-hdpi-v4/icon_monochrome.png", 162 },
267+
{ "res/mipmap-mdpi-v4/icon_monochrome.png", 108 },
268+
{ "res/mipmap/icon_monochrome.png", 432 }
269+
};
270+
249271
static const int EXPORT_FORMAT_APK = 0;
250272
static const int EXPORT_FORMAT_AAB = 1;
251273

@@ -1640,12 +1662,13 @@ void EditorExportPlatformAndroid::_process_launcher_icons(const String &p_file_n
16401662
}
16411663
}
16421664

1643-
void EditorExportPlatformAndroid::load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background) {
1665+
void EditorExportPlatformAndroid::load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background, Ref<Image> &monochrome) {
16441666
String project_icon_path = GLOBAL_GET("application/config/icon");
16451667

16461668
icon.instantiate();
16471669
foreground.instantiate();
16481670
background.instantiate();
1671+
monochrome.instantiate();
16491672

16501673
// Regular icon: user selection -> project icon -> default.
16511674
String path = static_cast<String>(p_preset->get(launcher_icon_option)).strip_edges();
@@ -1673,14 +1696,24 @@ void EditorExportPlatformAndroid::load_icon_refs(const Ref<EditorExportPreset> &
16731696
print_verbose("Loading adaptive background icon from " + path);
16741697
ImageLoader::load_image(path, background);
16751698
}
1699+
1700+
// Adaptive monochrome: user selection -> default.
1701+
path = static_cast<String>(p_preset->get(launcher_adaptive_icon_monochrome_option)).strip_edges();
1702+
if (!path.is_empty()) {
1703+
print_verbose("Loading adaptive monochrome icon from " + path);
1704+
ImageLoader::load_image(path, monochrome);
1705+
}
16761706
}
16771707

16781708
void EditorExportPlatformAndroid::_copy_icons_to_gradle_project(const Ref<EditorExportPreset> &p_preset,
16791709
const Ref<Image> &p_main_image,
16801710
const Ref<Image> &p_foreground,
1681-
const Ref<Image> &p_background) {
1711+
const Ref<Image> &p_background,
1712+
const Ref<Image> &p_monochrome) {
16821713
String gradle_build_dir = ExportTemplateManager::get_android_build_directory(p_preset);
16831714

1715+
String monochrome_tag = "";
1716+
16841717
// Prepare images to be resized for the icons. If some image ends up being uninitialized,
16851718
// the default image from the export template will be used.
16861719

@@ -1707,7 +1740,19 @@ void EditorExportPlatformAndroid::_copy_icons_to_gradle_project(const Ref<Editor
17071740
launcher_adaptive_icon_backgrounds[i].dimensions, data);
17081741
store_file_at_path(gradle_build_dir.path_join(launcher_adaptive_icon_backgrounds[i].export_path), data);
17091742
}
1743+
1744+
if (p_monochrome.is_valid() && !p_monochrome->is_empty()) {
1745+
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);
1746+
Vector<uint8_t> data;
1747+
_process_launcher_icons(launcher_adaptive_icon_monochromes[i].export_path, p_monochrome,
1748+
launcher_adaptive_icon_monochromes[i].dimensions, data);
1749+
store_file_at_path(gradle_build_dir.path_join(launcher_adaptive_icon_monochromes[i].export_path), data);
1750+
monochrome_tag = " <monochrome android:drawable=\"@mipmap/icon_monochrome\"/>\n";
1751+
}
17101752
}
1753+
1754+
// Finalize the icon.xml by formatting the template with the optional monochrome tag.
1755+
store_string_at_path(gradle_build_dir.path_join(ICON_XML_PATH), vformat(ICON_XML_TEMPLATE, monochrome_tag));
17111756
}
17121757

17131758
Vector<EditorExportPlatformAndroid::ABI> EditorExportPlatformAndroid::get_enabled_abis(const Ref<EditorExportPreset> &p_preset) {
@@ -1871,6 +1916,7 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
18711916
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_icon_option, PROPERTY_HINT_FILE, "*.png"), ""));
18721917
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_foreground_option, PROPERTY_HINT_FILE, "*.png"), ""));
18731918
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_background_option, PROPERTY_HINT_FILE, "*.png"), ""));
1919+
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_monochrome_option, PROPERTY_HINT_FILE, "*.png"), ""));
18741920

18751921
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/opengl_debug"), false));
18761922

@@ -3012,8 +3058,9 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
30123058
Ref<Image> main_image;
30133059
Ref<Image> foreground;
30143060
Ref<Image> background;
3061+
Ref<Image> monochrome;
30153062

3016-
load_icon_refs(p_preset, main_image, foreground, background);
3063+
load_icon_refs(p_preset, main_image, foreground, background, monochrome);
30173064

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

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

33713418
Vector<ABI> invalid_abis(enabled_abis);
3419+
3420+
//To temporarily store icon xml data.
3421+
Vector<uint8_t> themed_icon_xml_data;
3422+
int icon_xml_compression_method = -1;
3423+
33723424
while (ret == UNZ_OK) {
33733425
//get filename
33743426
unz_file_info info;
@@ -3398,6 +3450,20 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
33983450
_fix_resources(p_preset, data);
33993451
}
34003452

3453+
if (file == THEMED_ICON_XML_PATH) {
3454+
// Store themed_icon.xml data.
3455+
themed_icon_xml_data = data;
3456+
skip = true;
3457+
}
3458+
3459+
if (file == ICON_XML_PATH) {
3460+
if (monochrome.is_valid() && !monochrome->is_empty()) {
3461+
// Defer processing of icon.xml until after themed_icon.xml is read.
3462+
icon_xml_compression_method = info.compression_method;
3463+
skip = true;
3464+
}
3465+
}
3466+
34013467
if (file.ends_with(".png") && file.contains("mipmap")) {
34023468
for (int i = 0; i < icon_densities_count; ++i) {
34033469
if (main_image.is_valid() && !main_image->is_empty()) {
@@ -3415,6 +3481,11 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
34153481
_process_launcher_icons(file, background, launcher_adaptive_icon_backgrounds[i].dimensions, data);
34163482
}
34173483
}
3484+
if (monochrome.is_valid() && !monochrome->is_empty()) {
3485+
if (file == launcher_adaptive_icon_monochromes[i].export_path) {
3486+
_process_launcher_icons(file, monochrome, launcher_adaptive_icon_monochromes[i].dimensions, data);
3487+
}
3488+
}
34183489
}
34193490
}
34203491

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

3536+
// Process deferred icon.xml and replace it's data with themed_icon.xml.
3537+
if (monochrome.is_valid() && !monochrome->is_empty()) {
3538+
print_line("ADDING: " + ICON_XML_PATH + " (replacing with themed_icon.xml data)");
3539+
3540+
const bool uncompressed = icon_xml_compression_method == 0;
3541+
zip_fileinfo zipfi = get_zip_fileinfo();
3542+
3543+
zipOpenNewFileInZip(unaligned_apk,
3544+
ICON_XML_PATH.utf8().get_data(),
3545+
&zipfi,
3546+
nullptr,
3547+
0,
3548+
nullptr,
3549+
0,
3550+
nullptr,
3551+
uncompressed ? 0 : Z_DEFLATED,
3552+
Z_DEFAULT_COMPRESSION);
3553+
3554+
zipWriteInFileInZip(unaligned_apk, themed_icon_xml_data.ptr(), themed_icon_xml_data.size());
3555+
zipCloseFileInZip(unaligned_apk);
3556+
}
3557+
34653558
if (!invalid_abis.is_empty()) {
34663559
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)));
34673560
CLEANUP_AND_RETURN(ERR_FILE_NOT_FOUND);

platform/android/export/export_plugin.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -167,12 +167,13 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
167167

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

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

172172
void _copy_icons_to_gradle_project(const Ref<EditorExportPreset> &p_preset,
173173
const Ref<Image> &p_main_image,
174174
const Ref<Image> &p_foreground,
175-
const Ref<Image> &p_background);
175+
const Ref<Image> &p_background,
176+
const Ref<Image> &p_monochrome);
176177

177178
static void _create_editor_debug_keystore_if_needed();
178179

platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml

+3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
WARNING: The content of this file must always match the constant 'platform/android/export/export_plugin.cpp#ICON_XML_TEMPLATE'.
4+
-->
25
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
36
<background android:drawable="@mipmap/icon_background"/>
47
<foreground android:drawable="@mipmap/icon_foreground"/>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
This file is created to work alongside the icon.xml file.
4+
If the user provides a Monochrome icon in the export settings, its data will be used to overwrite the icon.xml file.
5+
We needed to create this file to get a reference for icon_monochrome.
6+
-->
7+
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
8+
<background android:drawable="@mipmap/icon_background"/>
9+
<foreground android:drawable="@mipmap/icon_foreground"/>
10+
<monochrome android:drawable="@mipmap/icon_monochrome"/>
11+
</adaptive-icon>
Loading

0 commit comments

Comments
 (0)