From 5e0805a8138702651cb8cc5fdb8ca5c38bcdc2f0 Mon Sep 17 00:00:00 2001
From: Fredia Huya-Kouadio <fhuya@meta.com>
Date: Thu, 26 Sep 2024 12:37:02 -0700
Subject: [PATCH] Provide access to the Android runtime to GDScript

Thanks for the fix of `JavaClassWrapper` in https://github.com/godotengine/godot/pull/96182 and the changes in the previous commit, this introduces an `AndroidRuntime` plugin which provides GDScript access to the Android runtime capabilities.

This allows developers to get access to various Android capabilities without the need of a plugin.
For example, the following logic can be used to check whether the device supports vibration:

```
var android_runtime = Engine.get_singleton("AndroidRuntime")
 if android_runtime:
 	print("Checking if the device supports vibration")
 	var vibrator_service = android_runtime.getApplicationContext().getSystemService("vibrator")
 	if vibrator_service:
 		if vibrator_service.hasVibrator():
 			print("Vibration is supported on device!")
 		else:
 			printerr("Vibration is not supported on device")
 	else:
 		printerr("Unable to retrieve the vibrator service")
 else:
 	printerr("Couldn't find AndroidRuntime singleton")
```
---
 .../lib/src/org/godotengine/godot/Godot.kt    |  6 +-
 .../godot/plugin/AndroidRuntimePlugin.kt      | 63 +++++++++++++++++++
 2 files changed, 68 insertions(+), 1 deletion(-)
 create mode 100644 platform/android/java/lib/src/org/godotengine/godot/plugin/AndroidRuntimePlugin.kt

diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
index 5b1d09e7492..567b134234e 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
@@ -58,6 +58,8 @@ import org.godotengine.godot.input.GodotEditText
 import org.godotengine.godot.input.GodotInputHandler
 import org.godotengine.godot.io.directory.DirectoryAccessHandler
 import org.godotengine.godot.io.file.FileAccessHandler
+import org.godotengine.godot.plugin.AndroidRuntimePlugin
+import org.godotengine.godot.plugin.GodotPlugin
 import org.godotengine.godot.plugin.GodotPluginRegistry
 import org.godotengine.godot.tts.GodotTTS
 import org.godotengine.godot.utils.CommandLineFileParser
@@ -228,7 +230,9 @@ class Godot(private val context: Context) {
 			window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
 
 			Log.v(TAG, "Initializing Godot plugin registry")
-			GodotPluginRegistry.initializePluginRegistry(this, primaryHost.getHostPlugins(this))
+			val runtimePlugins = mutableSetOf<GodotPlugin>(AndroidRuntimePlugin(this))
+			runtimePlugins.addAll(primaryHost.getHostPlugins(this))
+			GodotPluginRegistry.initializePluginRegistry(this, runtimePlugins)
 			if (io == null) {
 				io = GodotIO(activity)
 			}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/AndroidRuntimePlugin.kt b/platform/android/java/lib/src/org/godotengine/godot/plugin/AndroidRuntimePlugin.kt
new file mode 100644
index 00000000000..edb4e7c3578
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/AndroidRuntimePlugin.kt
@@ -0,0 +1,63 @@
+/**************************************************************************/
+/*  AndroidRuntimePlugin.kt                                               */
+/**************************************************************************/
+/*                         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.                 */
+/**************************************************************************/
+
+package org.godotengine.godot.plugin
+
+import org.godotengine.godot.Godot
+
+/**
+ * Provides access to the Android runtime capabilities.
+ *
+ * For example, from gdscript, developers can use [getApplicationContext] to access system services
+ * and check if the device supports vibration.
+ *
+ * var android_runtime = Engine.get_singleton("AndroidRuntime")
+ * 	if android_runtime:
+ * 		print("Checking if the device supports vibration")
+ * 		var vibrator_service = android_runtime.getApplicationContext().getSystemService("vibrator")
+ * 		if vibrator_service:
+ * 			if vibrator_service.hasVibrator():
+ * 				print("Vibration is supported on device!")
+ * 			else:
+ * 				printerr("Vibration is not supported on device")
+ * 		else:
+ * 			printerr("Unable to retrieve the vibrator service")
+ * 	else:
+ * 		printerr("Couldn't find AndroidRuntime singleton")
+ */
+class AndroidRuntimePlugin(godot: Godot) : GodotPlugin(godot) {
+	override fun getPluginName() = "AndroidRuntime"
+
+	@UsedByGodot
+	fun getApplicationContext() = activity?.applicationContext
+
+	@UsedByGodot
+	override fun getActivity() = super.getActivity()
+}