From ef44e63f2255d3ecd6e18cc9772178ee6049ba3d Mon Sep 17 00:00:00 2001
From: Fredia Huya-Kouadio <fhuya@fb.com>
Date: Sun, 14 Aug 2022 09:45:22 -0700
Subject: [PATCH 1/3] Fix GodotTTS native initialization

---
 .../android/java/lib/src/org/godotengine/godot/Godot.java     | 2 +-
 .../android/java/lib/src/org/godotengine/godot/GodotLib.java  | 3 ++-
 platform/android/java_godot_lib_jni.cpp                       | 4 ++--
 platform/android/java_godot_lib_jni.h                         | 2 +-
 4 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
index 2147607d54d7..8913e0faf569 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
@@ -587,7 +587,7 @@ private void initializeGodot() {
 		mMagnetometer = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
 		mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
 
-		GodotLib.initialize(activity, this, activity.getAssets(), io, netUtils, directoryAccessHandler, fileAccessHandler, use_apk_expansion);
+		GodotLib.initialize(activity, this, activity.getAssets(), io, netUtils, directoryAccessHandler, fileAccessHandler, use_apk_expansion, tts);
 
 		result_callback = null;
 
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 acad0a7948b6..cf34b95c7667 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
@@ -32,6 +32,7 @@
 
 import org.godotengine.godot.io.directory.DirectoryAccessHandler;
 import org.godotengine.godot.io.file.FileAccessHandler;
+import org.godotengine.godot.tts.GodotTTS;
 import org.godotengine.godot.utils.GodotNetUtils;
 
 import android.app.Activity;
@@ -52,7 +53,7 @@ public class GodotLib {
 	/**
 	 * Invoked on the main thread to initialize Godot native layer.
 	 */
-	public static native void initialize(Activity activity, Godot p_instance, AssetManager p_asset_manager, GodotIO godotIO, GodotNetUtils netUtils, DirectoryAccessHandler directoryAccessHandler, FileAccessHandler fileAccessHandler, boolean use_apk_expansion);
+	public static native void initialize(Activity activity, Godot p_instance, AssetManager p_asset_manager, GodotIO godotIO, GodotNetUtils netUtils, DirectoryAccessHandler directoryAccessHandler, FileAccessHandler fileAccessHandler, boolean use_apk_expansion, GodotTTS tts);
 
 	/**
 	 * Invoked on the main thread to clean up Godot native layer.
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index 87776d861089..6b06bff615d9 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -120,7 +120,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHei
 	}
 }
 
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject p_activity, jobject p_godot_instance, jobject p_asset_manager, jobject p_godot_io, jobject p_net_utils, jobject p_directory_access_handler, jobject p_file_access_handler, jboolean p_use_apk_expansion) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject p_activity, jobject p_godot_instance, jobject p_asset_manager, jobject p_godot_io, jobject p_net_utils, jobject p_directory_access_handler, jobject p_file_access_handler, jboolean p_use_apk_expansion, jobject p_godot_tts) {
 	JavaVM *jvm;
 	env->GetJavaVM(&jvm);
 
@@ -137,7 +137,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en
 	DirAccessJAndroid::setup(p_directory_access_handler);
 	FileAccessFilesystemJAndroid::setup(p_file_access_handler);
 	NetSocketAndroid::setup(p_net_utils);
-	TTS_Android::setup(godot_java->get_member_object("tts", "Lorg/godotengine/godot/tts/GodotTTS;", env));
+	TTS_Android::setup(p_godot_tts);
 
 	os_android = new OS_Android(godot_java, godot_io_java, p_use_apk_expansion);
 
diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h
index a9e677a90718..6c4d11272b24 100644
--- a/platform/android/java_godot_lib_jni.h
+++ b/platform/android/java_godot_lib_jni.h
@@ -37,7 +37,7 @@
 // These functions can be called from within JAVA and are the means by which our JAVA implementation calls back into our C++ code.
 // See java/src/org/godotengine/godot/GodotLib.java for the JAVA side of this (yes that's why we have the long names)
 extern "C" {
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject p_activity, jobject p_godot_instance, jobject p_asset_manager, jobject p_godot_io, jobject p_net_utils, jobject p_directory_access_handler, jobject p_file_access_handler, jboolean p_use_apk_expansion);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject p_activity, jobject p_godot_instance, jobject p_asset_manager, jobject p_godot_io, jobject p_net_utils, jobject p_directory_access_handler, jobject p_file_access_handler, jboolean p_use_apk_expansion, jobject p_godot_tts);
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz);
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline);
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jclass clazz, jint width, jint height);

From 30479543b07d5869eb024663c6588e608bee3c1b Mon Sep 17 00:00:00 2001
From: Fredia Huya-Kouadio <fhuyakou@gmail.com>
Date: Mon, 15 Aug 2022 00:35:21 -0700
Subject: [PATCH 2/3] Fix issue preventing the Android Editor from displaying
 the project content

The issue was causing by a bug within the logic for `FileAccessFilesystemJAndroid#eof_reached()` causing that value to remain false after the eof was reached.
This in turn caused an infinite loop in the file scanner preventing the project's content from showing up.
---
 .../android/file_access_filesystem_jandroid.cpp     | 13 +++++++++++++
 platform/android/file_access_filesystem_jandroid.h  |  3 +++
 .../src/org/godotengine/godot/io/file/DataAccess.kt |  8 ++------
 .../godotengine/godot/io/file/FileAccessHandler.kt  |  5 +++++
 4 files changed, 23 insertions(+), 6 deletions(-)

diff --git a/platform/android/file_access_filesystem_jandroid.cpp b/platform/android/file_access_filesystem_jandroid.cpp
index ec9ce759c5f1..df23ffc46d29 100644
--- a/platform/android/file_access_filesystem_jandroid.cpp
+++ b/platform/android/file_access_filesystem_jandroid.cpp
@@ -45,6 +45,7 @@ jmethodID FileAccessFilesystemJAndroid::_file_seek_end = nullptr;
 jmethodID FileAccessFilesystemJAndroid::_file_read = nullptr;
 jmethodID FileAccessFilesystemJAndroid::_file_tell = nullptr;
 jmethodID FileAccessFilesystemJAndroid::_file_eof = nullptr;
+jmethodID FileAccessFilesystemJAndroid::_file_set_eof = nullptr;
 jmethodID FileAccessFilesystemJAndroid::_file_close = nullptr;
 jmethodID FileAccessFilesystemJAndroid::_file_write = nullptr;
 jmethodID FileAccessFilesystemJAndroid::_file_flush = nullptr;
@@ -161,6 +162,16 @@ bool FileAccessFilesystemJAndroid::eof_reached() const {
 	}
 }
 
+void FileAccessFilesystemJAndroid::_set_eof(bool eof) {
+	if (_file_set_eof) {
+		ERR_FAIL_COND_MSG(!is_open(), "File must be opened before use.");
+
+		JNIEnv *env = get_jni_env();
+		ERR_FAIL_COND(env == nullptr);
+		env->CallVoidMethod(file_access_handler, _file_set_eof, id, eof);
+	}
+}
+
 uint8_t FileAccessFilesystemJAndroid::get_8() const {
 	ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use.");
 	uint8_t byte;
@@ -183,6 +194,7 @@ String FileAccessFilesystemJAndroid::get_line() const {
 	while (true) {
 		size_t line_buffer_size = MIN(buffer_size_limit, file_size - get_position());
 		if (line_buffer_size <= 0) {
+			const_cast<FileAccessFilesystemJAndroid *>(this)->_set_eof(true);
 			break;
 		}
 
@@ -309,6 +321,7 @@ void FileAccessFilesystemJAndroid::setup(jobject p_file_access_handler) {
 	_file_get_size = env->GetMethodID(cls, "fileGetSize", "(I)J");
 	_file_tell = env->GetMethodID(cls, "fileGetPosition", "(I)J");
 	_file_eof = env->GetMethodID(cls, "isFileEof", "(I)Z");
+	_file_set_eof = env->GetMethodID(cls, "setFileEof", "(IZ)V");
 	_file_seek = env->GetMethodID(cls, "fileSeek", "(IJ)V");
 	_file_seek_end = env->GetMethodID(cls, "fileSeekFromEnd", "(IJ)V");
 	_file_read = env->GetMethodID(cls, "fileRead", "(ILjava/nio/ByteBuffer;)I");
diff --git a/platform/android/file_access_filesystem_jandroid.h b/platform/android/file_access_filesystem_jandroid.h
index 3bf654a0302f..177df87dc142 100644
--- a/platform/android/file_access_filesystem_jandroid.h
+++ b/platform/android/file_access_filesystem_jandroid.h
@@ -44,6 +44,7 @@ class FileAccessFilesystemJAndroid : public FileAccess {
 	static jmethodID _file_seek_end;
 	static jmethodID _file_tell;
 	static jmethodID _file_eof;
+	static jmethodID _file_set_eof;
 	static jmethodID _file_read;
 	static jmethodID _file_write;
 	static jmethodID _file_flush;
@@ -55,6 +56,8 @@ class FileAccessFilesystemJAndroid : public FileAccess {
 	String absolute_path;
 	String path_src;
 
+	void _set_eof(bool eof);
+
 public:
 	virtual Error _open(const String &p_path, int p_mode_flags) override; ///< open a file
 	virtual void close() override; ///< close a file
diff --git a/platform/android/java/lib/src/org/godotengine/godot/io/file/DataAccess.kt b/platform/android/java/lib/src/org/godotengine/godot/io/file/DataAccess.kt
index 463dabfb232b..f23537a29e49 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/io/file/DataAccess.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/io/file/DataAccess.kt
@@ -104,7 +104,6 @@ internal abstract class DataAccess(private val filePath: String) {
 
 	protected abstract val fileChannel: FileChannel
 	internal var endOfFile = false
-		private set
 
 	fun close() {
 		try {
@@ -125,9 +124,7 @@ internal abstract class DataAccess(private val filePath: String) {
 	fun seek(position: Long) {
 		try {
 			fileChannel.position(position)
-			if (position <= size()) {
-				endOfFile = false
-			}
+			endOfFile = position >= fileChannel.size()
 		} catch (e: Exception) {
 			Log.w(TAG, "Exception when seeking file $filePath.", e)
 		}
@@ -161,8 +158,7 @@ internal abstract class DataAccess(private val filePath: String) {
 	fun read(buffer: ByteBuffer): Int {
 		return try {
 			val readBytes = fileChannel.read(buffer)
-			endOfFile = readBytes == -1
-					|| (fileChannel.position() >= fileChannel.size() && fileChannel.size() > 0)
+			endOfFile = readBytes == -1 || (fileChannel.position() >= fileChannel.size())
 			if (readBytes == -1) {
 				0
 			} else {
diff --git a/platform/android/java/lib/src/org/godotengine/godot/io/file/FileAccessHandler.kt b/platform/android/java/lib/src/org/godotengine/godot/io/file/FileAccessHandler.kt
index 04b6772c4508..83da3a24b3b5 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/io/file/FileAccessHandler.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/io/file/FileAccessHandler.kt
@@ -194,6 +194,11 @@ class FileAccessHandler(val context: Context) {
 		return files[fileId].endOfFile
 	}
 
+	fun setFileEof(fileId: Int, eof: Boolean) {
+		val file = files[fileId] ?: return
+		file.endOfFile = eof
+	}
+
 	fun fileClose(fileId: Int) {
 		if (hasFileId(fileId)) {
 			files[fileId].close()

From 3ac6b6a596dad2f57390a8d7926604e349d2d281 Mon Sep 17 00:00:00 2001
From: Fredia Huya-Kouadio <fhuyakou@gmail.com>
Date: Mon, 15 Aug 2022 02:30:08 -0700
Subject: [PATCH 3/3] Disable threads used to check on plugins to load

The functionality is unavailable on Android (requires export capability) and unnecessarily consumes resources
---
 platform/android/export/export_plugin.cpp | 10 ++++++----
 platform/android/export/export_plugin.h   |  2 ++
 platform/iphone/export/export.cpp         |  8 ++++++++
 3 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index 9eaad034f8e6..a4e24d124285 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -228,6 +228,7 @@ static const char *AAB_ASSETS_DIRECTORY = "res://android/build/assetPacks/instal
 static const int DEFAULT_MIN_SDK_VERSION = 19; // Should match the value in 'platform/android/java/app/config.gradle#minSdk'
 static const int DEFAULT_TARGET_SDK_VERSION = 32; // Should match the value in 'platform/android/java/app/config.gradle#targetSdk'
 
+#ifndef ANDROID_ENABLED
 void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) {
 	EditorExportPlatformAndroid *ea = (EditorExportPlatformAndroid *)ud;
 
@@ -259,7 +260,6 @@ void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) {
 			}
 		}
 
-#ifndef ANDROID_ENABLED
 		// Check for devices updates
 		String adb = get_adb_path();
 		if (FileAccess::exists(adb)) {
@@ -373,7 +373,6 @@ void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) {
 
 			ea->device_lock.unlock();
 		}
-#endif
 
 		uint64_t sleep = 300'000;
 		uint64_t wait = 3'000'000;
@@ -386,7 +385,6 @@ void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) {
 		}
 	}
 
-#ifndef ANDROID_ENABLED
 	if (EditorSettings::get_singleton()->get("export/android/shutdown_adb_on_exit")) {
 		String adb = get_adb_path();
 		if (!FileAccess::exists(adb)) {
@@ -397,8 +395,8 @@ void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) {
 		args.push_back("kill-server");
 		OS::get_singleton()->execute(adb, args, true);
 	}
-#endif
 }
+#endif
 
 String EditorExportPlatformAndroid::get_project_name(const String &p_name) const {
 	String aname;
@@ -3438,10 +3436,14 @@ EditorExportPlatformAndroid::EditorExportPlatformAndroid() {
 
 	devices_changed.set();
 	plugins_changed.set();
+#ifndef ANDROID_ENABLED
 	check_for_changes_thread.start(_check_for_changes_poll_thread, this);
+#endif
 }
 
 EditorExportPlatformAndroid::~EditorExportPlatformAndroid() {
+#ifndef ANDROID_ENABLED
 	quit_request.set();
 	check_for_changes_thread.wait_to_finish();
+#endif
 }
diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h
index 8898cf695520..22a56db948d5 100644
--- a/platform/android/export/export_plugin.h
+++ b/platform/android/export/export_plugin.h
@@ -98,10 +98,12 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
 	Vector<Device> devices;
 	SafeFlag devices_changed;
 	Mutex device_lock;
+#ifndef ANDROID_ENABLED
 	Thread check_for_changes_thread;
 	SafeFlag quit_request;
 
 	static void _check_for_changes_poll_thread(void *ud);
+#endif
 
 	String get_project_name(const String &p_name) const;
 
diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp
index 4a12233709ae..52ff17ec54d1 100644
--- a/platform/iphone/export/export.cpp
+++ b/platform/iphone/export/export.cpp
@@ -57,8 +57,10 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
 
 	// Plugins
 	SafeFlag plugins_changed;
+#ifndef ANDROID_ENABLED
 	Thread check_for_changes_thread;
 	SafeFlag quit_request;
+#endif
 	Mutex plugins_lock;
 	Vector<PluginConfigIOS> plugins;
 
@@ -142,6 +144,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
 		return true;
 	}
 
+#ifndef ANDROID_ENABLED
 	static void _check_for_changes_poll_thread(void *ud) {
 		EditorExportPlatformIOS *ea = (EditorExportPlatformIOS *)ud;
 
@@ -177,6 +180,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
 			}
 		}
 	}
+#endif
 
 protected:
 	virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features);
@@ -2159,12 +2163,16 @@ EditorExportPlatformIOS::EditorExportPlatformIOS() {
 
 	plugins_changed.set();
 
+#ifndef ANDROID_ENABLED
 	check_for_changes_thread.start(_check_for_changes_poll_thread, this);
+#endif
 }
 
 EditorExportPlatformIOS::~EditorExportPlatformIOS() {
+#ifndef ANDROID_ENABLED
 	quit_request.set();
 	check_for_changes_thread.wait_to_finish();
+#endif
 }
 
 void register_iphone_exporter() {