You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-06 12:20:30 +00:00
Re-architecture of the Godot Android plugin.
This commit is contained in:
@@ -2,6 +2,7 @@ apply plugin: 'com.android.library'
|
||||
|
||||
dependencies {
|
||||
implementation libraries.supportCoreUtils
|
||||
implementation libraries.v4Support
|
||||
}
|
||||
|
||||
def pathToRootDir = "../../../../"
|
||||
|
||||
@@ -61,8 +61,11 @@ import android.os.Messenger;
|
||||
import android.os.VibrationEffect;
|
||||
import android.os.Vibrator;
|
||||
import android.provider.Settings.Secure;
|
||||
import android.support.annotation.CallSuper;
|
||||
import android.support.annotation.Keep;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.view.Display;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
@@ -96,11 +99,13 @@ import java.util.Locale;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
import org.godotengine.godot.input.GodotEditText;
|
||||
import org.godotengine.godot.payments.PaymentsManager;
|
||||
import org.godotengine.godot.plugin.GodotPlugin;
|
||||
import org.godotengine.godot.plugin.GodotPluginRegistry;
|
||||
import org.godotengine.godot.utils.GodotNetUtils;
|
||||
import org.godotengine.godot.utils.PermissionsUtil;
|
||||
import org.godotengine.godot.xr.XRMode;
|
||||
|
||||
public abstract class Godot extends Activity implements SensorEventListener, IDownloaderClient {
|
||||
public abstract class Godot extends FragmentActivity implements SensorEventListener, IDownloaderClient {
|
||||
|
||||
static final int MAX_SINGLETONS = 64;
|
||||
private IStub mDownloaderClientStub;
|
||||
@@ -129,6 +134,8 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
|
||||
// Used to dispatch events to the main thread.
|
||||
private final Handler mainThreadHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
private GodotPluginRegistry pluginRegistry;
|
||||
|
||||
static private Intent mCurrentIntent;
|
||||
|
||||
@Override
|
||||
@@ -158,7 +165,7 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
|
||||
|
||||
protected void registerClass(String p_name, String[] p_methods) {
|
||||
|
||||
GodotLib.singleton(p_name, this);
|
||||
GodotPlugin.nativeRegisterSingleton(p_name, this);
|
||||
|
||||
Class clazz = getClass();
|
||||
Method[] methods = clazz.getDeclaredMethods();
|
||||
@@ -184,7 +191,7 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
|
||||
String[] pt = new String[ptr.size()];
|
||||
ptr.toArray(pt);
|
||||
|
||||
GodotLib.method(p_name, method.getName(), method.getReturnType().getName(), pt);
|
||||
GodotPlugin.nativeRegisterMethod(p_name, method.getName(), method.getReturnType().getName(), pt);
|
||||
}
|
||||
|
||||
Godot.singletons[Godot.singleton_count++] = this;
|
||||
@@ -269,6 +276,9 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
|
||||
|
||||
singletons[i].onMainActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
|
||||
plugin.onMainActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
@@ -276,12 +286,25 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
|
||||
for (int i = 0; i < singleton_count; i++) {
|
||||
singletons[i].onMainRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
}
|
||||
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
|
||||
plugin.onMainRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
}
|
||||
|
||||
for (int i = 0; i < permissions.length; i++) {
|
||||
GodotLib.requestPermissionResult(permissions[i], grantResults[i] == PackageManager.PERMISSION_GRANTED);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Invoked on the GL thread when the Godot main loop has started.
|
||||
*/
|
||||
@CallSuper
|
||||
protected void onGLGodotMainLoopStarted() {
|
||||
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
|
||||
plugin.onGLGodotMainLoopStarted();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by the native code (java_godot_lib_jni.cpp) to complete initialization of the GLSurfaceView view and renderer.
|
||||
*/
|
||||
@@ -304,14 +327,13 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
|
||||
edittext.setView(mView);
|
||||
io.setEdit(edittext);
|
||||
|
||||
final Godot godot = this;
|
||||
mView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
Point fullSize = new Point();
|
||||
godot.getWindowManager().getDefaultDisplay().getSize(fullSize);
|
||||
getWindowManager().getDefaultDisplay().getSize(fullSize);
|
||||
Rect gameSize = new Rect();
|
||||
godot.mView.getWindowVisibleDisplayFrame(gameSize);
|
||||
mView.getWindowVisibleDisplayFrame(gameSize);
|
||||
|
||||
final int keyboardHeight = fullSize.y - gameSize.bottom;
|
||||
GodotLib.setVirtualKeyboardHeight(keyboardHeight);
|
||||
@@ -323,6 +345,12 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
|
||||
@Override
|
||||
public void run() {
|
||||
GodotLib.setup(current_command_line);
|
||||
|
||||
// Must occur after GodotLib.setup has completed.
|
||||
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
|
||||
plugin.onGLRegisterPluginWithGodotNative();
|
||||
}
|
||||
|
||||
setKeepScreenOn("True".equals(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on")));
|
||||
|
||||
// The Godot Android plugins are setup on completion of GodotLib.setup
|
||||
@@ -340,6 +368,14 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Include the returned non-null views in the Godot view hierarchy.
|
||||
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
|
||||
View pluginView = plugin.onMainCreateView(this);
|
||||
if (pluginView != null) {
|
||||
layout.addView(pluginView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setKeepScreenOn(final boolean p_enabled) {
|
||||
@@ -537,6 +573,7 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
|
||||
Window window = getWindow();
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
|
||||
mClipboard = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
pluginRegistry = GodotPluginRegistry.initializePluginRegistry(this);
|
||||
|
||||
//check for apk expansion API
|
||||
if (true) {
|
||||
@@ -680,6 +717,9 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
|
||||
for (int i = 0; i < singleton_count; i++) {
|
||||
singletons[i].onMainDestroy();
|
||||
}
|
||||
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
|
||||
plugin.onMainDestroy();
|
||||
}
|
||||
|
||||
GodotLib.ondestroy(this);
|
||||
|
||||
@@ -708,6 +748,9 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
|
||||
for (int i = 0; i < singleton_count; i++) {
|
||||
singletons[i].onMainPause();
|
||||
}
|
||||
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
|
||||
plugin.onMainPause();
|
||||
}
|
||||
}
|
||||
|
||||
public String getClipboard() {
|
||||
@@ -761,6 +804,9 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
|
||||
|
||||
singletons[i].onMainResume();
|
||||
}
|
||||
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
|
||||
plugin.onMainResume();
|
||||
}
|
||||
}
|
||||
|
||||
public void UiChangeListener() {
|
||||
@@ -857,6 +903,11 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
|
||||
shouldQuit = false;
|
||||
}
|
||||
}
|
||||
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
|
||||
if (plugin.onMainBackPressed()) {
|
||||
shouldQuit = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldQuit && mView != null) {
|
||||
mView.queueEvent(new Runnable() {
|
||||
@@ -868,6 +919,17 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue a runnable to be run on the GL thread.
|
||||
* <p>
|
||||
* This must be called after the GL thread has started.
|
||||
*/
|
||||
public final void runOnGLThread(@NonNull Runnable action) {
|
||||
if (mView != null) {
|
||||
mView.queueEvent(action);
|
||||
}
|
||||
}
|
||||
|
||||
private void forceQuit() {
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
@@ -175,22 +175,6 @@ public class GodotLib {
|
||||
*/
|
||||
public static native void audio();
|
||||
|
||||
/**
|
||||
* Used to setup a {@link org.godotengine.godot.Godot.SingletonBase} instance.
|
||||
* @param p_name Name of the instance.
|
||||
* @param p_object Reference to the singleton instance.
|
||||
*/
|
||||
public static native void singleton(String p_name, Object p_object);
|
||||
|
||||
/**
|
||||
* Used to complete registration of the {@link org.godotengine.godot.Godot.SingletonBase} instance's methods.
|
||||
* @param p_sname Name of the instance
|
||||
* @param p_name Name of the method to register
|
||||
* @param p_ret Return type of the registered method
|
||||
* @param p_params Method parameters types
|
||||
*/
|
||||
public static native void method(String p_sname, String p_name, String p_ret, String[] p_params);
|
||||
|
||||
/**
|
||||
* Used to access Godot global properties.
|
||||
* @param p_key Property key
|
||||
|
||||
@@ -1,230 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* GodotPaymentV3.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;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.util.Log;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.godotengine.godot.payments.PaymentsManager;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class GodotPaymentV3 extends Godot.SingletonBase {
|
||||
|
||||
private Godot activity;
|
||||
private Integer purchaseCallbackId = 0;
|
||||
private String accessToken;
|
||||
private String purchaseValidationUrlPrefix;
|
||||
private String transactionId;
|
||||
private PaymentsManager mPaymentManager;
|
||||
private Dictionary mSkuDetails = new Dictionary();
|
||||
|
||||
public void purchase(final String sku, final String transactionId) {
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mPaymentManager.requestPurchase(sku, transactionId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static public Godot.SingletonBase initialize(Activity p_activity) {
|
||||
|
||||
return new GodotPaymentV3(p_activity);
|
||||
}
|
||||
|
||||
public GodotPaymentV3(Activity p_activity) {
|
||||
|
||||
registerClass("GodotPayments", new String[] { "purchase", "setPurchaseCallbackId", "setPurchaseValidationUrlPrefix", "setTransactionId", "getSignature", "consumeUnconsumedPurchases", "requestPurchased", "setAutoConsume", "consume", "querySkuDetails", "isConnected" });
|
||||
activity = (Godot)p_activity;
|
||||
mPaymentManager = activity.getPaymentsManager();
|
||||
mPaymentManager.setBaseSingleton(this);
|
||||
}
|
||||
|
||||
public void consumeUnconsumedPurchases() {
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mPaymentManager.consumeUnconsumedPurchases();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String signature;
|
||||
|
||||
public String getSignature() {
|
||||
return this.signature;
|
||||
}
|
||||
|
||||
public void callbackSuccess(String ticket, String signature, String sku) {
|
||||
GodotLib.calldeferred(purchaseCallbackId, "purchase_success", new Object[] { ticket, signature, sku });
|
||||
}
|
||||
|
||||
public void callbackSuccessProductMassConsumed(String ticket, String signature, String sku) {
|
||||
Log.d(this.getClass().getName(), "callbackSuccessProductMassConsumed > " + ticket + "," + signature + "," + sku);
|
||||
GodotLib.calldeferred(purchaseCallbackId, "consume_success", new Object[] { ticket, signature, sku });
|
||||
}
|
||||
|
||||
public void callbackSuccessNoUnconsumedPurchases() {
|
||||
GodotLib.calldeferred(purchaseCallbackId, "consume_not_required", new Object[] {});
|
||||
}
|
||||
|
||||
public void callbackFailConsume(String message) {
|
||||
GodotLib.calldeferred(purchaseCallbackId, "consume_fail", new Object[] { message });
|
||||
}
|
||||
|
||||
public void callbackFail(String message) {
|
||||
GodotLib.calldeferred(purchaseCallbackId, "purchase_fail", new Object[] { message });
|
||||
}
|
||||
|
||||
public void callbackCancel() {
|
||||
GodotLib.calldeferred(purchaseCallbackId, "purchase_cancel", new Object[] {});
|
||||
}
|
||||
|
||||
public void callbackAlreadyOwned(String sku) {
|
||||
GodotLib.calldeferred(purchaseCallbackId, "purchase_owned", new Object[] { sku });
|
||||
}
|
||||
|
||||
public int getPurchaseCallbackId() {
|
||||
return purchaseCallbackId;
|
||||
}
|
||||
|
||||
public void setPurchaseCallbackId(int purchaseCallbackId) {
|
||||
this.purchaseCallbackId = purchaseCallbackId;
|
||||
}
|
||||
|
||||
public String getPurchaseValidationUrlPrefix() {
|
||||
return this.purchaseValidationUrlPrefix;
|
||||
}
|
||||
|
||||
public void setPurchaseValidationUrlPrefix(String url) {
|
||||
this.purchaseValidationUrlPrefix = url;
|
||||
}
|
||||
|
||||
public String getAccessToken() {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
public void setAccessToken(String accessToken) {
|
||||
this.accessToken = accessToken;
|
||||
}
|
||||
|
||||
public void setTransactionId(String transactionId) {
|
||||
this.transactionId = transactionId;
|
||||
}
|
||||
|
||||
public String getTransactionId() {
|
||||
return this.transactionId;
|
||||
}
|
||||
|
||||
// request purchased items are not consumed
|
||||
public void requestPurchased() {
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mPaymentManager.requestPurchased();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// callback for requestPurchased()
|
||||
public void callbackPurchased(String receipt, String signature, String sku) {
|
||||
GodotLib.calldeferred(purchaseCallbackId, "has_purchased", new Object[] { receipt, signature, sku });
|
||||
}
|
||||
|
||||
public void callbackDisconnected() {
|
||||
GodotLib.calldeferred(purchaseCallbackId, "iap_disconnected", new Object[] {});
|
||||
}
|
||||
|
||||
public void callbackConnected() {
|
||||
GodotLib.calldeferred(purchaseCallbackId, "iap_connected", new Object[] {});
|
||||
}
|
||||
|
||||
// true if connected, false otherwise
|
||||
public boolean isConnected() {
|
||||
return mPaymentManager.isConnected();
|
||||
}
|
||||
|
||||
// consume item automatically after purchase. default is true.
|
||||
public void setAutoConsume(boolean autoConsume) {
|
||||
mPaymentManager.setAutoConsume(autoConsume);
|
||||
}
|
||||
|
||||
// consume a specific item
|
||||
public void consume(String sku) {
|
||||
mPaymentManager.consume(sku);
|
||||
}
|
||||
|
||||
// query in app item detail info
|
||||
public void querySkuDetails(String[] list) {
|
||||
List<String> nKeys = Arrays.asList(list);
|
||||
List<String> cKeys = Arrays.asList(mSkuDetails.get_keys());
|
||||
ArrayList<String> fKeys = new ArrayList<String>();
|
||||
for (String key : nKeys) {
|
||||
if (!cKeys.contains(key)) {
|
||||
fKeys.add(key);
|
||||
}
|
||||
}
|
||||
if (fKeys.size() > 0) {
|
||||
mPaymentManager.querySkuDetails(fKeys.toArray(new String[0]));
|
||||
} else {
|
||||
completeSkuDetail();
|
||||
}
|
||||
}
|
||||
|
||||
public void addSkuDetail(String itemJson) {
|
||||
JSONObject o = null;
|
||||
try {
|
||||
o = new JSONObject(itemJson);
|
||||
Dictionary item = new Dictionary();
|
||||
item.put("type", o.optString("type"));
|
||||
item.put("product_id", o.optString("productId"));
|
||||
item.put("title", o.optString("title"));
|
||||
item.put("description", o.optString("description"));
|
||||
item.put("price", o.optString("price"));
|
||||
item.put("price_currency_code", o.optString("price_currency_code"));
|
||||
item.put("price_amount", 0.000001d * o.optLong("price_amount_micros"));
|
||||
mSkuDetails.put(item.get("product_id").toString(), item);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void completeSkuDetail() {
|
||||
GodotLib.calldeferred(purchaseCallbackId, "sku_details_complete", new Object[] { mSkuDetails });
|
||||
}
|
||||
|
||||
public void errorSkuDetail(String errorMessage) {
|
||||
GodotLib.calldeferred(purchaseCallbackId, "sku_details_error", new Object[] { errorMessage });
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,8 @@ package org.godotengine.godot;
|
||||
import android.opengl.GLSurfaceView;
|
||||
import javax.microedition.khronos.egl.EGLConfig;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
import org.godotengine.godot.plugin.GodotPlugin;
|
||||
import org.godotengine.godot.plugin.GodotPluginRegistry;
|
||||
import org.godotengine.godot.utils.GLUtils;
|
||||
|
||||
/**
|
||||
@@ -40,8 +42,13 @@ import org.godotengine.godot.utils.GLUtils;
|
||||
*/
|
||||
class GodotRenderer implements GLSurfaceView.Renderer {
|
||||
|
||||
private final GodotPluginRegistry pluginRegistry;
|
||||
private boolean activityJustResumed = false;
|
||||
|
||||
GodotRenderer() {
|
||||
this.pluginRegistry = GodotPluginRegistry.getPluginRegistry();
|
||||
}
|
||||
|
||||
public void onDrawFrame(GL10 gl) {
|
||||
if (activityJustResumed) {
|
||||
GodotLib.onRendererResumed();
|
||||
@@ -52,18 +59,26 @@ class GodotRenderer implements GLSurfaceView.Renderer {
|
||||
for (int i = 0; i < Godot.singleton_count; i++) {
|
||||
Godot.singletons[i].onGLDrawFrame(gl);
|
||||
}
|
||||
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
|
||||
plugin.onGLDrawFrame(gl);
|
||||
}
|
||||
}
|
||||
|
||||
public void onSurfaceChanged(GL10 gl, int width, int height) {
|
||||
|
||||
GodotLib.resize(width, height);
|
||||
for (int i = 0; i < Godot.singleton_count; i++) {
|
||||
Godot.singletons[i].onGLSurfaceChanged(gl, width, height);
|
||||
}
|
||||
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
|
||||
plugin.onGLSurfaceChanged(gl, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
|
||||
GodotLib.newcontext(GLUtils.use_32);
|
||||
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
|
||||
plugin.onGLSurfaceCreated(gl, config);
|
||||
}
|
||||
}
|
||||
|
||||
void onActivityResumed() {
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*************************************************************************/
|
||||
/* GodotPaymentInterface.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.payments;
|
||||
|
||||
public interface GodotPaymentInterface {
|
||||
void purchase(String sku, String transactionId);
|
||||
|
||||
void consumeUnconsumedPurchases();
|
||||
|
||||
String getSignature();
|
||||
|
||||
void callbackSuccess(String ticket, String signature, String sku);
|
||||
|
||||
void callbackSuccessProductMassConsumed(String ticket, String signature, String sku);
|
||||
|
||||
void callbackSuccessNoUnconsumedPurchases();
|
||||
|
||||
void callbackFailConsume(String message);
|
||||
|
||||
void callbackFail(String message);
|
||||
|
||||
void callbackCancel();
|
||||
|
||||
void callbackAlreadyOwned(String sku);
|
||||
|
||||
int getPurchaseCallbackId();
|
||||
|
||||
void setPurchaseCallbackId(int purchaseCallbackId);
|
||||
|
||||
String getPurchaseValidationUrlPrefix();
|
||||
|
||||
void setPurchaseValidationUrlPrefix(String url);
|
||||
|
||||
String getAccessToken();
|
||||
|
||||
void setAccessToken(String accessToken);
|
||||
|
||||
void setTransactionId(String transactionId);
|
||||
|
||||
String getTransactionId();
|
||||
|
||||
// request purchased items are not consumed
|
||||
void requestPurchased();
|
||||
|
||||
// callback for requestPurchased()
|
||||
void callbackPurchased(String receipt, String signature, String sku);
|
||||
|
||||
void callbackDisconnected();
|
||||
|
||||
void callbackConnected();
|
||||
|
||||
// true if connected, false otherwise
|
||||
boolean isConnected();
|
||||
|
||||
// consume item automatically after purchase. default is true.
|
||||
void setAutoConsume(boolean autoConsume);
|
||||
|
||||
// consume a specific item
|
||||
void consume(String sku);
|
||||
|
||||
// query in app item detail info
|
||||
void querySkuDetails(String[] list);
|
||||
|
||||
void addSkuDetail(String itemJson);
|
||||
|
||||
void completeSkuDetail();
|
||||
|
||||
void errorSkuDetail(String errorMessage);
|
||||
}
|
||||
@@ -43,7 +43,6 @@ import android.util.Log;
|
||||
import com.android.vending.billing.IInAppBillingService;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import org.godotengine.godot.GodotPaymentV3;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
@@ -90,9 +89,9 @@ public class PaymentsManager {
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
mService = null;
|
||||
|
||||
// At this stage, godotPaymentV3 might not have been initialized yet.
|
||||
if (godotPaymentV3 != null) {
|
||||
godotPaymentV3.callbackDisconnected();
|
||||
// At this stage, godotPayment might not have been initialized yet.
|
||||
if (godotPayment != null) {
|
||||
godotPayment.callbackDisconnected();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,9 +99,9 @@ public class PaymentsManager {
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
mService = IInAppBillingService.Stub.asInterface(service);
|
||||
|
||||
// At this stage, godotPaymentV3 might not have been initialized yet.
|
||||
if (godotPaymentV3 != null) {
|
||||
godotPaymentV3.callbackConnected();
|
||||
// At this stage, godotPayment might not have been initialized yet.
|
||||
if (godotPayment != null) {
|
||||
godotPayment.callbackConnected();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -111,17 +110,17 @@ public class PaymentsManager {
|
||||
new PurchaseTask(mService, activity) {
|
||||
@Override
|
||||
protected void error(String message) {
|
||||
godotPaymentV3.callbackFail(message);
|
||||
godotPayment.callbackFail(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void canceled() {
|
||||
godotPaymentV3.callbackCancel();
|
||||
godotPayment.callbackCancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void alreadyOwned() {
|
||||
godotPaymentV3.callbackAlreadyOwned(sku);
|
||||
godotPayment.callbackAlreadyOwned(sku);
|
||||
}
|
||||
}
|
||||
.purchase(sku, transactionId);
|
||||
@@ -135,19 +134,19 @@ public class PaymentsManager {
|
||||
new ReleaseAllConsumablesTask(mService, activity) {
|
||||
@Override
|
||||
protected void success(String sku, String receipt, String signature, String token) {
|
||||
godotPaymentV3.callbackSuccessProductMassConsumed(receipt, signature, sku);
|
||||
godotPayment.callbackSuccessProductMassConsumed(receipt, signature, sku);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void error(String message) {
|
||||
Log.d("godot", "consumeUnconsumedPurchases :" + message);
|
||||
godotPaymentV3.callbackFailConsume(message);
|
||||
godotPayment.callbackFailConsume(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notRequired() {
|
||||
Log.d("godot", "callbackSuccessNoUnconsumedPurchases :");
|
||||
godotPaymentV3.callbackSuccessNoUnconsumedPurchases();
|
||||
godotPayment.callbackSuccessNoUnconsumedPurchases();
|
||||
}
|
||||
}
|
||||
.consumeItAll();
|
||||
@@ -168,7 +167,7 @@ public class PaymentsManager {
|
||||
final ArrayList<String> mySignatures = bundle.getStringArrayList("INAPP_DATA_SIGNATURE_LIST");
|
||||
|
||||
if (myPurchases == null || myPurchases.size() == 0) {
|
||||
godotPaymentV3.callbackPurchased("", "", "");
|
||||
godotPayment.callbackPurchased("", "", "");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -186,7 +185,7 @@ public class PaymentsManager {
|
||||
pc.setConsumableFlag("block", sku, true);
|
||||
pc.setConsumableValue("token", sku, token);
|
||||
|
||||
godotPaymentV3.callbackPurchased(receipt, signature, sku);
|
||||
godotPayment.callbackPurchased(receipt, signature, sku);
|
||||
} catch (JSONException e) {
|
||||
}
|
||||
}
|
||||
@@ -203,7 +202,7 @@ public class PaymentsManager {
|
||||
new HandlePurchaseTask(activity) {
|
||||
@Override
|
||||
protected void success(final String sku, final String signature, final String ticket) {
|
||||
godotPaymentV3.callbackSuccess(ticket, signature, sku);
|
||||
godotPayment.callbackSuccess(ticket, signature, sku);
|
||||
|
||||
if (auto_consume) {
|
||||
new ConsumeTask(mService, activity) {
|
||||
@@ -213,7 +212,7 @@ public class PaymentsManager {
|
||||
|
||||
@Override
|
||||
protected void error(String message) {
|
||||
godotPaymentV3.callbackFail(message);
|
||||
godotPayment.callbackFail(message);
|
||||
}
|
||||
}
|
||||
.consume(sku);
|
||||
@@ -222,12 +221,12 @@ public class PaymentsManager {
|
||||
|
||||
@Override
|
||||
protected void error(String message) {
|
||||
godotPaymentV3.callbackFail(message);
|
||||
godotPayment.callbackFail(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void canceled() {
|
||||
godotPaymentV3.callbackCancel();
|
||||
godotPayment.callbackCancel();
|
||||
}
|
||||
}
|
||||
.handlePurchaseRequest(resultCode, data);
|
||||
@@ -235,19 +234,19 @@ public class PaymentsManager {
|
||||
|
||||
public void validatePurchase(String purchaseToken, final String sku) {
|
||||
|
||||
new ValidateTask(activity, godotPaymentV3) {
|
||||
new ValidateTask(activity, godotPayment) {
|
||||
@Override
|
||||
protected void success() {
|
||||
|
||||
new ConsumeTask(mService, activity) {
|
||||
@Override
|
||||
protected void success(String ticket) {
|
||||
godotPaymentV3.callbackSuccess(ticket, null, sku);
|
||||
godotPayment.callbackSuccess(ticket, null, sku);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void error(String message) {
|
||||
godotPaymentV3.callbackFail(message);
|
||||
godotPayment.callbackFail(message);
|
||||
}
|
||||
}
|
||||
.consume(sku);
|
||||
@@ -255,12 +254,12 @@ public class PaymentsManager {
|
||||
|
||||
@Override
|
||||
protected void error(String message) {
|
||||
godotPaymentV3.callbackFail(message);
|
||||
godotPayment.callbackFail(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void canceled() {
|
||||
godotPaymentV3.callbackCancel();
|
||||
godotPayment.callbackCancel();
|
||||
}
|
||||
}
|
||||
.validatePurchase(sku);
|
||||
@@ -274,12 +273,12 @@ public class PaymentsManager {
|
||||
new ConsumeTask(mService, activity) {
|
||||
@Override
|
||||
protected void success(String ticket) {
|
||||
godotPaymentV3.callbackSuccessProductMassConsumed(ticket, "", sku);
|
||||
godotPayment.callbackSuccessProductMassConsumed(ticket, "", sku);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void error(String message) {
|
||||
godotPaymentV3.callbackFailConsume(message);
|
||||
godotPayment.callbackFailConsume(message);
|
||||
}
|
||||
}
|
||||
.consume(sku);
|
||||
@@ -387,9 +386,9 @@ public class PaymentsManager {
|
||||
if (!skuDetails.containsKey("DETAILS_LIST")) {
|
||||
int response = getResponseCodeFromBundle(skuDetails);
|
||||
if (response != BILLING_RESPONSE_RESULT_OK) {
|
||||
godotPaymentV3.errorSkuDetail(getResponseDesc(response));
|
||||
godotPayment.errorSkuDetail(getResponseDesc(response));
|
||||
} else {
|
||||
godotPaymentV3.errorSkuDetail("No error but no detail list.");
|
||||
godotPayment.errorSkuDetail("No error but no detail list.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -398,22 +397,22 @@ public class PaymentsManager {
|
||||
|
||||
for (String thisResponse : responseList) {
|
||||
Log.d("godot", "response = " + thisResponse);
|
||||
godotPaymentV3.addSkuDetail(thisResponse);
|
||||
godotPayment.addSkuDetail(thisResponse);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
godotPaymentV3.errorSkuDetail("RemoteException error!");
|
||||
godotPayment.errorSkuDetail("RemoteException error!");
|
||||
}
|
||||
}
|
||||
godotPaymentV3.completeSkuDetail();
|
||||
godotPayment.completeSkuDetail();
|
||||
}
|
||||
}))
|
||||
.start();
|
||||
}
|
||||
|
||||
private GodotPaymentV3 godotPaymentV3;
|
||||
private GodotPaymentInterface godotPayment;
|
||||
|
||||
public void setBaseSingleton(GodotPaymentV3 godotPaymentV3) {
|
||||
this.godotPaymentV3 = godotPaymentV3;
|
||||
public void setBaseSingleton(GodotPaymentInterface godotPaymentInterface) {
|
||||
this.godotPayment = godotPaymentInterface;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.os.AsyncTask;
|
||||
import java.lang.ref.WeakReference;
|
||||
import org.godotengine.godot.GodotPaymentV3;
|
||||
import org.godotengine.godot.utils.HttpRequester;
|
||||
import org.godotengine.godot.utils.RequestParams;
|
||||
import org.json.JSONException;
|
||||
@@ -43,7 +42,7 @@ import org.json.JSONObject;
|
||||
abstract public class ValidateTask {
|
||||
|
||||
private Activity context;
|
||||
private GodotPaymentV3 godotPaymentsV3;
|
||||
private GodotPaymentInterface godotPayments;
|
||||
private ProgressDialog dialog;
|
||||
private String mSku;
|
||||
|
||||
@@ -80,9 +79,9 @@ abstract public class ValidateTask {
|
||||
}
|
||||
}
|
||||
|
||||
public ValidateTask(Activity context, GodotPaymentV3 godotPaymentsV3) {
|
||||
public ValidateTask(Activity context, GodotPaymentInterface godotPayments) {
|
||||
this.context = context;
|
||||
this.godotPaymentsV3 = godotPaymentsV3;
|
||||
this.godotPayments = godotPayments;
|
||||
}
|
||||
|
||||
public void validatePurchase(final String sku) {
|
||||
@@ -96,7 +95,7 @@ abstract public class ValidateTask {
|
||||
|
||||
private String doInBackground(String... params) {
|
||||
PaymentsCache pc = new PaymentsCache(context);
|
||||
String url = godotPaymentsV3.getPurchaseValidationUrlPrefix();
|
||||
String url = godotPayments.getPurchaseValidationUrlPrefix();
|
||||
RequestParams param = new RequestParams();
|
||||
param.setUrl(url);
|
||||
param.put("ticket", pc.getConsumableValue("ticket", mSku));
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
/*************************************************************************/
|
||||
/* GodotPlugin.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.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.View;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.microedition.khronos.egl.EGLConfig;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
import org.godotengine.godot.Godot;
|
||||
|
||||
/**
|
||||
* Base class for the Godot Android plugins.
|
||||
* <p>
|
||||
* A Godot Android plugin is a regular Android library packaged as an aar archive file with the following caveats:
|
||||
* <p>
|
||||
* - The library must have a dependency on the Godot Android library (godot-lib.aar).
|
||||
* A stable version is available for each release.
|
||||
* <p>
|
||||
* - The library must include a <meta-data> tag in its manifest file setup as follow:
|
||||
* <meta-data android:name="org.godotengine.plugin.v1.[PluginName]" android:value="[plugin.init.ClassFullName]" />
|
||||
* Where:
|
||||
* - 'PluginName' is the name of the plugin.
|
||||
* - 'plugin.init.ClassFullName' is the full name (package + class name) of the plugin class
|
||||
* extending {@link GodotPlugin}.
|
||||
*
|
||||
* A plugin can also define and provide c/c++ gdnative libraries and nativescripts for the target
|
||||
* app/game to leverage.
|
||||
* The shared library for the gdnative library will be automatically bundled by the aar build
|
||||
* system.
|
||||
* Godot '*.gdnlib' and '*.gdns' resource files must however be manually defined in the project
|
||||
* 'assets' directory. The recommended path for these resources in the 'assets' directory should be:
|
||||
* 'godot/plugin/v1/[PluginName]/'
|
||||
*/
|
||||
public abstract class GodotPlugin {
|
||||
|
||||
private final Godot godot;
|
||||
|
||||
public GodotPlugin(Godot godot) {
|
||||
this.godot = godot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides access to the Godot engine.
|
||||
*/
|
||||
protected Godot getGodot() {
|
||||
return godot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the plugin with Godot native code.
|
||||
*/
|
||||
public final void onGLRegisterPluginWithGodotNative() {
|
||||
nativeRegisterSingleton(getPluginName(), this);
|
||||
|
||||
Class clazz = getClass();
|
||||
Method[] methods = clazz.getDeclaredMethods();
|
||||
for (Method method : methods) {
|
||||
boolean found = false;
|
||||
|
||||
for (String s : getPluginMethods()) {
|
||||
if (s.equals(method.getName())) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
continue;
|
||||
|
||||
List<String> ptr = new ArrayList<String>();
|
||||
|
||||
Class[] paramTypes = method.getParameterTypes();
|
||||
for (Class c : paramTypes) {
|
||||
ptr.add(c.getName());
|
||||
}
|
||||
|
||||
String[] pt = new String[ptr.size()];
|
||||
ptr.toArray(pt);
|
||||
|
||||
nativeRegisterMethod(getPluginName(), method.getName(), method.getReturnType().getName(), pt);
|
||||
}
|
||||
|
||||
// Get the list of gdnative libraries to register.
|
||||
Set<String> gdnativeLibrariesPaths = getPluginGDNativeLibrariesPaths();
|
||||
if (!gdnativeLibrariesPaths.isEmpty()) {
|
||||
nativeRegisterGDNativeLibraries(gdnativeLibrariesPaths.toArray(new String[0]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked once during the Godot Android initialization process after creation of the
|
||||
* {@link org.godotengine.godot.GodotView} view.
|
||||
* <p>
|
||||
* This method should be overridden by descendants of this class that would like to add
|
||||
* their view/layout to the Godot view hierarchy.
|
||||
*
|
||||
* @return the view to be included; null if no views should be included.
|
||||
*/
|
||||
@Nullable
|
||||
public View onMainCreateView(Activity activity) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Activity#onActivityResult(int, int, Intent)
|
||||
*/
|
||||
public void onMainActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Activity#onRequestPermissionsResult(int, String[], int[])
|
||||
*/
|
||||
public void onMainRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Activity#onPause()
|
||||
*/
|
||||
public void onMainPause() {}
|
||||
|
||||
/**
|
||||
* @see Activity#onResume()
|
||||
*/
|
||||
public void onMainResume() {}
|
||||
|
||||
/**
|
||||
* @see Activity#onDestroy()
|
||||
*/
|
||||
public void onMainDestroy() {}
|
||||
|
||||
/**
|
||||
* @see Activity#onBackPressed()
|
||||
*/
|
||||
public boolean onMainBackPressed() { return false; }
|
||||
|
||||
/**
|
||||
* Invoked on the GL thread when the Godot main loop has started.
|
||||
*/
|
||||
public void onGLGodotMainLoopStarted() {}
|
||||
|
||||
/**
|
||||
* Invoked once per frame on the GL thread after the frame is drawn.
|
||||
*/
|
||||
public void onGLDrawFrame(GL10 gl) {}
|
||||
|
||||
/**
|
||||
* Called on the GL thread after the surface is created and whenever the OpenGL ES surface size
|
||||
* changes.
|
||||
*/
|
||||
public void onGLSurfaceChanged(GL10 gl, int width, int height) {}
|
||||
|
||||
/**
|
||||
* Called on the GL thread when the surface is created or recreated.
|
||||
*/
|
||||
public void onGLSurfaceCreated(GL10 gl, EGLConfig config) {}
|
||||
|
||||
/**
|
||||
* Returns the name of the plugin.
|
||||
* <p>
|
||||
* This value must match the one listed in the plugin '<meta-data>' manifest entry.
|
||||
*/
|
||||
@NonNull
|
||||
public abstract String getPluginName();
|
||||
|
||||
/**
|
||||
* Returns the list of methods to be exposed to Godot.
|
||||
*/
|
||||
@NonNull
|
||||
public abstract List<String> getPluginMethods();
|
||||
|
||||
/**
|
||||
* Returns the paths for the plugin's gdnative libraries.
|
||||
*
|
||||
* The paths must be relative to the 'assets' directory and point to a '*.gdnlib' file.
|
||||
*/
|
||||
@NonNull
|
||||
protected Set<String> getPluginGDNativeLibrariesPaths() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the specified action on the UI thread. If the current thread is the UI
|
||||
* thread, then the action is executed immediately. If the current thread is
|
||||
* not the UI thread, the action is posted to the event queue of the UI thread.
|
||||
*
|
||||
* @param action the action to run on the UI thread
|
||||
*/
|
||||
protected void runOnUiThread(Runnable action) {
|
||||
godot.runOnUiThread(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue the specified action to be run on the GL thread.
|
||||
*
|
||||
* @param action the action to run on the GL thread
|
||||
*/
|
||||
protected void runOnGLThread(Runnable action) {
|
||||
godot.runOnGLThread(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to setup a {@link GodotPlugin} instance.
|
||||
* @param p_name Name of the instance.
|
||||
*/
|
||||
public static native void nativeRegisterSingleton(String p_name, Object object);
|
||||
|
||||
/**
|
||||
* Used to complete registration of the {@link GodotPlugin} instance's methods.
|
||||
* @param p_sname Name of the instance
|
||||
* @param p_name Name of the method to register
|
||||
* @param p_ret Return type of the registered method
|
||||
* @param p_params Method parameters types
|
||||
*/
|
||||
public static native void nativeRegisterMethod(String p_sname, String p_name, String p_ret, String[] p_params);
|
||||
|
||||
/**
|
||||
* Used to register gdnative libraries bundled by the plugin.
|
||||
* @param gdnlibPaths Paths to the libraries relative to the 'assets' directory.
|
||||
*/
|
||||
private native void nativeRegisterGDNativeLibraries(String[] gdnlibPaths);
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
/*************************************************************************/
|
||||
/* GodotPluginRegistry.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.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.godotengine.godot.Godot;
|
||||
|
||||
/**
|
||||
* Registry used to load and access the registered Godot Android plugins.
|
||||
*/
|
||||
public final class GodotPluginRegistry {
|
||||
|
||||
private static final String TAG = GodotPluginRegistry.class.getSimpleName();
|
||||
|
||||
private static final String GODOT_PLUGIN_V1_NAME_PREFIX = "org.godotengine.plugin.v1.";
|
||||
|
||||
/**
|
||||
* Name for the metadata containing the list of Godot plugins to enable.
|
||||
*/
|
||||
private static final String GODOT_ENABLED_PLUGINS_LABEL = "custom_template_plugins";
|
||||
|
||||
private static GodotPluginRegistry instance;
|
||||
private final ConcurrentHashMap<String, GodotPlugin> registry;
|
||||
|
||||
private GodotPluginRegistry(Godot godot) {
|
||||
registry = new ConcurrentHashMap<>();
|
||||
loadPlugins(godot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the plugin tied to the given plugin name.
|
||||
* @param pluginName Name of the plugin
|
||||
* @return {@link GodotPlugin} handle if it exists, null otherwise.
|
||||
*/
|
||||
@Nullable
|
||||
public GodotPlugin getPlugin(String pluginName) {
|
||||
return registry.get(pluginName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the full set of loaded plugins.
|
||||
*/
|
||||
public Collection<GodotPlugin> getAllPlugins() {
|
||||
return registry.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the manifest file and load all included Godot Android plugins.
|
||||
* <p>
|
||||
* A plugin manifest entry is a '<meta-data>' tag setup as described in the {@link GodotPlugin}
|
||||
* documentation.
|
||||
*
|
||||
* @param godot Godot instance
|
||||
* @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) {
|
||||
if (instance == null) {
|
||||
instance = new GodotPluginRegistry(godot);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public static GodotPluginRegistry getPluginRegistry() throws IllegalStateException {
|
||||
if (instance == null) {
|
||||
throw new IllegalStateException("Plugin registry hasn't been initialized.");
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
private void loadPlugins(Godot godot) {
|
||||
try {
|
||||
ApplicationInfo appInfo = godot
|
||||
.getPackageManager()
|
||||
.getApplicationInfo(godot.getPackageName(), PackageManager.GET_META_DATA);
|
||||
Bundle metaData = appInfo.metaData;
|
||||
if (metaData == null || metaData.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// When using the Godot editor for building and exporting the apk, this is used to check
|
||||
// which plugins to enable since the custom build template may contain prebuilt plugins.
|
||||
// When using a custom process to generate the apk, the metadata is not needed since
|
||||
// it's assumed that the developer is aware of the dependencies included in the apk.
|
||||
final Set<String> enabledPluginsSet;
|
||||
if (metaData.containsKey(GODOT_ENABLED_PLUGINS_LABEL)) {
|
||||
String enabledPlugins = metaData.getString(GODOT_ENABLED_PLUGINS_LABEL, "");
|
||||
String[] enabledPluginsList = enabledPlugins.split(",");
|
||||
if (enabledPluginsList.length == 0) {
|
||||
// No plugins to enable. Aborting early.
|
||||
return;
|
||||
}
|
||||
|
||||
enabledPluginsSet = new HashSet<>();
|
||||
for (String enabledPlugin : enabledPluginsList) {
|
||||
enabledPluginsSet.add(enabledPlugin.trim());
|
||||
}
|
||||
} else {
|
||||
enabledPluginsSet = null;
|
||||
}
|
||||
|
||||
int godotPluginV1NamePrefixLength = GODOT_PLUGIN_V1_NAME_PREFIX.length();
|
||||
for (String metaDataName : metaData.keySet()) {
|
||||
// Parse the meta-data looking for entry with the Godot plugin name prefix.
|
||||
if (metaDataName.startsWith(GODOT_PLUGIN_V1_NAME_PREFIX)) {
|
||||
String pluginName = metaDataName.substring(godotPluginV1NamePrefixLength).trim();
|
||||
if (enabledPluginsSet != null && !enabledPluginsSet.contains(pluginName)) {
|
||||
Log.w(TAG, "Plugin " + pluginName + " is listed in the dependencies but is not enabled.");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Retrieve the plugin class full name.
|
||||
String pluginHandleClassFullName = metaData.getString(metaDataName);
|
||||
if (!TextUtils.isEmpty(pluginHandleClassFullName)) {
|
||||
try {
|
||||
// Attempt to create the plugin init class via reflection.
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<GodotPlugin> pluginClass = (Class<GodotPlugin>)Class
|
||||
.forName(pluginHandleClassFullName);
|
||||
Constructor<GodotPlugin> pluginConstructor = pluginClass
|
||||
.getConstructor(Godot.class);
|
||||
GodotPlugin pluginHandle = pluginConstructor.newInstance(godot);
|
||||
|
||||
// Load the plugin initializer into the registry using the plugin name
|
||||
// as key.
|
||||
if (!pluginName.equals(pluginHandle.getPluginName())) {
|
||||
Log.w(TAG,
|
||||
"Meta-data plugin name does not match the value returned by the plugin handle: " + pluginName + " =/= " + pluginHandle.getPluginName());
|
||||
}
|
||||
registry.put(pluginName, pluginHandle);
|
||||
} catch (ClassNotFoundException e) {
|
||||
Log.w(TAG, "Unable to load Godot plugin " + pluginName, e);
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.w(TAG, "Unable to load Godot plugin " + pluginName, e);
|
||||
} catch (InstantiationException e) {
|
||||
Log.w(TAG, "Unable to load Godot plugin " + pluginName, e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
Log.w(TAG, "Unable to load Godot plugin " + pluginName, e);
|
||||
} catch (InvocationTargetException e) {
|
||||
Log.w(TAG, "Unable to load Godot plugin " + pluginName, e);
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "Invalid plugin loader class for " + pluginName);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.e(TAG, "Unable load Godot Android plugins from the manifest file.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user