From ee82a8f69dacb237624dd8034fac99bf5e8fe9ec Mon Sep 17 00:00:00 2001 From: Anish Kumar Date: Wed, 7 Jan 2026 14:05:27 +0530 Subject: [PATCH] Android: Add export options to customize splash screen --- .../EditorExportPlatformAndroid.xml | 16 +++ platform/android/export/export_plugin.cpp | 114 ++++++++++++++++-- platform/android/export/export_plugin.h | 6 +- .../android/java/app/res/values/themes.xml | 3 +- .../main/java/com/godot/game/GodotApp.java | 7 +- .../main/java/org/godotengine/godot/Godot.kt | 4 + .../res/drawable/splash_branding_image.webp | Bin 0 -> 102 bytes .../src/main/res/drawable/splash_icon.webp | Bin 0 -> 6698 bytes 8 files changed, 138 insertions(+), 12 deletions(-) create mode 100644 platform/android/java/lib/src/main/res/drawable/splash_branding_image.webp create mode 100644 platform/android/java/lib/src/main/res/drawable/splash_icon.webp diff --git a/platform/android/doc_classes/EditorExportPlatformAndroid.xml b/platform/android/doc_classes/EditorExportPlatformAndroid.xml index ec3b8076867..31de83fe708 100644 --- a/platform/android/doc_classes/EditorExportPlatformAndroid.xml +++ b/platform/android/doc_classes/EditorExportPlatformAndroid.xml @@ -632,6 +632,22 @@ If [code]true[/code], shaders will be compiled and embedded in the application. This option is only supported when using the Forward+ or Mobile renderers. [b]Note:[/b] When exporting as a dedicated server, the shader baker is always disabled since no rendering is performed. + + The background color used for the system splash screen window. + If not set, it will fallback to [member EditorExportPlatformAndroid.launcher_icons/adaptive_background_432x432]. + [b]Note:[/b] This is only applied if [member EditorExportPlatformAndroid.gradle_build/use_gradle_build] is enabled. + + + System splash screen branding image file. If left empty, no branding image will be used. See [url=https://developer.android.com/develop/ui/views/launch/splash-screen#dimensions]splash-screen dimensions[/url]. + [b]Note:[/b] Can be used to set an image to be shown at the bottom of the splash screen. + + + If [code]true[/code], Godot's boot splash will not be shown, and the system boot splash will remain visible for a longer time, until the mainloop starts. + + + System splash screen icon file. If left empty, it will fall back to [member EditorExportPlatformAndroid.launcher_icons/adaptive_foreground_432x432]. See [url=https://developer.android.com/develop/ui/views/launch/splash-screen#dimensions]splash-screen dimensions[/url]. + [b]Note:[/b] You can provide an [url=https://developer.android.com/reference/android/graphics/drawable/AnimatedVectorDrawable]AnimatedVectorDrawable (AVD)[/url] XML. However, the XML file will only be used if [member EditorExportPlatformAndroid.gradle_build/use_gradle_build] is enabled. If not, it will fall back to [member EditorExportPlatformAndroid.launcher_icons/adaptive_background_432x432]. + If [code]true[/code], allows the application to participate in the backup and restore infrastructure. diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index 29cf1f08a23..1aa4a082b35 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -244,6 +244,14 @@ static const String ICON_XML_TEMPLATE = 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 String ANDROID_SPLASH_ICON_PATH = "res/drawable/splash_icon.webp"; +static const String ANDROID_SPLASH_BRANDING_IMAGE_PATH = "res/drawable/splash_branding_image.webp"; + +static const char *DISABLE_GODOT_SPLASH_OPTION = PNAME("splash_screen/disable_godot_boot_splash"); +static const char *ANDROID_SPLASH_ICON_OPTION = PNAME("splash_screen/icon"); +static const char *ANDROID_SPLASH_BACKGROUND_COLOR_OPTION = PNAME("splash_screen/background_color"); +static const char *ANDROID_SPLASH_BRANDING_IMAGE_OPTION = PNAME("splash_screen/branding_image"); + 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"); @@ -1084,7 +1092,8 @@ bool EditorExportPlatformAndroid::_is_transparency_allowed(const Ref &p_preset) { - const String themes_xml_path = ExportTemplateManager::get_android_build_directory(p_preset).path_join("res/values/themes.xml"); + String gradle_build_dir = ExportTemplateManager::get_android_build_directory(p_preset); + const String themes_xml_path = gradle_build_dir.path_join("res/values/themes.xml"); if (!FileAccess::exists(themes_xml_path)) { print_error("res/values/themes.xml does not exist."); @@ -1104,8 +1113,24 @@ void EditorExportPlatformAndroid::_fix_themes_xml(const Ref } Dictionary splash_theme_attributes; - splash_theme_attributes["android:windowSplashScreenBackground"] = "@mipmap/icon_background"; - splash_theme_attributes["windowSplashScreenAnimatedIcon"] = "@mipmap/icon_foreground"; + + Color color = p_preset->get(ANDROID_SPLASH_BACKGROUND_COLOR_OPTION); + if (color == Color()) { + splash_theme_attributes["android:windowSplashScreenBackground"] = "@mipmap/icon_background"; + } else { + splash_theme_attributes["android:windowSplashScreenBackground"] = "#" + color.to_html(false); + } + splash_theme_attributes["windowSplashScreenAnimatedIcon"] = "@drawable/splash_icon"; + String splash_icon_path = p_preset->get(ANDROID_SPLASH_ICON_OPTION); + if (!splash_icon_path.is_empty() && splash_icon_path.get_extension() == "xml") { + Vector data = FileAccess::get_file_as_bytes(splash_icon_path); + store_file_at_path(gradle_build_dir.path_join("res/drawable/splash_icon_vector.xml"), data); + splash_theme_attributes["windowSplashScreenAnimatedIcon"] = "@drawable/splash_icon_vector"; + } + String splash_branding_image_path = p_preset->get(ANDROID_SPLASH_BRANDING_IMAGE_OPTION); + if (!splash_branding_image_path.is_empty()) { + splash_theme_attributes["android:windowSplashScreenBrandingImage"] = "@drawable/splash_branding_image"; + } splash_theme_attributes["postSplashScreenTheme"] = "@style/GodotAppMainTheme"; splash_theme_attributes["android:windowIsTranslucent"] = bool_to_string(transparency_allowed); @@ -1897,7 +1922,7 @@ void EditorExportPlatformAndroid::_process_launcher_icons(const String &p_file_n memcpy(p_data.ptrw(), buffer.ptr(), p_data.size()); } -void EditorExportPlatformAndroid::load_icon_refs(const Ref &p_preset, Ref &icon, Ref &foreground, Ref &background, Ref &monochrome) { +void EditorExportPlatformAndroid::load_icon_refs(const Ref &p_preset, Ref &icon, Ref &foreground, Ref &background, Ref &monochrome, Ref &splash_icon, Ref &splash_branding_image) { String project_icon_path = get_project_setting(p_preset, "application/config/icon"); Error err = OK; @@ -1941,15 +1966,56 @@ void EditorExportPlatformAndroid::load_icon_refs(const Ref & print_verbose("Loading adaptive monochrome icon from " + path); monochrome = _load_icon_or_splash_image(path, &err); } + + // Splash icon: user selection -> adaptive foreground icon (user selection -> regular icon). + path = static_cast(p_preset->get(ANDROID_SPLASH_ICON_OPTION)).strip_edges(); + if (path.get_extension() != "xml") { + // XML file is handled in _fix_themes_xml(). + print_verbose("Loading splash screen icon from " + path); + bool loaded_ok = false; + if (!path.is_empty()) { + splash_icon = _load_icon_or_splash_image(path, &err); + loaded_ok = (err == OK && splash_icon.is_valid() && !splash_icon->is_empty()); + } + if (!loaded_ok) { + print_verbose("- falling back to using the adaptive foreground icon"); + splash_icon = foreground; + } + } + + // Splash branding image: user selection -> No image. + // - Gradle build: If the path is empty, the splash theme attribute is not added in _fix_themes_xml(). + // - Legacy build: If the path is empty, a transparent image is used. + path = static_cast(p_preset->get(ANDROID_SPLASH_BRANDING_IMAGE_OPTION)).strip_edges(); + if (!path.is_empty()) { + print_verbose("Loading splash screen branding image from " + path); + splash_branding_image = _load_icon_or_splash_image(path, &err); + } } 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_monochrome) { + const Ref &p_monochrome, + const Ref &p_splash_icon, + const Ref &p_splash_branding_image) { String gradle_build_dir = ExportTemplateManager::get_android_build_directory(p_preset); + // Copy splash screen icon to the drawable directory. + // This is only for png/webp/svg file; XML file is handled in _fix_themes_xml(). + if (p_splash_icon.is_valid() && !p_splash_icon->is_empty()) { + print_verbose("Copying splash screen icon into " + ANDROID_SPLASH_ICON_PATH); + Vector buffer = p_splash_icon->save_webp_to_buffer(); + store_file_at_path(gradle_build_dir.path_join(ANDROID_SPLASH_ICON_PATH), buffer); + } + + if (p_splash_branding_image.is_valid() && !p_splash_branding_image->is_empty()) { + print_verbose("Copying splash screen branding image into " + ANDROID_SPLASH_BRANDING_IMAGE_PATH); + Vector buffer = p_splash_branding_image->save_webp_to_buffer(); + store_file_at_path(gradle_build_dir.path_join(ANDROID_SPLASH_BRANDING_IMAGE_PATH), buffer); + } + String monochrome_tag = ""; // Prepare images to be resized for the icons. If some image ends up being uninitialized, @@ -2211,6 +2277,11 @@ void EditorExportPlatformAndroid::get_export_options(List *r_optio r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_xlarge"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::COLOR, "screen/background_color", PROPERTY_HINT_COLOR_NO_ALPHA), Color())); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, DISABLE_GODOT_SPLASH_OPTION), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, ANDROID_SPLASH_ICON_OPTION, PROPERTY_HINT_FILE, "*.png,*.webp,*.svg,*.xml"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, ANDROID_SPLASH_BRANDING_IMAGE_OPTION, PROPERTY_HINT_FILE, "*.png,*.webp,*.svg"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::COLOR, ANDROID_SPLASH_BACKGROUND_COLOR_OPTION, PROPERTY_HINT_COLOR_NO_ALPHA), Color())); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "user_data_backup/allow"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args", PROPERTY_HINT_NONE, "monospace"), "")); @@ -2248,7 +2319,11 @@ bool EditorExportPlatformAndroid::get_export_option_visibility(const EditorExpor p_option == "gesture/swipe_to_dismiss" || p_option == "apk_expansion/enable" || p_option == "apk_expansion/SALT" || - p_option == "apk_expansion/public_key") { + p_option == "apk_expansion/public_key" || + p_option == DISABLE_GODOT_SPLASH_OPTION || + p_option == ANDROID_SPLASH_ICON_OPTION || + p_option == ANDROID_SPLASH_BACKGROUND_COLOR_OPTION || + p_option == ANDROID_SPLASH_BRANDING_IMAGE_OPTION) { return advanced_options_enabled; } if (p_option == "gradle_build/gradle_build_directory" || p_option == "gradle_build/android_source_template") { @@ -3244,6 +3319,11 @@ void EditorExportPlatformAndroid::get_command_line_flags(const Refget(DISABLE_GODOT_SPLASH_OPTION); + if (disable_godot_splash) { + command_line_strings.push_back("--disable_godot_splash"); + } + bool debug_opengl = p_preset->get("graphics/opengl_debug"); if (debug_opengl) { command_line_strings.push_back("--debug_opengl"); @@ -3692,8 +3772,10 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref foreground; Ref background; Ref monochrome; + Ref splash_icon; + Ref splash_branding_image; - load_icon_refs(p_preset, main_image, foreground, background, monochrome); + load_icon_refs(p_preset, main_image, foreground, background, monochrome, splash_icon, splash_branding_image); Vector command_line_flags; // Write command line flags into the command_line_flags variable. @@ -3766,7 +3848,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Refis_empty()) { + Vector buffer = splash_icon->save_webp_to_buffer(); + data.resize(buffer.size()); + memcpy(data.ptrw(), buffer.ptr(), data.size()); + } + } + + if (file == ANDROID_SPLASH_BRANDING_IMAGE_PATH) { + if (splash_branding_image.is_valid() && !splash_branding_image->is_empty()) { + Vector buffer = splash_branding_image->save_webp_to_buffer(); + data.resize(buffer.size()); + memcpy(data.ptrw(), buffer.ptr(), data.size()); + } + } + if (file == THEMED_ICON_XML_PATH) { // Store themed_icon.xml data. themed_icon_xml_data = data; diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h index 2f08e89fc80..2b40e251c2e 100644 --- a/platform/android/export/export_plugin.h +++ b/platform/android/export/export_plugin.h @@ -187,13 +187,15 @@ 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, Ref &monochrome); + void load_icon_refs(const Ref &p_preset, Ref &icon, Ref &foreground, Ref &background, Ref &monochrome, Ref &splash_icon, Ref &splash_branding_image); 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_monochrome); + const Ref &p_monochrome, + const Ref &p_splash_icon, + const Ref &p_splash_branding_image); static void _create_editor_debug_keystore_if_needed(); diff --git a/platform/android/java/app/res/values/themes.xml b/platform/android/java/app/res/values/themes.xml index af2dd509cf6..6125707c442 100644 --- a/platform/android/java/app/res/values/themes.xml +++ b/platform/android/java/app/res/values/themes.xml @@ -11,7 +11,8 @@ To add custom attributes, use the "gradle_build/custom_theme_attributes" Android export option. --> diff --git a/platform/android/java/app/src/main/java/com/godot/game/GodotApp.java b/platform/android/java/app/src/main/java/com/godot/game/GodotApp.java index 8573f713269..4406bf0b5b4 100644 --- a/platform/android/java/app/src/main/java/com/godot/game/GodotApp.java +++ b/platform/android/java/app/src/main/java/com/godot/game/GodotApp.java @@ -67,9 +67,14 @@ public class GodotApp extends GodotActivity { @Override public void onCreate(Bundle savedInstanceState) { - SplashScreen.installSplashScreen(this); + SplashScreen splashScreen = SplashScreen.installSplashScreen(this); EdgeToEdge.enable(this); super.onCreate(savedInstanceState); + + Godot godot = getGodot(); + if (godot != null && godot.getDisableGodotSplash()) { + splashScreen.setKeepOnScreenCondition(() -> godot.getRunStatus() != Godot.RunStatus.STARTED); + } } @Override diff --git a/platform/android/java/lib/src/main/java/org/godotengine/godot/Godot.kt b/platform/android/java/lib/src/main/java/org/godotengine/godot/Godot.kt index e6a3ea342f9..86261b02159 100644 --- a/platform/android/java/lib/src/main/java/org/godotengine/godot/Godot.kt +++ b/platform/android/java/lib/src/main/java/org/godotengine/godot/Godot.kt @@ -204,6 +204,8 @@ class Godot private constructor(val context: Context) { internal var darkMode = false private var backgroundColor: Int = Color.BLACK private var orientation = Configuration.ORIENTATION_UNDEFINED + var disableGodotSplash = false + private set internal var containerLayout: FrameLayout? = null var renderView: GodotRenderView? = null @@ -276,6 +278,8 @@ class Godot private constructor(val context: Context) { newArgs.add(commandLine[i]) } else if (commandLine[i] == "--background_color") { setWindowColor(commandLine[i + 1]) + } else if (commandLine[i] == "--disable_godot_splash") { + disableGodotSplash = true } else if (commandLine[i] == "--use_apk_expansion") { useApkExpansion = true } else if (hasExtra && commandLine[i] == "--apk_expansion_md5") { diff --git a/platform/android/java/lib/src/main/res/drawable/splash_branding_image.webp b/platform/android/java/lib/src/main/res/drawable/splash_branding_image.webp new file mode 100644 index 0000000000000000000000000000000000000000..bfd4f7cb0a60882839f90c50db3e366bb4c2743e GIT binary patch literal 102 zcmWIYbaRViU|?NUB2yJ(0&7alPD`~Umxtp<=X0EM|8r~m)} literal 0 HcmV?d00001 diff --git a/platform/android/java/lib/src/main/res/drawable/splash_icon.webp b/platform/android/java/lib/src/main/res/drawable/splash_icon.webp new file mode 100644 index 0000000000000000000000000000000000000000..1403678b074a1de6bfb7c1d1e43bb2cf2c30e64e GIT binary patch literal 6698 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 A00000 literal 0 HcmV?d00001