Преглед на файлове

Merge pull request #29325 from m4gr3d/setup_godot_for_ovr_support

Setup Godot to support the Oculus Mobile SDK.
Rémi Verschelde преди 6 години
родител
ревизия
f6f9f89078
променени са 18 файла, в които са добавени 1330 реда и са изтрити 661 реда
  1. 21 8
      modules/gdnative/android/android_gdn.cpp
  2. 13 1
      modules/gdnative/gdnative_api.json
  3. 2 0
      modules/gdnative/include/android/godot_android.h
  4. 22 4
      platform/android/java/src/org/godotengine/godot/Godot.java
  5. 61 0
      platform/android/java/src/org/godotengine/godot/GodotRenderer.java
  6. 72 648
      platform/android/java/src/org/godotengine/godot/GodotView.java
  7. 352 0
      platform/android/java/src/org/godotengine/godot/input/GodotInputHandler.java
  8. 44 0
      platform/android/java/src/org/godotengine/godot/input/Joystick.java
  9. 157 0
      platform/android/java/src/org/godotengine/godot/utils/GLUtils.java
  10. 39 0
      platform/android/java/src/org/godotengine/godot/xr/XRMode.java
  11. 112 0
      platform/android/java/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java
  12. 58 0
      platform/android/java/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java
  13. 60 0
      platform/android/java/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java
  14. 151 0
      platform/android/java/src/org/godotengine/godot/xr/pancake/PancakeConfigChooser.java
  15. 81 0
      platform/android/java/src/org/godotengine/godot/xr/pancake/PancakeContextFactory.java
  16. 61 0
      platform/android/java/src/org/godotengine/godot/xr/pancake/PancakeFallbackConfigChooser.java
  17. 20 0
      platform/android/java_godot_wrapper.cpp
  18. 4 0
      platform/android/java_godot_wrapper.h

+ 21 - 8
modules/gdnative/android/android_gdn.cpp

@@ -34,6 +34,8 @@
 // These entry points are only for the android platform and are simple stubs in all others.
 
 #ifdef __ANDROID__
+#include "platform/android/java_godot_wrapper.h"
+#include "platform/android/os_android.h"
 #include "platform/android/thread_jandroid.h"
 #else
 #define JNIEnv void
@@ -54,20 +56,31 @@ JNIEnv *GDAPI godot_android_get_env() {
 
 jobject GDAPI godot_android_get_activity() {
 #ifdef __ANDROID__
-	JNIEnv *env = ThreadAndroid::get_env();
-
-	jclass activityThread = env->FindClass("android/app/ActivityThread");
-	jmethodID currentActivityThread = env->GetStaticMethodID(activityThread, "currentActivityThread", "()Landroid/app/ActivityThread;");
-	jobject at = env->CallStaticObjectMethod(activityThread, currentActivityThread);
-	jmethodID getApplication = env->GetMethodID(activityThread, "getApplication", "()Landroid/app/Application;");
-	jobject context = env->CallObjectMethod(at, getApplication);
+	OS_Android *os_android = (OS_Android *)OS::get_singleton();
+	return os_android->get_godot_java()->get_activity();
+#else
+	return NULL;
+#endif
+}
 
-	return env->NewGlobalRef(context);
+jobject GDAPI godot_android_get_surface() {
+#ifdef __ANDROID__
+	OS_Android *os_android = (OS_Android *)OS::get_singleton();
+	return os_android->get_godot_java()->get_surface();
 #else
 	return NULL;
 #endif
 }
 
+bool GDAPI godot_android_is_activity_resumed() {
+#ifdef __ANDROID__
+	OS_Android *os_android = (OS_Android *)OS::get_singleton();
+	return os_android->get_godot_java()->is_activity_resumed();
+#else
+	return false;
+#endif
+}
+
 #ifdef __cplusplus
 }
 #endif

+ 13 - 1
modules/gdnative/gdnative_api.json

@@ -6304,7 +6304,7 @@
       "type": "ANDROID",
       "version": {
         "major": 1,
-        "minor": 0
+        "minor": 1
       },
       "next": null,
       "api": [
@@ -6319,6 +6319,18 @@
           "return_type": "jobject",
           "arguments": [
           ]
+        },
+        {
+          "name": "godot_android_get_surface",
+          "return_type": "jobject",
+          "arguments": [
+          ]
+        },
+        {
+          "name": "godot_android_is_activity_resumed",
+          "return_type": "bool",
+          "arguments": [
+          ]
         }
       ]
     },

+ 2 - 0
modules/gdnative/include/android/godot_android.h

@@ -46,6 +46,8 @@ extern "C" {
 
 JNIEnv *GDAPI godot_android_get_env();
 jobject GDAPI godot_android_get_activity();
+jobject GDAPI godot_android_get_surface();
+bool GDAPI godot_android_is_activity_resumed();
 
 #ifdef __cplusplus
 }

+ 22 - 4
platform/android/java/src/org/godotengine/godot/Godot.java

@@ -30,8 +30,6 @@
 
 package org.godotengine.godot;
 
-//import android.R;
-
 import android.Manifest;
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -64,6 +62,7 @@ import android.util.Log;
 import android.view.Display;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
+import android.view.Surface;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
@@ -73,7 +72,6 @@ import android.view.WindowManager;
 import android.widget.Button;
 import android.widget.FrameLayout;
 import android.widget.ProgressBar;
-import android.widget.RelativeLayout;
 import android.widget.TextView;
 import com.google.android.vending.expansion.downloader.DownloadProgressInfo;
 import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller;
@@ -94,6 +92,7 @@ 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.xr.XRMode;
 
 public class Godot extends Activity implements SensorEventListener, IDownloaderClient {
 
@@ -120,6 +119,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 	private boolean use_immersive = false;
 	private boolean use_debug_opengl = false;
 	private boolean mStatePaused;
+	private boolean activityResumed;
 	private int mState;
 
 	static private Intent mCurrentIntent;
@@ -282,7 +282,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 		// ...add to FrameLayout
 		layout.addView(edittext);
 
-		mView = new GodotView(getApplication(), io, use_gl3, use_32_bits, use_debug_opengl, this);
+		mView = new GodotView(this, XRMode.PANCAKE, use_gl3, use_32_bits, use_debug_opengl);
 		layout.addView(mView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
 		edittext.setView(mView);
 		io.setEdit(edittext);
@@ -402,6 +402,20 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 		}
 	}
 
+	/**
+	 * Used by the native code (java_godot_wrapper.h) to check whether the activity is resumed or paused.
+	 */
+	private boolean isActivityResumed() {
+		return activityResumed;
+	}
+
+	/**
+	 * Used by the native code (java_godot_wrapper.h) to access the Android surface.
+	 */
+	private Surface getSurface() {
+		return mView.getHolder().getSurface();
+	}
+
 	String expansion_pack_path;
 
 	private void initializeGodot() {
@@ -612,6 +626,8 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 	@Override
 	protected void onPause() {
 		super.onPause();
+		activityResumed = false;
+
 		if (!godot_initialized) {
 			if (null != mDownloaderClientStub) {
 				mDownloaderClientStub.disconnect(this);
@@ -687,6 +703,8 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 
 			singletons[i].onMainResume();
 		}
+
+		activityResumed = true;
 	}
 
 	public void UiChangeListener() {

+ 61 - 0
platform/android/java/src/org/godotengine/godot/GodotRenderer.java

@@ -0,0 +1,61 @@
+/*************************************************************************/
+/*  GodotRenderer.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;
+
+import android.opengl.GLSurfaceView;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+import org.godotengine.godot.utils.GLUtils;
+
+/**
+ * Godot's renderer implementation.
+ */
+class GodotRenderer implements GLSurfaceView.Renderer {
+
+	public void onDrawFrame(GL10 gl) {
+		GodotLib.step();
+		for (int i = 0; i < Godot.singleton_count; i++) {
+			Godot.singletons[i].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);
+		}
+	}
+
+	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+		GodotLib.newcontext(GLUtils.use_32);
+	}
+}

+ 72 - 648
platform/android/java/src/org/godotengine/godot/GodotView.java

@@ -30,28 +30,20 @@
 
 package org.godotengine.godot;
 import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.ContextWrapper;
 import android.graphics.PixelFormat;
-import android.hardware.input.InputManager;
 import android.opengl.GLSurfaceView;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.InputDevice;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import javax.microedition.khronos.egl.EGL10;
-import javax.microedition.khronos.egl.EGLConfig;
-import javax.microedition.khronos.egl.EGLContext;
-import javax.microedition.khronos.egl.EGLDisplay;
-import javax.microedition.khronos.opengles.GL10;
-import org.godotengine.godot.input.InputManagerCompat;
-import org.godotengine.godot.input.InputManagerCompat.InputDeviceListener;
+import org.godotengine.godot.input.GodotInputHandler;
+import org.godotengine.godot.utils.GLUtils;
+import org.godotengine.godot.xr.XRMode;
+import org.godotengine.godot.xr.ovr.OvrConfigChooser;
+import org.godotengine.godot.xr.ovr.OvrContextFactory;
+import org.godotengine.godot.xr.ovr.OvrWindowSurfaceFactory;
+import org.godotengine.godot.xr.pancake.PancakeConfigChooser;
+import org.godotengine.godot.xr.pancake.PancakeContextFactory;
+import org.godotengine.godot.xr.pancake.PancakeFallbackConfigChooser;
+
 /**
  * A simple GLSurfaceView sub-class that demonstrate how to perform
  * OpenGL ES 2.0 rendering into a GL Surface. Note the following important
@@ -70,57 +62,26 @@ import org.godotengine.godot.input.InputManagerCompat.InputDeviceListener;
  *   that matches it exactly (with regards to red/green/blue/alpha channels
  *   bit depths). Failure to do so would result in an EGL_BAD_MATCH error.
  */
-public class GodotView extends GLSurfaceView implements InputDeviceListener {
-
-	private static String TAG = "GodotView";
-	private static final boolean DEBUG = false;
-	private Context ctx;
-
-	private GodotIO io;
-	private static boolean use_gl3 = false;
-	private static boolean use_32 = false;
-	private static boolean use_debug_opengl = false;
+public class GodotView extends GLSurfaceView {
 
-	private Godot activity;
+	private static String TAG = GodotView.class.getSimpleName();
 
-	private InputManagerCompat mInputManager;
-	public GodotView(Context context, GodotIO p_io, boolean p_use_gl3, boolean p_use_32_bits, boolean p_use_debug_opengl, Godot p_activity) {
-		super(context);
-		ctx = context;
-		io = p_io;
-		use_gl3 = p_use_gl3;
-		use_32 = p_use_32_bits;
-		use_debug_opengl = p_use_debug_opengl;
+	private final Godot activity;
+	private final GodotInputHandler inputHandler;
 
-		activity = p_activity;
-
-		setPreserveEGLContextOnPause(true);
-
-		mInputManager = InputManagerCompat.Factory.getInputManager(this.getContext());
-		mInputManager.registerInputDeviceListener(this, null);
-		init(false, 16, 0);
-	}
-
-	public GodotView(Context context) {
-		super(context);
-		ctx = context;
-	}
+	public GodotView(Godot activity, XRMode xrMode, boolean p_use_gl3, 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;
 
-	public GodotView(Context context, boolean translucent, int depth, int stencil) {
-		super(context);
-		init(translucent, depth, stencil);
+		this.activity = activity;
+		this.inputHandler = new GodotInputHandler(this);
+		init(xrMode, false, 16, 0);
 	}
 
 	public void initInputDevices() {
-		/* initially add input devices*/
-		int[] deviceIds = mInputManager.getInputDeviceIds();
-		for (int deviceId : deviceIds) {
-			InputDevice device = mInputManager.getInputDevice(deviceId);
-			if (DEBUG) {
-				Log.v("GodotView", String.format("init() deviceId:%d, Name:%s\n", deviceId, device.getName()));
-			}
-			onInputDeviceAdded(deviceId);
-		}
+		this.inputHandler.initInputDevices();
 	}
 
 	@SuppressLint("ClickableViewAccessibility")
@@ -130,617 +91,80 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener {
 		return activity.gotTouchEvent(event);
 	}
 
-	public int get_godot_button(int keyCode) {
-
-		int button;
-		switch (keyCode) {
-			case KeyEvent.KEYCODE_BUTTON_A: // Android A is SNES B
-				button = 0;
-				break;
-			case KeyEvent.KEYCODE_BUTTON_B:
-				button = 1;
-				break;
-			case KeyEvent.KEYCODE_BUTTON_X: // Android X is SNES Y
-				button = 2;
-				break;
-			case KeyEvent.KEYCODE_BUTTON_Y:
-				button = 3;
-				break;
-			case KeyEvent.KEYCODE_BUTTON_L1:
-				button = 9;
-				break;
-			case KeyEvent.KEYCODE_BUTTON_L2:
-				button = 15;
-				break;
-			case KeyEvent.KEYCODE_BUTTON_R1:
-				button = 10;
-				break;
-			case KeyEvent.KEYCODE_BUTTON_R2:
-				button = 16;
-				break;
-			case KeyEvent.KEYCODE_BUTTON_SELECT:
-				button = 4;
-				break;
-			case KeyEvent.KEYCODE_BUTTON_START:
-				button = 6;
-				break;
-			case KeyEvent.KEYCODE_BUTTON_THUMBL:
-				button = 7;
-				break;
-			case KeyEvent.KEYCODE_BUTTON_THUMBR:
-				button = 8;
-				break;
-			case KeyEvent.KEYCODE_DPAD_UP:
-				button = 11;
-				break;
-			case KeyEvent.KEYCODE_DPAD_DOWN:
-				button = 12;
-				break;
-			case KeyEvent.KEYCODE_DPAD_LEFT:
-				button = 13;
-				break;
-			case KeyEvent.KEYCODE_DPAD_RIGHT:
-				button = 14;
-				break;
-			case KeyEvent.KEYCODE_BUTTON_C:
-				button = 17;
-				break;
-			case KeyEvent.KEYCODE_BUTTON_Z:
-				button = 18;
-				break;
-
-			default:
-				button = keyCode - KeyEvent.KEYCODE_BUTTON_1 + 20;
-				break;
-		}
-		return button;
-	};
-
-	private static class joystick {
-		public int device_id;
-		public String name;
-		public ArrayList<InputDevice.MotionRange> axes;
-		public ArrayList<InputDevice.MotionRange> hats;
-	}
-
-	private static class RangeComparator implements Comparator<InputDevice.MotionRange> {
-		@Override
-		public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) {
-			return arg0.getAxis() - arg1.getAxis();
-		}
-	}
-
-	ArrayList<joystick> joy_devices = new ArrayList<joystick>();
-
-	private int find_joy_device(int device_id) {
-		for (int i = 0; i < joy_devices.size(); i++) {
-			if (joy_devices.get(i).device_id == device_id) {
-				return i;
-			}
-		}
-
-		return -1;
-	}
-
-	@Override
-	public void onInputDeviceAdded(int deviceId) {
-		int id = find_joy_device(deviceId);
-
-		// Check if the device has not been already added
-		if (id < 0) {
-			InputDevice device = mInputManager.getInputDevice(deviceId);
-			//device can be null if deviceId is not found
-			if (device != null) {
-				int sources = device.getSources();
-				if (((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) ||
-						((sources & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK)) {
-					id = joy_devices.size();
-
-					joystick joy = new joystick();
-					joy.device_id = deviceId;
-					joy.name = device.getName();
-					joy.axes = new ArrayList<InputDevice.MotionRange>();
-					joy.hats = new ArrayList<InputDevice.MotionRange>();
-
-					List<InputDevice.MotionRange> ranges = device.getMotionRanges();
-					Collections.sort(ranges, new RangeComparator());
-
-					for (InputDevice.MotionRange range : ranges) {
-						if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) {
-							joy.hats.add(range);
-						} else {
-							joy.axes.add(range);
-						}
-					}
-
-					joy_devices.add(joy);
-
-					final int device_id = id;
-					final String name = joy.name;
-					queueEvent(new Runnable() {
-						@Override
-						public void run() {
-							GodotLib.joyconnectionchanged(device_id, true, name);
-						}
-					});
-				}
-			}
-		}
-	}
-
-	@Override
-	public void onInputDeviceRemoved(int deviceId) {
-		final int device_id = find_joy_device(deviceId);
-
-		// Check if the evice has not been already removed
-		if (device_id > -1) {
-			joy_devices.remove(device_id);
-
-			queueEvent(new Runnable() {
-				@Override
-				public void run() {
-					GodotLib.joyconnectionchanged(device_id, false, "");
-				}
-			});
-		}
-	}
-
-	@Override
-	public void onInputDeviceChanged(int deviceId) {
-		onInputDeviceRemoved(deviceId);
-		onInputDeviceAdded(deviceId);
-	}
 	@Override
 	public boolean onKeyUp(final int keyCode, KeyEvent event) {
-
-		if (keyCode == KeyEvent.KEYCODE_BACK) {
-			return true;
-		}
-
-		if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
-			return super.onKeyUp(keyCode, event);
-		};
-
-		int source = event.getSource();
-		if ((source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK || (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD || (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
-
-			final int button = get_godot_button(keyCode);
-			final int device_id = find_joy_device(event.getDeviceId());
-
-			// Check if the device exists
-			if (device_id > -1) {
-				queueEvent(new Runnable() {
-					@Override
-					public void run() {
-						GodotLib.joybutton(device_id, button, false);
-					}
-				});
-				return true;
-			}
-		} else {
-			final int chr = event.getUnicodeChar(0);
-			queueEvent(new Runnable() {
-				@Override
-				public void run() {
-					GodotLib.key(keyCode, chr, false);
-				}
-			});
-		};
-
-		return super.onKeyUp(keyCode, event);
-	};
+		return inputHandler.onKeyUp(keyCode, event) || super.onKeyUp(keyCode, event);
+	}
 
 	@Override
 	public boolean onKeyDown(final int keyCode, KeyEvent event) {
-
-		if (keyCode == KeyEvent.KEYCODE_BACK) {
-			activity.onBackPressed();
-			// press 'back' button should not terminate program
-			//normal handle 'back' event in game logic
-			return true;
-		}
-
-		if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
-			return super.onKeyDown(keyCode, event);
-		};
-
-		int source = event.getSource();
-		//Log.e(TAG, String.format("Key down! source %d, device %d, joystick %d, %d, %d", event.getDeviceId(), source, (source & InputDevice.SOURCE_JOYSTICK), (source & InputDevice.SOURCE_DPAD), (source & InputDevice.SOURCE_GAMEPAD)));
-
-		if ((source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK || (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD || (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
-
-			if (event.getRepeatCount() > 0) // ignore key echo
-				return true;
-
-			final int button = get_godot_button(keyCode);
-			final int device_id = find_joy_device(event.getDeviceId());
-
-			// Check if the device exists
-			if (device_id > -1) {
-				queueEvent(new Runnable() {
-					@Override
-					public void run() {
-						GodotLib.joybutton(device_id, button, true);
-					}
-				});
-				return true;
-			}
-		} else {
-			final int chr = event.getUnicodeChar(0);
-			queueEvent(new Runnable() {
-				@Override
-				public void run() {
-					GodotLib.key(keyCode, chr, true);
-				}
-			});
-		};
-
-		return super.onKeyDown(keyCode, event);
+		return inputHandler.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
 	}
 
 	@Override
 	public boolean onGenericMotionEvent(MotionEvent event) {
-
-		if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK && event.getAction() == MotionEvent.ACTION_MOVE) {
-
-			final int device_id = find_joy_device(event.getDeviceId());
-
-			// Check if the device exists
-			if (device_id > -1) {
-				joystick joy = joy_devices.get(device_id);
-
-				for (int i = 0; i < joy.axes.size(); i++) {
-					InputDevice.MotionRange range = joy.axes.get(i);
-					final float value = (event.getAxisValue(range.getAxis()) - range.getMin()) / range.getRange() * 2.0f - 1.0f;
-					final int idx = i;
-					queueEvent(new Runnable() {
-						@Override
-						public void run() {
-							GodotLib.joyaxis(device_id, idx, value);
-						}
-					});
-				}
-
-				for (int i = 0; i < joy.hats.size(); i += 2) {
-					final int hatX = Math.round(event.getAxisValue(joy.hats.get(i).getAxis()));
-					final int hatY = Math.round(event.getAxisValue(joy.hats.get(i + 1).getAxis()));
-					queueEvent(new Runnable() {
-						@Override
-						public void run() {
-							GodotLib.joyhat(device_id, hatX, hatY);
-						}
-					});
-				}
-				return true;
-			}
-		};
-
-		return super.onGenericMotionEvent(event);
-	};
-
-	private void init(boolean translucent, int depth, int stencil) {
-
-		this.setFocusableInTouchMode(true);
-		/* By default, GLSurfaceView() creates a RGB_565 opaque surface.
-		 * If we want a translucent one, we should change the surface's
-		 * format here, using PixelFormat.TRANSLUCENT for GL Surfaces
-		 * is interpreted as any 32-bit surface with alpha by SurfaceFlinger.
-		 */
-		if (translucent) {
-			this.getHolder().setFormat(PixelFormat.TRANSLUCENT);
-		}
-
-		/* Setup the context factory for 2.0 rendering.
-		 * See ContextFactory class definition below
-		 */
-		setEGLContextFactory(new ContextFactory());
-
-		/* We need to choose an EGLConfig that matches the format of
-		 * our surface exactly. This is going to be done in our
-		 * custom config chooser. See ConfigChooser class definition
-		 * below.
-		 */
-
-		if (use_32) {
-			setEGLConfigChooser(translucent ?
-										new FallbackConfigChooser(8, 8, 8, 8, 24, stencil, new ConfigChooser(8, 8, 8, 8, 16, stencil)) :
-										new FallbackConfigChooser(8, 8, 8, 8, 24, stencil, new ConfigChooser(5, 6, 5, 0, 16, stencil)));
-
-		} else {
-			setEGLConfigChooser(translucent ?
-										new ConfigChooser(8, 8, 8, 8, 16, stencil) :
-										new ConfigChooser(5, 6, 5, 0, 16, stencil));
-		}
-
-		/* Set the renderer responsible for frame rendering */
-		setRenderer(new Renderer());
-	}
-
-	private static final int _EGL_CONTEXT_FLAGS_KHR = 0x30FC;
-	private static final int _EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR = 0x00000001;
-
-	private static class ContextFactory implements GLSurfaceView.EGLContextFactory {
-		private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
-		public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) {
-			String driver_name = GodotLib.getGlobal("rendering/quality/driver/driver_name");
-			if (use_gl3 && !driver_name.equals("GLES3")) {
-				use_gl3 = false;
-			}
-			if (use_gl3)
-				Log.w(TAG, "creating OpenGL ES 3.0 context :");
-			else
-				Log.w(TAG, "creating OpenGL ES 2.0 context :");
-
-			checkEglError("Before eglCreateContext", egl);
-			EGLContext context;
-			if (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, use_gl3 ? attrib_list3 : 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, use_gl3 ? attrib_list3 : attrib_list2);
-			}
-			checkEglError("After eglCreateContext", egl);
-			return context;
-		}
-
-		public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) {
-			egl.eglDestroyContext(display, context);
-		}
-	}
-
-	private static void checkEglError(String prompt, EGL10 egl) {
-		int error;
-		while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) {
-			Log.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error));
-		}
-	}
-	/* Fallback if 32bit View is not supported*/
-	private static class FallbackConfigChooser extends ConfigChooser {
-		private ConfigChooser fallback;
-
-		public FallbackConfigChooser(int r, int g, int b, int a, int depth, int stencil, ConfigChooser fallback) {
-			super(r, g, b, a, depth, stencil);
-			this.fallback = fallback;
-		}
-
-		@Override
-		public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) {
-			EGLConfig ec = super.chooseConfig(egl, display, configs);
-			if (ec == null) {
-				Log.w(TAG, "Trying ConfigChooser fallback");
-				ec = fallback.chooseConfig(egl, display, configs);
-				use_32 = false;
-			}
-			return ec;
-		}
+		return inputHandler.onGenericMotionEvent(event) || super.onGenericMotionEvent(event);
 	}
 
-	private static class ConfigChooser implements GLSurfaceView.EGLConfigChooser {
-
-		public ConfigChooser(int r, int g, int b, int a, int depth, int stencil) {
-			mRedSize = r;
-			mGreenSize = g;
-			mBlueSize = b;
-			mAlphaSize = a;
-			mDepthSize = depth;
-			mStencilSize = stencil;
-		}
-
-		/* 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.
-		 */
-		private static int EGL_OPENGL_ES2_BIT = 4;
-		private static int[] s_configAttribs2 = {
-			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,
-			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 EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
-
-			/* Get the number of minimally matching EGL configurations
-			 */
-			int[] num_config = new int[1];
-			egl.eglChooseConfig(display, use_gl3 ? s_configAttribs3 : s_configAttribs2, null, 0, num_config);
-
-			int numConfigs = num_config[0];
-
-			if (numConfigs <= 0) {
-				throw new IllegalArgumentException("No configs match configSpec");
-			}
-
-			/* Allocate then read the array of minimally matching EGL configs
-			 */
-			EGLConfig[] configs = new EGLConfig[numConfigs];
-			egl.eglChooseConfig(display, use_gl3 ? s_configAttribs3 : s_configAttribs2, configs, numConfigs, num_config);
+	private void init(XRMode xrMode, boolean translucent, int depth, int stencil) {
 
-			if (DEBUG) {
-				printConfigs(egl, display, configs);
-			}
-			/* Now return the "best" one
-			 */
-			return chooseConfig(egl, display, configs);
-		}
+		setPreserveEGLContextOnPause(true);
+		setFocusableInTouchMode(true);
+		switch (xrMode) {
 
-		public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
-				EGLConfig[] configs) {
-			for (EGLConfig config : configs) {
-				int d = findConfigAttrib(egl, display, config,
-						EGL10.EGL_DEPTH_SIZE, 0);
-				int s = findConfigAttrib(egl, display, config,
-						EGL10.EGL_STENCIL_SIZE, 0);
+			case OVR:
+				// Replace the default egl config chooser.
+				setEGLConfigChooser(new OvrConfigChooser());
 
-				// We need at least mDepthSize and mStencilSize bits
-				if (d < mDepthSize || s < mStencilSize)
-					continue;
+				// Replace the default context factory.
+				setEGLContextFactory(new OvrContextFactory());
 
-				// We want an *exact* match for red/green/blue/alpha
-				int r = findConfigAttrib(egl, display, config,
-						EGL10.EGL_RED_SIZE, 0);
-				int g = findConfigAttrib(egl, display, config,
-						EGL10.EGL_GREEN_SIZE, 0);
-				int b = findConfigAttrib(egl, display, config,
-						EGL10.EGL_BLUE_SIZE, 0);
-				int a = findConfigAttrib(egl, display, config,
-						EGL10.EGL_ALPHA_SIZE, 0);
+				// Replace the default window surface factory.
+				setEGLWindowSurfaceFactory(new OvrWindowSurfaceFactory());
+				break;
 
-				if (r == mRedSize && g == mGreenSize && b == mBlueSize && a == mAlphaSize)
-					return config;
-			}
-			return null;
-		}
+			case PANCAKE:
+			default:
+				/* By default, GLSurfaceView() creates a RGB_565 opaque surface.
+				 * If we want a translucent one, we should change the surface's
+				 * format here, using PixelFormat.TRANSLUCENT for GL Surfaces
+				 * is interpreted as any 32-bit surface with alpha by SurfaceFlinger.
+				 */
+				if (translucent) {
+					this.getHolder().setFormat(PixelFormat.TRANSLUCENT);
+				}
 
-		private int findConfigAttrib(EGL10 egl, EGLDisplay display,
-				EGLConfig config, int attribute, int defaultValue) {
+				/* Setup the context factory for 2.0 rendering.
+				 * See ContextFactory class definition below
+				 */
+				setEGLContextFactory(new PancakeContextFactory());
 
-			if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
-				return mValue[0];
-			}
-			return defaultValue;
-		}
+				/* We need to choose an EGLConfig that matches the format of
+				 * our surface exactly. This is going to be done in our
+				 * custom config chooser. See ConfigChooser class definition
+				 * below.
+				 */
 
-		private void printConfigs(EGL10 egl, EGLDisplay display,
-				EGLConfig[] configs) {
-			int numConfigs = configs.length;
-			Log.w(TAG, String.format("%d configurations", numConfigs));
-			for (int i = 0; i < numConfigs; i++) {
-				Log.w(TAG, String.format("Configuration %d:\n", i));
-				printConfig(egl, display, configs[i]);
-			}
-		}
+				if (GLUtils.use_32) {
+					setEGLConfigChooser(translucent ?
+												new PancakeFallbackConfigChooser(8, 8, 8, 8, 24, stencil,
+														new PancakeConfigChooser(8, 8, 8, 8, 16, stencil)) :
+												new PancakeFallbackConfigChooser(8, 8, 8, 8, 24, stencil,
+														new PancakeConfigChooser(5, 6, 5, 0, 16, stencil)));
 
-		private void printConfig(EGL10 egl, EGLDisplay display,
-				EGLConfig config) {
-			int[] attributes = {
-				EGL10.EGL_BUFFER_SIZE,
-				EGL10.EGL_ALPHA_SIZE,
-				EGL10.EGL_BLUE_SIZE,
-				EGL10.EGL_GREEN_SIZE,
-				EGL10.EGL_RED_SIZE,
-				EGL10.EGL_DEPTH_SIZE,
-				EGL10.EGL_STENCIL_SIZE,
-				EGL10.EGL_CONFIG_CAVEAT,
-				EGL10.EGL_CONFIG_ID,
-				EGL10.EGL_LEVEL,
-				EGL10.EGL_MAX_PBUFFER_HEIGHT,
-				EGL10.EGL_MAX_PBUFFER_PIXELS,
-				EGL10.EGL_MAX_PBUFFER_WIDTH,
-				EGL10.EGL_NATIVE_RENDERABLE,
-				EGL10.EGL_NATIVE_VISUAL_ID,
-				EGL10.EGL_NATIVE_VISUAL_TYPE,
-				0x3030, // EGL10.EGL_PRESERVED_RESOURCES,
-				EGL10.EGL_SAMPLES,
-				EGL10.EGL_SAMPLE_BUFFERS,
-				EGL10.EGL_SURFACE_TYPE,
-				EGL10.EGL_TRANSPARENT_TYPE,
-				EGL10.EGL_TRANSPARENT_RED_VALUE,
-				EGL10.EGL_TRANSPARENT_GREEN_VALUE,
-				EGL10.EGL_TRANSPARENT_BLUE_VALUE,
-				0x3039, // EGL10.EGL_BIND_TO_TEXTURE_RGB,
-				0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGBA,
-				0x303B, // EGL10.EGL_MIN_SWAP_INTERVAL,
-				0x303C, // EGL10.EGL_MAX_SWAP_INTERVAL,
-				EGL10.EGL_LUMINANCE_SIZE,
-				EGL10.EGL_ALPHA_MASK_SIZE,
-				EGL10.EGL_COLOR_BUFFER_TYPE,
-				EGL10.EGL_RENDERABLE_TYPE,
-				0x3042 // EGL10.EGL_CONFORMANT
-			};
-			String[] names = {
-				"EGL_BUFFER_SIZE",
-				"EGL_ALPHA_SIZE",
-				"EGL_BLUE_SIZE",
-				"EGL_GREEN_SIZE",
-				"EGL_RED_SIZE",
-				"EGL_DEPTH_SIZE",
-				"EGL_STENCIL_SIZE",
-				"EGL_CONFIG_CAVEAT",
-				"EGL_CONFIG_ID",
-				"EGL_LEVEL",
-				"EGL_MAX_PBUFFER_HEIGHT",
-				"EGL_MAX_PBUFFER_PIXELS",
-				"EGL_MAX_PBUFFER_WIDTH",
-				"EGL_NATIVE_RENDERABLE",
-				"EGL_NATIVE_VISUAL_ID",
-				"EGL_NATIVE_VISUAL_TYPE",
-				"EGL_PRESERVED_RESOURCES",
-				"EGL_SAMPLES",
-				"EGL_SAMPLE_BUFFERS",
-				"EGL_SURFACE_TYPE",
-				"EGL_TRANSPARENT_TYPE",
-				"EGL_TRANSPARENT_RED_VALUE",
-				"EGL_TRANSPARENT_GREEN_VALUE",
-				"EGL_TRANSPARENT_BLUE_VALUE",
-				"EGL_BIND_TO_TEXTURE_RGB",
-				"EGL_BIND_TO_TEXTURE_RGBA",
-				"EGL_MIN_SWAP_INTERVAL",
-				"EGL_MAX_SWAP_INTERVAL",
-				"EGL_LUMINANCE_SIZE",
-				"EGL_ALPHA_MASK_SIZE",
-				"EGL_COLOR_BUFFER_TYPE",
-				"EGL_RENDERABLE_TYPE",
-				"EGL_CONFORMANT"
-			};
-			int[] value = new int[1];
-			for (int i = 0; i < attributes.length; i++) {
-				int attribute = attributes[i];
-				String name = names[i];
-				if (egl.eglGetConfigAttrib(display, config, attribute, value)) {
-					Log.w(TAG, String.format("  %s: %d\n", name, value[0]));
 				} else {
-					// Log.w(TAG, String.format("  %s: failed\n", name));
-					while (egl.eglGetError() != EGL10.EGL_SUCCESS)
-						;
+					setEGLConfigChooser(translucent ?
+												new PancakeConfigChooser(8, 8, 8, 8, 16, stencil) :
+												new PancakeConfigChooser(5, 6, 5, 0, 16, stencil));
 				}
-			}
+				break;
 		}
 
-		// Subclasses can adjust these values:
-		protected int mRedSize;
-		protected int mGreenSize;
-		protected int mBlueSize;
-		protected int mAlphaSize;
-		protected int mDepthSize;
-		protected int mStencilSize;
-		private int[] mValue = new int[1];
+		/* Set the renderer responsible for frame rendering */
+		setRenderer(new GodotRenderer());
 	}
 
-	private static class Renderer implements GLSurfaceView.Renderer {
-
-		public void onDrawFrame(GL10 gl) {
-			GodotLib.step();
-			for (int i = 0; i < Godot.singleton_count; i++) {
-				Godot.singletons[i].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);
-			}
-		}
-
-		public void onSurfaceCreated(GL10 gl, EGLConfig config) {
-			GodotLib.newcontext(use_32);
-		}
+	public void onBackPressed() {
+		activity.onBackPressed();
 	}
 }

+ 352 - 0
platform/android/java/src/org/godotengine/godot/input/GodotInputHandler.java

@@ -0,0 +1,352 @@
+/*************************************************************************/
+/*  GodotInputHandler.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.input;
+
+import static org.godotengine.godot.utils.GLUtils.DEBUG;
+
+import android.util.Log;
+import android.view.InputDevice;
+import android.view.InputDevice.MotionRange;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import org.godotengine.godot.GodotLib;
+import org.godotengine.godot.GodotView;
+import org.godotengine.godot.input.InputManagerCompat.InputDeviceListener;
+
+/**
+ * Handles input related events for the {@link GodotView} view.
+ */
+public class GodotInputHandler implements InputDeviceListener {
+
+	private final ArrayList<Joystick> joysticksDevices = new ArrayList<Joystick>();
+
+	private final GodotView godotView;
+	private final InputManagerCompat inputManager;
+
+	public GodotInputHandler(GodotView godotView) {
+		this.godotView = godotView;
+		this.inputManager = InputManagerCompat.Factory.getInputManager(godotView.getContext());
+		this.inputManager.registerInputDeviceListener(this, null);
+	}
+
+	private void queueEvent(Runnable task) {
+		godotView.queueEvent(task);
+	}
+
+	public boolean onKeyUp(final int keyCode, KeyEvent event) {
+		if (keyCode == KeyEvent.KEYCODE_BACK) {
+			return true;
+		}
+
+		if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
+			return false;
+		};
+
+		int source = event.getSource();
+		if ((source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK || (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD || (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
+
+			final int button = getGodotButton(keyCode);
+			final int device_id = findJoystickDevice(event.getDeviceId());
+
+			// Check if the device exists
+			if (device_id > -1) {
+				queueEvent(new Runnable() {
+					@Override
+					public void run() {
+						GodotLib.joybutton(device_id, button, false);
+					}
+				});
+				return true;
+			}
+		} else {
+			final int chr = event.getUnicodeChar(0);
+			queueEvent(new Runnable() {
+				@Override
+				public void run() {
+					GodotLib.key(keyCode, chr, false);
+				}
+			});
+		};
+
+		return false;
+	}
+
+	public boolean onKeyDown(final int keyCode, KeyEvent event) {
+		if (keyCode == KeyEvent.KEYCODE_BACK) {
+			godotView.onBackPressed();
+			// press 'back' button should not terminate program
+			//normal handle 'back' event in game logic
+			return true;
+		}
+
+		if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
+			return false;
+		};
+
+		int source = event.getSource();
+		//Log.e(TAG, String.format("Key down! source %d, device %d, joystick %d, %d, %d", event.getDeviceId(), source, (source & InputDevice.SOURCE_JOYSTICK), (source & InputDevice.SOURCE_DPAD), (source & InputDevice.SOURCE_GAMEPAD)));
+
+		if ((source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK || (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD || (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
+
+			if (event.getRepeatCount() > 0) // ignore key echo
+				return true;
+
+			final int button = getGodotButton(keyCode);
+			final int device_id = findJoystickDevice(event.getDeviceId());
+
+			// Check if the device exists
+			if (device_id > -1) {
+				queueEvent(new Runnable() {
+					@Override
+					public void run() {
+						GodotLib.joybutton(device_id, button, true);
+					}
+				});
+				return true;
+			}
+		} else {
+			final int chr = event.getUnicodeChar(0);
+			queueEvent(new Runnable() {
+				@Override
+				public void run() {
+					GodotLib.key(keyCode, chr, true);
+				}
+			});
+		};
+
+		return false;
+	}
+
+	public boolean onGenericMotionEvent(MotionEvent event) {
+		if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK && event.getAction() == MotionEvent.ACTION_MOVE) {
+
+			final int device_id = findJoystickDevice(event.getDeviceId());
+
+			// Check if the device exists
+			if (device_id > -1) {
+				Joystick joy = joysticksDevices.get(device_id);
+
+				for (int i = 0; i < joy.axes.size(); i++) {
+					InputDevice.MotionRange range = joy.axes.get(i);
+					final float value = (event.getAxisValue(range.getAxis()) - range.getMin()) / range.getRange() * 2.0f - 1.0f;
+					final int idx = i;
+					queueEvent(new Runnable() {
+						@Override
+						public void run() {
+							GodotLib.joyaxis(device_id, idx, value);
+						}
+					});
+				}
+
+				for (int i = 0; i < joy.hats.size(); i += 2) {
+					final int hatX = Math.round(event.getAxisValue(joy.hats.get(i).getAxis()));
+					final int hatY = Math.round(event.getAxisValue(joy.hats.get(i + 1).getAxis()));
+					queueEvent(new Runnable() {
+						@Override
+						public void run() {
+							GodotLib.joyhat(device_id, hatX, hatY);
+						}
+					});
+				}
+				return true;
+			}
+		};
+
+		return false;
+	}
+
+	public void initInputDevices() {
+		/* initially add input devices*/
+		int[] deviceIds = inputManager.getInputDeviceIds();
+		for (int deviceId : deviceIds) {
+			InputDevice device = inputManager.getInputDevice(deviceId);
+			if (DEBUG) {
+				Log.v("GodotView", String.format("init() deviceId:%d, Name:%s\n", deviceId, device.getName()));
+			}
+			onInputDeviceAdded(deviceId);
+		}
+	}
+
+	@Override
+	public void onInputDeviceAdded(int deviceId) {
+		int id = findJoystickDevice(deviceId);
+
+		// Check if the device has not been already added
+		if (id < 0) {
+			InputDevice device = inputManager.getInputDevice(deviceId);
+			//device can be null if deviceId is not found
+			if (device != null) {
+				int sources = device.getSources();
+				if (((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) ||
+						((sources & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK)) {
+					id = joysticksDevices.size();
+
+					Joystick joy = new Joystick();
+					joy.device_id = deviceId;
+					joy.name = device.getName();
+					joy.axes = new ArrayList<InputDevice.MotionRange>();
+					joy.hats = new ArrayList<InputDevice.MotionRange>();
+
+					List<InputDevice.MotionRange> ranges = device.getMotionRanges();
+					Collections.sort(ranges, new RangeComparator());
+
+					for (InputDevice.MotionRange range : ranges) {
+						if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) {
+							joy.hats.add(range);
+						} else {
+							joy.axes.add(range);
+						}
+					}
+
+					joysticksDevices.add(joy);
+
+					final int device_id = id;
+					final String name = joy.name;
+					queueEvent(new Runnable() {
+						@Override
+						public void run() {
+							GodotLib.joyconnectionchanged(device_id, true, name);
+						}
+					});
+				}
+			}
+		}
+	}
+
+	@Override
+	public void onInputDeviceRemoved(int deviceId) {
+		final int device_id = findJoystickDevice(deviceId);
+
+		// Check if the evice has not been already removed
+		if (device_id > -1) {
+			joysticksDevices.remove(device_id);
+
+			queueEvent(new Runnable() {
+				@Override
+				public void run() {
+					GodotLib.joyconnectionchanged(device_id, false, "");
+				}
+			});
+		}
+	}
+
+	@Override
+	public void onInputDeviceChanged(int deviceId) {
+		onInputDeviceRemoved(deviceId);
+		onInputDeviceAdded(deviceId);
+	}
+
+	private static class RangeComparator implements Comparator<MotionRange> {
+		@Override
+		public int compare(MotionRange arg0, MotionRange arg1) {
+			return arg0.getAxis() - arg1.getAxis();
+		}
+	}
+
+	public static int getGodotButton(int keyCode) {
+		int button;
+		switch (keyCode) {
+			case KeyEvent.KEYCODE_BUTTON_A: // Android A is SNES B
+				button = 0;
+				break;
+			case KeyEvent.KEYCODE_BUTTON_B:
+				button = 1;
+				break;
+			case KeyEvent.KEYCODE_BUTTON_X: // Android X is SNES Y
+				button = 2;
+				break;
+			case KeyEvent.KEYCODE_BUTTON_Y:
+				button = 3;
+				break;
+			case KeyEvent.KEYCODE_BUTTON_L1:
+				button = 9;
+				break;
+			case KeyEvent.KEYCODE_BUTTON_L2:
+				button = 15;
+				break;
+			case KeyEvent.KEYCODE_BUTTON_R1:
+				button = 10;
+				break;
+			case KeyEvent.KEYCODE_BUTTON_R2:
+				button = 16;
+				break;
+			case KeyEvent.KEYCODE_BUTTON_SELECT:
+				button = 4;
+				break;
+			case KeyEvent.KEYCODE_BUTTON_START:
+				button = 6;
+				break;
+			case KeyEvent.KEYCODE_BUTTON_THUMBL:
+				button = 7;
+				break;
+			case KeyEvent.KEYCODE_BUTTON_THUMBR:
+				button = 8;
+				break;
+			case KeyEvent.KEYCODE_DPAD_UP:
+				button = 11;
+				break;
+			case KeyEvent.KEYCODE_DPAD_DOWN:
+				button = 12;
+				break;
+			case KeyEvent.KEYCODE_DPAD_LEFT:
+				button = 13;
+				break;
+			case KeyEvent.KEYCODE_DPAD_RIGHT:
+				button = 14;
+				break;
+			case KeyEvent.KEYCODE_BUTTON_C:
+				button = 17;
+				break;
+			case KeyEvent.KEYCODE_BUTTON_Z:
+				button = 18;
+				break;
+
+			default:
+				button = keyCode - KeyEvent.KEYCODE_BUTTON_1 + 20;
+				break;
+		}
+		return button;
+	}
+
+	private int findJoystickDevice(int device_id) {
+		for (int i = 0; i < joysticksDevices.size(); i++) {
+			if (joysticksDevices.get(i).device_id == device_id) {
+				return i;
+			}
+		}
+
+		return -1;
+	}
+}

+ 44 - 0
platform/android/java/src/org/godotengine/godot/input/Joystick.java

@@ -0,0 +1,44 @@
+/*************************************************************************/
+/*  Joystick.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.input;
+
+import android.view.InputDevice.MotionRange;
+import java.util.ArrayList;
+
+/**
+ * POJO class to represent a Joystick input device.
+ */
+class Joystick {
+	int device_id;
+	String name;
+	ArrayList<MotionRange> axes;
+	ArrayList<MotionRange> hats;
+}

+ 157 - 0
platform/android/java/src/org/godotengine/godot/utils/GLUtils.java

@@ -0,0 +1,157 @@
+/*************************************************************************/
+/*  GLUtils.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.utils;
+
+import android.util.Log;
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLDisplay;
+
+/**
+ * Contains GL utilities methods.
+ */
+public class GLUtils {
+
+	private static final String TAG = GLUtils.class.getSimpleName();
+
+	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;
+
+	private static final String[] ATTRIBUTES_NAMES = new String[] {
+		"EGL_BUFFER_SIZE",
+		"EGL_ALPHA_SIZE",
+		"EGL_BLUE_SIZE",
+		"EGL_GREEN_SIZE",
+		"EGL_RED_SIZE",
+		"EGL_DEPTH_SIZE",
+		"EGL_STENCIL_SIZE",
+		"EGL_CONFIG_CAVEAT",
+		"EGL_CONFIG_ID",
+		"EGL_LEVEL",
+		"EGL_MAX_PBUFFER_HEIGHT",
+		"EGL_MAX_PBUFFER_PIXELS",
+		"EGL_MAX_PBUFFER_WIDTH",
+		"EGL_NATIVE_RENDERABLE",
+		"EGL_NATIVE_VISUAL_ID",
+		"EGL_NATIVE_VISUAL_TYPE",
+		"EGL_PRESERVED_RESOURCES",
+		"EGL_SAMPLES",
+		"EGL_SAMPLE_BUFFERS",
+		"EGL_SURFACE_TYPE",
+		"EGL_TRANSPARENT_TYPE",
+		"EGL_TRANSPARENT_RED_VALUE",
+		"EGL_TRANSPARENT_GREEN_VALUE",
+		"EGL_TRANSPARENT_BLUE_VALUE",
+		"EGL_BIND_TO_TEXTURE_RGB",
+		"EGL_BIND_TO_TEXTURE_RGBA",
+		"EGL_MIN_SWAP_INTERVAL",
+		"EGL_MAX_SWAP_INTERVAL",
+		"EGL_LUMINANCE_SIZE",
+		"EGL_ALPHA_MASK_SIZE",
+		"EGL_COLOR_BUFFER_TYPE",
+		"EGL_RENDERABLE_TYPE",
+		"EGL_CONFORMANT"
+	};
+
+	private static final int[] ATTRIBUTES = new int[] {
+		EGL10.EGL_BUFFER_SIZE,
+		EGL10.EGL_ALPHA_SIZE,
+		EGL10.EGL_BLUE_SIZE,
+		EGL10.EGL_GREEN_SIZE,
+		EGL10.EGL_RED_SIZE,
+		EGL10.EGL_DEPTH_SIZE,
+		EGL10.EGL_STENCIL_SIZE,
+		EGL10.EGL_CONFIG_CAVEAT,
+		EGL10.EGL_CONFIG_ID,
+		EGL10.EGL_LEVEL,
+		EGL10.EGL_MAX_PBUFFER_HEIGHT,
+		EGL10.EGL_MAX_PBUFFER_PIXELS,
+		EGL10.EGL_MAX_PBUFFER_WIDTH,
+		EGL10.EGL_NATIVE_RENDERABLE,
+		EGL10.EGL_NATIVE_VISUAL_ID,
+		EGL10.EGL_NATIVE_VISUAL_TYPE,
+		0x3030, // EGL10.EGL_PRESERVED_RESOURCES,
+		EGL10.EGL_SAMPLES,
+		EGL10.EGL_SAMPLE_BUFFERS,
+		EGL10.EGL_SURFACE_TYPE,
+		EGL10.EGL_TRANSPARENT_TYPE,
+		EGL10.EGL_TRANSPARENT_RED_VALUE,
+		EGL10.EGL_TRANSPARENT_GREEN_VALUE,
+		EGL10.EGL_TRANSPARENT_BLUE_VALUE,
+		0x3039, // EGL10.EGL_BIND_TO_TEXTURE_RGB,
+		0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGBA,
+		0x303B, // EGL10.EGL_MIN_SWAP_INTERVAL,
+		0x303C, // EGL10.EGL_MAX_SWAP_INTERVAL,
+		EGL10.EGL_LUMINANCE_SIZE,
+		EGL10.EGL_ALPHA_MASK_SIZE,
+		EGL10.EGL_COLOR_BUFFER_TYPE,
+		EGL10.EGL_RENDERABLE_TYPE,
+		0x3042 // EGL10.EGL_CONFORMANT
+	};
+
+	private GLUtils() {}
+
+	public static void checkEglError(String tag, String prompt, EGL10 egl) {
+		int error;
+		while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) {
+			Log.e(tag, String.format("%s: EGL error: 0x%x", prompt, error));
+		}
+	}
+
+	public static void printConfigs(EGL10 egl, EGLDisplay display,
+			EGLConfig[] configs) {
+		int numConfigs = configs.length;
+		Log.v(TAG, String.format("%d configurations", numConfigs));
+		for (int i = 0; i < numConfigs; i++) {
+			Log.v(TAG, String.format("Configuration %d:\n", i));
+			printConfig(egl, display, configs[i]);
+		}
+	}
+
+	private static void printConfig(EGL10 egl, EGLDisplay display,
+			EGLConfig config) {
+		int[] value = new int[1];
+		for (int i = 0; i < ATTRIBUTES.length; i++) {
+			int attribute = ATTRIBUTES[i];
+			String name = ATTRIBUTES_NAMES[i];
+			if (egl.eglGetConfigAttrib(display, config, attribute, value)) {
+				Log.i(TAG, String.format("  %s: %d\n", name, value[0]));
+			} else {
+				// Log.w(TAG, String.format("  %s: failed\n", name));
+				while (egl.eglGetError() != EGL10.EGL_SUCCESS)
+					;
+			}
+		}
+	}
+}

+ 39 - 0
platform/android/java/src/org/godotengine/godot/xr/XRMode.java

@@ -0,0 +1,39 @@
+/*************************************************************************/
+/*  XRMode.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.xr;
+
+/**
+ * Godot available XR modes.
+ */
+public enum XRMode {
+	PANCAKE, // Regular/flatscreen
+	OVR, // Oculus mobile VR SDK
+}

+ 112 - 0
platform/android/java/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java

@@ -0,0 +1,112 @@
+/*************************************************************************/
+/*  OvrConfigChooser.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.xr.ovr;
+
+import android.opengl.EGLExt;
+import android.opengl.GLSurfaceView;
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLDisplay;
+
+/**
+ * EGL config chooser for the Oculus Mobile VR SDK.
+ */
+public class OvrConfigChooser implements GLSurfaceView.EGLConfigChooser {
+
+	private static final int[] CONFIG_ATTRIBS = {
+		EGL10.EGL_RED_SIZE, 8,
+		EGL10.EGL_GREEN_SIZE, 8,
+		EGL10.EGL_BLUE_SIZE, 8,
+		EGL10.EGL_ALPHA_SIZE, 8, // Need alpha for the multi-pass timewarp compositor
+		EGL10.EGL_DEPTH_SIZE, 0,
+		EGL10.EGL_STENCIL_SIZE, 0,
+		EGL10.EGL_SAMPLES, 0,
+		EGL10.EGL_NONE
+	};
+
+	@Override
+	public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
+		// Do NOT use eglChooseConfig, because the Android EGL code pushes in
+		// multisample flags in eglChooseConfig if the user has selected the "force 4x
+		// MSAA" option in settings, and that is completely wasted for our warp
+		// target.
+		int[] numConfig = new int[1];
+		if (!egl.eglGetConfigs(display, null, 0, numConfig)) {
+			throw new IllegalArgumentException("eglGetConfigs failed.");
+		}
+
+		int configsCount = numConfig[0];
+		if (configsCount <= 0) {
+			throw new IllegalArgumentException("No configs match configSpec");
+		}
+
+		EGLConfig[] configs = new EGLConfig[configsCount];
+		if (!egl.eglGetConfigs(display, configs, configsCount, numConfig)) {
+			throw new IllegalArgumentException("eglGetConfigs #2 failed.");
+		}
+
+		int[] value = new int[1];
+		for (EGLConfig config : configs) {
+			egl.eglGetConfigAttrib(display, config, EGL10.EGL_RENDERABLE_TYPE, value);
+			if ((value[0] & EGLExt.EGL_OPENGL_ES3_BIT_KHR) != EGLExt.EGL_OPENGL_ES3_BIT_KHR) {
+				continue;
+			}
+
+			// The pbuffer config also needs to be compatible with normal window rendering
+			// so it can share textures with the window context.
+			egl.eglGetConfigAttrib(display, config, EGL10.EGL_SURFACE_TYPE, value);
+			if ((value[0] & (EGL10.EGL_WINDOW_BIT | EGL10.EGL_PBUFFER_BIT)) != (EGL10.EGL_WINDOW_BIT | EGL10.EGL_PBUFFER_BIT)) {
+				continue;
+			}
+
+			// Check each attribute in CONFIG_ATTRIBS (which are the attributes we care about)
+			// and ensure the value in config matches.
+			int attribIndex = 0;
+			while (CONFIG_ATTRIBS[attribIndex] != EGL10.EGL_NONE) {
+				egl.eglGetConfigAttrib(display, config, CONFIG_ATTRIBS[attribIndex], value);
+				if (value[0] != CONFIG_ATTRIBS[attribIndex + 1]) {
+					// Attribute key's value does not match the configs value.
+					// Start checking next config.
+					break;
+				}
+
+				// Step by two because CONFIG_ATTRIBS is in key/value pairs.
+				attribIndex += 2;
+			}
+
+			if (CONFIG_ATTRIBS[attribIndex] == EGL10.EGL_NONE) {
+				// All relevant attributes match, set the config and stop checking the rest.
+				return config;
+			}
+		}
+		return null;
+	}
+}

+ 58 - 0
platform/android/java/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java

@@ -0,0 +1,58 @@
+/*************************************************************************/
+/*  OvrContextFactory.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.xr.ovr;
+
+import android.opengl.EGL14;
+import android.opengl.GLSurfaceView;
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+
+/**
+ * EGL Context factory for the Oculus mobile VR SDK.
+ */
+public class OvrContextFactory implements GLSurfaceView.EGLContextFactory {
+
+	private static final int[] CONTEXT_ATTRIBS = {
+		EGL14.EGL_CONTEXT_CLIENT_VERSION, 3, EGL10.EGL_NONE
+	};
+
+	@Override
+	public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) {
+		return egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, CONTEXT_ATTRIBS);
+	}
+
+	@Override
+	public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) {
+		egl.eglDestroyContext(display, context);
+	}
+}

+ 60 - 0
platform/android/java/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java

@@ -0,0 +1,60 @@
+/*************************************************************************/
+/*  OvrWindowSurfaceFactory.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.xr.ovr;
+
+import android.opengl.GLSurfaceView;
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
+
+/**
+ * EGL window surface factory for the Oculus mobile VR SDK.
+ */
+public class OvrWindowSurfaceFactory implements GLSurfaceView.EGLWindowSurfaceFactory {
+
+	private final static int[] SURFACE_ATTRIBS = {
+		EGL10.EGL_WIDTH, 16,
+		EGL10.EGL_HEIGHT, 16,
+		EGL10.EGL_NONE
+	};
+
+	@Override
+	public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config,
+			Object nativeWindow) {
+		return egl.eglCreatePbufferSurface(display, config, SURFACE_ATTRIBS);
+	}
+
+	@Override
+	public void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface) {
+		egl.eglDestroySurface(display, surface);
+	}
+}

+ 151 - 0
platform/android/java/src/org/godotengine/godot/xr/pancake/PancakeConfigChooser.java

@@ -0,0 +1,151 @@
+/*************************************************************************/
+/*  PancakeConfigChooser.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.xr.pancake;
+
+import android.opengl.GLSurfaceView;
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLDisplay;
+import org.godotengine.godot.utils.GLUtils;
+
+/**
+ * Used to select the egl config for pancake games.
+ */
+public class PancakeConfigChooser implements GLSurfaceView.EGLConfigChooser {
+
+	private static final String TAG = PancakeConfigChooser.class.getSimpleName();
+
+	private int[] mValue = new int[1];
+
+	/* 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.
+	 */
+	private static int EGL_OPENGL_ES2_BIT = 4;
+	private static int[] s_configAttribs2 = {
+		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,
+		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 PancakeConfigChooser(int r, int g, int b, int a, int depth, int stencil) {
+		mRedSize = r;
+		mGreenSize = g;
+		mBlueSize = b;
+		mAlphaSize = a;
+		mDepthSize = depth;
+		mStencilSize = stencil;
+	}
+
+	public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
+
+		/* 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);
+
+		int numConfigs = num_config[0];
+
+		if (numConfigs <= 0) {
+			throw new IllegalArgumentException("No configs match configSpec");
+		}
+
+		/* 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);
+
+		if (GLUtils.DEBUG) {
+			GLUtils.printConfigs(egl, display, configs);
+		}
+		/* Now return the "best" one
+		 */
+		return chooseConfig(egl, display, configs);
+	}
+
+	public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
+			EGLConfig[] configs) {
+		for (EGLConfig config : configs) {
+			int d = findConfigAttrib(egl, display, config,
+					EGL10.EGL_DEPTH_SIZE, 0);
+			int s = findConfigAttrib(egl, display, config,
+					EGL10.EGL_STENCIL_SIZE, 0);
+
+			// We need at least mDepthSize and mStencilSize bits
+			if (d < mDepthSize || s < mStencilSize)
+				continue;
+
+			// We want an *exact* match for red/green/blue/alpha
+			int r = findConfigAttrib(egl, display, config,
+					EGL10.EGL_RED_SIZE, 0);
+			int g = findConfigAttrib(egl, display, config,
+					EGL10.EGL_GREEN_SIZE, 0);
+			int b = findConfigAttrib(egl, display, config,
+					EGL10.EGL_BLUE_SIZE, 0);
+			int a = findConfigAttrib(egl, display, config,
+					EGL10.EGL_ALPHA_SIZE, 0);
+
+			if (r == mRedSize && g == mGreenSize && b == mBlueSize && a == mAlphaSize)
+				return config;
+		}
+		return null;
+	}
+
+	private int findConfigAttrib(EGL10 egl, EGLDisplay display,
+			EGLConfig config, int attribute, int defaultValue) {
+
+		if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
+			return mValue[0];
+		}
+		return defaultValue;
+	}
+
+	// Subclasses can adjust these values:
+	protected int mRedSize;
+	protected int mGreenSize;
+	protected int mBlueSize;
+	protected int mAlphaSize;
+	protected int mDepthSize;
+	protected int mStencilSize;
+}

+ 81 - 0
platform/android/java/src/org/godotengine/godot/xr/pancake/PancakeContextFactory.java

@@ -0,0 +1,81 @@
+/*************************************************************************/
+/*  PancakeContextFactory.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.xr.pancake;
+
+import android.opengl.GLSurfaceView;
+import android.util.Log;
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import org.godotengine.godot.GodotLib;
+import org.godotengine.godot.utils.GLUtils;
+
+/**
+ * Factory used to setup the opengl context for pancake games.
+ */
+public class PancakeContextFactory implements GLSurfaceView.EGLContextFactory {
+	private static final String TAG = PancakeContextFactory.class.getSimpleName();
+
+	private static final int _EGL_CONTEXT_FLAGS_KHR = 0x30FC;
+	private static final int _EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR = 0x00000001;
+
+	private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+
+	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 :");
+
+		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);
+		} 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);
+		}
+		GLUtils.checkEglError(TAG, "After eglCreateContext", egl);
+		return context;
+	}
+
+	public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) {
+		egl.eglDestroyContext(display, context);
+	}
+}

+ 61 - 0
platform/android/java/src/org/godotengine/godot/xr/pancake/PancakeFallbackConfigChooser.java

@@ -0,0 +1,61 @@
+/*************************************************************************/
+/*  PancakeFallbackConfigChooser.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.xr.pancake;
+
+import android.util.Log;
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLDisplay;
+import org.godotengine.godot.utils.GLUtils;
+
+/* Fallback if 32bit View is not supported*/
+public class PancakeFallbackConfigChooser extends PancakeConfigChooser {
+
+	private static final String TAG = PancakeFallbackConfigChooser.class.getSimpleName();
+
+	private PancakeConfigChooser fallback;
+
+	public PancakeFallbackConfigChooser(int r, int g, int b, int a, int depth, int stencil, PancakeConfigChooser fallback) {
+		super(r, g, b, a, depth, stencil);
+		this.fallback = fallback;
+	}
+
+	@Override
+	public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) {
+		EGLConfig ec = super.chooseConfig(egl, display, configs);
+		if (ec == null) {
+			Log.w(TAG, "Trying ConfigChooser fallback");
+			ec = fallback.chooseConfig(egl, display, configs);
+			GLUtils.use_32 = false;
+		}
+		return ec;
+	}
+}

+ 20 - 0
platform/android/java_godot_wrapper.cpp

@@ -60,6 +60,8 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance) {
 	_set_clipboard = p_env->GetMethodID(cls, "setClipboard", "(Ljava/lang/String;)V");
 	_request_permission = p_env->GetMethodID(cls, "requestPermission", "(Ljava/lang/String;)Z");
 	_init_input_devices = p_env->GetMethodID(cls, "initInputDevices", "()V");
+	_get_surface = p_env->GetMethodID(cls, "getSurface", "()Landroid/view/Surface;");
+	_is_activity_resumed = p_env->GetMethodID(cls, "isActivityResumed", "()Z");
 }
 
 GodotJavaWrapper::~GodotJavaWrapper() {
@@ -191,3 +193,21 @@ void GodotJavaWrapper::init_input_devices() {
 		env->CallVoidMethod(godot_instance, _init_input_devices);
 	}
 }
+
+jobject GodotJavaWrapper::get_surface() {
+	if (_get_surface) {
+		JNIEnv *env = ThreadAndroid::get_env();
+		return env->CallObjectMethod(godot_instance, _get_surface);
+	} else {
+		return NULL;
+	}
+}
+
+bool GodotJavaWrapper::is_activity_resumed() {
+	if (_is_activity_resumed) {
+		JNIEnv *env = ThreadAndroid::get_env();
+		return env->CallBooleanMethod(godot_instance, _is_activity_resumed);
+	} else {
+		return false;
+	}
+}

+ 4 - 0
platform/android/java_godot_wrapper.h

@@ -55,6 +55,8 @@ private:
 	jmethodID _set_clipboard = 0;
 	jmethodID _request_permission = 0;
 	jmethodID _init_input_devices = 0;
+	jmethodID _get_surface = 0;
+	jmethodID _is_activity_resumed = 0;
 
 public:
 	GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance);
@@ -78,6 +80,8 @@ public:
 	void set_clipboard(const String &p_text);
 	bool request_permission(const String &p_name);
 	void init_input_devices();
+	jobject get_surface();
+	bool is_activity_resumed();
 };
 
 #endif /* !JAVA_GODOT_WRAPPER_H */