diff options
Diffstat (limited to 'platform/android')
31 files changed, 441 insertions, 190 deletions
diff --git a/platform/android/dir_access_jandroid.cpp b/platform/android/dir_access_jandroid.cpp index 69178317f7..f52b511522 100644 --- a/platform/android/dir_access_jandroid.cpp +++ b/platform/android/dir_access_jandroid.cpp @@ -110,7 +110,6 @@ String DirAccessJAndroid::get_drive(int p_drive) {  Error DirAccessJAndroid::change_dir(String p_dir) {  	JNIEnv *env = ThreadAndroid::get_env(); -	p_dir = p_dir.simplify_path();  	if (p_dir == "" || p_dir == "." || (p_dir == ".." && current_dir == ""))  		return OK; diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 9167a291f8..eb461860ed 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -30,6 +30,7 @@  #include "export.h" +#include "core/io/image_loader.h"  #include "core/io/marshalls.h"  #include "core/io/zip_io.h"  #include "core/os/dir_access.h" @@ -37,6 +38,7 @@  #include "core/os/os.h"  #include "core/project_settings.h"  #include "core/version.h" +#include "drivers/png/png_driver_common.h"  #include "editor/editor_export.h"  #include "editor/editor_log.h"  #include "editor/editor_node.h" @@ -196,16 +198,40 @@ static const char *android_perms[] = {  };  struct LauncherIcon { -	const char *option_id;  	const char *export_path; +	int dimensions;  }; -static const LauncherIcon launcher_icons[] = { -	{ "launcher_icons/xxxhdpi_192x192", "res/drawable-xxxhdpi-v4/icon.png" }, -	{ "launcher_icons/xxhdpi_144x144", "res/drawable-xxhdpi-v4/icon.png" }, -	{ "launcher_icons/xhdpi_96x96", "res/drawable-xhdpi-v4/icon.png" }, -	{ "launcher_icons/hdpi_72x72", "res/drawable-hdpi-v4/icon.png" }, -	{ "launcher_icons/mdpi_48x48", "res/drawable-mdpi-v4/icon.png" } +static const int icon_densities_count = 6; +static const char *launcher_icon_option = "launcher_icon/xxxhdpi_192x192"; +static const char *launcher_adaptive_icon_foreground_option = "launcher_adaptive_icon_foreground/xxxhdpi_432x432"; +static const char *launcher_adaptive_icon_background_option = "launcher_adaptive_icon_background/xxxhdpi_432x432"; + +static const LauncherIcon launcher_icons[icon_densities_count] = { +	{ "res/mipmap-xxxhdpi-v4/icon.png", 192 }, +	{ "res/mipmap-xxhdpi-v4/icon.png", 144 }, +	{ "res/mipmap-xhdpi-v4/icon.png", 96 }, +	{ "res/mipmap-hdpi-v4/icon.png", 72 }, +	{ "res/mipmap-mdpi-v4/icon.png", 48 }, +	{ "res/mipmap/icon.png", 192 } +}; + +static const LauncherIcon launcher_adaptive_icon_foregrounds[icon_densities_count] = { +	{ "res/mipmap-xxxhdpi-v4/icon_foreground.png", 432 }, +	{ "res/mipmap-xxhdpi-v4/icon_foreground.png", 324 }, +	{ "res/mipmap-xhdpi-v4/icon_foreground.png", 216 }, +	{ "res/mipmap-hdpi-v4/icon_foreground.png", 162 }, +	{ "res/mipmap-mdpi-v4/icon_foreground.png", 108 }, +	{ "res/mipmap/icon_foreground.png", 432 } +}; + +static const LauncherIcon launcher_adaptive_icon_backgrounds[icon_densities_count] = { +	{ "res/mipmap-xxxhdpi-v4/icon_background.png", 432 }, +	{ "res/mipmap-xxhdpi-v4/icon_background.png", 324 }, +	{ "res/mipmap-xhdpi-v4/icon_background.png", 216 }, +	{ "res/mipmap-hdpi-v4/icon_background.png", 162 }, +	{ "res/mipmap-mdpi-v4/icon_background.png", 108 }, +	{ "res/mipmap/icon_background.png", 432 }  };  class EditorExportPlatformAndroid : public EditorExportPlatform { @@ -680,7 +706,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {  		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("graphics/xr_mode"); +		int xr_mode_index = p_preset->get("xr_features/xr_mode");  		Vector<String> perms; @@ -859,135 +885,174 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {  					uint32_t name = decode_uint32(&p_manifest[iofs + 12]);  					String tname = string_table[name]; -					int dof_index = p_preset->get("graphics/degrees_of_freedom"); // 0: none, 1: 3dof and 6dof, 2: 6dof +					if (tname == "uses-feature") { +						Vector<String> feature_names; +						Vector<bool> feature_required_list; +						Vector<int> feature_versions; + +						if (xr_mode_index == 1 /* XRMode.OVR */) { +							// Check for degrees of freedom +							int dof_index = p_preset->get("xr_features/degrees_of_freedom"); // 0: none, 1: 3dof and 6dof, 2: 6dof + +							if (dof_index > 0) { +								feature_names.push_back("android.hardware.vr.headtracking"); +								feature_required_list.push_back(dof_index == 2); +								feature_versions.push_back(1); +							} + +							// Check for hand tracking +							int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required +							if (hand_tracking_index > 0) { +								feature_names.push_back("oculus.software.handtracking"); +								feature_required_list.push_back(hand_tracking_index == 2); +								feature_versions.push_back(-1); // no version attribute should be added. -					if (tname == "uses-feature" && dof_index > 0) { -						if (xr_mode_index == 0) { -							WARN_PRINT("VR DOF feature setting is only valid for oculus HMDs with an XR mode set to VR"); +								if (perms.find("oculus.permission.handtracking") == -1) { +									perms.push_back("oculus.permission.handtracking"); +								} +							}  						} -						ofs += 24; // skip over end tag -						// save manifest ending so we can restore it -						Vector<uint8_t> manifest_end; -						uint32_t manifest_cur_size = p_manifest.size(); +						if (feature_names.size() > 0) { +							ofs += 24; // skip over end tag -						manifest_end.resize(p_manifest.size() - ofs); -						memcpy(manifest_end.ptrw(), &p_manifest[ofs], manifest_end.size()); +							// save manifest ending so we can restore it +							Vector<uint8_t> manifest_end; +							uint32_t manifest_cur_size = p_manifest.size(); -						int32_t attr_name_string = string_table.find("name"); -						ERR_FAIL_COND_MSG(attr_name_string == -1, "Template does not have 'name' attribute."); +							manifest_end.resize(p_manifest.size() - ofs); +							memcpy(manifest_end.ptrw(), &p_manifest[ofs], manifest_end.size()); -						int32_t ns_android_string = string_table.find("http://schemas.android.com/apk/res/android"); -						if (ns_android_string == -1) { -							string_table.push_back("http://schemas.android.com/apk/res/android"); -							ns_android_string = string_table.size() - 1; -						} +							int32_t attr_name_string = string_table.find("name"); +							ERR_FAIL_COND_MSG(attr_name_string == -1, "Template does not have 'name' attribute."); -						int32_t attr_uses_permission_string = string_table.find("uses-feature"); -						if (attr_uses_permission_string == -1) { -							string_table.push_back("uses-feature"); -							attr_uses_permission_string = string_table.size() - 1; -						} +							int32_t ns_android_string = string_table.find("http://schemas.android.com/apk/res/android"); +							if (ns_android_string == -1) { +								string_table.push_back("http://schemas.android.com/apk/res/android"); +								ns_android_string = string_table.size() - 1; +							} -						int32_t attr_required_string = string_table.find("required"); -						if (attr_required_string == -1) { -							string_table.push_back("required"); -							attr_required_string = string_table.size() - 1; -						} +							int32_t attr_uses_feature_string = string_table.find("uses-feature"); +							if (attr_uses_feature_string == -1) { +								string_table.push_back("uses-feature"); +								attr_uses_feature_string = string_table.size() - 1; +							} -						int32_t attr_version_string = string_table.find("version"); -						if (attr_version_string == -1) { -							string_table.push_back("version"); -							attr_version_string = string_table.size() - 1; -						} +							int32_t attr_required_string = string_table.find("required"); +							if (attr_required_string == -1) { +								string_table.push_back("required"); +								attr_required_string = string_table.size() - 1; +							} -						String required_value_string; -						if (dof_index == 1) { -							required_value_string = "false"; -						} else if (dof_index == 2) { -							required_value_string = "true"; -						} else { -							ERR_FAIL_MSG("Unknown DoF index: " + itos(dof_index) + "."); -						} -						int32_t required_value = string_table.find(required_value_string); -						if (required_value == -1) { -							string_table.push_back(required_value_string); -							required_value = string_table.size() - 1; -						} +							for (int i = 0; i < feature_names.size(); i++) { +								String feature_name = feature_names[i]; +								bool feature_required = feature_required_list[i]; +								int feature_version = feature_versions[i]; +								bool has_version_attribute = feature_version != -1; -						int32_t version_value = string_table.find("1"); -						if (version_value == -1) { -							string_table.push_back("1"); -							version_value = string_table.size() - 1; -						} +								print_line("Adding feature " + feature_name); -						int32_t feature_string = string_table.find("android.hardware.vr.headtracking"); -						if (feature_string == -1) { -							string_table.push_back("android.hardware.vr.headtracking"); -							feature_string = string_table.size() - 1; -						} +								int32_t feature_string = string_table.find(feature_name); +								if (feature_string == -1) { +									string_table.push_back(feature_name); +									feature_string = string_table.size() - 1; +								} -						{ -							manifest_cur_size += 96 + 20; // node and three attrs + end node -							p_manifest.resize(manifest_cur_size); +								String required_value_string = feature_required ? "true" : "false"; +								int32_t required_value = string_table.find(required_value_string); +								if (required_value == -1) { +									string_table.push_back(required_value_string); +									required_value = string_table.size() - 1; +								} -							// start tag -							encode_uint16(0x102, &p_manifest.write[ofs]); // type -							encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize -							encode_uint32(96, &p_manifest.write[ofs + 4]); // size -							encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno -							encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment -							encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns -							encode_uint32(attr_uses_permission_string, &p_manifest.write[ofs + 20]); // name -							encode_uint16(20, &p_manifest.write[ofs + 24]); // attr_start -							encode_uint16(20, &p_manifest.write[ofs + 26]); // attr_size -							encode_uint16(3, &p_manifest.write[ofs + 28]); // num_attrs -							encode_uint16(0, &p_manifest.write[ofs + 30]); // id_index -							encode_uint16(0, &p_manifest.write[ofs + 32]); // class_index -							encode_uint16(0, &p_manifest.write[ofs + 34]); // style_index +								int32_t attr_version_string = -1; +								int32_t version_value = -1; +								int tag_size; +								int attr_count; +								if (has_version_attribute) { +									attr_version_string = string_table.find("version"); +									if (attr_version_string == -1) { +										string_table.push_back("version"); +										attr_version_string = string_table.size() - 1; +									} -							// android:name attribute -							encode_uint32(ns_android_string, &p_manifest.write[ofs + 36]); // ns -							encode_uint32(attr_name_string, &p_manifest.write[ofs + 40]); // 'name' -							encode_uint32(feature_string, &p_manifest.write[ofs + 44]); // raw_value -							encode_uint16(8, &p_manifest.write[ofs + 48]); // typedvalue_size -							p_manifest.write[ofs + 50] = 0; // typedvalue_always0 -							p_manifest.write[ofs + 51] = 0x03; // typedvalue_type (string) -							encode_uint32(feature_string, &p_manifest.write[ofs + 52]); // typedvalue reference - -							// android:required attribute -							encode_uint32(ns_android_string, &p_manifest.write[ofs + 56]); // ns -							encode_uint32(attr_required_string, &p_manifest.write[ofs + 60]); // 'name' -							encode_uint32(required_value, &p_manifest.write[ofs + 64]); // raw_value -							encode_uint16(8, &p_manifest.write[ofs + 68]); // typedvalue_size -							p_manifest.write[ofs + 70] = 0; // typedvalue_always0 -							p_manifest.write[ofs + 71] = 0x03; // typedvalue_type (string) -							encode_uint32(required_value, &p_manifest.write[ofs + 72]); // typedvalue reference - -							// android:version attribute -							encode_uint32(ns_android_string, &p_manifest.write[ofs + 76]); // ns -							encode_uint32(attr_version_string, &p_manifest.write[ofs + 80]); // 'name' -							encode_uint32(version_value, &p_manifest.write[ofs + 84]); // raw_value -							encode_uint16(8, &p_manifest.write[ofs + 88]); // typedvalue_size -							p_manifest.write[ofs + 90] = 0; // typedvalue_always0 -							p_manifest.write[ofs + 91] = 0x03; // typedvalue_type (string) -							encode_uint32(version_value, &p_manifest.write[ofs + 92]); // typedvalue reference - -							ofs += 96; +									version_value = string_table.find(itos(feature_version)); +									if (version_value == -1) { +										string_table.push_back(itos(feature_version)); +										version_value = string_table.size() - 1; +									} -							// end tag -							encode_uint16(0x103, &p_manifest.write[ofs]); // type -							encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize -							encode_uint32(24, &p_manifest.write[ofs + 4]); // size -							encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno -							encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment -							encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns -							encode_uint32(attr_uses_permission_string, &p_manifest.write[ofs + 20]); // name +									tag_size = 96; // node and three attrs + end node +									attr_count = 3; +								} else { +									tag_size = 76; // node and two attrs + end node +									attr_count = 2; +								} +								manifest_cur_size += tag_size + 24; +								p_manifest.resize(manifest_cur_size); + +								// start tag +								encode_uint16(0x102, &p_manifest.write[ofs]); // type +								encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize +								encode_uint32(tag_size, &p_manifest.write[ofs + 4]); // size +								encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno +								encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment +								encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns +								encode_uint32(attr_uses_feature_string, &p_manifest.write[ofs + 20]); // name +								encode_uint16(20, &p_manifest.write[ofs + 24]); // attr_start +								encode_uint16(20, &p_manifest.write[ofs + 26]); // attr_size +								encode_uint16(attr_count, &p_manifest.write[ofs + 28]); // num_attrs +								encode_uint16(0, &p_manifest.write[ofs + 30]); // id_index +								encode_uint16(0, &p_manifest.write[ofs + 32]); // class_index +								encode_uint16(0, &p_manifest.write[ofs + 34]); // style_index + +								// android:name attribute +								encode_uint32(ns_android_string, &p_manifest.write[ofs + 36]); // ns +								encode_uint32(attr_name_string, &p_manifest.write[ofs + 40]); // 'name' +								encode_uint32(feature_string, &p_manifest.write[ofs + 44]); // raw_value +								encode_uint16(8, &p_manifest.write[ofs + 48]); // typedvalue_size +								p_manifest.write[ofs + 50] = 0; // typedvalue_always0 +								p_manifest.write[ofs + 51] = 0x03; // typedvalue_type (string) +								encode_uint32(feature_string, &p_manifest.write[ofs + 52]); // typedvalue reference + +								// android:required attribute +								encode_uint32(ns_android_string, &p_manifest.write[ofs + 56]); // ns +								encode_uint32(attr_required_string, &p_manifest.write[ofs + 60]); // 'name' +								encode_uint32(required_value, &p_manifest.write[ofs + 64]); // raw_value +								encode_uint16(8, &p_manifest.write[ofs + 68]); // typedvalue_size +								p_manifest.write[ofs + 70] = 0; // typedvalue_always0 +								p_manifest.write[ofs + 71] = 0x03; // typedvalue_type (string) +								encode_uint32(required_value, &p_manifest.write[ofs + 72]); // typedvalue reference + +								ofs += 76; + +								if (has_version_attribute) { +									// android:version attribute +									encode_uint32(ns_android_string, &p_manifest.write[ofs]); // ns +									encode_uint32(attr_version_string, &p_manifest.write[ofs + 4]); // 'name' +									encode_uint32(version_value, &p_manifest.write[ofs + 8]); // raw_value +									encode_uint16(8, &p_manifest.write[ofs + 12]); // typedvalue_size +									p_manifest.write[ofs + 14] = 0; // typedvalue_always0 +									p_manifest.write[ofs + 15] = 0x03; // typedvalue_type (string) +									encode_uint32(version_value, &p_manifest.write[ofs + 16]); // typedvalue reference + +									ofs += 20; +								} -							ofs += 24; +								// end tag +								encode_uint16(0x103, &p_manifest.write[ofs]); // type +								encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize +								encode_uint32(24, &p_manifest.write[ofs + 4]); // size +								encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno +								encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment +								encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns +								encode_uint32(attr_uses_feature_string, &p_manifest.write[ofs + 20]); // name + +								ofs += 24; +							} +							memcpy(&p_manifest.write[ofs], manifest_end.ptr(), manifest_end.size()); +							ofs -= 24; // go back over back end  						} -						memcpy(&p_manifest.write[ofs], manifest_end.ptr(), manifest_end.size()); -						ofs -= 24; // go back over back end  					}  					if (tname == "manifest") { @@ -1259,6 +1324,27 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {  		//printf("end\n");  	} +	void _process_launcher_icons(const String &p_processing_file_name, const Ref<Image> &p_source_image, const LauncherIcon p_icon, Vector<uint8_t> &p_data) { +		if (p_processing_file_name == p_icon.export_path) { +			Ref<Image> working_image = p_source_image; + +			if (p_source_image->get_width() != p_icon.dimensions || p_source_image->get_height() != p_icon.dimensions) { +				working_image = p_source_image->duplicate(); +				working_image->resize(p_icon.dimensions, p_icon.dimensions, Image::Interpolation::INTERPOLATE_LANCZOS); +			} + +			PoolVector<uint8_t> png_buffer; +			Error err = PNGDriverCommon::image_to_png(working_image, png_buffer); +			if (err == OK) { +				p_data.resize(png_buffer.size()); +				memcpy(p_data.ptrw(), png_buffer.read().ptr(), p_data.size()); +			} else { +				String err_str = String("Failed to convert resized icon (") + p_processing_file_name + ") to png."; +				WARN_PRINT(err_str.utf8().get_data()); +			} +		} +	} +  	static Vector<String> get_enabled_abis(const Ref<EditorExportPreset> &p_preset) {  		Vector<String> abis = get_abis();  		Vector<String> enabled_abis; @@ -1295,13 +1381,14 @@ public:  	virtual void get_export_options(List<ExportOption> *r_options) { -		r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "graphics/xr_mode", PROPERTY_HINT_ENUM, "Regular,Oculus Mobile VR"), 0)); -		r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "graphics/degrees_of_freedom", PROPERTY_HINT_ENUM, "None,3DOF and 6DOF,6DOF"), 0));  		r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/32_bits_framebuffer"), true)); +		r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/xr_mode", PROPERTY_HINT_ENUM, "Regular,Oculus Mobile VR"), 0)); +		r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/degrees_of_freedom", PROPERTY_HINT_ENUM, "None,3DOF and 6DOF,6DOF"), 0)); +		r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/hand_tracking", PROPERTY_HINT_ENUM, "None,Optional,Required"), 0));  		r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "one_click_deploy/clear_previous_install"), false)); -		r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_package/debug", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), "")); -		r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_package/release", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), "")); -		r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "custom_package/use_custom_build"), false)); +		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_template/use_custom_build"), false));  		r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), ""));  		r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1));  		r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0")); @@ -1315,11 +1402,9 @@ public:  		r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_large"), true));  		r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_xlarge"), true));  		r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/opengl_debug"), false)); - -		for (uint64_t i = 0; i < sizeof(launcher_icons) / sizeof(launcher_icons[0]); ++i) { -			r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_icons[i].option_id, PROPERTY_HINT_FILE, "*.png"), "")); -		} - +		r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_icon_option, PROPERTY_HINT_FILE, "*.png"), "")); +		r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_foreground_option, PROPERTY_HINT_FILE, "*.png"), "")); +		r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_background_option, PROPERTY_HINT_FILE, "*.png"), ""));  		r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug", PROPERTY_HINT_GLOBAL_FILE, "*.keystore"), ""));  		r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_user"), ""));  		r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_password"), "")); @@ -1577,29 +1662,34 @@ public:  	virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {  		String err; +		bool valid = false; -		if (!bool(p_preset->get("custom_package/use_custom_build"))) { +		// Look for export templates (first official, and if defined custom templates). -			r_missing_templates = find_export_template("android_debug.apk") == String() || find_export_template("android_release.apk") == String(); +		if (!bool(p_preset->get("custom_template/use_custom_build"))) { +			bool dvalid = exists_export_template("android_debug.apk", &err); +			bool rvalid = exists_export_template("android_release.apk", &err); -			if (p_preset->get("custom_package/debug") != "") { -				if (FileAccess::exists(p_preset->get("custom_package/debug"))) { -					r_missing_templates = false; -				} else { +			if (p_preset->get("custom_template/debug") != "") { +				dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); +				if (!dvalid) {  					err += TTR("Custom debug template not found.") + "\n";  				}  			} - -			if (p_preset->get("custom_package/release") != "") { -				if (FileAccess::exists(p_preset->get("custom_package/release"))) { -					r_missing_templates = false; -				} else { +			if (p_preset->get("custom_template/release") != "") { +				rvalid = FileAccess::exists(p_preset->get("custom_template/release")); +				if (!rvalid) {  					err += TTR("Custom release template not found.") + "\n";  				}  			} + +			valid = dvalid || rvalid; +		} else { +			valid = exists_export_template("android_source.zip", &err);  		} +		r_missing_templates = !valid; -		bool valid = !r_missing_templates; +		// Validate the rest of the configuration.  		String adb = EditorSettings::get_singleton()->get("export/android/adb"); @@ -1628,7 +1718,7 @@ public:  			}  		} -		if (bool(p_preset->get("custom_package/use_custom_build"))) { +		if (bool(p_preset->get("custom_template/use_custom_build"))) {  			String sdk_path = EditorSettings::get_singleton()->get("export/android/custom_build_sdk_path");  			if (sdk_path == "") {  				err += TTR("Custom build requires a valid Android SDK path in Editor Settings.") + "\n"; @@ -1912,7 +2002,7 @@ public:  							}  						} else if (l.strip_edges().begins_with("<application")) { -							String last_tag = "android:icon=\"@drawable/icon\""; +							String last_tag = "android:icon=\"@mipmap/icon\"";  							int last_tag_pos = l.find(last_tag);  							if (last_tag_pos == -1) {  								ERR_PRINTS("Not adding application attributes as the expected tag was not found in '<application': " + last_tag); @@ -1949,7 +2039,7 @@ public:  		EditorProgress ep("export", "Exporting for Android", 105, true); -		if (bool(p_preset->get("custom_package/use_custom_build"))) { //custom build +		if (bool(p_preset->get("custom_template/use_custom_build"))) { //custom build  			//re-generate build.gradle and AndroidManifest.xml  			{ //test that installed build version is alright @@ -2017,9 +2107,9 @@ public:  		} else {  			if (p_debug) -				src_apk = p_preset->get("custom_package/debug"); +				src_apk = p_preset->get("custom_template/debug");  			else -				src_apk = p_preset->get("custom_package/release"); +				src_apk = p_preset->get("custom_template/release");  			src_apk = src_apk.strip_edges();  			if (src_apk == "") { @@ -2091,6 +2181,35 @@ public:  		Vector<String> enabled_abis = get_enabled_abis(p_preset); +		String project_icon_path = ProjectSettings::get_singleton()->get("application/config/icon"); + +		// Prepare images to be resized for the icons. If some image ends up being uninitialized, the default image from the export template will be used. +		Ref<Image> launcher_icon_image; +		Ref<Image> launcher_adaptive_icon_foreground_image; +		Ref<Image> launcher_adaptive_icon_background_image; + +		launcher_icon_image.instance(); +		launcher_adaptive_icon_foreground_image.instance(); +		launcher_adaptive_icon_background_image.instance(); + +		// Regular icon: user selection -> project icon -> default. +		String path = static_cast<String>(p_preset->get(launcher_icon_option)).strip_edges(); +		if (path.empty() || ImageLoader::load_image(path, launcher_icon_image) != OK) { +			ImageLoader::load_image(project_icon_path, launcher_icon_image); +		} + +		// Adaptive foreground: user selection -> regular icon (user selection -> project icon -> default). +		path = static_cast<String>(p_preset->get(launcher_adaptive_icon_foreground_option)).strip_edges(); +		if (path.empty() || ImageLoader::load_image(path, launcher_adaptive_icon_foreground_image) != OK) { +			launcher_adaptive_icon_foreground_image = launcher_icon_image; +		} + +		// Adaptive background: user selection -> default. +		path = static_cast<String>(p_preset->get(launcher_adaptive_icon_background_option)).strip_edges(); +		if (!path.empty()) { +			ImageLoader::load_image(path, launcher_adaptive_icon_background_image); +		} +  		while (ret == UNZ_OK) {  			//get filename @@ -2113,41 +2232,22 @@ public:  			//write  			if (file == "AndroidManifest.xml") { -  				_fix_manifest(p_preset, data, p_flags & (DEBUG_FLAG_DUMB_CLIENT | DEBUG_FLAG_REMOTE_DEBUG));  			}  			if (file == "resources.arsc") { -  				_fix_resources(p_preset, data);  			} -			if (file == "res/drawable-nodpi-v4/icon.png") { -				bool found = false; -				for (uint64_t i = 0; i < sizeof(launcher_icons) / sizeof(launcher_icons[0]); ++i) { -					String icon_path = String(p_preset->get(launcher_icons[i].option_id)).strip_edges(); -					if (icon_path != "" && icon_path.ends_with(".png")) { -						FileAccess *f = FileAccess::open(icon_path, FileAccess::READ); -						if (f) { -							data.resize(f->get_len()); -							f->get_buffer(data.ptrw(), data.size()); -							memdelete(f); -							found = true; -							break; -						} -					} +			for (int i = 0; i < icon_densities_count; ++i) { +				if (launcher_icon_image.is_valid() && !launcher_icon_image->empty()) { +					_process_launcher_icons(file, launcher_icon_image, launcher_icons[i], data);  				} -				if (!found) { - -					String appicon = ProjectSettings::get_singleton()->get("application/config/icon"); -					if (appicon != "" && appicon.ends_with(".png")) { -						FileAccess *f = FileAccess::open(appicon, FileAccess::READ); -						if (f) { -							data.resize(f->get_len()); -							f->get_buffer(data.ptrw(), data.size()); -							memdelete(f); -						} -					} +				if (launcher_adaptive_icon_foreground_image.is_valid() && !launcher_adaptive_icon_foreground_image->empty()) { +					_process_launcher_icons(file, launcher_adaptive_icon_foreground_image, launcher_adaptive_icon_foregrounds[i], data); +				} +				if (launcher_adaptive_icon_background_image.is_valid() && !launcher_adaptive_icon_background_image->empty()) { +					_process_launcher_icons(file, launcher_adaptive_icon_background_image, launcher_adaptive_icon_backgrounds[i], data);  				}  			} @@ -2246,20 +2346,7 @@ public:  			}  		} -		if (!err) { -			APKExportData ed; -			ed.ep = &ep; -			ed.apk = unaligned_apk; -			for (uint64_t i = 0; i < sizeof(launcher_icons) / sizeof(launcher_icons[0]); ++i) { -				String icon_path = String(p_preset->get(launcher_icons[i].option_id)).strip_edges(); -				if (icon_path != "" && icon_path.ends_with(".png") && FileAccess::exists(icon_path)) { -					Vector<uint8_t> data = FileAccess::get_file_as_array(icon_path); -					store_in_apk(&ed, launcher_icons[i].export_path, data); -				} -			} -		} - -		int xr_mode_index = p_preset->get("graphics/xr_mode"); +		int xr_mode_index = p_preset->get("xr_features/xr_mode");  		if (xr_mode_index == 1 /* XRMode.OVR */) {  			cl.push_back("--xr_mode_ovr");  		} else { diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml index ba01ec313b..7e9ce70d80 100644 --- a/platform/android/java/app/AndroidManifest.xml +++ b/platform/android/java/app/AndroidManifest.xml @@ -27,7 +27,7 @@      <!-- Any tag in this line after android:icon will be erased when doing custom builds. -->      <!-- If you want to add tags manually, do before it. -->      <!-- WARNING: This should stay on a single line until the parsing code is improved. See GH-32414. --> -    <application android:label="@string/godot_project_name_string" android:allowBackup="false" tools:ignore="GoogleAppIndexingWarning" android:icon="@drawable/icon" > +    <application android:label="@string/godot_project_name_string" android:allowBackup="false" tools:ignore="GoogleAppIndexingWarning" android:icon="@mipmap/icon" >          <!-- The following metadata values are replaced when Godot exports, modifying them here has no effect. -->          <!-- Do these changes in the export preset. Adding new ones is fine. --> diff --git a/platform/android/java/lib/AndroidManifest.xml b/platform/android/java/lib/AndroidManifest.xml index 517fc403b2..b133585f99 100644 --- a/platform/android/java/lib/AndroidManifest.xml +++ b/platform/android/java/lib/AndroidManifest.xml @@ -11,7 +11,7 @@      </application>      <instrumentation -        android:icon="@drawable/icon" +        android:icon="@mipmap/icon"          android:label="@string/godot_project_name_string"          android:name=".GodotInstrumentation"          android:targetPackage="org.godotengine.godot" /> diff --git a/platform/android/java/lib/res/drawable-nodpi/icon.png b/platform/android/java/lib/res/drawable-nodpi/icon.png Binary files differdeleted file mode 100644 index 6ad9b43117..0000000000 --- a/platform/android/java/lib/res/drawable-nodpi/icon.png +++ /dev/null diff --git a/platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml b/platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml new file mode 100644 index 0000000000..1ed4037035 --- /dev/null +++ b/platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> +    <background android:drawable="@mipmap/icon_background"/> +    <foreground android:drawable="@mipmap/icon_foreground"/> +</adaptive-icon>
\ No newline at end of file diff --git a/platform/android/java/lib/res/mipmap-hdpi/icon.png b/platform/android/java/lib/res/mipmap-hdpi/icon.png Binary files differnew file mode 100644 index 0000000000..cc6e113e89 --- /dev/null +++ b/platform/android/java/lib/res/mipmap-hdpi/icon.png diff --git a/platform/android/java/lib/res/mipmap-hdpi/icon_background.png b/platform/android/java/lib/res/mipmap-hdpi/icon_background.png Binary files differnew file mode 100644 index 0000000000..78445c0181 --- /dev/null +++ b/platform/android/java/lib/res/mipmap-hdpi/icon_background.png diff --git a/platform/android/java/lib/res/mipmap-hdpi/icon_foreground.png b/platform/android/java/lib/res/mipmap-hdpi/icon_foreground.png Binary files differnew file mode 100644 index 0000000000..75e409ff74 --- /dev/null +++ b/platform/android/java/lib/res/mipmap-hdpi/icon_foreground.png diff --git a/platform/android/java/lib/res/mipmap-mdpi/icon.png b/platform/android/java/lib/res/mipmap-mdpi/icon.png Binary files differnew file mode 100644 index 0000000000..e1968fe142 --- /dev/null +++ b/platform/android/java/lib/res/mipmap-mdpi/icon.png diff --git a/platform/android/java/lib/res/mipmap-mdpi/icon_background.png b/platform/android/java/lib/res/mipmap-mdpi/icon_background.png Binary files differnew file mode 100644 index 0000000000..5813f751ed --- /dev/null +++ b/platform/android/java/lib/res/mipmap-mdpi/icon_background.png diff --git a/platform/android/java/lib/res/mipmap-mdpi/icon_foreground.png b/platform/android/java/lib/res/mipmap-mdpi/icon_foreground.png Binary files differnew file mode 100644 index 0000000000..982b69be1e --- /dev/null +++ b/platform/android/java/lib/res/mipmap-mdpi/icon_foreground.png diff --git a/platform/android/java/lib/res/mipmap-xhdpi/icon.png b/platform/android/java/lib/res/mipmap-xhdpi/icon.png Binary files differnew file mode 100644 index 0000000000..9281d8da48 --- /dev/null +++ b/platform/android/java/lib/res/mipmap-xhdpi/icon.png diff --git a/platform/android/java/lib/res/mipmap-xhdpi/icon_background.png b/platform/android/java/lib/res/mipmap-xhdpi/icon_background.png Binary files differnew file mode 100644 index 0000000000..4269c822a7 --- /dev/null +++ b/platform/android/java/lib/res/mipmap-xhdpi/icon_background.png diff --git a/platform/android/java/lib/res/mipmap-xhdpi/icon_foreground.png b/platform/android/java/lib/res/mipmap-xhdpi/icon_foreground.png Binary files differnew file mode 100644 index 0000000000..726b267ad6 --- /dev/null +++ b/platform/android/java/lib/res/mipmap-xhdpi/icon_foreground.png diff --git a/platform/android/java/lib/res/mipmap-xxhdpi/icon.png b/platform/android/java/lib/res/mipmap-xxhdpi/icon.png Binary files differnew file mode 100644 index 0000000000..7a6b67d273 --- /dev/null +++ b/platform/android/java/lib/res/mipmap-xxhdpi/icon.png diff --git a/platform/android/java/lib/res/mipmap-xxhdpi/icon_background.png b/platform/android/java/lib/res/mipmap-xxhdpi/icon_background.png Binary files differnew file mode 100644 index 0000000000..5e8b518d17 --- /dev/null +++ b/platform/android/java/lib/res/mipmap-xxhdpi/icon_background.png diff --git a/platform/android/java/lib/res/mipmap-xxhdpi/icon_foreground.png b/platform/android/java/lib/res/mipmap-xxhdpi/icon_foreground.png Binary files differnew file mode 100644 index 0000000000..b0c727f74c --- /dev/null +++ b/platform/android/java/lib/res/mipmap-xxhdpi/icon_foreground.png diff --git a/platform/android/java/lib/res/mipmap-xxxhdpi/icon.png b/platform/android/java/lib/res/mipmap-xxxhdpi/icon.png Binary files differnew file mode 100644 index 0000000000..0881245802 --- /dev/null +++ b/platform/android/java/lib/res/mipmap-xxxhdpi/icon.png diff --git a/platform/android/java/lib/res/mipmap-xxxhdpi/icon_background.png b/platform/android/java/lib/res/mipmap-xxxhdpi/icon_background.png Binary files differnew file mode 100644 index 0000000000..004b6fd508 --- /dev/null +++ b/platform/android/java/lib/res/mipmap-xxxhdpi/icon_background.png diff --git a/platform/android/java/lib/res/mipmap-xxxhdpi/icon_foreground.png b/platform/android/java/lib/res/mipmap-xxxhdpi/icon_foreground.png Binary files differnew file mode 100644 index 0000000000..72e6f92b6e --- /dev/null +++ b/platform/android/java/lib/res/mipmap-xxxhdpi/icon_foreground.png diff --git a/platform/android/java/lib/res/mipmap/icon.png b/platform/android/java/lib/res/mipmap/icon.png Binary files differnew file mode 100644 index 0000000000..0881245802 --- /dev/null +++ b/platform/android/java/lib/res/mipmap/icon.png diff --git a/platform/android/java/lib/res/mipmap/icon_background.png b/platform/android/java/lib/res/mipmap/icon_background.png Binary files differnew file mode 100644 index 0000000000..5813f751ed --- /dev/null +++ b/platform/android/java/lib/res/mipmap/icon_background.png diff --git a/platform/android/java/lib/res/mipmap/icon_foreground.png b/platform/android/java/lib/res/mipmap/icon_foreground.png Binary files differnew file mode 100644 index 0000000000..982b69be1e --- /dev/null +++ b/platform/android/java/lib/res/mipmap/icon_foreground.png diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java index 8447af10e4..e0b46673ba 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java @@ -100,6 +100,16 @@ public class GodotLib {  	public static native void hover(int type, int x, int y);  	/** +	 * Forward double_tap events from the main thread to the GL thread. +	 */ +	public static native void doubletap(int x, int y); + +	/** +	 * Forward scroll events from the main thread to the GL thread. +	 */ +	public static native void scroll(int x, int y); + +	/**  	 * Forward accelerometer sensor events from the main thread to the GL thread.  	 * @see android.hardware.SensorEventListener#onSensorChanged(SensorEvent)  	 */ diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotView.java index 78f3763d84..f938583082 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotView.java @@ -32,8 +32,10 @@ package org.godotengine.godot;  import android.annotation.SuppressLint;  import android.graphics.PixelFormat;  import android.opengl.GLSurfaceView; +import android.view.GestureDetector;  import android.view.KeyEvent;  import android.view.MotionEvent; +import org.godotengine.godot.input.GodotGestureHandler;  import org.godotengine.godot.input.GodotInputHandler;  import org.godotengine.godot.utils.GLUtils;  import org.godotengine.godot.xr.XRMode; @@ -68,6 +70,7 @@ public class GodotView extends GLSurfaceView {  	private final Godot activity;  	private final GodotInputHandler inputHandler; +	private final GestureDetector detector;  	private final GodotRenderer godotRenderer;  	public GodotView(Godot activity, XRMode xrMode, boolean p_use_gl3, boolean p_use_32_bits, boolean p_use_debug_opengl) { @@ -78,6 +81,7 @@ public class GodotView extends GLSurfaceView {  		this.activity = activity;  		this.inputHandler = new GodotInputHandler(this); +		this.detector = new GestureDetector(activity, new GodotGestureHandler(this));  		this.godotRenderer = new GodotRenderer();  		init(xrMode, false, 16, 0);  	} @@ -90,6 +94,7 @@ public class GodotView extends GLSurfaceView {  	@Override  	public boolean onTouchEvent(MotionEvent event) {  		super.onTouchEvent(event); +		this.detector.onTouchEvent(event);  		return activity.gotTouchEvent(event);  	} diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java new file mode 100644 index 0000000000..1a38a9c3d2 --- /dev/null +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java @@ -0,0 +1,106 @@ +/*************************************************************************/ +/*  GodotGestureHandler.java                                             */ +/*************************************************************************/ +/*                       This file is part of:                           */ +/*                           GODOT ENGINE                                */ +/*                      https://godotengine.org                          */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */ +/* Copyright (c) 2014-2020 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.                */ +/*************************************************************************/ + +package org.godotengine.godot.input; + +import android.util.Log; +import android.view.GestureDetector; +import android.view.MotionEvent; +import org.godotengine.godot.GodotLib; +import org.godotengine.godot.GodotView; + +/** + * Handles gesture input related events for the {@link GodotView} view. + * https://developer.android.com/reference/android/view/GestureDetector.SimpleOnGestureListener + */ +public class GodotGestureHandler extends GestureDetector.SimpleOnGestureListener { + +	private final GodotView godotView; + +	public GodotGestureHandler(GodotView godotView) { +		this.godotView = godotView; +	} + +	private void queueEvent(Runnable task) { +		godotView.queueEvent(task); +	} + +	@Override +	public boolean onDown(MotionEvent event) { +		super.onDown(event); +		//Log.i("GodotGesture", "onDown"); +		return true; +	} + +	@Override +	public boolean onSingleTapConfirmed(MotionEvent event) { +		super.onSingleTapConfirmed(event); +		return true; +	} + +	@Override +	public void onLongPress(MotionEvent event) { +		//Log.i("GodotGesture", "onLongPress"); +	} + +	@Override +	public boolean onDoubleTap(MotionEvent event) { +		//Log.i("GodotGesture", "onDoubleTap"); +		final int x = Math.round(event.getX()); +		final int y = Math.round(event.getY()); +		queueEvent(new Runnable() { +			@Override +			public void run() { +				GodotLib.doubletap(x, y); +			} +		}); +		return true; +	} + +	@Override +	public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { +		//Log.i("GodotGesture", "onScroll"); +		final int x = Math.round(distanceX); +		final int y = Math.round(distanceY); +		queueEvent(new Runnable() { +			@Override +			public void run() { +				GodotLib.scroll(x, y); +			} +		}); +		return true; +	} + +	@Override +	public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) { +		//Log.i("GodotGesture", "onFling"); +		return true; +	} +} diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index 0a32f4423b..858ff89cbc 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -835,6 +835,20 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jo  	os_android->process_hover(p_type, Point2(p_x, p_y));  } +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubletap(JNIEnv *env, jobject obj, jint p_x, jint p_y) { +	if (step == 0) +		return; + +	os_android->process_double_tap(Point2(p_x, p_y)); +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_scroll(JNIEnv *env, jobject obj, jint p_x, jint p_y) { +	if (step == 0) +		return; + +	os_android->process_scroll(Point2(p_x, p_y)); +} +  /*   * Android Key codes.   */ diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h index 8c18c52d2b..71d4547f65 100644 --- a/platform/android/java_godot_lib_jni.h +++ b/platform/android/java_godot_lib_jni.h @@ -46,6 +46,8 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, job  JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jobject obj);  JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jobject obj, jint ev, jint pointer, jint count, jintArray positions);  JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jobject obj, jint p_type, jint p_x, jint p_y); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubletap(JNIEnv *env, jobject obj, jint p_x, jint p_y); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_scroll(JNIEnv *env, jobject obj, jint p_x, jint p_y);  JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jobject obj, jint p_scancode, jint p_unicode_char, jboolean p_pressed);  JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jobject obj, jint p_device, jint p_button, jboolean p_pressed);  JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jobject obj, jint p_device, jint p_axis, jfloat p_value); diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index 4c560273e8..bbea5e3699 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -502,6 +502,26 @@ void OS_Android::process_hover(int p_type, Point2 p_pos) {  	}  } +void OS_Android::process_double_tap(Point2 p_pos) { +	Ref<InputEventMouseButton> ev; +	ev.instance(); +	ev->set_position(p_pos); +	ev->set_global_position(p_pos); +	ev->set_pressed(true); +	ev->set_doubleclick(true); +	ev->set_button_index(1); +	input->parse_input_event(ev); +} + +void OS_Android::process_scroll(Point2 p_pos) { +	Ref<InputEventPanGesture> ev; +	ev.instance(); +	ev->set_position(p_pos); +	ev->set_delta(p_pos - scroll_prev_pos); +	input->parse_input_event(ev); +	scroll_prev_pos = p_pos; +} +  void OS_Android::process_accelerometer(const Vector3 &p_accelerometer) {  	input->set_accelerometer(p_accelerometer); diff --git a/platform/android/os_android.h b/platform/android/os_android.h index 5ac50ddef2..1cf64a2e84 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -70,6 +70,7 @@ public:  private:  	Vector<TouchPos> touch;  	Point2 hover_prev_pos; // needed to calculate the relative position on hover events +	Point2 scroll_prev_pos; // needed to calculate the relative position on scroll events  	bool use_gl2;  	bool use_apk_expansion; @@ -187,6 +188,8 @@ public:  	void process_gyroscope(const Vector3 &p_gyroscope);  	void process_touch(int p_what, int p_pointer, const Vector<TouchPos> &p_points);  	void process_hover(int p_type, Point2 p_pos); +	void process_double_tap(Point2 p_pos); +	void process_scroll(Point2 p_pos);  	void process_joy_event(JoypadEvent p_event);  	void process_event(Ref<InputEvent> p_event);  	void init_video_mode(int p_video_width, int p_video_height);  |