From 948c3cbb889801cfcb4479366c3c5f96a06c8111 Mon Sep 17 00:00:00 2001 From: fhuya Date: Wed, 25 Mar 2020 14:01:12 -0700 Subject: Add signal support to Godot Android plugin: Supports registering and emitting signal from a Godot Android plugin --- .../org/godotengine/godot/plugin/GodotPlugin.java | 85 ++++++++++++++++++- .../org/godotengine/godot/plugin/SignalInfo.java | 98 ++++++++++++++++++++++ 2 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 platform/android/java/lib/src/org/godotengine/godot/plugin/SignalInfo.java (limited to 'platform/android/java/lib') diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java index e3683704e1..a051164d15 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java +++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java @@ -34,6 +34,8 @@ import android.app.Activity; import android.content.Intent; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.text.TextUtils; +import android.util.Log; import android.view.Surface; import android.view.View; import java.lang.reflect.Method; @@ -41,8 +43,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; +import org.godotengine.godot.BuildConfig; import org.godotengine.godot.Godot; /** @@ -70,7 +74,10 @@ import org.godotengine.godot.Godot; */ public abstract class GodotPlugin { + private static final String TAG = GodotPlugin.class.getSimpleName(); + private final Godot godot; + private final ConcurrentHashMap registeredSignals = new ConcurrentHashMap<>(); public GodotPlugin(Godot godot) { this.godot = godot; @@ -118,6 +125,13 @@ public abstract class GodotPlugin { nativeRegisterMethod(getPluginName(), method.getName(), method.getReturnType().getName(), pt); } + // Register the signals for this plugin. + for (SignalInfo signalInfo : getPluginSignals()) { + String signalName = signalInfo.getName(); + nativeRegisterSignal(getPluginName(), signalName, signalInfo.getParamTypesNames()); + registeredSignals.put(signalName, signalInfo); + } + // Get the list of gdnative libraries to register. Set gdnativeLibrariesPaths = getPluginGDNativeLibrariesPaths(); if (!gdnativeLibrariesPaths.isEmpty()) { @@ -220,7 +234,17 @@ public abstract class GodotPlugin { * Returns the list of methods to be exposed to Godot. */ @NonNull - public abstract List getPluginMethods(); + public List getPluginMethods() { + return Collections.emptyList(); + } + + /** + * Returns the list of signals to be exposed to Godot. + */ + @NonNull + public Set getPluginSignals() { + return Collections.emptySet(); + } /** * Returns the paths for the plugin's gdnative libraries. @@ -252,6 +276,49 @@ public abstract class GodotPlugin { godot.runOnRenderThread(action); } + /** + * Emit a registered Godot signal. + * @param signalName + * @param signalArgs + */ + protected void emitSignal(final String signalName, final Object... signalArgs) { + try { + // Check that the given signal is among the registered set. + SignalInfo signalInfo = registeredSignals.get(signalName); + if (signalInfo == null) { + throw new IllegalArgumentException( + "Signal " + signalName + " is not registered for this plugin."); + } + + // Validate the arguments count. + Class[] signalParamTypes = signalInfo.getParamTypes(); + if (signalArgs.length != signalParamTypes.length) { + throw new IllegalArgumentException( + "Invalid arguments count. Should be " + signalParamTypes.length + " but is " + signalArgs.length); + } + + // Validate the argument's types. + for (int i = 0; i < signalParamTypes.length; i++) { + if (!signalParamTypes[i].isInstance(signalArgs[i])) { + throw new IllegalArgumentException( + "Invalid type for argument #" + i + ". Should be of type " + signalParamTypes[i].getName()); + } + } + + runOnRenderThread(new Runnable() { + @Override + public void run() { + nativeEmitSignal(getPluginName(), signalName, signalArgs); + } + }); + } catch (IllegalArgumentException exception) { + Log.w(TAG, exception.getMessage()); + if (BuildConfig.DEBUG) { + throw exception; + } + } + } + /** * Used to setup a {@link GodotPlugin} instance. * @param p_name Name of the instance. @@ -272,4 +339,20 @@ public abstract class GodotPlugin { * @param gdnlibPaths Paths to the libraries relative to the 'assets' directory. */ private native void nativeRegisterGDNativeLibraries(String[] gdnlibPaths); + + /** + * Used to complete registration of the {@link GodotPlugin} instance's methods. + * @param pluginName Name of the plugin + * @param signalName Name of the signal to register + * @param signalParamTypes Signal parameters types + */ + private native void nativeRegisterSignal(String pluginName, String signalName, String[] signalParamTypes); + + /** + * Used to emit signal by {@link GodotPlugin} instance. + * @param pluginName Name of the plugin + * @param signalName Name of the signal to emit + * @param signalParams Signal parameters + */ + private native void nativeEmitSignal(String pluginName, String signalName, Object[] signalParams); } diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/SignalInfo.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/SignalInfo.java new file mode 100644 index 0000000000..f907706889 --- /dev/null +++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/SignalInfo.java @@ -0,0 +1,98 @@ +/*************************************************************************/ +/* SignalInfo.java */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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.plugin; + +import android.support.annotation.NonNull; +import android.text.TextUtils; +import java.util.Arrays; + +/** + * Store information about a {@link GodotPlugin}'s signal. + */ +public final class SignalInfo { + + private final String name; + private final Class[] paramTypes; + private final String[] paramTypesNames; + + public SignalInfo(@NonNull String signalName, Class... paramTypes) { + if (TextUtils.isEmpty(signalName)) { + throw new IllegalArgumentException("Invalid signal name: " + signalName); + } + + this.name = signalName; + this.paramTypes = paramTypes == null ? new Class[ 0 ] : paramTypes; + this.paramTypesNames = new String[this.paramTypes.length]; + for (int i = 0; i < this.paramTypes.length; i++) { + this.paramTypesNames[i] = this.paramTypes[i].getName(); + } + } + + public String getName() { + return name; + } + + Class[] getParamTypes() { + return paramTypes; + } + + String[] getParamTypesNames() { + return paramTypesNames; + } + + @Override + public String toString() { + return "SignalInfo{" + + + "name='" + name + '\'' + + ", paramsTypes=" + Arrays.toString(paramTypes) + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof SignalInfo)) { + return false; + } + + SignalInfo that = (SignalInfo)o; + + return name.equals(that.name); + } + + @Override + public int hashCode() { + return name.hashCode(); + } +} -- cgit v1.2.3