diff options
Diffstat (limited to 'platform/android/export')
-rw-r--r-- | platform/android/export/export.cpp | 58 | ||||
-rw-r--r-- | platform/android/export/export.h | 58 | ||||
-rw-r--r-- | platform/android/export/export_plugin.cpp | 418 | ||||
-rw-r--r-- | platform/android/export/export_plugin.h | 73 | ||||
-rw-r--r-- | platform/android/export/godot_plugin_config.cpp | 58 | ||||
-rw-r--r-- | platform/android/export/godot_plugin_config.h | 58 | ||||
-rw-r--r-- | platform/android/export/gradle_export_util.cpp | 161 | ||||
-rw-r--r-- | platform/android/export/gradle_export_util.h | 80 |
8 files changed, 588 insertions, 376 deletions
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index f4c4e985fe..d7fe89d97d 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* export.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* export.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #include "export.h" diff --git a/platform/android/export/export.h b/platform/android/export/export.h index 82ce40f95d..7ada91aec9 100644 --- a/platform/android/export/export.h +++ b/platform/android/export/export.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* export.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* export.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef ANDROID_EXPORT_H #define ANDROID_EXPORT_H diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index 9919268375..fdd2fed836 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* export_plugin.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* export_plugin.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #include "export_plugin.h" @@ -43,10 +43,16 @@ #include "editor/editor_log.h" #include "editor/editor_node.h" #include "editor/editor_paths.h" +#include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "main/splash.gen.h" -#include "platform/android/logo.gen.h" -#include "platform/android/run_icon.gen.h" +#include "platform/android/logo_svg.gen.h" +#include "platform/android/run_icon_svg.gen.h" + +#include "modules/modules_enabled.gen.h" // For svg. +#ifdef MODULE_SVG_ENABLED +#include "modules/svg/image_loader_svg.h" +#endif #include <string.h> @@ -205,7 +211,7 @@ static const char *LEGACY_BUILD_SPLASH_IMAGE_EXPORT_PATH = "res/drawable-nodpi-v static const char *SPLASH_BG_COLOR_PATH = "res/drawable-nodpi/splash_bg_color.png"; static const char *LEGACY_BUILD_SPLASH_BG_COLOR_PATH = "res/drawable-nodpi-v4/splash_bg_color.png"; static const char *SPLASH_CONFIG_PATH = "res://android/build/res/drawable/splash_drawable.xml"; -static const char *GDNATIVE_LIBS_PATH = "res://android/build/libs/gdnativelibs.json"; +static const char *GDEXTENSION_LIBS_PATH = "res://android/build/libs/gdextensionlibs.json"; static const int icon_densities_count = 6; static const char *launcher_icon_option = PNAME("launcher_icons/main_192x192"); @@ -245,7 +251,8 @@ static const int EXPORT_FORMAT_AAB = 1; static const char *APK_ASSETS_DIRECTORY = "res://android/build/assets"; static const char *AAB_ASSETS_DIRECTORY = "res://android/build/assetPacks/installTime/src/main/assets"; -static const int DEFAULT_MIN_SDK_VERSION = 19; // Should match the value in 'platform/android/java/app/config.gradle#minSdk' +static const int DEFAULT_MIN_SDK_VERSION = 21; // Should match the value in 'platform/android/java/app/config.gradle#minSdk' +static const int VULKAN_MIN_SDK_VERSION = 24; static const int DEFAULT_TARGET_SDK_VERSION = 32; // Should match the value in 'platform/android/java/app/config.gradle#targetSdk' #ifndef ANDROID_ENABLED @@ -431,6 +438,14 @@ String EditorExportPlatformAndroid::get_project_name(const String &p_name) const String EditorExportPlatformAndroid::get_package_name(const String &p_package) const { String pname = p_package; + String name = get_valid_basename(); + pname = pname.replace("$genname", name); + return pname; +} + +// Returns the project name without invalid characters +// or the "noname" string if all characters are invalid. +String EditorExportPlatformAndroid::get_valid_basename() const { String basename = GLOBAL_GET("application/config/name"); basename = basename.to_lower(); @@ -446,13 +461,12 @@ String EditorExportPlatformAndroid::get_package_name(const String &p_package) co first = false; } } + if (name.is_empty()) { name = "noname"; } - pname = pname.replace("$genname", name); - - return pname; + return name; } String EditorExportPlatformAndroid::get_assets_directory(const Ref<EditorExportPreset> &p_preset, int p_export_format) const { @@ -460,7 +474,7 @@ String EditorExportPlatformAndroid::get_assets_directory(const Ref<EditorExportP } bool EditorExportPlatformAndroid::is_package_name_valid(const String &p_package, String *r_error) const { - String pname = p_package; + String pname = get_package_name(p_package); if (pname.length() == 0) { if (r_error) { @@ -519,6 +533,24 @@ bool EditorExportPlatformAndroid::is_package_name_valid(const String &p_package, return false; } + if (p_package.find("$genname") >= 0 && !is_project_name_valid()) { + if (r_error) { + *r_error = TTR("The project name does not meet the requirement for the package name format. Please explicitly specify the package name."); + } + return false; + } + + return true; +} + +bool EditorExportPlatformAndroid::is_project_name_valid() const { + // Get the original project name and convert to lowercase. + String basename = GLOBAL_GET("application/config/name"); + basename = basename.to_lower(); + // Check if there are invalid characters. + if (basename != get_valid_basename()) { + return false; + } return true; } @@ -703,7 +735,7 @@ Error EditorExportPlatformAndroid::save_apk_so(void *p_userdata, const SharedObj exported = true; String abi = abis[abi_index].abi; String dst_path = String("lib").path_join(abi).path_join(p_so.path.get_file()); - Vector<uint8_t> array = FileAccess::get_file_as_array(p_so.path); + Vector<uint8_t> array = FileAccess::get_file_as_bytes(p_so.path); Error store_err = store_in_apk(ed, dst_path, array); ERR_FAIL_COND_V_MSG(store_err, store_err, "Cannot store in apk file '" + dst_path + "'."); } @@ -748,7 +780,7 @@ Error EditorExportPlatformAndroid::copy_gradle_so(void *p_userdata, const Shared String abi = abis[abi_index].abi; String filename = p_so.path.get_file(); String dst_path = base.path_join(type).path_join(abi).path_join(filename); - Vector<uint8_t> data = FileAccess::get_file_as_array(p_so.path); + Vector<uint8_t> data = FileAccess::get_file_as_bytes(p_so.path); print_verbose("Copying .so file from " + p_so.path + " to " + dst_path); Error err = store_file_at_path(dst_path, data); ERR_FAIL_COND_V_MSG(err, err, "Failed to copy .so file from " + p_so.path + " to " + dst_path); @@ -768,6 +800,12 @@ bool EditorExportPlatformAndroid::_has_manage_external_storage_permission(const return p_permissions.find("android.permission.MANAGE_EXTERNAL_STORAGE") != -1; } +bool EditorExportPlatformAndroid::_uses_vulkan() { + String current_renderer = GLOBAL_GET("rendering/renderer/rendering_method.mobile"); + bool uses_vulkan = (current_renderer == "forward_plus" || current_renderer == "mobile") && GLOBAL_GET("rendering/rendering_device/driver.android") == "vulkan"; + return uses_vulkan; +} + void EditorExportPlatformAndroid::_get_permissions(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, Vector<String> &r_permissions) { const char **aperms = android_perms; while (*aperms) { @@ -802,7 +840,7 @@ void EditorExportPlatformAndroid::_get_permissions(const Ref<EditorExportPreset> } void EditorExportPlatformAndroid::_write_tmp_manifest(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, bool p_debug) { - print_verbose("Building temporary manifest.."); + print_verbose("Building temporary manifest..."); String manifest_text = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" @@ -822,7 +860,7 @@ void EditorExportPlatformAndroid::_write_tmp_manifest(const Ref<EditorExportPres } } - manifest_text += _get_xr_features_tag(p_preset); + manifest_text += _get_xr_features_tag(p_preset, _uses_vulkan()); manifest_text += _get_application_tag(p_preset, _has_read_write_storage_permission(perms)); manifest_text += "</manifest>\n"; String manifest_path = vformat("res://android/build/src/%s/AndroidManifest.xml", (p_debug ? "debug" : "release")); @@ -868,12 +906,8 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p bool screen_support_large = p_preset->get("screen/support_large"); bool screen_support_xlarge = p_preset->get("screen/support_xlarge"); - int xr_mode_index = p_preset->get("xr_features/xr_mode"); - int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); - int hand_tracking_frequency_index = p_preset->get("xr_features/hand_tracking_frequency"); - bool backup_allowed = p_preset->get("user_data_backup/allow"); - bool classify_as_game = p_preset->get("package/classify_as_game"); + int app_category = p_preset->get("package/app_category"); bool retain_data_on_uninstall = p_preset->get("package/retain_data_on_uninstall"); bool exclude_from_recents = p_preset->get("package/exclude_from_recents"); bool is_resizeable = bool(GLOBAL_GET("display/window/size/resizable")); @@ -972,8 +1006,12 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p encode_uint32(backup_allowed, &p_manifest.write[iofs + 16]); } + if (tname == "application" && attrname == "appCategory") { + encode_uint32(_get_app_category_value(app_category), &p_manifest.write[iofs + 16]); + } + if (tname == "application" && attrname == "isGame") { - encode_uint32(classify_as_game, &p_manifest.write[iofs + 16]); + encode_uint32(app_category == APP_CATEGORY_GAME, &p_manifest.write[iofs + 16]); } if (tname == "application" && attrname == "hasFragileUserData") { @@ -992,6 +1030,10 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p encode_uint32(is_resizeable, &p_manifest.write[iofs + 16]); } + if (tname == "provider" && attrname == "authorities") { + string_table.write[attr_value] = get_package_name(package_name) + String(".fileprovider"); + } + if (tname == "supports-screens") { if (attrname == "smallScreens") { encode_uint32(screen_support_small ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]); @@ -1007,25 +1049,6 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p } } - // Hand tracking related configurations - if (xr_mode_index == XR_MODE_OPENXR && hand_tracking_index > XR_HAND_TRACKING_NONE) { - if (tname == "meta-data" && attrname == "name" && value == "xr_hand_tracking_metadata_name") { - string_table.write[attr_value] = "com.oculus.handtracking.frequency"; - } - - if (tname == "meta-data" && attrname == "value" && value == "xr_hand_tracking_metadata_value") { - string_table.write[attr_value] = (hand_tracking_frequency_index == XR_HAND_TRACKING_FREQUENCY_LOW ? "LOW" : "HIGH"); - } - - if (tname == "meta-data" && attrname == "name" && value == "xr_hand_tracking_version_name") { - string_table.write[attr_value] = "com.oculus.handtracking.version"; - } - - if (tname == "meta-data" && attrname == "name" && value == "xr_hand_tracking_version_value") { - string_table.write[attr_value] = "V2.0"; - } - } - iofs += 20; } @@ -1040,26 +1063,11 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p Vector<bool> feature_required_list; Vector<int> feature_versions; - if (xr_mode_index == XR_MODE_OPENXR) { - // Set degrees of freedom - feature_names.push_back("android.hardware.vr.headtracking"); + if (_uses_vulkan()) { + // Require vulkan hardware level 1 support + feature_names.push_back("android.hardware.vulkan.level"); feature_required_list.push_back(true); feature_versions.push_back(1); - - // Check for hand tracking - if (hand_tracking_index > XR_HAND_TRACKING_NONE) { - feature_names.push_back("oculus.software.handtracking"); - feature_required_list.push_back(hand_tracking_index == XR_HAND_TRACKING_REQUIRED); - feature_versions.push_back(-1); // no version attribute should be added. - } - - // Check for passthrough - int passthrough_mode = p_preset->get("xr_features/passthrough"); - if (passthrough_mode > XR_PASSTHROUGH_NONE) { - feature_names.push_back("com.oculus.feature.PASSTHROUGH"); - feature_required_list.push_back(passthrough_mode == XR_PASSTHROUGH_REQUIRED); - feature_versions.push_back(-1); - } } if (feature_names.size() > 0) { @@ -1681,6 +1689,7 @@ Vector<EditorExportPlatformAndroid::ABI> EditorExportPlatformAndroid::get_enable void EditorExportPlatformAndroid::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const { r_features->push_back("etc2"); + r_features->push_back("astc"); Vector<ABI> abis = get_enabled_abis(p_preset); for (int i = 0; i < abis.size(); ++i) { @@ -1693,12 +1702,12 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "custom_build/use_custom_build"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "custom_build/export_format", PROPERTY_HINT_ENUM, "Export APK,Export AAB"), EXPORT_FORMAT_APK)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "gradle_build/use_gradle_build"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "gradle_build/export_format", PROPERTY_HINT_ENUM, "Export APK,Export AAB"), EXPORT_FORMAT_APK)); // Using String instead of int to default to an empty string (no override) with placeholder for instructions (see GH-62465). // This implies doing validation that the string is a proper int. - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_build/min_sdk", PROPERTY_HINT_PLACEHOLDER_TEXT, vformat("%d (default)", DEFAULT_MIN_SDK_VERSION)), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_build/target_sdk", PROPERTY_HINT_PLACEHOLDER_TEXT, vformat("%d (default)", DEFAULT_TARGET_SDK_VERSION)), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "gradle_build/min_sdk", PROPERTY_HINT_PLACEHOLDER_TEXT, vformat("%d (default)", DEFAULT_MIN_SDK_VERSION)), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "gradle_build/target_sdk", PROPERTY_HINT_PLACEHOLDER_TEXT, vformat("%d (default)", DEFAULT_TARGET_SDK_VERSION)), "")); Vector<PluginConfigAndroid> plugins_configs = get_plugins(); for (int i = 0; i < plugins_configs.size(); i++) { @@ -1731,7 +1740,7 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "ext.domain.name"), "org.godotengine.$genname")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name [default if blank]"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/signed"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/classify_as_game"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "package/app_category", PROPERTY_HINT_ENUM, "Accessibility,Audio,Game,Image,Maps,News,Productivity,Social,Video"), APP_CATEGORY_GAME)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/retain_data_on_uninstall"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/exclude_from_recents"), false)); @@ -2011,7 +2020,10 @@ String EditorExportPlatformAndroid::get_adb_path() { return sdk_path.path_join("platform-tools/adb" + exe_ext); } -String EditorExportPlatformAndroid::get_apksigner_path() { +String EditorExportPlatformAndroid::get_apksigner_path(int p_target_sdk, bool p_check_executes) { + if (p_target_sdk == -1) { + p_target_sdk = DEFAULT_TARGET_SDK_VERSION; + } String exe_ext = ""; if (OS::get_singleton()->get_name() == "Windows") { exe_ext = ".bat"; @@ -2029,23 +2041,89 @@ String EditorExportPlatformAndroid::get_apksigner_path() { } // There are additional versions directories we need to go through. - da->list_dir_begin(); - String sub_dir = da->get_next(); - while (!sub_dir.is_empty()) { - if (!sub_dir.begins_with(".") && da->current_is_dir()) { - // Check if the tool is here. - String tool_path = build_tools_dir.path_join(sub_dir).path_join(apksigner_command_name); - if (FileAccess::exists(tool_path)) { - apksigner_path = tool_path; - break; + Vector<String> dir_list = da->get_directories(); + + // We need to use the version of build_tools that matches the Target SDK + // If somehow we can't find that, we see if a version between 28 and the default target SDK exists. + // We need to avoid versions <= 27 because they fail on Java versions >9 + // If we can't find that, we just use the first valid version. + Vector<String> ideal_versions; + Vector<String> other_versions; + Vector<String> versions; + bool found_target_sdk = false; + // We only allow for versions <= 27 if specifically set + int min_version = p_target_sdk <= 27 ? p_target_sdk : 28; + for (String sub_dir : dir_list) { + if (!sub_dir.begins_with(".")) { + Vector<String> ver_numbers = sub_dir.split("."); + // Dir not a version number, will use as last resort + if (!ver_numbers.size() || !ver_numbers[0].is_valid_int()) { + other_versions.push_back(sub_dir); + continue; + } + int ver_number = ver_numbers[0].to_int(); + if (ver_number == p_target_sdk) { + found_target_sdk = true; + //ensure this is in front of the ones we check + versions.push_back(sub_dir); + } else { + if (ver_number >= min_version && ver_number <= DEFAULT_TARGET_SDK_VERSION) { + ideal_versions.push_back(sub_dir); + } else { + other_versions.push_back(sub_dir); + } } } - sub_dir = da->get_next(); } - da->list_dir_end(); + // we will check ideal versions first, then other versions. + versions.append_array(ideal_versions); + versions.append_array(other_versions); - if (apksigner_path.is_empty()) { + if (!versions.size()) { print_error("Unable to find the 'apksigner' tool."); + return apksigner_path; + } + + int i; + bool failed = false; + String version_to_use; + + List<String> args; + args.push_back("--version"); + String output; + int retval; + Error err; + for (i = 0; i < versions.size(); i++) { + // Check if the tool is here. + apksigner_path = build_tools_dir.path_join(versions[i]).path_join(apksigner_command_name); + if (FileAccess::exists(apksigner_path)) { + version_to_use = versions[i]; + // If we aren't exporting, just break here. + if (!p_check_executes) { + break; + } + // we only check to see if it executes on export because it is slow to load + err = OS::get_singleton()->execute(apksigner_path, args, &output, &retval, false); + if (err || retval) { + failed = true; + } else { + break; + } + } + } + if (i == versions.size()) { + if (failed) { + print_error("All located 'apksigner' tools in " + build_tools_dir + " failed to execute"); + return "<FAILED>"; + } else { + print_error("Unable to find the 'apksigner' tool."); + return ""; + } + } + if (!found_target_sdk) { + print_line("Could not find version of build tools that matches Target SDK, using " + version_to_use); + } else if (failed && found_target_sdk) { + print_line("Version of build tools that matches Target SDK failed to execute, using " + version_to_use); } return apksigner_path; @@ -2054,11 +2132,11 @@ String EditorExportPlatformAndroid::get_apksigner_path() { bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { String err; bool valid = false; - const bool custom_build_enabled = p_preset->get("custom_build/use_custom_build"); + const bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build"); // Look for export templates (first official, and if defined custom templates). - if (!custom_build_enabled) { + if (!gradle_build_enabled) { String template_err; bool dvalid = false; bool rvalid = false; @@ -2165,8 +2243,12 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito valid = false; } + String target_sdk_version = p_preset->get("gradle_build/target_sdk"); + if (!target_sdk_version.is_valid_int()) { + target_sdk_version = itos(DEFAULT_TARGET_SDK_VERSION); + } // Validate that apksigner is available - String apksigner_path = get_apksigner_path(); + String apksigner_path = get_apksigner_path(target_sdk_version.to_int()); if (!FileAccess::exists(apksigner_path)) { err += TTR("Unable to find Android SDK build-tools' apksigner command."); err += TTR("Please check in the Android SDK directory specified in Editor Settings."); @@ -2185,7 +2267,7 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const { String err; bool valid = true; - const bool custom_build_enabled = p_preset->get("custom_build/use_custom_build"); + const bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build"); // Validate the project configuration. bool apk_expansion = p_preset->get("apk_expansion/enable"); @@ -2203,7 +2285,7 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit String pn = p_preset->get("package/unique_name"); String pn_err; - if (!is_package_name_valid(get_package_name(pn), &pn_err)) { + if (!is_package_name_valid(pn, &pn_err)) { valid = false; err += TTR("Invalid package name:") + " " + pn_err + "\n"; } @@ -2214,11 +2296,11 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit err += etc_error; } - // Ensure that `Use Custom Build` is enabled if a plugin is selected. + // Ensure that `Use Gradle Build` is enabled if a plugin is selected. String enabled_plugins_names = PluginConfigAndroid::get_plugins_names(get_enabled_plugins(p_preset)); - if (!enabled_plugins_names.is_empty() && !custom_build_enabled) { + if (!enabled_plugins_names.is_empty() && !gradle_build_enabled) { valid = false; - err += TTR("\"Use Custom Build\" must be enabled to use the plugins."); + err += TTR("\"Use Gradle Build\" must be enabled to use the plugins."); err += "\n"; } @@ -2226,6 +2308,12 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit int xr_mode_index = p_preset->get("xr_features/xr_mode"); int hand_tracking = p_preset->get("xr_features/hand_tracking"); int passthrough_mode = p_preset->get("xr_features/passthrough"); + if (xr_mode_index == XR_MODE_OPENXR && !gradle_build_enabled) { + valid = false; + err += TTR("OpenXR requires \"Use Gradle Build\" to be enabled"); + err += "\n"; + } + if (xr_mode_index != XR_MODE_OPENXR) { if (hand_tracking > XR_HAND_TRACKING_NONE) { valid = false; @@ -2240,20 +2328,20 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit } } - if (int(p_preset->get("custom_build/export_format")) == EXPORT_FORMAT_AAB && - !custom_build_enabled) { + if (int(p_preset->get("gradle_build/export_format")) == EXPORT_FORMAT_AAB && + !gradle_build_enabled) { valid = false; - err += TTR("\"Export AAB\" is only valid when \"Use Custom Build\" is enabled."); + err += TTR("\"Export AAB\" is only valid when \"Use Gradle Build\" is enabled."); err += "\n"; } // Check the min sdk version. - String min_sdk_str = p_preset->get("custom_build/min_sdk"); + String min_sdk_str = p_preset->get("gradle_build/min_sdk"); int min_sdk_int = DEFAULT_MIN_SDK_VERSION; if (!min_sdk_str.is_empty()) { // Empty means no override, nothing to do. - if (!custom_build_enabled) { + if (!gradle_build_enabled) { valid = false; - err += TTR("\"Min SDK\" can only be overridden when \"Use Custom Build\" is enabled."); + err += TTR("\"Min SDK\" can only be overridden when \"Use Gradle Build\" is enabled."); err += "\n"; } if (!min_sdk_str.is_valid_int()) { @@ -2271,12 +2359,12 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit } // Check the target sdk version. - String target_sdk_str = p_preset->get("custom_build/target_sdk"); + String target_sdk_str = p_preset->get("gradle_build/target_sdk"); int target_sdk_int = DEFAULT_TARGET_SDK_VERSION; if (!target_sdk_str.is_empty()) { // Empty means no override, nothing to do. - if (!custom_build_enabled) { + if (!gradle_build_enabled) { valid = false; - err += TTR("\"Target SDK\" can only be overridden when \"Use Custom Build\" is enabled."); + err += TTR("\"Target SDK\" can only be overridden when \"Use Gradle Build\" is enabled."); err += "\n"; } if (!target_sdk_str.is_valid_int()) { @@ -2299,6 +2387,18 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit err += "\n"; } + String current_renderer = GLOBAL_GET("rendering/renderer/rendering_method.mobile"); + if (current_renderer == "forward_plus") { + // Warning only, so don't override `valid`. + err += vformat(TTR("The \"%s\" renderer is designed for Desktop devices, and is not suitable for Android devices."), current_renderer); + err += "\n"; + } + if (_uses_vulkan() && min_sdk_int < VULKAN_MIN_SDK_VERSION) { + // Warning only, so don't override `valid`. + err += vformat(TTR("\"Min SDK\" should be greater or equal to %d for the \"%s\" renderer."), VULKAN_MIN_SDK_VERSION, current_renderer); + err += "\n"; + } + r_error = err; return valid; } @@ -2384,14 +2484,21 @@ void EditorExportPlatformAndroid::get_command_line_flags(const Ref<EditorExportP } Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &export_path, EditorProgress &ep) { - int export_format = int(p_preset->get("custom_build/export_format")); + int export_format = int(p_preset->get("gradle_build/export_format")); String export_label = export_format == EXPORT_FORMAT_AAB ? "AAB" : "APK"; String release_keystore = p_preset->get("keystore/release"); String release_username = p_preset->get("keystore/release_user"); String release_password = p_preset->get("keystore/release_password"); - - String apksigner = get_apksigner_path(); + String target_sdk_version = p_preset->get("gradle_build/target_sdk"); + if (!target_sdk_version.is_valid_int()) { + target_sdk_version = itos(DEFAULT_TARGET_SDK_VERSION); + } + String apksigner = get_apksigner_path(target_sdk_version.to_int(), true); print_verbose("Starting signing of the " + export_label + " binary using " + apksigner); + if (apksigner == "<FAILED>") { + add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("All 'apksigner' tools located in Android SDK 'build-tools' directory failed to execute. Please check that you have the correct version installed for your target sdk version. The resulting %s is unsigned."), export_label)); + return OK; + } if (!FileAccess::exists(apksigner)) { add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("'apksigner' could not be found. Please check that the command is available in the Android SDK build-tools directory. The resulting %s is unsigned."), export_label)); return OK; @@ -2441,20 +2548,27 @@ Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_pre args.push_back("--ks-key-alias"); args.push_back(user); args.push_back(export_path); - if (p_debug) { - // We only print verbose logs for debug builds to avoid leaking release keystore credentials. + if (OS::get_singleton()->is_stdout_verbose() && p_debug) { + // We only print verbose logs with credentials for debug builds to avoid leaking release keystore credentials. print_verbose("Signing debug binary using: " + String("\n") + apksigner + " " + join_list(args, String(" "))); + } else { + List<String> redacted_args = List<String>(args); + redacted_args.find(keystore)->set("<REDACTED>"); + redacted_args.find("pass:" + password)->set("pass:<REDACTED>"); + redacted_args.find(user)->set("<REDACTED>"); + print_line("Signing binary using: " + String("\n") + apksigner + " " + join_list(redacted_args, String(" "))); } int retval; - output.clear(); Error err = OS::get_singleton()->execute(apksigner, args, &output, &retval, true); if (err != OK) { add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Could not start apksigner executable.")); return err; } - print_verbose(output); + // By design, apksigner does not output credentials in its output unless --verbose is used + print_line(output); if (retval) { add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("'apksigner' returned with error #%d"), retval)); + add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("output: \n%s"), output)); return ERR_CANT_CREATE; } @@ -2479,6 +2593,7 @@ Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_pre print_verbose(output); if (retval) { add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("'apksigner' verification of %s failed."), export_label)); + add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("output: \n%s"), output)); return ERR_CANT_CREATE; } @@ -2491,7 +2606,7 @@ void EditorExportPlatformAndroid::_clear_assets_directory() { // Clear the APK assets directory if (da_res->dir_exists(APK_ASSETS_DIRECTORY)) { - print_verbose("Clearing APK assets directory.."); + print_verbose("Clearing APK assets directory..."); Ref<DirAccess> da_assets = DirAccess::open(APK_ASSETS_DIRECTORY); da_assets->erase_contents_recursive(); da_res->remove(APK_ASSETS_DIRECTORY); @@ -2499,7 +2614,7 @@ void EditorExportPlatformAndroid::_clear_assets_directory() { // Clear the AAB assets directory if (da_res->dir_exists(AAB_ASSETS_DIRECTORY)) { - print_verbose("Clearing AAB assets directory.."); + print_verbose("Clearing AAB assets directory..."); Ref<DirAccess> da_assets = DirAccess::open(AAB_ASSETS_DIRECTORY); da_assets->erase_contents_recursive(); da_res->remove(AAB_ASSETS_DIRECTORY); @@ -2509,7 +2624,7 @@ void EditorExportPlatformAndroid::_clear_assets_directory() { void EditorExportPlatformAndroid::_remove_copied_libs() { print_verbose("Removing previously installed libraries..."); Error error; - String libs_json = FileAccess::get_file_as_string(GDNATIVE_LIBS_PATH, &error); + String libs_json = FileAccess::get_file_as_string(GDEXTENSION_LIBS_PATH, &error); if (error || libs_json.is_empty()) { print_verbose("No previously installed libraries found"); return; @@ -2525,7 +2640,7 @@ void EditorExportPlatformAndroid::_remove_copied_libs() { print_verbose("Removing previously installed library " + libs[i]); da->remove(libs[i]); } - da->remove(GDNATIVE_LIBS_PATH); + da->remove(GDEXTENSION_LIBS_PATH); } String EditorExportPlatformAndroid::join_list(const List<String> &p_parts, const String &p_separator) { @@ -2551,7 +2666,7 @@ String EditorExportPlatformAndroid::join_abis(const Vector<EditorExportPlatformA } Error EditorExportPlatformAndroid::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { - int export_format = int(p_preset->get("custom_build/export_format")); + int export_format = int(p_preset->get("gradle_build/export_format")); bool should_sign = p_preset->get("package/signed"); return export_project_helper(p_preset, p_debug, p_path, export_format, should_sign, p_flags); } @@ -2564,7 +2679,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP EditorProgress ep("export", TTR("Exporting for Android"), 105, true); - bool use_custom_build = bool(p_preset->get("custom_build/use_custom_build")); + bool use_gradle_build = bool(p_preset->get("gradle_build/use_gradle_build")); bool p_give_internet = p_flags & (DEBUG_FLAG_DUMB_CLIENT | DEBUG_FLAG_REMOTE_DEBUG); bool apk_expansion = p_preset->get("apk_expansion/enable"); Vector<ABI> enabled_abis = get_enabled_abis(p_preset); @@ -2574,7 +2689,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP print_verbose("- export path: " + p_path); print_verbose("- export format: " + itos(export_format)); print_verbose("- sign build: " + bool_to_string(should_sign)); - print_verbose("- custom build enabled: " + bool_to_string(use_custom_build)); + print_verbose("- gradle build enabled: " + bool_to_string(use_gradle_build)); print_verbose("- apk expansion enabled: " + bool_to_string(apk_expansion)); print_verbose("- enabled abis: " + join_abis(enabled_abis, ",", false)); print_verbose("- export filter: " + itos(p_preset->get_export_filter())); @@ -2614,14 +2729,14 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP return ERR_UNCONFIGURED; } - if (use_custom_build) { - print_verbose("Starting custom build.."); + if (use_gradle_build) { + print_verbose("Starting gradle build..."); //test that installed build version is alright { - print_verbose("Checking build version.."); + print_verbose("Checking build version..."); Ref<FileAccess> f = FileAccess::open("res://android/.build_version", FileAccess::READ); if (f.is_null()) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Trying to build from a custom built template, but no version info for it exists. Please reinstall from the 'Project' menu.")); + add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Trying to build from a gradle built template, but no version info for it exists. Please reinstall from the 'Project' menu.")); return ERR_UNCONFIGURED; } String version = f->get_line().strip_edges(); @@ -2651,7 +2766,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP _clear_assets_directory(); _remove_copied_libs(); if (!apk_expansion) { - print_verbose("Exporting project files.."); + print_verbose("Exporting project files..."); CustomExportData user_data; user_data.assets_directory = assets_directory; user_data.debug = p_debug; @@ -2661,18 +2776,18 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP return err; } if (user_data.libs.size() > 0) { - Ref<FileAccess> fa = FileAccess::open(GDNATIVE_LIBS_PATH, FileAccess::WRITE); + Ref<FileAccess> fa = FileAccess::open(GDEXTENSION_LIBS_PATH, FileAccess::WRITE); fa->store_string(JSON::stringify(user_data.libs, "\t")); } } else { - print_verbose("Saving apk expansion file.."); + print_verbose("Saving apk expansion file..."); err = save_apk_expansion_file(p_preset, p_debug, p_path); if (err != OK) { add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Could not write expansion package file!")); return err; } } - print_verbose("Storing command line flags.."); + print_verbose("Storing command line flags..."); store_file_at_path(assets_directory + "/_cl_", command_line_flags); print_verbose("Updating ANDROID_HOME environment to " + sdk_path); @@ -2691,11 +2806,11 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP String package_name = get_package_name(p_preset->get("package/unique_name")); String version_code = itos(p_preset->get("version/code")); String version_name = p_preset->get("version/name"); - String min_sdk_version = p_preset->get("custom_build/min_sdk"); + String min_sdk_version = p_preset->get("gradle_build/min_sdk"); if (!min_sdk_version.is_valid_int()) { min_sdk_version = itos(DEFAULT_MIN_SDK_VERSION); } - String target_sdk_version = p_preset->get("custom_build/target_sdk"); + String target_sdk_version = p_preset->get("gradle_build/target_sdk"); if (!target_sdk_version.is_valid_int()) { target_sdk_version = itos(DEFAULT_TARGET_SDK_VERSION); } @@ -2821,11 +2936,11 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP return ERR_CANT_CREATE; } - print_verbose("Successfully completed Android custom build."); + print_verbose("Successfully completed Android gradle build."); return OK; } // This is the start of the Legacy build system - print_verbose("Starting legacy build system.."); + print_verbose("Starting legacy build system..."); if (p_debug) { src_apk = p_preset->get("custom_template/debug"); } else { @@ -3146,8 +3261,17 @@ void EditorExportPlatformAndroid::resolve_platform_feature_priorities(const Ref< } EditorExportPlatformAndroid::EditorExportPlatformAndroid() { - logo = ImageTexture::create_from_image(memnew(Image(_android_logo))); - run_icon = ImageTexture::create_from_image(memnew(Image(_android_run_icon))); +#ifdef MODULE_SVG_ENABLED + Ref<Image> img = memnew(Image); + const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE); + + ImageLoaderSVG img_loader; + img_loader.create_image_from_string(img, _android_logo_svg, EDSCALE, upsample, false); + logo = ImageTexture::create_from_image(img); + + img_loader.create_image_from_string(img, _android_run_icon_svg, EDSCALE, upsample, false); + run_icon = ImageTexture::create_from_image(img); +#endif devices_changed.set(); plugins_changed.set(); diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h index c8fcb761fe..337a0228d0 100644 --- a/platform/android/export/export_plugin.h +++ b/platform/android/export/export_plugin.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* export_plugin.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* export_plugin.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef ANDROID_EXPORT_PLUGIN_H #define ANDROID_EXPORT_PLUGIN_H @@ -74,7 +74,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { Vector<PluginConfigAndroid> plugins; String last_plugin_names; - uint64_t last_custom_build_time = 0; + uint64_t last_gradle_build_time = 0; SafeFlag plugins_changed; Mutex plugins_lock; Vector<Device> devices; @@ -91,9 +91,12 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { String get_package_name(const String &p_package) const; + String get_valid_basename() const; + String get_assets_directory(const Ref<EditorExportPreset> &p_preset, int p_export_format) const; bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const; + bool is_project_name_valid() const; static bool _should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data); @@ -169,6 +172,8 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { static Vector<ABI> get_enabled_abis(const Ref<EditorExportPreset> &p_preset); + static bool _uses_vulkan(); + public: typedef Error (*EditorExportSaveFunction)(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); @@ -201,7 +206,7 @@ public: static String get_adb_path(); - static String get_apksigner_path(); + static String get_apksigner_path(int p_target_sdk = -1, bool p_check_executes = false); virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override; virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override; @@ -210,14 +215,14 @@ public: inline bool is_clean_build_required(Vector<PluginConfigAndroid> enabled_plugins) { String plugin_names = PluginConfigAndroid::get_plugins_names(enabled_plugins); - bool first_build = last_custom_build_time == 0; + bool first_build = last_gradle_build_time == 0; bool have_plugins_changed = false; if (!first_build) { have_plugins_changed = plugin_names != last_plugin_names; if (!have_plugins_changed) { for (int i = 0; i < enabled_plugins.size(); i++) { - if (enabled_plugins.get(i).last_updated > last_custom_build_time) { + if (enabled_plugins.get(i).last_updated > last_gradle_build_time) { have_plugins_changed = true; break; } @@ -225,7 +230,7 @@ public: } } - last_custom_build_time = OS::get_singleton()->get_unix_time(); + last_gradle_build_time = OS::get_singleton()->get_unix_time(); last_plugin_names = plugin_names; return have_plugins_changed || first_build; diff --git a/platform/android/export/godot_plugin_config.cpp b/platform/android/export/godot_plugin_config.cpp index 21580ae907..56431c25de 100644 --- a/platform/android/export/godot_plugin_config.cpp +++ b/platform/android/export/godot_plugin_config.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* godot_plugin_config.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* godot_plugin_config.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #include "godot_plugin_config.h" /* diff --git a/platform/android/export/godot_plugin_config.h b/platform/android/export/godot_plugin_config.h index 5188f615d4..bef00979a9 100644 --- a/platform/android/export/godot_plugin_config.h +++ b/platform/android/export/godot_plugin_config.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* godot_plugin_config.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* godot_plugin_config.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef ANDROID_GODOT_PLUGIN_CONFIG_H #define ANDROID_GODOT_PLUGIN_CONFIG_H diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp index 8d016d3fac..b889d58199 100644 --- a/platform/android/export/gradle_export_util.cpp +++ b/platform/android/export/gradle_export_util.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* gradle_export_util.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* gradle_export_util.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #include "gradle_export_util.h" @@ -72,6 +72,54 @@ String _get_android_orientation_label(DisplayServer::ScreenOrientation screen_or } } +int _get_app_category_value(int category_index) { + switch (category_index) { + case APP_CATEGORY_ACCESSIBILITY: + return 8; + case APP_CATEGORY_AUDIO: + return 1; + case APP_CATEGORY_IMAGE: + return 3; + case APP_CATEGORY_MAPS: + return 6; + case APP_CATEGORY_NEWS: + return 5; + case APP_CATEGORY_PRODUCTIVITY: + return 7; + case APP_CATEGORY_SOCIAL: + return 4; + case APP_CATEGORY_VIDEO: + return 2; + case APP_CATEGORY_GAME: + default: + return 0; + } +} + +String _get_app_category_label(int category_index) { + switch (category_index) { + case APP_CATEGORY_ACCESSIBILITY: + return "accessibility"; + case APP_CATEGORY_AUDIO: + return "audio"; + case APP_CATEGORY_IMAGE: + return "image"; + case APP_CATEGORY_MAPS: + return "maps"; + case APP_CATEGORY_NEWS: + return "news"; + case APP_CATEGORY_PRODUCTIVITY: + return "productivity"; + case APP_CATEGORY_SOCIAL: + return "social"; + case APP_CATEGORY_VIDEO: + return "video"; + case APP_CATEGORY_GAME: + default: + return "game"; + } +} + // Utility method used to create a directory. Error create_directory(const String &p_dir) { if (!DirAccess::exists(p_dir)) { @@ -118,7 +166,7 @@ Error store_string_at_path(const String &p_path, const String &p_data) { // This method will only be called as an input to export_project_files. // It is used by the export_project_files method to save all the asset files into the gradle project. // It's functionality mirrors that of the method save_apk_file. -// This method will be called ONLY when custom build is enabled. +// This method will be called ONLY when gradle build is enabled. Error rename_and_store_file_in_gradle_project(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { CustomExportData *export_data = static_cast<CustomExportData *>(p_userdata); String dst_path = p_path.replace_first("res://", export_data->assets_directory + "/"); @@ -206,13 +254,11 @@ String _get_screen_sizes_tag(const Ref<EditorExportPreset> &p_preset) { return manifest_screen_sizes; } -String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset) { +String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset, bool p_uses_vulkan) { String manifest_xr_features; int xr_mode_index = (int)(p_preset->get("xr_features/xr_mode")); bool uses_xr = xr_mode_index == XR_MODE_OPENXR; if (uses_xr) { - manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"android.hardware.vr.headtracking\" android:required=\"true\" android:version=\"1\" />\n"; - int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required if (hand_tracking_index == XR_HAND_TRACKING_OPTIONAL) { manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"oculus.software.handtracking\" android:required=\"false\" />\n"; @@ -227,47 +273,70 @@ String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset) { manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"com.oculus.feature.PASSTHROUGH\" android:required=\"true\" />\n"; } } + + if (p_uses_vulkan) { + manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"android.hardware.vulkan.level\" android:required=\"true\" android:version=\"1\" />\n"; + } return manifest_xr_features; } -String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) { - int xr_mode_index = (int)(p_preset->get("xr_features/xr_mode")); - bool uses_xr = xr_mode_index == XR_MODE_OPENXR; +String _get_activity_tag(const Ref<EditorExportPreset> &p_preset, bool p_uses_xr) { String orientation = _get_android_orientation_label(DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation")))); String manifest_activity_text = vformat( " <activity android:name=\"com.godot.game.GodotApp\" " "tools:replace=\"android:screenOrientation,android:excludeFromRecents,android:resizeableActivity\" " + "tools:node=\"mergeOnlyAttributes\" " "android:excludeFromRecents=\"%s\" " "android:screenOrientation=\"%s\" " "android:resizeableActivity=\"%s\">\n", bool_to_string(p_preset->get("package/exclude_from_recents")), orientation, bool_to_string(bool(GLOBAL_GET("display/window/size/resizable")))); - if (uses_xr) { - manifest_activity_text += " <meta-data tools:node=\"replace\" android:name=\"com.oculus.vr.focusaware\" android:value=\"true\" />\n"; + + if (p_uses_xr) { + manifest_activity_text += " <intent-filter>\n" + " <action android:name=\"android.intent.action.MAIN\" />\n" + " <category android:name=\"android.intent.category.LAUNCHER\" />\n" + "\n" + " <!-- Enable access to OpenXR on Oculus mobile devices, no-op on other Android\n" + " platforms. -->\n" + " <category android:name=\"com.oculus.intent.category.VR\" />\n" + "\n" + " <!-- OpenXR category tag to indicate the activity starts in an immersive OpenXR mode. \n" + " See https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#android-runtime-category. -->\n" + " <category android:name=\"org.khronos.openxr.intent.category.IMMERSIVE_HMD\" />\n" + " </intent-filter>\n"; } else { - manifest_activity_text += " <meta-data tools:node=\"remove\" android:name=\"com.oculus.vr.focusaware\" />\n"; + manifest_activity_text += " <intent-filter>\n" + " <action android:name=\"android.intent.action.MAIN\" />\n" + " <category android:name=\"android.intent.category.LAUNCHER\" />\n" + " </intent-filter>\n"; } + manifest_activity_text += " </activity>\n"; return manifest_activity_text; } String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_read_write_storage_permission) { + int app_category_index = (int)(p_preset->get("package/app_category")); + bool is_game = app_category_index == APP_CATEGORY_GAME; + int xr_mode_index = (int)(p_preset->get("xr_features/xr_mode")); bool uses_xr = xr_mode_index == XR_MODE_OPENXR; + String manifest_application_text = vformat( " <application android:label=\"@string/godot_project_name_string\"\n" " android:allowBackup=\"%s\"\n" " android:icon=\"@mipmap/icon\"\n" + " android:appCategory=\"%s\"\n" " android:isGame=\"%s\"\n" " android:hasFragileUserData=\"%s\"\n" " android:requestLegacyExternalStorage=\"%s\"\n" - " tools:replace=\"android:allowBackup,android:isGame,android:hasFragileUserData,android:requestLegacyExternalStorage\"\n" - " tools:ignore=\"GoogleAppIndexingWarning\">\n\n" - " <meta-data tools:node=\"remove\" android:name=\"xr_hand_tracking_version_name\" />\n" - " <meta-data tools:node=\"remove\" android:name=\"xr_hand_tracking_metadata_name\" />\n", + " tools:replace=\"android:allowBackup,android:appCategory,android:isGame,android:hasFragileUserData,android:requestLegacyExternalStorage\"\n" + " tools:ignore=\"GoogleAppIndexingWarning\">\n\n", bool_to_string(p_preset->get("user_data_backup/allow")), - bool_to_string(p_preset->get("package/classify_as_game")), + _get_app_category_label(app_category_index), + bool_to_string(is_game), bool_to_string(p_preset->get("package/retain_data_on_uninstall")), bool_to_string(p_has_read_write_storage_permission)); @@ -281,10 +350,8 @@ String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_ hand_tracking_frequency); manifest_application_text += " <meta-data tools:node=\"replace\" android:name=\"com.oculus.handtracking.version\" android:value=\"V2.0\" />\n"; } - } else { - manifest_application_text += " <meta-data tools:node=\"remove\" android:name=\"com.oculus.supportedDevices\" />\n"; } - manifest_application_text += _get_activity_tag(p_preset); + manifest_application_text += _get_activity_tag(p_preset, uses_xr); manifest_application_text += " </application>\n"; return manifest_application_text; } diff --git a/platform/android/export/gradle_export_util.h b/platform/android/export/gradle_export_util.h index 232b4458c6..8a885a0d12 100644 --- a/platform/android/export/gradle_export_util.h +++ b/platform/android/export/gradle_export_util.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* gradle_export_util.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* gradle_export_util.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef ANDROID_GRADLE_EXPORT_UTIL_H #define ANDROID_GRADLE_EXPORT_UTIL_H @@ -44,6 +44,18 @@ const String godot_project_name_xml_string = R"(<?xml version="1.0" encoding="ut </resources> )"; +// Application category. +// See https://developer.android.com/guide/topics/manifest/application-element#appCategory for standards +static const int APP_CATEGORY_ACCESSIBILITY = 0; +static const int APP_CATEGORY_AUDIO = 1; +static const int APP_CATEGORY_GAME = 2; +static const int APP_CATEGORY_IMAGE = 3; +static const int APP_CATEGORY_MAPS = 4; +static const int APP_CATEGORY_NEWS = 5; +static const int APP_CATEGORY_PRODUCTIVITY = 6; +static const int APP_CATEGORY_SOCIAL = 7; +static const int APP_CATEGORY_VIDEO = 8; + // Supported XR modes. // This should match the entries in 'platform/android/java/lib/src/org/godotengine/godot/xr/XRMode.java' static const int XR_MODE_REGULAR = 0; @@ -73,6 +85,10 @@ int _get_android_orientation_value(DisplayServer::ScreenOrientation screen_orien String _get_android_orientation_label(DisplayServer::ScreenOrientation screen_orientation); +int _get_app_category_value(int category_index); + +String _get_app_category_label(int category_index); + // Utility method used to create a directory. Error create_directory(const String &p_dir); @@ -88,7 +104,7 @@ Error store_string_at_path(const String &p_path, const String &p_data); // This method will only be called as an input to export_project_files. // It is used by the export_project_files method to save all the asset files into the gradle project. // It's functionality mirrors that of the method save_apk_file. -// This method will be called ONLY when custom build is enabled. +// This method will be called ONLY when gradle build is enabled. Error rename_and_store_file_in_gradle_project(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); // Creates strings.xml files inside the gradle project for different locales. @@ -100,9 +116,9 @@ String _get_gles_tag(); String _get_screen_sizes_tag(const Ref<EditorExportPreset> &p_preset); -String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset); +String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset, bool p_uses_vulkan); -String _get_activity_tag(const Ref<EditorExportPreset> &p_preset); +String _get_activity_tag(const Ref<EditorExportPreset> &p_preset, bool p_uses_xr); String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_read_write_storage_permission); |