You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-04 12:00:25 +00:00
Android: Add export option for custom theme attributes
- Regenerates the `GodotAppMainTheme` and `GodotAppSplashTheme` during Android export. Any manual changes to these styles will be cleared and replaced with default theme attributes. - Adds a new export option `gradle_build/custom_theme_attributes` for injecting custom theme attributes directly via the export window, avoiding the need to manually modify themes.xml.
This commit is contained in:
@@ -52,6 +52,12 @@
|
|||||||
<member name="gradle_build/android_source_template" type="String" setter="" getter="">
|
<member name="gradle_build/android_source_template" type="String" setter="" getter="">
|
||||||
Path to a ZIP file holding the source for the export template used in a Gradle build. If left empty, the default template is used.
|
Path to a ZIP file holding the source for the export template used in a Gradle build. If left empty, the default template is used.
|
||||||
</member>
|
</member>
|
||||||
|
<member name="gradle_build/custom_theme_attributes" type="Dictionary" setter="" getter="">
|
||||||
|
A dictionary of custom theme attributes to include in the exported Android project. Each entry defines a theme attribute name and its value, and will be added to the [b]GodotAppMainTheme[/b].
|
||||||
|
For example, the key [code]android:windowSwipeToDismiss[/code] with the value [code]false[/code] is resolved to [code]<item name="android:windowSwipeToDismiss">false</item>[/code].
|
||||||
|
[b]Note:[/b] To add a custom attribute to the [b]GodotAppSplashTheme[/b], prefix the attribute name with [code][splash][/code].
|
||||||
|
[b]Note:[/b] Reserved attributes configured via other export options or project settings cannot be overridden by [code]custom_theme_attributes[/code] and are skipped during export.
|
||||||
|
</member>
|
||||||
<member name="gradle_build/export_format" type="int" setter="" getter="">
|
<member name="gradle_build/export_format" type="int" setter="" getter="">
|
||||||
Application export format (*.apk or *.aab).
|
Application export format (*.apk or *.aab).
|
||||||
</member>
|
</member>
|
||||||
|
|||||||
@@ -1039,52 +1039,100 @@ void EditorExportPlatformAndroid::_write_tmp_manifest(const Ref<EditorExportPres
|
|||||||
|
|
||||||
void EditorExportPlatformAndroid::_fix_themes_xml(const Ref<EditorExportPreset> &p_preset) {
|
void EditorExportPlatformAndroid::_fix_themes_xml(const Ref<EditorExportPreset> &p_preset) {
|
||||||
const String themes_xml_path = ExportTemplateManager::get_android_build_directory(p_preset).path_join("res/values/themes.xml");
|
const String themes_xml_path = ExportTemplateManager::get_android_build_directory(p_preset).path_join("res/values/themes.xml");
|
||||||
bool enable_swipe_to_dismiss = p_preset->get("gesture/swipe_to_dismiss");
|
|
||||||
|
|
||||||
if (!FileAccess::exists(themes_xml_path)) {
|
if (!FileAccess::exists(themes_xml_path)) {
|
||||||
print_error("res/values/themes.xml does not exist.");
|
print_error("res/values/themes.xml does not exist.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String xml_content;
|
// Default/Reserved theme attributes.
|
||||||
Ref<FileAccess> file = FileAccess::open(themes_xml_path, FileAccess::READ);
|
Dictionary main_theme_attributes;
|
||||||
PackedStringArray lines = file->get_as_text().split("\n");
|
main_theme_attributes["android:windowDrawsSystemBarBackgrounds"] = "false";
|
||||||
file->close();
|
main_theme_attributes["android:windowSwipeToDismiss"] = bool_to_string(p_preset->get("gesture/swipe_to_dismiss"));
|
||||||
|
|
||||||
// Check if the themes.xml already contains <item name="android:windowSwipeToDismiss"> element.
|
Dictionary splash_theme_attributes;
|
||||||
// If found, update its value based on `enable_swipe_to_dismiss`.
|
splash_theme_attributes["android:windowSplashScreenBackground"] = "@mipmap/icon_background";
|
||||||
bool found = false;
|
splash_theme_attributes["windowSplashScreenAnimatedIcon"] = "@mipmap/icon_foreground";
|
||||||
bool modified = false;
|
splash_theme_attributes["postSplashScreenTheme"] = "@style/GodotAppMainTheme";
|
||||||
for (int i = 0; i < lines.size(); i++) {
|
|
||||||
String line = lines[i];
|
|
||||||
if (line.contains("<item name") && line.contains("\"android:windowSwipeToDismiss\">")) {
|
|
||||||
lines.set(i, vformat(" <item name=\"android:windowSwipeToDismiss\">%s</item>", bool_to_string(enable_swipe_to_dismiss)));
|
|
||||||
found = true;
|
|
||||||
modified = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If <item name="android:windowSwipeToDismiss"> is not found and `enable_swipe_to_dismiss` is false:
|
Dictionary custom_theme_attributes = p_preset->get("gradle_build/custom_theme_attributes");
|
||||||
// Add a new <item> element before the closing </style> tag.
|
|
||||||
if (!found && !enable_swipe_to_dismiss) {
|
// Does not override default/reserved theme attributes; skips any duplicates from custom_theme_attributes.
|
||||||
for (int i = 0; i < lines.size(); i++) {
|
for (const Variant &k : custom_theme_attributes.keys()) {
|
||||||
if (lines[i].contains("</style>")) {
|
String key = k;
|
||||||
lines.insert(i, " <item name=\"android:windowSwipeToDismiss\">false</item>");
|
String value = custom_theme_attributes[k];
|
||||||
modified = true;
|
if (key.begins_with("[splash]")) {
|
||||||
break;
|
String splash_key = key.trim_prefix("[splash]");
|
||||||
|
if (splash_theme_attributes.has(splash_key)) {
|
||||||
|
WARN_PRINT(vformat("Skipped custom_theme_attribute '%s'; this is a reserved attribute configured via other export options or project settings.", splash_key));
|
||||||
|
} else {
|
||||||
|
splash_theme_attributes[splash_key] = value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (main_theme_attributes.has(key)) {
|
||||||
|
WARN_PRINT(vformat("Skipped custom_theme_attribute '%s'; this is a reserved attribute configured via other export options or project settings.", key));
|
||||||
|
} else {
|
||||||
|
main_theme_attributes[key] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reconstruct the XML content from the modified lines.
|
Ref<FileAccess> file = FileAccess::open(themes_xml_path, FileAccess::READ);
|
||||||
if (modified) {
|
PackedStringArray lines = file->get_as_text().split("\n");
|
||||||
xml_content = String("\n").join(lines);
|
file->close();
|
||||||
store_string_at_path(themes_xml_path, xml_content);
|
|
||||||
print_verbose("Successfully modified " + themes_xml_path + ": " + "\n" + xml_content);
|
PackedStringArray new_lines;
|
||||||
} else {
|
bool inside_main_theme = false;
|
||||||
print_verbose("No changes needed for " + themes_xml_path);
|
bool inside_splash_theme = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < lines.size(); i++) {
|
||||||
|
String line = lines[i];
|
||||||
|
|
||||||
|
if (line.contains("<style name=\"GodotAppMainTheme\"")) {
|
||||||
|
inside_main_theme = true;
|
||||||
|
new_lines.append(line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (line.contains("<style name=\"GodotAppSplashTheme\"")) {
|
||||||
|
inside_splash_theme = true;
|
||||||
|
new_lines.append(line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inject GodotAppMainTheme attributes.
|
||||||
|
if (inside_main_theme && line.contains("</style>")) {
|
||||||
|
for (const Variant &attribute : main_theme_attributes.keys()) {
|
||||||
|
String value = main_theme_attributes[attribute];
|
||||||
|
String item_line = vformat(" <item name=\"%s\">%s</item>", attribute, value);
|
||||||
|
new_lines.append(item_line);
|
||||||
|
}
|
||||||
|
new_lines.append(line); // Add </style> in the end.
|
||||||
|
inside_main_theme = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inject GodotAppSplashTheme attributes.
|
||||||
|
if (inside_splash_theme && line.contains("</style>")) {
|
||||||
|
for (const Variant &attribute : splash_theme_attributes.keys()) {
|
||||||
|
String value = splash_theme_attributes[attribute];
|
||||||
|
String item_line = vformat(" <item name=\"%s\">%s</item>", attribute, value);
|
||||||
|
new_lines.append(item_line);
|
||||||
|
}
|
||||||
|
new_lines.append(line); // Add </style> in the end.
|
||||||
|
inside_splash_theme = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add all other lines unchanged.
|
||||||
|
if (!inside_main_theme && !inside_splash_theme) {
|
||||||
|
new_lines.append(line);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reconstruct the XML content from the modified lines.
|
||||||
|
String xml_content = String("\n").join(new_lines);
|
||||||
|
store_string_at_path(themes_xml_path, xml_content);
|
||||||
|
print_verbose("Successfully modified " + themes_xml_path + ": " + "\n" + xml_content);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_manifest, bool p_give_internet) {
|
void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_manifest, bool p_give_internet) {
|
||||||
@@ -1994,6 +2042,11 @@ String EditorExportPlatformAndroid::get_export_option_warning(const EditorExport
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (p_name == "gradle_build/custom_theme_attributes") {
|
||||||
|
bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build");
|
||||||
|
if (bool(p_preset->get("gradle_build/custom_theme_attributes")) && !gradle_build_enabled) {
|
||||||
|
return TTR("\"Use Gradle Build\" is required to add custom theme attributes.");
|
||||||
|
}
|
||||||
} else if (p_name == "package/show_in_android_tv") {
|
} else if (p_name == "package/show_in_android_tv") {
|
||||||
bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build");
|
bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build");
|
||||||
if (bool(p_preset->get("package/show_in_android_tv")) && !gradle_build_enabled) {
|
if (bool(p_preset->get("package/show_in_android_tv")) && !gradle_build_enabled) {
|
||||||
@@ -2027,6 +2080,8 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
|
|||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "gradle_build/min_sdk", PROPERTY_HINT_PLACEHOLDER_TEXT, vformat("%d (default)", DEFAULT_MIN_SDK_VERSION)), "", false, true));
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "gradle_build/min_sdk", PROPERTY_HINT_PLACEHOLDER_TEXT, vformat("%d (default)", DEFAULT_MIN_SDK_VERSION)), "", false, true));
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "gradle_build/target_sdk", PROPERTY_HINT_PLACEHOLDER_TEXT, vformat("%d (default)", DEFAULT_TARGET_SDK_VERSION)), "", false, true));
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "gradle_build/target_sdk", PROPERTY_HINT_PLACEHOLDER_TEXT, vformat("%d (default)", DEFAULT_TARGET_SDK_VERSION)), "", false, true));
|
||||||
|
|
||||||
|
r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "gradle_build/custom_theme_attributes", PROPERTY_HINT_DICTIONARY_TYPE, "String;String"), Dictionary()));
|
||||||
|
|
||||||
#ifndef DISABLE_DEPRECATED
|
#ifndef DISABLE_DEPRECATED
|
||||||
Vector<PluginConfigAndroid> plugins_configs = get_plugins();
|
Vector<PluginConfigAndroid> plugins_configs = get_plugins();
|
||||||
for (int i = 0; i < plugins_configs.size(); i++) {
|
for (int i = 0; i < plugins_configs.size(); i++) {
|
||||||
@@ -2108,6 +2163,7 @@ bool EditorExportPlatformAndroid::get_export_option_visibility(const EditorExpor
|
|||||||
|
|
||||||
bool advanced_options_enabled = p_preset->are_advanced_options_enabled();
|
bool advanced_options_enabled = p_preset->are_advanced_options_enabled();
|
||||||
if (p_option == "graphics/opengl_debug" ||
|
if (p_option == "graphics/opengl_debug" ||
|
||||||
|
p_option == "gradle_build/custom_theme_attributes" ||
|
||||||
p_option == "command_line/extra_args" ||
|
p_option == "command_line/extra_args" ||
|
||||||
p_option == "permissions/custom_permissions" ||
|
p_option == "permissions/custom_permissions" ||
|
||||||
p_option == "keystore/debug" ||
|
p_option == "keystore/debug" ||
|
||||||
|
|||||||
@@ -1,22 +1,17 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
<!-- GodotAppMainTheme is auto-generated during export. Manual changes will be overwritten.
|
||||||
|
To add custom attributes, use the "gradle_build/custom_theme_attributes" Android export option. -->
|
||||||
<style name="GodotAppMainTheme" parent="@android:style/Theme.DeviceDefault.NoActionBar">
|
<style name="GodotAppMainTheme" parent="@android:style/Theme.DeviceDefault.NoActionBar">
|
||||||
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
|
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
|
||||||
<item name="android:windowSwipeToDismiss">false</item>
|
<item name="android:windowSwipeToDismiss">false</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<!-- GodotAppSplashTheme is auto-generated during export. Manual changes will be overwritten.
|
||||||
|
To add custom attributes, use the "gradle_build/custom_theme_attributes" Android export option. -->
|
||||||
<style name="GodotAppSplashTheme" parent="Theme.SplashScreen">
|
<style name="GodotAppSplashTheme" parent="Theme.SplashScreen">
|
||||||
<!-- Set the splash screen background, animated icon, and animation
|
|
||||||
duration. -->
|
|
||||||
<item name="android:windowSplashScreenBackground">@mipmap/icon_background</item>
|
<item name="android:windowSplashScreenBackground">@mipmap/icon_background</item>
|
||||||
|
|
||||||
<!-- Use windowSplashScreenAnimatedIcon to add a drawable or an animated
|
|
||||||
drawable. One of these is required. -->
|
|
||||||
<item name="windowSplashScreenAnimatedIcon">@mipmap/icon_foreground</item>
|
<item name="windowSplashScreenAnimatedIcon">@mipmap/icon_foreground</item>
|
||||||
|
|
||||||
<!-- Set the theme of the Activity that directly follows your splash
|
|
||||||
screen. This is required. -->
|
|
||||||
<item name="postSplashScreenTheme">@style/GodotAppMainTheme</item>
|
<item name="postSplashScreenTheme">@style/GodotAppMainTheme</item>
|
||||||
</style>
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user