diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index b49de068fd31..bef5ea2dfc72 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -1309,6 +1309,12 @@ Override for [member input_devices/pen_tablet/driver] on Windows. + + If [code]true[/code], long press events on an Android touchscreen are transformed into right click events. + + + If [code]true[/code], multi-touch pan and scale gestures are enabled on Android devices. + If [code]true[/code], sends mouse input events when tapping or swiping on the touchscreen. diff --git a/main/main.cpp b/main/main.cpp index 8e217575c12d..fb509f1dbe8a 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2565,6 +2565,9 @@ Error Main::setup2() { id->set_emulate_mouse_from_touch(bool(GLOBAL_DEF_BASIC("input_devices/pointing/emulate_mouse_from_touch", true))); } + GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_long_press_as_right_click", false); + GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_pan_and_scale_gestures", false); + MAIN_PRINT("Main: Load Translations and Remaps"); translation_server->setup(); //register translations, load them, etc. 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 f819063a2235..04d196a6e9c6 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt @@ -84,7 +84,7 @@ class Godot(private val context: Context) : SensorEventListener { } private val pluginRegistry: GodotPluginRegistry by lazy { - GodotPluginRegistry.initializePluginRegistry(this) + GodotPluginRegistry.getPluginRegistry() } private val mSensorManager: SensorManager by lazy { requireActivity().getSystemService(Context.SENSOR_SERVICE) as SensorManager @@ -190,7 +190,7 @@ class Godot(private val context: Context) : SensorEventListener { val activity = requireActivity() val window = activity.window window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) - GodotPluginRegistry.initializePluginRegistry(this) + GodotPluginRegistry.initializePluginRegistry(this, primaryHost.getHostPlugins(this)) if (io == null) { io = GodotIO(activity) } @@ -250,11 +250,7 @@ class Godot(private val context: Context) : SensorEventListener { } i++ } - if (newArgs.isEmpty()) { - commandLine = mutableListOf() - } else { - commandLine = newArgs - } + commandLine = if (newArgs.isEmpty()) { mutableListOf() } else { newArgs } if (useApkExpansion && mainPackMd5 != null && mainPackKey != null) { // Build the full path to the app's expansion files try { @@ -392,6 +388,10 @@ class Godot(private val context: Context) : SensorEventListener { // Fallback to openGl GodotGLRenderView(host, this, xrMode, useDebugOpengl) } + + renderView?.inputHandler?.enableLongPress(java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_long_press_as_right_click"))) + renderView?.inputHandler?.enablePanningAndScalingGestures(java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_pan_and_scale_gestures"))) + if (host == primaryHost) { renderView!!.startRenderer() } @@ -616,7 +616,7 @@ class Godot(private val context: Context) : SensorEventListener { private fun alert(message: String, title: String, okCallback: Runnable?) { val activity: Activity = getActivity() ?: return - runOnUiThread(Runnable { + runOnUiThread { val builder = AlertDialog.Builder(activity) builder.setMessage(message).setTitle(title) builder.setPositiveButton( @@ -627,7 +627,7 @@ class Godot(private val context: Context) : SensorEventListener { } val dialog = builder.create() dialog.show() - }) + } } /** @@ -685,9 +685,9 @@ class Godot(private val context: Context) : SensorEventListener { return false } - private fun setKeepScreenOn(p_enabled: Boolean) { + private fun setKeepScreenOn(enabled: Boolean) { runOnUiThread { - if (p_enabled) { + if (enabled) { getActivity()?.window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) } else { getActivity()?.window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) @@ -835,9 +835,7 @@ class Godot(private val context: Context) : SensorEventListener { } } - override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) { - // Do something here if sensor accuracy changes. - } + override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {} /** * Used by the native code (java_godot_wrapper.h) to vibrate the device. @@ -865,7 +863,7 @@ class Godot(private val context: Context) : SensorEventListener { private fun getCommandLine(): MutableList { val original: MutableList = parseCommandLine() val hostCommandLine = primaryHost?.commandLine - if (hostCommandLine != null && hostCommandLine.isNotEmpty()) { + if (!hostCommandLine.isNullOrEmpty()) { original.addAll(hostCommandLine) } return original diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt b/platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt index 417be7cef47f..a60f6e997ec7 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt @@ -173,6 +173,10 @@ abstract class GodotActivity : FragmentActivity(), GodotHost { return this } + override fun getGodot(): Godot? { + return godotFragment?.godot + } + /** * Used to initialize the Godot fragment instance in [onCreate]. */ diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java b/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java index 9a8b10ea3ee4..120e1722e5e1 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java @@ -30,6 +30,7 @@ package org.godotengine.godot; +import org.godotengine.godot.plugin.GodotPlugin; import org.godotengine.godot.utils.BenchmarkUtils; import android.app.Activity; @@ -65,6 +66,7 @@ import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.Set; /** * Base fragment for Android apps intending to use Godot for part of the app's UI. @@ -122,6 +124,7 @@ public interface ResultCallback { } public ResultCallback resultCallback; + @Override public Godot getGodot() { return godot; } @@ -426,4 +429,13 @@ public int onNewGodotInstanceRequested(String[] args) { } return 0; } + + @Override + @CallSuper + public Set getHostPlugins(Godot engine) { + if (parentHost != null) { + return parentHost.getHostPlugins(engine); + } + return Collections.emptySet(); + } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java index e5333085dd3a..1862b9fa9be7 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java @@ -30,39 +30,42 @@ package org.godotengine.godot; +import org.godotengine.godot.plugin.GodotPlugin; + import android.app.Activity; import java.util.Collections; import java.util.List; +import java.util.Set; /** * Denotate a component (e.g: Activity, Fragment) that hosts the {@link Godot} engine. */ public interface GodotHost { /** - * Provides a set of command line parameters to setup the engine. + * Provides a set of command line parameters to setup the {@link Godot} engine. */ default List getCommandLine() { return Collections.emptyList(); } /** - * Invoked on the render thread when the Godot setup is complete. + * Invoked on the render thread when setup of the {@link Godot} engine is complete. */ default void onGodotSetupCompleted() {} /** - * Invoked on the render thread when the Godot main loop has started. + * Invoked on the render thread when the {@link Godot} engine main loop has started. */ default void onGodotMainLoopStarted() {} /** - * Invoked on the render thread to terminate the given Godot instance. + * Invoked on the render thread to terminate the given {@link Godot} engine instance. */ default void onGodotForceQuit(Godot instance) {} /** - * Invoked on the render thread to terminate the Godot instance with the given id. + * Invoked on the render thread to terminate the {@link Godot} engine instance with the given id. * @param godotInstanceId id of the Godot instance to terminate. See {@code onNewGodotInstanceRequested} * * @return true if successful, false otherwise. @@ -90,7 +93,19 @@ default int onNewGodotInstanceRequested(String[] args) { } /** - * Provide access to the Activity hosting the Godot engine. + * Provide access to the Activity hosting the {@link Godot} engine. */ Activity getActivity(); + + /** + * Provide access to the hosted {@link Godot} engine. + */ + Godot getGodot(); + + /** + * Returns a set of {@link GodotPlugin} to be registered with the hosted {@link Godot} engine. + */ + default Set getHostPlugins(Godot engine) { + return Collections.emptySet(); + } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotService.kt b/platform/android/java/lib/src/org/godotengine/godot/GodotService.kt index 68cd2c13586e..795dc921c7fa 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotService.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotService.kt @@ -8,8 +8,10 @@ import android.util.Log /** * Godot service responsible for hosting the Godot engine instance. + * + * Note: Still in development, so it's made private and inaccessible until completed. */ -class GodotService : Service() { +private class GodotService : Service() { companion object { private val TAG = GodotService::class.java.simpleName diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java index c8b222254e60..38c115ad7f6a 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java @@ -95,7 +95,7 @@ public void enableLongPress(boolean enable) { /** * Enable multi-fingers pan & scale gestures. This is false by default. - * + *

* Note: This may interfere with multi-touch handling / support. */ public void enablePanningAndScalingGestures(boolean enable) { diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java index d338b724418e..711bca02e7d8 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java +++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java @@ -42,8 +42,8 @@ import androidx.annotation.Nullable; import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; import java.util.Collection; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** @@ -64,9 +64,8 @@ public final class GodotPluginRegistry { private static GodotPluginRegistry instance; private final ConcurrentHashMap registry; - private GodotPluginRegistry(Godot godot) { + private GodotPluginRegistry() { registry = new ConcurrentHashMap<>(); - loadPlugins(godot); } /** @@ -93,12 +92,14 @@ public Collection getAllPlugins() { * documentation. * * @param godot Godot instance + * @param runtimePlugins Set of plugins provided at runtime for registration * @return A singleton instance of {@link GodotPluginRegistry}. This ensures that only one instance * of each Godot Android plugins is available at runtime. */ - public static GodotPluginRegistry initializePluginRegistry(Godot godot) { + public static GodotPluginRegistry initializePluginRegistry(Godot godot, Set runtimePlugins) { if (instance == null) { - instance = new GodotPluginRegistry(godot); + instance = new GodotPluginRegistry(); + instance.loadPlugins(godot, runtimePlugins); } return instance; @@ -108,7 +109,7 @@ public static GodotPluginRegistry initializePluginRegistry(Godot godot) { * Return the plugin registry if it's initialized. * Throws a {@link IllegalStateException} exception if not. * - * @throws IllegalStateException if {@link GodotPluginRegistry#initializePluginRegistry(Godot)} has not been called prior to calling this method. + * @throws IllegalStateException if {@link GodotPluginRegistry#initializePluginRegistry(Godot, Set)} has not been called prior to calling this method. */ public static GodotPluginRegistry getPluginRegistry() throws IllegalStateException { if (instance == null) { @@ -118,7 +119,16 @@ public static GodotPluginRegistry getPluginRegistry() throws IllegalStateExcepti return instance; } - private void loadPlugins(Godot godot) { + private void loadPlugins(Godot godot, Set runtimePlugins) { + // Register the runtime plugins + if (runtimePlugins != null && !runtimePlugins.isEmpty()) { + for (GodotPlugin plugin : runtimePlugins) { + Log.i(TAG, "Registering runtime plugin " + plugin.getPluginName()); + registry.put(plugin.getPluginName(), plugin); + } + } + + // Register the manifest plugins try { final Activity activity = godot.getActivity(); ApplicationInfo appInfo = activity