summaryrefslogtreecommitdiff
path: root/platform/android/java/lib/src
diff options
context:
space:
mode:
Diffstat (limited to 'platform/android/java/lib/src')
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java201
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotLib.java18
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotPaymentV3.java230
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java20
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotView.java3
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java6
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java24
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/payments/ConsumeTask.java116
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/payments/HandlePurchaseTask.java93
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/payments/PaymentsManager.java419
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/payments/PurchaseTask.java118
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/payments/ReleaseAllConsumablesTask.java141
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/payments/ValidateTask.java142
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java256
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java199
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java1
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt (renamed from platform/android/java/lib/src/org/godotengine/godot/payments/PaymentsCache.java)85
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt136
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt230
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java15
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java15
22 files changed, 987 insertions, 1483 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 2f6a93fbb1..4e605f9950 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
@@ -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;
@@ -87,22 +90,19 @@ import com.google.android.vending.expansion.downloader.IStub;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
-import java.lang.reflect.Method;
import java.security.MessageDigest;
-import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
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;
private TextView mStatusText;
private TextView mProgressFraction;
@@ -126,8 +126,7 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
private boolean activityResumed;
private int mState;
- // Used to dispatch events to the main thread.
- private final Handler mainThreadHandler = new Handler(Looper.getMainLooper());
+ private GodotPluginRegistry pluginRegistry;
static private Intent mCurrentIntent;
@@ -154,83 +153,6 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
mPauseButton.setText(stringResourceID);
}
- static public class SingletonBase {
-
- protected void registerClass(String p_name, String[] p_methods) {
-
- GodotLib.singleton(p_name, this);
-
- Class clazz = getClass();
- Method[] methods = clazz.getDeclaredMethods();
- for (Method method : methods) {
- boolean found = false;
-
- for (String s : p_methods) {
- 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);
-
- GodotLib.method(p_name, method.getName(), method.getReturnType().getName(), pt);
- }
-
- Godot.singletons[Godot.singleton_count++] = this;
- }
-
- /**
- * Invoked once during the Godot Android initialization process after creation of the
- * {@link 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
- protected View onMainCreateView(Activity activity) {
- return null;
- }
-
- protected void onMainActivityResult(int requestCode, int resultCode, Intent data) {
- }
-
- protected void onMainRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
- }
-
- protected void onMainPause() {}
- protected void onMainResume() {}
- protected void onMainDestroy() {}
- protected boolean onMainBackPressed() { return false; }
-
- protected void onGLDrawFrame(GL10 gl) {}
- protected void onGLSurfaceChanged(GL10 gl, int width, int height) {} // singletons will always miss first onGLSurfaceChanged call
- //protected void onGLSurfaceCreated(GL10 gl, EGLConfig config) {} // singletons won't be ready until first GodotLib.step()
-
- public void registerMethods() {}
- }
-
- /*
- protected List<SingletonBase> singletons = new ArrayList<SingletonBase>();
- protected void instanceSingleton(SingletonBase s) {
-
- s.registerMethods();
- singletons.add(s);
- }
- */
-
private String[] command_line;
private boolean use_apk_expansion;
@@ -246,35 +168,27 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
public static GodotIO io;
public static GodotNetUtils netUtils;
- static SingletonBase[] singletons = new SingletonBase[MAX_SINGLETONS];
- static int singleton_count = 0;
-
public interface ResultCallback {
public void callback(int requestCode, int resultCode, Intent data);
}
public ResultCallback result_callback;
- private PaymentsManager mPaymentsManager = null;
-
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (requestCode == PaymentsManager.REQUEST_CODE_FOR_PURCHASE) {
- mPaymentsManager.processPurchaseResponse(resultCode, data);
- } else if (result_callback != null) {
+ if (result_callback != null) {
result_callback.callback(requestCode, resultCode, data);
result_callback = null;
- };
-
- for (int i = 0; i < singleton_count; i++) {
+ }
- singletons[i].onMainActivityResult(requestCode, resultCode, data);
+ for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
+ plugin.onMainActivityResult(requestCode, resultCode, data);
}
- };
+ }
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
- 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++) {
@@ -283,12 +197,20 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
};
/**
+ * 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.
*/
@Keep
private void onVideoInit() {
- boolean use_gl3 = getGLESVersionCode() >= 0x00030000;
-
final FrameLayout layout = new FrameLayout(this);
layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
setContentView(layout);
@@ -299,19 +221,18 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
// ...add to FrameLayout
layout.addView(edittext);
- mView = new GodotView(this, xrMode, use_gl3, use_32_bits, use_debug_opengl);
+ mView = new GodotView(this, xrMode, use_32_bits, use_debug_opengl);
layout.addView(mView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
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,23 +244,23 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
@Override
public void run() {
GodotLib.setup(current_command_line);
- setKeepScreenOn("True".equals(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on")));
- // The Godot Android plugins are setup on completion of GodotLib.setup
- mainThreadHandler.post(new Runnable() {
- @Override
- public void run() {
- // Include the non-null views returned in the Godot view hierarchy.
- for (int i = 0; i < singleton_count; i++) {
- View view = singletons[i].onMainCreateView(Godot.this);
- if (view != null) {
- layout.addView(view);
- }
- }
- }
- });
+ // 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")));
}
});
+
+ // 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) {
@@ -519,8 +440,6 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
result_callback = null;
- mPaymentsManager = PaymentsManager.createManager(this).initService();
-
godot_initialized = true;
}
@@ -537,6 +456,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) {
@@ -676,9 +596,8 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
@Override
protected void onDestroy() {
- if (mPaymentsManager != null) mPaymentsManager.destroy();
- for (int i = 0; i < singleton_count; i++) {
- singletons[i].onMainDestroy();
+ for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
+ plugin.onMainDestroy();
}
GodotLib.ondestroy(this);
@@ -705,8 +624,8 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
mSensorManager.unregisterListener(this);
- for (int i = 0; i < singleton_count; i++) {
- singletons[i].onMainPause();
+ for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
+ plugin.onMainPause();
}
}
@@ -757,9 +676,8 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
}
- for (int i = 0; i < singleton_count; i++) {
-
- singletons[i].onMainResume();
+ for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
+ plugin.onMainResume();
}
}
@@ -852,8 +770,8 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
public void onBackPressed() {
boolean shouldQuit = true;
- for (int i = 0; i < singleton_count; i++) {
- if (singletons[i].onMainBackPressed()) {
+ for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
+ if (plugin.onMainBackPressed()) {
shouldQuit = false;
}
}
@@ -868,6 +786,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);
}
@@ -992,8 +921,8 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
int keyCode;
if ((keyCode = cc[i]) != 0) {
// Simulate key down and up...
- GodotLib.key(0, keyCode, true);
- GodotLib.key(0, keyCode, false);
+ GodotLib.key(0, 0, keyCode, true);
+ GodotLib.key(0, 0, keyCode, false);
}
}
}
@@ -1001,10 +930,6 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
return true;
}
- public PaymentsManager getPaymentsManager() {
- return mPaymentsManager;
- }
-
public boolean requestPermission(String p_name) {
return PermissionsUtil.requestPermission(p_name, this);
}
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 e0b46673ba..89a65aea24 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
@@ -136,7 +136,7 @@ public class GodotLib {
/**
* Forward regular key events from the main thread to the GL thread.
*/
- public static native void key(int p_scancode, int p_unicode_char, boolean p_pressed);
+ public static native void key(int p_keycode, int p_scancode, int p_unicode_char, boolean p_pressed);
/**
* Forward game device's key events from the main thread to the GL thread.
@@ -176,22 +176,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
* @return String value of the property
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotPaymentV3.java b/platform/android/java/lib/src/org/godotengine/godot/GodotPaymentV3.java
deleted file mode 100644
index 93265d509f..0000000000
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotPaymentV3.java
+++ /dev/null
@@ -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 });
- }
-}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java
index 26fa033f12..ee9a2aee4f 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java
@@ -30,9 +30,12 @@
package org.godotengine.godot;
+import android.content.Context;
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 +43,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();
@@ -49,21 +57,23 @@ class GodotRenderer implements GLSurfaceView.Renderer {
}
GodotLib.step();
- 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() {
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotView.java
index f938583082..8d3c2ae319 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotView.java
@@ -73,9 +73,8 @@ public class GodotView extends GLSurfaceView {
private final GestureDetector detector;
private final GodotRenderer godotRenderer;
- public GodotView(Godot activity, XRMode xrMode, boolean p_use_gl3, boolean p_use_32_bits, boolean p_use_debug_opengl) {
+ public GodotView(Godot activity, XRMode xrMode, boolean p_use_32_bits, boolean p_use_debug_opengl) {
super(activity);
- GLUtils.use_gl3 = p_use_gl3;
GLUtils.use_32 = p_use_32_bits;
GLUtils.use_debug_opengl = p_use_debug_opengl;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java
index 44998aa6c0..e901b4b36d 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java
@@ -123,7 +123,7 @@ public class GodotEditText extends EditText {
}
private void setMaxInputLength(EditText p_edit_text, int p_max_input_length) {
- if (p_max_input_length >= 0) {
+ if (p_max_input_length > 0) {
InputFilter[] filters = new InputFilter[1];
filters[0] = new InputFilter.LengthFilter(p_max_input_length);
p_edit_text.setFilters(filters);
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 b2b88088e8..e00ca86c41 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
@@ -98,11 +98,12 @@ public class GodotInputHandler implements InputDeviceListener {
});
}
} else {
+ final int scanCode = event.getScanCode();
final int chr = event.getUnicodeChar(0);
queueEvent(new Runnable() {
@Override
public void run() {
- GodotLib.key(keyCode, chr, false);
+ GodotLib.key(keyCode, scanCode, chr, false);
}
});
};
@@ -143,11 +144,12 @@ public class GodotInputHandler implements InputDeviceListener {
});
}
} else {
+ final int scanCode = event.getScanCode();
final int chr = event.getUnicodeChar(0);
queueEvent(new Runnable() {
@Override
public void run() {
- GodotLib.key(keyCode, chr, true);
+ GodotLib.key(keyCode, scanCode, chr, true);
}
});
};
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java
index 3a154f1bf3..18f2d57661 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java
@@ -91,8 +91,8 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
@Override
public void run() {
for (int i = 0; i < count; ++i) {
- GodotLib.key(KeyEvent.KEYCODE_DEL, 0, true);
- GodotLib.key(KeyEvent.KEYCODE_DEL, 0, false);
+ GodotLib.key(KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL, 0, true);
+ GodotLib.key(KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL, 0, false);
}
}
});
@@ -110,8 +110,13 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
@Override
public void run() {
for (int i = 0; i < count; ++i) {
- GodotLib.key(0, newChars[i], true);
- GodotLib.key(0, newChars[i], false);
+ int key = newChars[i];
+ if (key == '\n') {
+ // Return keys are handled through action events
+ continue;
+ }
+ GodotLib.key(0, 0, key, true);
+ GodotLib.key(0, 0, key, false);
}
}
});
@@ -127,15 +132,20 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
public void run() {
for (int i = 0; i < characters.length(); i++) {
final int ch = characters.codePointAt(i);
- GodotLib.key(0, ch, true);
- GodotLib.key(0, ch, false);
+ GodotLib.key(0, 0, ch, true);
+ GodotLib.key(0, 0, ch, false);
}
}
});
}
- if (pActionID == EditorInfo.IME_ACTION_DONE) {
+ if (pActionID == EditorInfo.IME_NULL) {
+ // Enter key has been pressed
+ GodotLib.key(KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_ENTER, 0, true);
+ GodotLib.key(KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_ENTER, 0, false);
+
this.mView.requestFocus();
+ return true;
}
return false;
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/ConsumeTask.java b/platform/android/java/lib/src/org/godotengine/godot/payments/ConsumeTask.java
deleted file mode 100644
index 95cc48f536..0000000000
--- a/platform/android/java/lib/src/org/godotengine/godot/payments/ConsumeTask.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*************************************************************************/
-/* ConsumeTask.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.payments;
-
-import android.content.Context;
-import android.os.AsyncTask;
-import android.os.RemoteException;
-import com.android.vending.billing.IInAppBillingService;
-import java.lang.ref.WeakReference;
-
-abstract public class ConsumeTask {
-
- private Context context;
- private IInAppBillingService mService;
-
- private String mSku;
- private String mToken;
-
- private static class ConsumeAsyncTask extends AsyncTask<String, String, String> {
-
- private WeakReference<ConsumeTask> mTask;
-
- ConsumeAsyncTask(ConsumeTask consume) {
- mTask = new WeakReference<>(consume);
- }
-
- @Override
- protected String doInBackground(String... strings) {
- ConsumeTask consume = mTask.get();
- if (consume != null) {
- return consume.doInBackground(strings);
- }
- return null;
- }
-
- @Override
- protected void onPostExecute(String param) {
- ConsumeTask consume = mTask.get();
- if (consume != null) {
- consume.onPostExecute(param);
- }
- }
- }
-
- public ConsumeTask(IInAppBillingService mService, Context context) {
- this.context = context;
- this.mService = mService;
- }
-
- public void consume(final String sku) {
- mSku = sku;
- PaymentsCache pc = new PaymentsCache(context);
- Boolean isBlocked = pc.getConsumableFlag("block", sku);
- mToken = pc.getConsumableValue("token", sku);
- if (!isBlocked && mToken == null) {
- // Consuming task is processing
- } else if (!isBlocked) {
- return;
- } else if (mToken == null) {
- this.error("No token for sku:" + sku);
- return;
- }
- new ConsumeAsyncTask(this).execute();
- }
-
- private String doInBackground(String... params) {
- try {
- int response = mService.consumePurchase(3, context.getPackageName(), mToken);
- if (response == 0 || response == 8) {
- return null;
- }
- } catch (RemoteException e) {
- return e.getMessage();
- }
- return "Some error";
- }
-
- private void onPostExecute(String param) {
- if (param == null) {
- success(new PaymentsCache(context).getConsumableValue("ticket", mSku));
- } else {
- error(param);
- }
- }
-
- abstract protected void success(String ticket);
- abstract protected void error(String message);
-}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/HandlePurchaseTask.java b/platform/android/java/lib/src/org/godotengine/godot/payments/HandlePurchaseTask.java
deleted file mode 100644
index 23d693cc8c..0000000000
--- a/platform/android/java/lib/src/org/godotengine/godot/payments/HandlePurchaseTask.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*************************************************************************/
-/* HandlePurchaseTask.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.payments;
-
-import android.app.Activity;
-import android.content.Intent;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-abstract public class HandlePurchaseTask {
-
- private Activity context;
-
- public HandlePurchaseTask(Activity context) {
- this.context = context;
- }
-
- public void handlePurchaseRequest(int resultCode, Intent data) {
- //Log.d("XXX", "Handling purchase response");
- if (resultCode == Activity.RESULT_OK) {
- try {
- //int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
- PaymentsCache pc = new PaymentsCache(context);
-
- String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
- //Log.d("XXX", "Purchase data:" + purchaseData);
- String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
- //Log.d("XXX", "Purchase signature:" + dataSignature);
- //Log.d("SARLANGA", purchaseData);
-
- JSONObject jo = new JSONObject(purchaseData);
- //String sku = jo.getString("productId");
- //alert("You have bought the " + sku + ". Excellent choice, aventurer!");
- //String orderId = jo.getString("orderId");
- //String packageName = jo.getString("packageName");
- String productId = jo.getString("productId");
- //Long purchaseTime = jo.getLong("purchaseTime");
- //Integer state = jo.getInt("purchaseState");
- String developerPayload = jo.getString("developerPayload");
- String purchaseToken = jo.getString("purchaseToken");
-
- if (!pc.getConsumableValue("validation_hash", productId).equals(developerPayload)) {
- error("Untrusted callback");
- return;
- }
- //Log.d("XXX", "Este es el product ID:" + productId);
- pc.setConsumableValue("ticket_signautre", productId, dataSignature);
- pc.setConsumableValue("ticket", productId, purchaseData);
- pc.setConsumableFlag("block", productId, true);
- pc.setConsumableValue("token", productId, purchaseToken);
-
- success(productId, dataSignature, purchaseData);
- return;
- } catch (JSONException e) {
- error(e.getMessage());
- }
- } else if (resultCode == Activity.RESULT_CANCELED) {
- canceled();
- }
- }
-
- abstract protected void success(String sku, String signature, String ticket);
- abstract protected void error(String message);
- abstract protected void canceled();
-}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/PaymentsManager.java b/platform/android/java/lib/src/org/godotengine/godot/payments/PaymentsManager.java
deleted file mode 100644
index 90b958266b..0000000000
--- a/platform/android/java/lib/src/org/godotengine/godot/payments/PaymentsManager.java
+++ /dev/null
@@ -1,419 +0,0 @@
-/*************************************************************************/
-/* PaymentsManager.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.payments;
-
-import android.app.Activity;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.text.TextUtils;
-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;
-
-public class PaymentsManager {
-
- public static final int BILLING_RESPONSE_RESULT_OK = 0;
- public static final int REQUEST_CODE_FOR_PURCHASE = 0x1001;
- private static boolean auto_consume = true;
-
- private Activity activity;
- IInAppBillingService mService;
-
- public void setActivity(Activity activity) {
- this.activity = activity;
- }
-
- public static PaymentsManager createManager(Activity activity) {
- PaymentsManager manager = new PaymentsManager(activity);
- return manager;
- }
-
- private PaymentsManager(Activity activity) {
- this.activity = activity;
- }
-
- public PaymentsManager initService() {
- Intent intent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
- intent.setPackage("com.android.vending");
- activity.bindService(
- intent,
- mServiceConn,
- Context.BIND_AUTO_CREATE);
- return this;
- }
-
- public void destroy() {
- if (mService != null) {
- activity.unbindService(mServiceConn);
- }
- }
-
- ServiceConnection mServiceConn = new ServiceConnection() {
- @Override
- public void onServiceDisconnected(ComponentName name) {
- mService = null;
-
- // At this stage, godotPaymentV3 might not have been initialized yet.
- if (godotPaymentV3 != null) {
- godotPaymentV3.callbackDisconnected();
- }
- }
-
- @Override
- 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();
- }
- }
- };
-
- public void requestPurchase(final String sku, String transactionId) {
- new PurchaseTask(mService, activity) {
- @Override
- protected void error(String message) {
- godotPaymentV3.callbackFail(message);
- }
-
- @Override
- protected void canceled() {
- godotPaymentV3.callbackCancel();
- }
-
- @Override
- protected void alreadyOwned() {
- godotPaymentV3.callbackAlreadyOwned(sku);
- }
- }
- .purchase(sku, transactionId);
- }
-
- public boolean isConnected() {
- return mService != null;
- }
-
- public void consumeUnconsumedPurchases() {
- new ReleaseAllConsumablesTask(mService, activity) {
- @Override
- protected void success(String sku, String receipt, String signature, String token) {
- godotPaymentV3.callbackSuccessProductMassConsumed(receipt, signature, sku);
- }
-
- @Override
- protected void error(String message) {
- Log.d("godot", "consumeUnconsumedPurchases :" + message);
- godotPaymentV3.callbackFailConsume(message);
- }
-
- @Override
- protected void notRequired() {
- Log.d("godot", "callbackSuccessNoUnconsumedPurchases :");
- godotPaymentV3.callbackSuccessNoUnconsumedPurchases();
- }
- }
- .consumeItAll();
- }
-
- public void requestPurchased() {
- try {
- PaymentsCache pc = new PaymentsCache(activity);
-
- String continueToken = null;
-
- do {
- Bundle bundle = mService.getPurchases(3, activity.getPackageName(), "inapp", continueToken);
-
- if (bundle.getInt("RESPONSE_CODE") == 0) {
-
- final ArrayList<String> myPurchases = bundle.getStringArrayList("INAPP_PURCHASE_DATA_LIST");
- final ArrayList<String> mySignatures = bundle.getStringArrayList("INAPP_DATA_SIGNATURE_LIST");
-
- if (myPurchases == null || myPurchases.size() == 0) {
- godotPaymentV3.callbackPurchased("", "", "");
- return;
- }
-
- for (int i = 0; i < myPurchases.size(); i++) {
-
- try {
- String receipt = myPurchases.get(i);
- JSONObject inappPurchaseData = new JSONObject(receipt);
- String sku = inappPurchaseData.getString("productId");
- String token = inappPurchaseData.getString("purchaseToken");
- String signature = mySignatures.get(i);
-
- pc.setConsumableValue("ticket_signautre", sku, signature);
- pc.setConsumableValue("ticket", sku, receipt);
- pc.setConsumableFlag("block", sku, true);
- pc.setConsumableValue("token", sku, token);
-
- godotPaymentV3.callbackPurchased(receipt, signature, sku);
- } catch (JSONException e) {
- }
- }
- }
- continueToken = bundle.getString("INAPP_CONTINUATION_TOKEN");
- Log.d("godot", "continue token = " + continueToken);
- } while (!TextUtils.isEmpty(continueToken));
- } catch (Exception e) {
- Log.d("godot", "Error requesting purchased products:" + e.getClass().getName() + ":" + e.getMessage());
- }
- }
-
- public void processPurchaseResponse(int resultCode, Intent data) {
- new HandlePurchaseTask(activity) {
- @Override
- protected void success(final String sku, final String signature, final String ticket) {
- godotPaymentV3.callbackSuccess(ticket, signature, sku);
-
- if (auto_consume) {
- new ConsumeTask(mService, activity) {
- @Override
- protected void success(String ticket) {
- }
-
- @Override
- protected void error(String message) {
- godotPaymentV3.callbackFail(message);
- }
- }
- .consume(sku);
- }
- }
-
- @Override
- protected void error(String message) {
- godotPaymentV3.callbackFail(message);
- }
-
- @Override
- protected void canceled() {
- godotPaymentV3.callbackCancel();
- }
- }
- .handlePurchaseRequest(resultCode, data);
- }
-
- public void validatePurchase(String purchaseToken, final String sku) {
-
- new ValidateTask(activity, godotPaymentV3) {
- @Override
- protected void success() {
-
- new ConsumeTask(mService, activity) {
- @Override
- protected void success(String ticket) {
- godotPaymentV3.callbackSuccess(ticket, null, sku);
- }
-
- @Override
- protected void error(String message) {
- godotPaymentV3.callbackFail(message);
- }
- }
- .consume(sku);
- }
-
- @Override
- protected void error(String message) {
- godotPaymentV3.callbackFail(message);
- }
-
- @Override
- protected void canceled() {
- godotPaymentV3.callbackCancel();
- }
- }
- .validatePurchase(sku);
- }
-
- public void setAutoConsume(boolean autoConsume) {
- auto_consume = autoConsume;
- }
-
- public void consume(final String sku) {
- new ConsumeTask(mService, activity) {
- @Override
- protected void success(String ticket) {
- godotPaymentV3.callbackSuccessProductMassConsumed(ticket, "", sku);
- }
-
- @Override
- protected void error(String message) {
- godotPaymentV3.callbackFailConsume(message);
- }
- }
- .consume(sku);
- }
-
- // Workaround to bug where sometimes response codes come as Long instead of Integer
- int getResponseCodeFromBundle(Bundle b) {
- Object o = b.get("RESPONSE_CODE");
- if (o == null) {
- //logDebug("Bundle with null response code, assuming OK (known issue)");
- return BILLING_RESPONSE_RESULT_OK;
- } else if (o instanceof Integer)
- return ((Integer)o).intValue();
- else if (o instanceof Long)
- return (int)((Long)o).longValue();
- else {
- //logError("Unexpected type for bundle response code.");
- //logError(o.getClass().getName());
- throw new RuntimeException("Unexpected type for bundle response code: " + o.getClass().getName());
- }
- }
-
- /**
- * Returns a human-readable description for the given response code.
- *
- * @param code The response code
- * @return A human-readable string explaining the result code.
- * It also includes the result code numerically.
- */
- public static String getResponseDesc(int code) {
- String[] iab_msgs = ("0:OK/1:User Canceled/2:Unknown/"
- +
- "3:Billing Unavailable/4:Item unavailable/"
- +
- "5:Developer Error/6:Error/7:Item Already Owned/"
- +
- "8:Item not owned")
- .split("/");
- String[] iabhelper_msgs = ("0:OK/-1001:Remote exception during initialization/"
- +
- "-1002:Bad response received/"
- +
- "-1003:Purchase signature verification failed/"
- +
- "-1004:Send intent failed/"
- +
- "-1005:User cancelled/"
- +
- "-1006:Unknown purchase response/"
- +
- "-1007:Missing token/"
- +
- "-1008:Unknown error/"
- +
- "-1009:Subscriptions not available/"
- +
- "-1010:Invalid consumption attempt")
- .split("/");
-
- if (code <= -1000) {
- int index = -1000 - code;
- if (index >= 0 && index < iabhelper_msgs.length)
- return iabhelper_msgs[index];
- else
- return String.valueOf(code) + ":Unknown IAB Helper Error";
- } else if (code < 0 || code >= iab_msgs.length)
- return String.valueOf(code) + ":Unknown";
- else
- return iab_msgs[code];
- }
-
- public void querySkuDetails(final String[] list) {
- (new Thread(new Runnable() {
- @Override
- public void run() {
- ArrayList<String> skuList = new ArrayList<String>(Arrays.asList(list));
- if (skuList.size() == 0) {
- return;
- }
- // Split the sku list in blocks of no more than 20 elements.
- ArrayList<ArrayList<String>> packs = new ArrayList<ArrayList<String>>();
- ArrayList<String> tempList;
- int n = skuList.size() / 20;
- int mod = skuList.size() % 20;
- for (int i = 0; i < n; i++) {
- tempList = new ArrayList<String>();
- for (String s : skuList.subList(i * 20, i * 20 + 20)) {
- tempList.add(s);
- }
- packs.add(tempList);
- }
- if (mod != 0) {
- tempList = new ArrayList<String>();
- for (String s : skuList.subList(n * 20, n * 20 + mod)) {
- tempList.add(s);
- }
- packs.add(tempList);
- }
- for (ArrayList<String> skuPartList : packs) {
- Bundle querySkus = new Bundle();
- querySkus.putStringArrayList("ITEM_ID_LIST", skuPartList);
- Bundle skuDetails = null;
- try {
- skuDetails = mService.getSkuDetails(3, activity.getPackageName(), "inapp", querySkus);
- if (!skuDetails.containsKey("DETAILS_LIST")) {
- int response = getResponseCodeFromBundle(skuDetails);
- if (response != BILLING_RESPONSE_RESULT_OK) {
- godotPaymentV3.errorSkuDetail(getResponseDesc(response));
- } else {
- godotPaymentV3.errorSkuDetail("No error but no detail list.");
- }
- return;
- }
-
- ArrayList<String> responseList = skuDetails.getStringArrayList("DETAILS_LIST");
-
- for (String thisResponse : responseList) {
- Log.d("godot", "response = " + thisResponse);
- godotPaymentV3.addSkuDetail(thisResponse);
- }
- } catch (RemoteException e) {
- e.printStackTrace();
- godotPaymentV3.errorSkuDetail("RemoteException error!");
- }
- }
- godotPaymentV3.completeSkuDetail();
- }
- }))
- .start();
- }
-
- private GodotPaymentV3 godotPaymentV3;
-
- public void setBaseSingleton(GodotPaymentV3 godotPaymentV3) {
- this.godotPaymentV3 = godotPaymentV3;
- }
-}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/PurchaseTask.java b/platform/android/java/lib/src/org/godotengine/godot/payments/PurchaseTask.java
deleted file mode 100644
index 09c9349124..0000000000
--- a/platform/android/java/lib/src/org/godotengine/godot/payments/PurchaseTask.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*************************************************************************/
-/* PurchaseTask.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.payments;
-
-import android.app.Activity;
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.content.IntentSender.SendIntentException;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.util.Log;
-import com.android.vending.billing.IInAppBillingService;
-
-abstract public class PurchaseTask {
-
- private Activity context;
-
- private IInAppBillingService mService;
- public PurchaseTask(IInAppBillingService mService, Activity context) {
- this.context = context;
- this.mService = mService;
- }
-
- private boolean isLooping = false;
-
- public void purchase(final String sku, final String transactionId) {
- Log.d("XXX", "Starting purchase for: " + sku);
- PaymentsCache pc = new PaymentsCache(context);
- Boolean isBlocked = pc.getConsumableFlag("block", sku);
- /*
- if(isBlocked){
- Log.d("XXX", "Is awaiting payment confirmation");
- error("Awaiting payment confirmation");
- return;
- }
- */
- final String hash = transactionId;
-
- Bundle buyIntentBundle;
- try {
- buyIntentBundle = mService.getBuyIntent(3, context.getApplicationContext().getPackageName(), sku, "inapp", hash);
- } catch (RemoteException e) {
- //Log.d("XXX", "Error: " + e.getMessage());
- error(e.getMessage());
- return;
- }
- Object rc = buyIntentBundle.get("RESPONSE_CODE");
- int responseCode = 0;
- if (rc == null) {
- responseCode = PaymentsManager.BILLING_RESPONSE_RESULT_OK;
- } else if (rc instanceof Integer) {
- responseCode = ((Integer)rc).intValue();
- } else if (rc instanceof Long) {
- responseCode = (int)((Long)rc).longValue();
- }
- //Log.d("XXX", "Buy intent response code: " + responseCode);
- if (responseCode == 1 || responseCode == 3 || responseCode == 4) {
- canceled();
- return;
- }
- if (responseCode == 7) {
- alreadyOwned();
- return;
- }
-
- PendingIntent pendingIntent = buyIntentBundle.getParcelable("BUY_INTENT");
- pc.setConsumableValue("validation_hash", sku, hash);
- try {
- if (context == null) {
- //Log.d("XXX", "No context!");
- }
- if (pendingIntent == null) {
- //Log.d("XXX", "No pending intent");
- }
- //Log.d("XXX", "Starting activity for purchase!");
- context.startIntentSenderForResult(
- pendingIntent.getIntentSender(),
- PaymentsManager.REQUEST_CODE_FOR_PURCHASE,
- new Intent(),
- Integer.valueOf(0), Integer.valueOf(0),
- Integer.valueOf(0));
- } catch (SendIntentException e) {
- error(e.getMessage());
- }
- }
-
- abstract protected void error(String message);
- abstract protected void canceled();
- abstract protected void alreadyOwned();
-}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/ReleaseAllConsumablesTask.java b/platform/android/java/lib/src/org/godotengine/godot/payments/ReleaseAllConsumablesTask.java
deleted file mode 100644
index a101780511..0000000000
--- a/platform/android/java/lib/src/org/godotengine/godot/payments/ReleaseAllConsumablesTask.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*************************************************************************/
-/* ReleaseAllConsumablesTask.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.payments;
-
-import android.content.Context;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.util.Log;
-import com.android.vending.billing.IInAppBillingService;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-abstract public class ReleaseAllConsumablesTask {
-
- private Context context;
- private IInAppBillingService mService;
-
- private static class ReleaseAllConsumablesAsyncTask extends AsyncTask<String, String, String> {
-
- private WeakReference<ReleaseAllConsumablesTask> mTask;
- private String mSku;
- private String mReceipt;
- private String mSignature;
- private String mToken;
-
- ReleaseAllConsumablesAsyncTask(ReleaseAllConsumablesTask task, String sku, String receipt, String signature, String token) {
- mTask = new WeakReference<ReleaseAllConsumablesTask>(task);
-
- mSku = sku;
- mReceipt = receipt;
- mSignature = signature;
- mToken = token;
- }
-
- @Override
- protected String doInBackground(String... params) {
- ReleaseAllConsumablesTask consume = mTask.get();
- if (consume != null) {
- return consume.doInBackground(mToken);
- }
- return null;
- }
-
- @Override
- protected void onPostExecute(String param) {
- ReleaseAllConsumablesTask consume = mTask.get();
- if (consume != null) {
- consume.success(mSku, mReceipt, mSignature, mToken);
- }
- }
- }
-
- public ReleaseAllConsumablesTask(IInAppBillingService mService, Context context) {
- this.context = context;
- this.mService = mService;
- }
-
- public void consumeItAll() {
- try {
- //Log.d("godot", "consumeItall for " + context.getPackageName());
- Bundle bundle = mService.getPurchases(3, context.getPackageName(), "inapp", null);
-
- if (bundle.getInt("RESPONSE_CODE") == 0) {
-
- final ArrayList<String> myPurchases = bundle.getStringArrayList("INAPP_PURCHASE_DATA_LIST");
- final ArrayList<String> mySignatures = bundle.getStringArrayList("INAPP_DATA_SIGNATURE_LIST");
-
- if (myPurchases == null || myPurchases.size() == 0) {
- //Log.d("godot", "No purchases!");
- notRequired();
- return;
- }
-
- //Log.d("godot", "# products to be consumed:" + myPurchases.size());
- for (int i = 0; i < myPurchases.size(); i++) {
-
- try {
- String receipt = myPurchases.get(i);
- JSONObject inappPurchaseData = new JSONObject(receipt);
- String sku = inappPurchaseData.getString("productId");
- String token = inappPurchaseData.getString("purchaseToken");
- String signature = mySignatures.get(i);
- //Log.d("godot", "A punto de consumir un item con token:" + token + "\n" + receipt);
- new ReleaseAllConsumablesAsyncTask(this, sku, receipt, signature, token).execute();
- } catch (JSONException e) {
- }
- }
- }
- } catch (Exception e) {
- Log.d("godot", "Error releasing products:" + e.getClass().getName() + ":" + e.getMessage());
- }
- }
-
- private String doInBackground(String token) {
- try {
- //Log.d("godot", "Requesting to consume an item with token ." + token);
- int response = mService.consumePurchase(3, context.getPackageName(), token);
- //Log.d("godot", "consumePurchase response: " + response);
- if (response == 0 || response == 8) {
- return null;
- }
- } catch (Exception e) {
- Log.d("godot", "Error " + e.getClass().getName() + ":" + e.getMessage());
- }
- return null;
- }
-
- abstract protected void success(String sku, String receipt, String signature, String token);
- abstract protected void error(String message);
- abstract protected void notRequired();
-}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/ValidateTask.java b/platform/android/java/lib/src/org/godotengine/godot/payments/ValidateTask.java
deleted file mode 100644
index dbb6b8a783..0000000000
--- a/platform/android/java/lib/src/org/godotengine/godot/payments/ValidateTask.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*************************************************************************/
-/* ValidateTask.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.payments;
-
-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;
-import org.json.JSONObject;
-
-abstract public class ValidateTask {
-
- private Activity context;
- private GodotPaymentV3 godotPaymentsV3;
- private ProgressDialog dialog;
- private String mSku;
-
- private static class ValidateAsyncTask extends AsyncTask<String, String, String> {
- private WeakReference<ValidateTask> mTask;
-
- ValidateAsyncTask(ValidateTask task) {
- mTask = new WeakReference<>(task);
- }
-
- @Override
- protected void onPreExecute() {
- ValidateTask task = mTask.get();
- if (task != null) {
- task.onPreExecute();
- }
- }
-
- @Override
- protected String doInBackground(String... params) {
- ValidateTask task = mTask.get();
- if (task != null) {
- return task.doInBackground(params);
- }
- return null;
- }
-
- @Override
- protected void onPostExecute(String response) {
- ValidateTask task = mTask.get();
- if (task != null) {
- task.onPostExecute(response);
- }
- }
- }
-
- public ValidateTask(Activity context, GodotPaymentV3 godotPaymentsV3) {
- this.context = context;
- this.godotPaymentsV3 = godotPaymentsV3;
- }
-
- public void validatePurchase(final String sku) {
- mSku = sku;
- new ValidateAsyncTask(this).execute();
- }
-
- private void onPreExecute() {
- dialog = ProgressDialog.show(context, null, "Please wait...");
- }
-
- private String doInBackground(String... params) {
- PaymentsCache pc = new PaymentsCache(context);
- String url = godotPaymentsV3.getPurchaseValidationUrlPrefix();
- RequestParams param = new RequestParams();
- param.setUrl(url);
- param.put("ticket", pc.getConsumableValue("ticket", mSku));
- param.put("purchaseToken", pc.getConsumableValue("token", mSku));
- param.put("sku", mSku);
- //Log.d("XXX", "Haciendo request a " + url);
- //Log.d("XXX", "ticket: " + pc.getConsumableValue("ticket", sku));
- //Log.d("XXX", "purchaseToken: " + pc.getConsumableValue("token", sku));
- //Log.d("XXX", "sku: " + sku);
- param.put("package", context.getApplicationContext().getPackageName());
- HttpRequester requester = new HttpRequester();
- String jsonResponse = requester.post(param);
- //Log.d("XXX", "Validation response:\n"+jsonResponse);
- return jsonResponse;
- }
-
- private void onPostExecute(String response) {
- if (dialog != null) {
- dialog.dismiss();
- dialog = null;
- }
- JSONObject j;
- try {
- j = new JSONObject(response);
- if (j.getString("status").equals("OK")) {
- success();
- return;
- } else if (j.getString("status") != null) {
- error(j.getString("message"));
- } else {
- error("Connection error");
- }
- } catch (JSONException e) {
- error(e.getMessage());
- } catch (Exception e) {
- error(e.getMessage());
- }
- }
-
- abstract protected void success();
- abstract protected void error(String message);
- abstract protected void canceled();
-}
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
new file mode 100644
index 0000000000..d5bf4fc70e
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java
@@ -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());
+
+ 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.
+ */
+ private native void nativeRegisterSingleton(String p_name);
+
+ /**
+ * 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
+ */
+ private 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);
+}
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
new file mode 100644
index 0000000000..b6d949b7bf
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java
@@ -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);
+ }
+ }
+}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java b/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java
index bbf876ea1f..9d29551f89 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java
@@ -44,7 +44,6 @@ public class GLUtils {
public static final boolean DEBUG = false;
- public static boolean use_gl3 = false;
public static boolean use_32 = false;
public static boolean use_debug_opengl = false;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/PaymentsCache.java b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt
index 84a7eda6e0..67faad8ddd 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/payments/PaymentsCache.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* PaymentsCache.java */
+/* VkRenderer.kt */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,45 +28,72 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-package org.godotengine.godot.payments;
+@file:JvmName("VkRenderer")
+package org.godotengine.godot.vulkan
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.util.Log;
+import android.view.Surface
-public class PaymentsCache {
+/**
+ * Responsible to setting up and driving the Vulkan rendering logic.
+ *
+ * <h3>Threading</h3>
+ * The renderer will be called on a separate thread, so that rendering
+ * performance is decoupled from the UI thread. Clients typically need to
+ * communicate with the renderer from the UI thread, because that's where
+ * input events are received. Clients can communicate using any of the
+ * standard Java techniques for cross-thread communication, or they can
+ * use the [VkSurfaceView.queueOnVkThread] convenience method.
+ *
+ * @see [VkSurfaceView.startRenderer]
+ */
+internal class VkRenderer {
- public Context context;
+ /**
+ * Called when the surface is created and signals the beginning of rendering.
+ */
+ fun onVkSurfaceCreated(surface: Surface) {
+ nativeOnVkSurfaceCreated(surface)
+ }
- public PaymentsCache(Context context) {
- this.context = context;
+ /**
+ * Called after the surface is created and whenever its size changes.
+ */
+ fun onVkSurfaceChanged(surface: Surface, width: Int, height: Int) {
+ nativeOnVkSurfaceChanged(surface, width, height)
}
- public void setConsumableFlag(String set, String sku, Boolean flag) {
- SharedPreferences sharedPref = context.getSharedPreferences("consumables_" + set, Context.MODE_PRIVATE);
- SharedPreferences.Editor editor = sharedPref.edit();
- editor.putBoolean(sku, flag);
- editor.apply();
+ /**
+ * Called to draw the current frame.
+ */
+ fun onVkDrawFrame() {
+ nativeOnVkDrawFrame()
}
- public boolean getConsumableFlag(String set, String sku) {
- SharedPreferences sharedPref = context.getSharedPreferences(
- "consumables_" + set, Context.MODE_PRIVATE);
- return sharedPref.getBoolean(sku, false);
+ /**
+ * Called when the rendering thread is resumed.
+ */
+ fun onVkResume() {
+ nativeOnVkResume()
}
- public void setConsumableValue(String set, String sku, String value) {
- SharedPreferences sharedPref = context.getSharedPreferences("consumables_" + set, Context.MODE_PRIVATE);
- SharedPreferences.Editor editor = sharedPref.edit();
- editor.putString(sku, value);
- //Log.d("XXX", "Setting asset: consumables_" + set + ":" + sku);
- editor.apply();
+ /**
+ * Called when the rendering thread is paused.
+ */
+ fun onVkPause() {
+ nativeOnVkPause()
}
- public String getConsumableValue(String set, String sku) {
- SharedPreferences sharedPref = context.getSharedPreferences(
- "consumables_" + set, Context.MODE_PRIVATE);
- //Log.d("XXX", "Getting asset: consumables_" + set + ":" + sku);
- return sharedPref.getString(sku, null);
+ /**
+ * Called when the rendering thread is destroyed and used as signal to tear down the Vulkan logic.
+ */
+ fun onVkDestroy() {
+ nativeOnVkDestroy()
}
+
+ private external fun nativeOnVkSurfaceCreated(surface: Surface)
+ private external fun nativeOnVkSurfaceChanged(surface: Surface, width: Int, height: Int)
+ private external fun nativeOnVkResume()
+ private external fun nativeOnVkDrawFrame()
+ private external fun nativeOnVkPause()
+ private external fun nativeOnVkDestroy()
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt
new file mode 100644
index 0000000000..1c594f3201
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt
@@ -0,0 +1,136 @@
+/*************************************************************************/
+/* VkSurfaceView.kt */
+/*************************************************************************/
+/* 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. */
+/*************************************************************************/
+
+@file:JvmName("VkSurfaceView")
+package org.godotengine.godot.vulkan
+
+import android.content.Context
+import android.view.SurfaceHolder
+import android.view.SurfaceView
+
+/**
+ * An implementation of SurfaceView that uses the dedicated surface for
+ * displaying Vulkan rendering.
+ * <p>
+ * A [VkSurfaceView] provides the following features:
+ * <p>
+ * <ul>
+ * <li>Manages a surface, which is a special piece of memory that can be
+ * composited into the Android view system.
+ * <li>Accepts a user-provided [VkRenderer] object that does the actual rendering.
+ * <li>Renders on a dedicated [VkThread] thread to decouple rendering performance from the
+ * UI thread.
+ * </ul>
+ */
+internal class VkSurfaceView(context: Context) : SurfaceView(context), SurfaceHolder.Callback {
+
+ companion object {
+ fun checkState(expression: Boolean, errorMessage: Any) {
+ check(expression) { errorMessage.toString() }
+ }
+ }
+
+ /**
+ * Thread used to drive the vulkan logic.
+ */
+ private val vkThread: VkThread by lazy {
+ VkThread(this, renderer)
+ }
+
+ /**
+ * Performs the actual rendering.
+ */
+ private lateinit var renderer: VkRenderer
+
+ init {
+ isClickable = true
+ holder.addCallback(this)
+ }
+
+ /**
+ * Set the [VkRenderer] associated with the view, and starts the thread that will drive the vulkan
+ * rendering.
+ *
+ * This method should be called once and only once in the life-cycle of [VkSurfaceView].
+ */
+ fun startRenderer(renderer: VkRenderer) {
+ checkState(!this::renderer.isInitialized, "startRenderer must only be invoked once")
+ this.renderer = renderer
+ vkThread.start()
+ }
+
+ /**
+ * Queues a runnable to be run on the Vulkan rendering thread.
+ *
+ * Must not be called before a [VkRenderer] has been set.
+ */
+ fun queueOnVkThread(runnable: Runnable) {
+ vkThread.queueEvent(runnable)
+ }
+
+ /**
+ * Resumes the rendering thread.
+ *
+ * Must not be called before a [VkRenderer] has been set.
+ */
+ fun onResume() {
+ vkThread.onResume()
+ }
+
+ /**
+ * Pauses the rendering thread.
+ *
+ * Must not be called before a [VkRenderer] has been set.
+ */
+ fun onPause() {
+ vkThread.onPause()
+ }
+
+ /**
+ * Tear down the rendering thread.
+ *
+ * Must not be called before a [VkRenderer] has been set.
+ */
+ fun onDestroy() {
+ vkThread.blockingExit()
+ }
+
+ override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
+ vkThread.onSurfaceChanged(width, height)
+ }
+
+ override fun surfaceDestroyed(holder: SurfaceHolder) {
+ vkThread.onSurfaceDestroyed()
+ }
+
+ override fun surfaceCreated(holder: SurfaceHolder) {
+ vkThread.onSurfaceCreated()
+ }
+}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt
new file mode 100644
index 0000000000..2e332840bf
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt
@@ -0,0 +1,230 @@
+/*************************************************************************/
+/* VkThread.kt */
+/*************************************************************************/
+/* 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. */
+/*************************************************************************/
+
+@file:JvmName("VkThread")
+package org.godotengine.godot.vulkan
+
+import android.util.Log
+import java.util.concurrent.locks.ReentrantLock
+import kotlin.concurrent.withLock
+
+/**
+ * Thread implementation for the [VkSurfaceView] onto which the vulkan logic is ran.
+ *
+ * The implementation is modeled after [android.opengl.GLSurfaceView]'s GLThread.
+ */
+internal class VkThread(private val vkSurfaceView: VkSurfaceView, private val vkRenderer: VkRenderer) : Thread(TAG) {
+
+ companion object {
+ private val TAG = VkThread::class.java.simpleName
+ }
+
+ /**
+ * Used to run events scheduled on the thread.
+ */
+ private val eventQueue = ArrayList<Runnable>()
+
+ /**
+ * Used to synchronize interaction with other threads (e.g: main thread).
+ */
+ private val lock = ReentrantLock()
+ private val lockCondition = lock.newCondition()
+
+ private var shouldExit = false
+ private var exited = false
+ private var rendererInitialized = false
+ private var rendererResumed = false
+ private var resumed = false
+ private var hasSurface = false
+ private var width = 0
+ private var height = 0
+
+ /**
+ * Determine when drawing can occur on the thread. This usually occurs after the
+ * [android.view.Surface] is available, the app is in a resumed state.
+ */
+ private val readyToDraw
+ get() = hasSurface && resumed
+
+ private fun threadExiting() {
+ lock.withLock {
+ exited = true
+ lockCondition.signalAll()
+ }
+ }
+
+ /**
+ * Queue an event on the [VkThread].
+ */
+ fun queueEvent(event: Runnable) {
+ lock.withLock {
+ eventQueue.add(event)
+ lockCondition.signalAll()
+ }
+ }
+
+ /**
+ * Request the thread to exit and block until it's done.
+ */
+ fun blockingExit() {
+ lock.withLock {
+ shouldExit = true
+ lockCondition.signalAll()
+ while (!exited) {
+ try {
+ Log.i(TAG, "Waiting on exit for $name")
+ lockCondition.await()
+ } catch (ex: InterruptedException) {
+ currentThread().interrupt()
+ }
+ }
+ }
+ }
+
+ /**
+ * Invoked when the app resumes.
+ */
+ fun onResume() {
+ lock.withLock {
+ resumed = true
+ lockCondition.signalAll()
+ }
+ }
+
+ /**
+ * Invoked when the app pauses.
+ */
+ fun onPause() {
+ lock.withLock {
+ resumed = false
+ lockCondition.signalAll()
+ }
+ }
+
+ /**
+ * Invoked when the [android.view.Surface] has been created.
+ */
+ fun onSurfaceCreated() {
+ // This is a no op because surface creation will always be followed by surfaceChanged()
+ // which provide all the needed information.
+ }
+
+ /**
+ * Invoked following structural updates to [android.view.Surface].
+ */
+ fun onSurfaceChanged(width: Int, height: Int) {
+ lock.withLock {
+ hasSurface = true
+ this.width = width
+ this.height = height
+ lockCondition.signalAll()
+ }
+ }
+
+ /**
+ * Invoked when the [android.view.Surface] is no longer available.
+ */
+ fun onSurfaceDestroyed() {
+ lock.withLock {
+ hasSurface = false
+ lockCondition.signalAll()
+ }
+ }
+
+ /**
+ * Thread loop modeled after [android.opengl.GLSurfaceView]'s GLThread.
+ */
+ override fun run() {
+ try {
+ while (true) {
+ var event: Runnable? = null
+ lock.withLock {
+ while (true) {
+ // Code path for exiting the thread loop.
+ if (shouldExit) {
+ vkRenderer.onVkDestroy()
+ return
+ }
+
+ // Check for events and execute them outside of the loop if found to avoid
+ // blocking the thread lifecycle by holding onto the lock.
+ if (eventQueue.isNotEmpty()) {
+ event = eventQueue.removeAt(0)
+ break;
+ }
+
+ if (readyToDraw) {
+ if (!rendererResumed) {
+ rendererResumed = true
+ vkRenderer.onVkResume()
+
+ if (!rendererInitialized) {
+ rendererInitialized = true
+ vkRenderer.onVkSurfaceCreated(vkSurfaceView.holder.surface)
+ }
+
+ vkRenderer.onVkSurfaceChanged(vkSurfaceView.holder.surface, width, height)
+ }
+
+ // Break out of the loop so drawing can occur without holding onto the lock.
+ break;
+ } else if (rendererResumed) {
+ // If we aren't ready to draw but are resumed, that means we either lost a surface
+ // or the app was paused.
+ rendererResumed = false
+ vkRenderer.onVkPause()
+ }
+ // We only reach this state if we are not ready to draw and have no queued events, so
+ // we wait.
+ // On state change, the thread will be awoken using the [lock] and [lockCondition], and
+ // we will resume execution.
+ lockCondition.await()
+ }
+ }
+
+ // Run queued event.
+ if (event != null) {
+ event?.run()
+ continue
+ }
+
+ // Draw only when there no more queued events.
+ vkRenderer.onVkDrawFrame()
+ }
+ } catch (ex: InterruptedException) {
+ Log.i(TAG, ex.message)
+ } catch (ex: IllegalStateException) {
+ Log.i(TAG, ex.message)
+ } finally {
+ threadExiting()
+ }
+ }
+
+}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java
index ce4defd7a7..8409e37f8f 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java
@@ -45,6 +45,8 @@ public class RegularConfigChooser implements GLSurfaceView.EGLConfigChooser {
private int[] mValue = new int[1];
+ // FIXME: Add support for Vulkan.
+
/* This EGL config specification is used to specify 2.0 rendering.
* We use a minimum size of 4 bits for red/green/blue, but will
* perform actual matching in chooseConfig() below.
@@ -59,15 +61,6 @@ public class RegularConfigChooser implements GLSurfaceView.EGLConfigChooser {
EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL10.EGL_NONE
};
- private static int[] s_configAttribs3 = {
- EGL10.EGL_RED_SIZE, 4,
- EGL10.EGL_GREEN_SIZE, 4,
- EGL10.EGL_BLUE_SIZE, 4,
- // EGL10.EGL_DEPTH_SIZE, 16,
- // EGL10.EGL_STENCIL_SIZE, EGL10.EGL_DONT_CARE,
- EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, //apparently there is no EGL_OPENGL_ES3_BIT
- EGL10.EGL_NONE
- };
public RegularConfigChooser(int r, int g, int b, int a, int depth, int stencil) {
mRedSize = r;
@@ -83,7 +76,7 @@ public class RegularConfigChooser implements GLSurfaceView.EGLConfigChooser {
/* Get the number of minimally matching EGL configurations
*/
int[] num_config = new int[1];
- egl.eglChooseConfig(display, GLUtils.use_gl3 ? s_configAttribs3 : s_configAttribs2, null, 0, num_config);
+ egl.eglChooseConfig(display, s_configAttribs2, null, 0, num_config);
int numConfigs = num_config[0];
@@ -94,7 +87,7 @@ public class RegularConfigChooser implements GLSurfaceView.EGLConfigChooser {
/* Allocate then read the array of minimally matching EGL configs
*/
EGLConfig[] configs = new EGLConfig[numConfigs];
- egl.eglChooseConfig(display, GLUtils.use_gl3 ? s_configAttribs3 : s_configAttribs2, configs, numConfigs, num_config);
+ egl.eglChooseConfig(display, s_configAttribs2, configs, numConfigs, num_config);
if (GLUtils.DEBUG) {
GLUtils.printConfigs(egl, display, configs);
diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java
index 22bd4ced87..f2b4c95a2c 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java
@@ -52,24 +52,17 @@ public class RegularContextFactory implements GLSurfaceView.EGLContextFactory {
public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) {
String driver_name = GodotLib.getGlobal("rendering/quality/driver/driver_name");
- if (GLUtils.use_gl3 && !driver_name.equals("GLES3")) {
- GLUtils.use_gl3 = false;
- }
- if (GLUtils.use_gl3)
- Log.w(TAG, "creating OpenGL ES 3.0 context :");
- else
- Log.w(TAG, "creating OpenGL ES 2.0 context :");
+ // FIXME: Add support for Vulkan.
+ Log.w(TAG, "creating OpenGL ES 2.0 context :");
GLUtils.checkEglError(TAG, "Before eglCreateContext", egl);
EGLContext context;
if (GLUtils.use_debug_opengl) {
int[] attrib_list2 = { EGL_CONTEXT_CLIENT_VERSION, 2, _EGL_CONTEXT_FLAGS_KHR, _EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL10.EGL_NONE };
- int[] attrib_list3 = { EGL_CONTEXT_CLIENT_VERSION, 3, _EGL_CONTEXT_FLAGS_KHR, _EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL10.EGL_NONE };
- context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, GLUtils.use_gl3 ? attrib_list3 : attrib_list2);
+ context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list2);
} else {
int[] attrib_list2 = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
- int[] attrib_list3 = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL10.EGL_NONE };
- context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, GLUtils.use_gl3 ? attrib_list3 : attrib_list2);
+ context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list2);
}
GLUtils.checkEglError(TAG, "After eglCreateContext", egl);
return context;