Parcourir la source

Merge pull request #10422 from endragor/gdnative-android

Make GDNative work on Android
Rémi Verschelde il y a 8 ans
Parent
commit
6f762ad709

+ 12 - 2
main/main.cpp

@@ -1112,10 +1112,20 @@ bool Main::start() {
 				test = args[i + 1];
 			} else if (args[i] == "--export") {
 				editor = true; //needs editor
-				_export_preset = args[i + 1];
+				if (i + 1 < args.size()) {
+					_export_preset = args[i + 1];
+				} else {
+					ERR_PRINT("Export preset name not specified");
+					return false;
+				}
 			} else if (args[i] == "--export-debug") {
 				editor = true; //needs editor
-				_export_preset = args[i + 1];
+				if (i + 1 < args.size()) {
+					_export_preset = args[i + 1];
+				} else {
+					ERR_PRINT("Export preset name not specified");
+					return false;
+				}
 				export_debug = true;
 			} else {
 				// The parameter does not match anything known, don't skip the next argument

+ 26 - 10
modules/gdnative/gdnative.cpp

@@ -40,7 +40,7 @@
 const String init_symbol = "godot_gdnative_init";
 const String terminate_symbol = "godot_gdnative_terminate";
 
-String GDNativeLibrary::platform_names[NUM_PLATFORMS] = {
+String GDNativeLibrary::platform_names[NUM_PLATFORMS + 1] = {
 	"X11_32bit",
 	"X11_64bit",
 	"Windows_32bit",
@@ -48,11 +48,15 @@ String GDNativeLibrary::platform_names[NUM_PLATFORMS] = {
 	"OSX",
 
 	"Android",
-	"iOS",
 
-	"WebAssembly"
+	"iOS_32bit",
+	"iOS_64bit",
+
+	"WebAssembly",
+
+	""
 };
-String GDNativeLibrary::platform_lib_ext[NUM_PLATFORMS] = {
+String GDNativeLibrary::platform_lib_ext[NUM_PLATFORMS + 1] = {
 	"so",
 	"so",
 	"dll",
@@ -60,21 +64,30 @@ String GDNativeLibrary::platform_lib_ext[NUM_PLATFORMS] = {
 	"dylib",
 
 	"so",
+
 	"dylib",
+	"dylib",
+
+	"wasm",
 
-	"wasm"
+	""
 };
 
-// TODO(karroffel): make this actually do the right thing.
 GDNativeLibrary::Platform GDNativeLibrary::current_platform =
 #if defined(X11_ENABLED)
-		X11_64BIT;
+		(sizeof(void *) == 8 ? X11_64BIT : X11_32BIT);
 #elif defined(WINDOWS_ENABLED)
-		WINDOWS_64BIT;
+		(sizeof(void *) == 8 ? WINDOWS_64BIT : WINDOWS_32BIT);
 #elif defined(OSX_ENABLED)
 		OSX;
+#elif defined(IPHONE_ENABLED)
+		(sizeof(void *) == 8 ? IOS_64BIT : IOS_32BIT);
+#elif defined(ANDROID_ENABLED)
+		ANDROID;
+#elif defined(JAVASCRIPT_ENABLED)
+		WASM;
 #else
-		X11_64BIT; // need a sensible default..
+		NUM_PLATFORMS;
 #endif
 
 GDNativeLibrary::GDNativeLibrary()
@@ -151,7 +164,10 @@ String GDNativeLibrary::get_library_path(StringName p_platform) const {
 }
 
 String GDNativeLibrary::get_active_library_path() const {
-	return library_paths[GDNativeLibrary::current_platform];
+	if (GDNativeLibrary::current_platform != NUM_PLATFORMS) {
+		return library_paths[GDNativeLibrary::current_platform];
+	}
+	return "";
 }
 
 GDNative::GDNative() {

+ 11 - 6
modules/gdnative/gdnative.h

@@ -48,11 +48,17 @@ class GDNativeLibrary : public Resource {
 		// NOTE(karroffel): I heard OSX 32 bit is dead, so 64 only
 		OSX,
 
-		// TODO(karroffel): all different android versions and archs
+		// Android .so files must be located in directories corresponding to Android ABI names:
+		// https://developer.android.com/ndk/guides/abis.html
+		// Android runtime will select the matching library depending on the device.
+		// The value here must simply point to the .so name, for example:
+		// "res://libmy_gdnative.so" or "libmy_gdnative.so",
+		// while in the project the actual paths can be "lib/android/armeabi-v7a/libmy_gdnative.so",
+		// "lib/android/arm64-v8a/libmy_gdnative.so".
 		ANDROID,
 
-		// TODO(karroffe): all different iOS versions and archs
-		IOS,
+		IOS_32BIT,
+		IOS_64BIT,
 
 		// TODO(karroffel): figure out how to deal with web stuff at all...
 		WASM,
@@ -64,10 +70,9 @@ class GDNativeLibrary : public Resource {
 
 	};
 
-	static String platform_names[NUM_PLATFORMS];
-	static String platform_lib_ext[NUM_PLATFORMS];
+	static String platform_names[NUM_PLATFORMS + 1];
+	static String platform_lib_ext[NUM_PLATFORMS + 1];
 
-	// TODO(karroffel): make this actually do something lol.
 	static Platform current_platform;
 
 	String library_paths[NUM_PLATFORMS];

+ 1 - 1
modules/gdnative/godot/array.h

@@ -37,7 +37,7 @@ extern "C" {
 
 #include <stdint.h>
 
-#define GODOT_ARRAY_SIZE 8
+#define GODOT_ARRAY_SIZE sizeof(void *)
 
 #ifndef GODOT_CORE_API_GODOT_ARRAY_TYPE_DEFINED
 #define GODOT_CORE_API_GODOT_ARRAY_TYPE_DEFINED

+ 1 - 1
modules/gdnative/godot/dictionary.h

@@ -36,7 +36,7 @@ extern "C" {
 
 #include <stdint.h>
 
-#define GODOT_DICTIONARY_SIZE 8
+#define GODOT_DICTIONARY_SIZE sizeof(void *)
 
 #ifndef GODOT_CORE_API_GODOT_DICTIONARY_TYPE_DEFINED
 #define GODOT_CORE_API_GODOT_DICTIONARY_TYPE_DEFINED

+ 1 - 1
modules/gdnative/godot/gdnative.h

@@ -56,7 +56,7 @@ extern "C" {
 #define GDAPI GDCALLINGCONV
 #endif
 #else
-#define GDCALLINGCONV __attribute__((sysv_abi))
+#define GDCALLINGCONV __attribute__((sysv_abi, visibility("default")))
 #define GDAPI GDCALLINGCONV
 #endif
 

+ 1 - 1
modules/gdnative/godot/node_path.h

@@ -36,7 +36,7 @@ extern "C" {
 
 #include <stdint.h>
 
-#define GODOT_NODE_PATH_SIZE 8
+#define GODOT_NODE_PATH_SIZE sizeof(void *)
 
 #ifndef GODOT_CORE_API_GODOT_NODE_PATH_TYPE_DEFINED
 #define GODOT_CORE_API_GODOT_NODE_PATH_TYPE_DEFINED

+ 7 - 7
modules/gdnative/godot/pool_arrays.h

@@ -38,7 +38,7 @@ extern "C" {
 
 /////// PoolByteArray
 
-#define GODOT_POOL_BYTE_ARRAY_SIZE 8
+#define GODOT_POOL_BYTE_ARRAY_SIZE sizeof(void *)
 
 #ifndef GODOT_CORE_API_GODOT_POOL_BYTE_ARRAY_TYPE_DEFINED
 #define GODOT_CORE_API_GODOT_POOL_BYTE_ARRAY_TYPE_DEFINED
@@ -49,7 +49,7 @@ typedef struct {
 
 /////// PoolIntArray
 
-#define GODOT_POOL_INT_ARRAY_SIZE 8
+#define GODOT_POOL_INT_ARRAY_SIZE sizeof(void *)
 
 #ifndef GODOT_CORE_API_GODOT_POOL_INT_ARRAY_TYPE_DEFINED
 #define GODOT_CORE_API_GODOT_POOL_INT_ARRAY_TYPE_DEFINED
@@ -60,7 +60,7 @@ typedef struct {
 
 /////// PoolRealArray
 
-#define GODOT_POOL_REAL_ARRAY_SIZE 8
+#define GODOT_POOL_REAL_ARRAY_SIZE sizeof(void *)
 
 #ifndef GODOT_CORE_API_GODOT_POOL_REAL_ARRAY_TYPE_DEFINED
 #define GODOT_CORE_API_GODOT_POOL_REAL_ARRAY_TYPE_DEFINED
@@ -71,7 +71,7 @@ typedef struct {
 
 /////// PoolStringArray
 
-#define GODOT_POOL_STRING_ARRAY_SIZE 8
+#define GODOT_POOL_STRING_ARRAY_SIZE sizeof(void *)
 
 #ifndef GODOT_CORE_API_GODOT_POOL_STRING_ARRAY_TYPE_DEFINED
 #define GODOT_CORE_API_GODOT_POOL_STRING_ARRAY_TYPE_DEFINED
@@ -82,7 +82,7 @@ typedef struct {
 
 /////// PoolVector2Array
 
-#define GODOT_POOL_VECTOR2_ARRAY_SIZE 8
+#define GODOT_POOL_VECTOR2_ARRAY_SIZE sizeof(void *)
 
 #ifndef GODOT_CORE_API_GODOT_POOL_VECTOR2_ARRAY_TYPE_DEFINED
 #define GODOT_CORE_API_GODOT_POOL_VECTOR2_ARRAY_TYPE_DEFINED
@@ -93,7 +93,7 @@ typedef struct {
 
 /////// PoolVector3Array
 
-#define GODOT_POOL_VECTOR3_ARRAY_SIZE 8
+#define GODOT_POOL_VECTOR3_ARRAY_SIZE sizeof(void *)
 
 #ifndef GODOT_CORE_API_GODOT_POOL_VECTOR3_ARRAY_TYPE_DEFINED
 #define GODOT_CORE_API_GODOT_POOL_VECTOR3_ARRAY_TYPE_DEFINED
@@ -104,7 +104,7 @@ typedef struct {
 
 /////// PoolColorArray
 
-#define GODOT_POOL_COLOR_ARRAY_SIZE 8
+#define GODOT_POOL_COLOR_ARRAY_SIZE sizeof(void *)
 
 #ifndef GODOT_CORE_API_GODOT_POOL_COLOR_ARRAY_TYPE_DEFINED
 #define GODOT_CORE_API_GODOT_POOL_COLOR_ARRAY_TYPE_DEFINED

+ 1 - 1
modules/gdnative/godot/rid.h

@@ -36,7 +36,7 @@ extern "C" {
 
 #include <stdint.h>
 
-#define GODOT_RID_SIZE 8
+#define GODOT_RID_SIZE sizeof(void *)
 
 #ifndef GODOT_CORE_API_GODOT_RID_TYPE_DEFINED
 #define GODOT_CORE_API_GODOT_RID_TYPE_DEFINED

+ 1 - 1
modules/gdnative/godot/string.h

@@ -37,7 +37,7 @@ extern "C" {
 #include <stdint.h>
 #include <wchar.h>
 
-#define GODOT_STRING_SIZE 8
+#define GODOT_STRING_SIZE sizeof(void *)
 
 #ifndef GODOT_CORE_API_GODOT_STRING_TYPE_DEFINED
 #define GODOT_CORE_API_GODOT_STRING_TYPE_DEFINED

+ 1 - 1
modules/gdnative/godot/variant.h

@@ -36,7 +36,7 @@ extern "C" {
 
 #include <stdint.h>
 
-#define GODOT_VARIANT_SIZE 24
+#define GODOT_VARIANT_SIZE (16 + sizeof(void *))
 
 #ifndef GODOT_CORE_API_GODOT_VARIANT_TYPE_DEFINED
 #define GODOT_CORE_API_GODOT_VARIANT_TYPE_DEFINED

+ 1 - 1
modules/nativescript/godot_nativescript.h

@@ -58,7 +58,7 @@ extern "C" {
 #define GDAPI GDCALLINGCONV
 #endif
 #else
-#define GDCALLINGCONV __attribute__((sysv_abi))
+#define GDCALLINGCONV __attribute__((sysv_abi, visibility("default")))
 #define GDAPI GDCALLINGCONV
 #endif
 

+ 2 - 0
modules/nativescript/nativescript.cpp

@@ -994,6 +994,8 @@ void NativeScriptLanguage::init_library(const Ref<GDNativeLibrary> &lib) {
 #endif
 	// See if this library was "registered" already.
 	const String &lib_path = lib->get_active_library_path();
+	ERR_EXPLAIN(lib->get_name() + " does not have a library for the current platform");
+	ERR_FAIL_COND(lib_path.length() == 0);
 	Map<String, Ref<GDNative> >::Element *E = library_gdnatives.find(lib_path);
 
 	if (!E) {

+ 1 - 1
platform/android/detect.py

@@ -240,7 +240,7 @@ def configure(env):
 
     env.Append(CPPPATH=['#platform/android'])
     env.Append(CPPFLAGS=['-DANDROID_ENABLED', '-DUNIX_ENABLED', '-DNO_FCNTL', '-DMPC_FIXED_POINT'])
-    env.Append(LIBS=['OpenSLES', 'EGL', 'GLESv3', 'android', 'log', 'z'])
+    env.Append(LIBS=['OpenSLES', 'EGL', 'GLESv3', 'android', 'log', 'z', 'dl'])
 
     # TODO: Move that to opus module's config
     if("module_opus_enabled" in env and env["module_opus_enabled"] != "no"):

+ 30 - 5
platform/android/export/export.cpp

@@ -468,11 +468,36 @@ class EditorExportAndroid : public EditorExportPlatform {
 		return zipfi;
 	}
 
-	static Error save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) {
+	static Set<String> get_abis() {
+		Set<String> abis;
+		abis.insert("armeabi");
+		abis.insert("armeabi-v7a");
+		abis.insert("arm64-v8a");
+		abis.insert("x86");
+		abis.insert("x86_64");
+		abis.insert("mips");
+		abis.insert("mips64");
+		return abis;
+	}
 
+	static Error save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) {
 		APKExportData *ed = (APKExportData *)p_userdata;
 		String dst_path = p_path;
-		dst_path = dst_path.replace_first("res://", "assets/");
+		static Set<String> android_abis = get_abis();
+
+		if (dst_path.ends_with(".so")) {
+			String abi = dst_path.get_base_dir().get_file().strip_edges(); // parent dir name
+			if (android_abis.has(abi)) {
+				dst_path = "lib/" + abi + "/" + dst_path.get_file();
+			} else {
+				String err = "Dynamic libraries must be located in the folder named after Android ABI they were compiled for. " +
+							 p_path + " does not follow this convention.";
+				ERR_PRINT(err.utf8().get_data());
+				return ERR_FILE_BAD_PATH;
+			}
+		} else {
+			dst_path = dst_path.replace_first("res://", "assets/");
+		}
 
 		zip_fileinfo zipfi = get_zip_fileinfo();
 
@@ -1393,15 +1418,15 @@ public:
 				}
 			}
 
-			if (file == "lib/x86/libgodot_android.so" && !export_x86) {
+			if (file == "lib/x86/*.so" && !export_x86) {
 				skip = true;
 			}
 
-			if (file.match("lib/armeabi*/libgodot_android.so") && !export_arm) {
+			if (file.match("lib/armeabi*/*.so") && !export_arm) {
 				skip = true;
 			}
 
-			if (file.match("lib/arm64*/libgodot_android.so") && !export_arm64) {
+			if (file.match("lib/arm64*/*.so") && !export_arm64) {
 				skip = true;
 			}
 

+ 150 - 105
platform/android/java/src/org/godotengine/godot/Godot.java

@@ -92,7 +92,7 @@ import android.os.SystemClock;
 
 
 public class Godot extends Activity implements SensorEventListener, IDownloaderClient
-{	
+{
 
 	static final int MAX_SINGLETONS = 64;
 	private IStub mDownloaderClientStub;
@@ -139,9 +139,9 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
         	com.godot.game.R.string.text_button_pause;
         mPauseButton.setText(stringResourceID);
     }
-    
+
 	static public class SingletonBase {
-        
+
 		protected void registerClass(String p_name, String[] p_methods) {
 
 			GodotLib.singleton(p_name,this);
@@ -210,6 +210,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 */
 
 	private String[] command_line;
+	private boolean use_apk_expansion;
 
 	public GodotView mView;
 	private boolean godot_initialized=false;
@@ -264,27 +265,40 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 		layout = new FrameLayout(this);
 		layout.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT));
 		setContentView(layout);
-		
+
 		// GodotEditText layout
-		GodotEditText edittext = new GodotEditText(this); 
+		GodotEditText edittext = new GodotEditText(this);
 		   edittext.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.WRAP_CONTENT));
         // ...add to FrameLayout
 		   layout.addView(edittext);
-		
+
 		mView = new GodotView(getApplication(),io,use_gl2,use_32_bits, this);
 		layout.addView(mView,new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT));
-		setKeepScreenOn(GodotLib.getGlobal("display/driver/keep_screen_on").equals("True"));
-		
-        edittext.setView(mView);
-        io.setEdit(edittext);
-		
+		edittext.setView(mView);
+		io.setEdit(edittext);
+
 		// Ad layout
 		adLayout = new RelativeLayout(this);
 		adLayout.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT));
 		layout.addView(adLayout);
-		
+
+		final String[] current_command_line = command_line;
+		final GodotView view = mView;
+		mView.queueEvent(new Runnable() {
+			@Override
+			public void run() {
+				GodotLib.setup(current_command_line);
+				runOnUiThread(new Runnable() {
+					@Override
+					public void run() {
+						view.setKeepScreenOn("True".equals(GodotLib.getGlobal("display/driver/keep_screen_on")));
+					}
+				});
+			}
+		});
+
 	}
-	
+
 	public void setKeepScreenOn(final boolean p_enabled) {
 		keep_screen_on = p_enabled;
 		if (mView != null){
@@ -315,13 +329,13 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 			}
 		});
 	}
-	
+
 	private static Godot _self;
-	
+
 	public static Godot getInstance(){
 		return Godot._self;
 	}
-	
+
 
 	private String[] getCommandLine() {
             InputStream is;
@@ -402,7 +416,6 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 		        Log.d("GODOT","   " + command_line[w]);
 		    }
 		}*/
-		GodotLib.initialize(this,io.needsReloadHooks(),command_line,getAssets());
 		mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
 		mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
 		mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME);
@@ -411,9 +424,12 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 		mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
 		mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME);
 
+		GodotLib.initialize(this, io.needsReloadHooks(), getAssets(), use_apk_expansion);
+
 		result_callback = null;
 
 		mPaymentsManager = PaymentsManager.createManager(this).initService();
+
 		godot_initialized=true;
 
 	}
@@ -426,7 +442,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 
 
 
-	@Override 
+	@Override
 	protected void onCreate(Bundle icicle) {
 
 		Log.d("GODOT", "** GODOT ACTIVITY CREATED HERE ***\n");
@@ -437,12 +453,10 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 		//window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
 		window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
 
-
 		//check for apk expansion API
 		if (true) {
 		        boolean md5mismatch = false;
 			command_line = getCommandLine();
-			boolean use_apk_expansion=false;
 			String main_pack_md5=null;
 			String main_pack_key=null;
 
@@ -528,23 +542,23 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 
 				if (!pack_valid) {
 					Log.d("GODOT", "Pack Invalid, try re-downloading.");
-					
+
 					Intent notifierIntent = new Intent(this, this.getClass());
 					notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
 			                                Intent.FLAG_ACTIVITY_CLEAR_TOP);
-			        
+
                                                 PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
 			                notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT);
-					
+
 			        int startResult;
 					try {
 						Log.d("GODOT", "INITIALIZING DOWNLOAD");
 						startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(
-								getApplicationContext(), 
-								pendingIntent, 
+								getApplicationContext(),
+								pendingIntent,
 								GodotDownloaderService.class);
 						Log.d("GODOT", "DOWNLOAD SERVICE FINISHED:" + startResult);
-					
+
 			        if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
 						Log.d("GODOT", "DOWNLOAD REQUIRED");
 			            // This is where you do set up to display the download
@@ -563,7 +577,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 			            mCellMessage = findViewById(com.godot.game.R.id.approveCellular);
 			            mPauseButton = (Button) findViewById(com.godot.game.R.id.pauseButton);
 			            mWiFiSettingsButton = (Button) findViewById(com.godot.game.R.id.wifiSettingsButton);
-			        	
+
 			            return;
 			        } else{
 			        	Log.d("GODOT", "NO DOWNLOAD REQUIRED");
@@ -582,7 +596,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 
 		initializeGodot();
 
-		
+
 		//instanceSingleton( new GodotFacebook(this) );
 
 
@@ -590,14 +604,14 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 
 
 	@Override protected void onDestroy(){
-		
+
 		if(mPaymentsManager != null ) mPaymentsManager.destroy();
 		for(int i=0;i<singleton_count;i++) {
 			singletons[i].onMainDestroy();
 		}
 		super.onDestroy();
 	}
-	
+
 	@Override protected void onPause() {
 		super.onPause();
 		if (!godot_initialized){
@@ -607,8 +621,13 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 			return;
 		}
 		mView.onPause();
+		mView.queueEvent(new Runnable() {
+			@Override
+			public void run() {
+				GodotLib.focusout();
+			}
+		});
 		mSensorManager.unregisterListener(this);
-		GodotLib.focusout();
 
 		for(int i=0;i<singleton_count;i++) {
 			singletons[i].onMainPause();
@@ -625,10 +644,16 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 		}
 
 		mView.onResume();
+		mView.queueEvent(new Runnable() {
+			@Override
+			public void run() {
+				GodotLib.focusin();
+			}
+		});
 		mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME);
 		mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME);
 		mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME);
-		GodotLib.focusin();
+
 		if(use_immersive && Build.VERSION.SDK_INT >= 19.0){ // check if the application runs on an android 4.4+
 			Window window = getWindow();
 			window.getDecorView().setSystemUiVisibility(
@@ -644,8 +669,8 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 
 			singletons[i].onMainResume();
 		}
-		
-		
+
+
 
 	}
 
@@ -670,32 +695,39 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 	@Override public void onSensorChanged(SensorEvent event) {
 		Display display = ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
 		int displayRotation = display.getRotation();
-		
+
 		float[] adjustedValues = new float[3];
 		final int axisSwap[][] = {
-		{  1,  -1,  0,  1  },     // ROTATION_0 
-		{-1,  -1,  1,  0  },     // ROTATION_90 
-		{-1,    1,  0,  1  },     // ROTATION_180 
-		{  1,    1,  1,  0  }  }; // ROTATION_270 
-
-		final int[] as = axisSwap[displayRotation]; 
-		adjustedValues[0]  =  (float)as[0] * event.values[ as[2] ]; 
-		adjustedValues[1]  =  (float)as[1] * event.values[ as[3] ]; 
+		{  1,  -1,  0,  1  },     // ROTATION_0
+		{-1,  -1,  1,  0  },     // ROTATION_90
+		{-1,    1,  0,  1  },     // ROTATION_180
+		{  1,    1,  1,  0  }  }; // ROTATION_270
+
+		final int[] as = axisSwap[displayRotation];
+		adjustedValues[0]  =  (float)as[0] * event.values[ as[2] ];
+		adjustedValues[1]  =  (float)as[1] * event.values[ as[3] ];
 		adjustedValues[2]  =  event.values[2];
-		
-		float x = adjustedValues[0];
-		float y = adjustedValues[1];
-		float z = adjustedValues[2];
-
-		int typeOfSensor = event.sensor.getType();
-		if (typeOfSensor == event.sensor.TYPE_ACCELEROMETER) {
-			GodotLib.accelerometer(x,y,z);
-		}
-		if (typeOfSensor == event.sensor.TYPE_MAGNETIC_FIELD) {
-			GodotLib.magnetometer(x,y,z);
-		}
-		if (typeOfSensor == event.sensor.TYPE_GYROSCOPE) {
-			GodotLib.gyroscope(x,y,z);
+
+		final float x = adjustedValues[0];
+		final float y = adjustedValues[1];
+		final float z = adjustedValues[2];
+
+		final int typeOfSensor = event.sensor.getType();
+		if (mView != null) {
+			mView.queueEvent(new Runnable() {
+				@Override
+				public void run() {
+					if (typeOfSensor == Sensor.TYPE_ACCELEROMETER) {
+						GodotLib.accelerometer(x,y,z);
+					}
+					if (typeOfSensor == Sensor.TYPE_MAGNETIC_FIELD) {
+						GodotLib.magnetometer(x,y,z);
+					}
+					if (typeOfSensor == Sensor.TYPE_GYROSCOPE) {
+						GodotLib.gyroscope(x,y,z);
+					}
+				}
+			});
 		}
 	}
 
@@ -722,7 +754,14 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 	@Override public void onBackPressed() {
 
 		System.out.printf("** BACK REQUEST!\n");
-		//GodotLib.back();
+		if (mView != null) {
+			mView.queueEvent(new Runnable() {
+				@Override
+				public void run() {
+					GodotLib.back();
+				}
+			});
+		}
 	}
 
 	public void forceQuit() {
@@ -733,7 +772,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 
 
 	private boolean obbIsCorrupted(String f, String main_pack_md5){
-	
+
 		    try {
 
 			    InputStream fis =  new FileInputStream(f);
@@ -780,58 +819,64 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 	}
 
 	//@Override public boolean dispatchTouchEvent (MotionEvent event) {
-	public boolean gotTouchEvent(MotionEvent event) {
+	public boolean gotTouchEvent(final MotionEvent event) {
 
 		super.onTouchEvent(event);
-		int evcount=event.getPointerCount();
+		final int evcount=event.getPointerCount();
 		if (evcount==0)
 			return true;
 
-		int[] arr = new int[event.getPointerCount()*3];
+		if (mView != null) {
+			final int[] arr = new int[event.getPointerCount()*3];
 
-		for(int i=0;i<event.getPointerCount();i++) {
+			for(int i=0;i<event.getPointerCount();i++) {
 
-			arr[i*3+0]=(int)event.getPointerId(i);
-			arr[i*3+1]=(int)event.getX(i);
-			arr[i*3+2]=(int)event.getY(i);
-		}
+				arr[i*3+0]=(int)event.getPointerId(i);
+				arr[i*3+1]=(int)event.getX(i);
+				arr[i*3+2]=(int)event.getY(i);
+			}
 
-		//System.out.printf("gaction: %d\n",event.getAction());
-		switch(event.getAction()&MotionEvent.ACTION_MASK) {
-
-			case MotionEvent.ACTION_DOWN: {
-				GodotLib.touch(0,0,evcount,arr);
-				//System.out.printf("action down at: %f,%f\n", event.getX(),event.getY());
-			} break;
-			case MotionEvent.ACTION_MOVE: {
-				GodotLib.touch(1,0,evcount,arr);
-				/*
-				for(int i=0;i<event.getPointerCount();i++) {
-					System.out.printf("%d - moved to: %f,%f\n",i, event.getX(i),event.getY(i));
-				}
-				*/
-			} break;
-			case MotionEvent.ACTION_POINTER_UP: {
-				final int indexPointUp = event.getActionIndex();
-				final int pointer_idx = event.getPointerId(indexPointUp); 
-				GodotLib.touch(4,pointer_idx,evcount,arr);
-				//System.out.printf("%d - s.up at: %f,%f\n",pointer_idx, event.getX(pointer_idx),event.getY(pointer_idx));
-			} break;
-			case MotionEvent.ACTION_POINTER_DOWN: {
-				int pointer_idx = event.getActionIndex();
-				GodotLib.touch(3,pointer_idx,evcount,arr);
-				//System.out.printf("%d - s.down at: %f,%f\n",pointer_idx, event.getX(pointer_idx),event.getY(pointer_idx));
-			} break;
-			case MotionEvent.ACTION_CANCEL:
-			case MotionEvent.ACTION_UP: {
-				GodotLib.touch(2,0,evcount,arr);
-				/*
-				for(int i=0;i<event.getPointerCount();i++) {
-					System.out.printf("%d - up! %f,%f\n",i, event.getX(i),event.getY(i));
+			//System.out.printf("gaction: %d\n",event.getAction());
+			final int action = event.getAction() & MotionEvent.ACTION_MASK;
+			mView.queueEvent(new Runnable() {
+				@Override
+				public void run() {
+					switch(action) {
+						case MotionEvent.ACTION_DOWN: {
+							GodotLib.touch(0,0,evcount,arr);
+							//System.out.printf("action down at: %f,%f\n", event.getX(),event.getY());
+						} break;
+						case MotionEvent.ACTION_MOVE: {
+							GodotLib.touch(1,0,evcount,arr);
+							/*
+							for(int i=0;i<event.getPointerCount();i++) {
+								System.out.printf("%d - moved to: %f,%f\n",i, event.getX(i),event.getY(i));
+							}
+							*/
+						} break;
+						case MotionEvent.ACTION_POINTER_UP: {
+							final int indexPointUp = event.getActionIndex();
+							final int pointer_idx = event.getPointerId(indexPointUp);
+							GodotLib.touch(4,pointer_idx,evcount,arr);
+							//System.out.printf("%d - s.up at: %f,%f\n",pointer_idx, event.getX(pointer_idx),event.getY(pointer_idx));
+						} break;
+						case MotionEvent.ACTION_POINTER_DOWN: {
+							int pointer_idx = event.getActionIndex();
+							GodotLib.touch(3,pointer_idx,evcount,arr);
+							//System.out.printf("%d - s.down at: %f,%f\n",pointer_idx, event.getX(pointer_idx),event.getY(pointer_idx));
+						} break;
+						case MotionEvent.ACTION_CANCEL:
+						case MotionEvent.ACTION_UP: {
+							GodotLib.touch(2,0,evcount,arr);
+							/*
+							for(int i=0;i<event.getPointerCount();i++) {
+								System.out.printf("%d - up! %f,%f\n",i, event.getX(i),event.getY(i));
+							}
+							*/
+						} break;
+					}
 				}
-				*/
-			} break;
-
+			});
 		}
 		return true;
 	}
@@ -864,7 +909,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 
 	private void queueEvent(Runnable runnable) {
 		// TODO Auto-generated method stub
-		
+
 	}
 
 	public PaymentsManager getPaymentsManager() {
@@ -988,7 +1033,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
         mProgressFraction.setText(Helpers.getDownloadProgressString
                 (progress.mOverallProgress,
                         progress.mOverallTotal));
-		
+
 	}
-    
+
 }

+ 3 - 2
platform/android/java/src/org/godotengine/godot/GodotLib.java

@@ -37,7 +37,7 @@ public class GodotLib {
      public static GodotIO io;
 
      static {
-	 System.loadLibrary("godot_android");
+       System.loadLibrary("godot_android");
      }
 
     /**
@@ -45,7 +45,8 @@ public class GodotLib {
      * @param height the current view height
      */
 
-     public static native void initialize(Godot p_instance,boolean need_reload_hook,String[] p_cmdline,Object p_asset_manager);
+     public static native void initialize(Godot p_instance,boolean need_reload_hook,Object p_asset_manager, boolean use_apk_expansion);
+		 public static native void setup(String[] p_cmdline);
      public static native void resize(int width, int height,boolean reload);
      public static native void newcontext(boolean p_32_bits);
      public static native void back();

+ 1 - 1
platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java

@@ -89,7 +89,7 @@ public class GodotPaymentV3 extends Godot.SingletonBase {
 	}
 
 	public void callbackSuccess(String ticket, String signature, String sku) {
-		GodotLib.callobject(purchaseCallbackId, "purchase_success", new Object[]{ticket, signature, sku});
+		GodotLib.calldeferred(purchaseCallbackId, "purchase_success", new Object[]{ticket, signature, sku});
 	}
 
 	public void callbackSuccessProductMassConsumed(String ticket, String signature, String sku) {

+ 71 - 23
platform/android/java/src/org/godotengine/godot/GodotView.java

@@ -208,8 +208,9 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener {
 	@Override public void onInputDeviceAdded(int deviceId) {
 		joystick joy = new joystick();
 		joy.device_id = deviceId;
-		int id = joy_devices.size();
+		final int id = joy_devices.size();
 		InputDevice device = mInputManager.getInputDevice(deviceId);
+		final String name = device.getName();
 		joy.name = device.getName();
 		joy.axes = new ArrayList<InputDevice.MotionRange>();
 		joy.hats = new ArrayList<InputDevice.MotionRange>();
@@ -224,19 +225,29 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener {
 			}
 		}
 		joy_devices.add(joy);
-		GodotLib.joyconnectionchanged(id, true, joy.name);
+		queueEvent(new Runnable() {
+			@Override
+			public void run() {
+				GodotLib.joyconnectionchanged(id, true, name);
+			}
+		});
   }
 
 	@Override public void onInputDeviceRemoved(int deviceId) {
-		int id = find_joy_device(deviceId);
+		final int id = find_joy_device(deviceId);
 		joy_devices.remove(id);
-		GodotLib.joyconnectionchanged(id, false, "");
+		queueEvent(new Runnable() {
+			@Override
+			public void run() {
+				GodotLib.joyconnectionchanged(id, false, "");
+			}
+		});
 	}
 
 	@Override public void onInputDeviceChanged(int deviceId) {
 
 	}
-	@Override public boolean onKeyUp(int keyCode, KeyEvent event) {
+	@Override public boolean onKeyUp(final int keyCode, KeyEvent event) {
 
 		if (keyCode == KeyEvent.KEYCODE_BACK) {
 			return true;
@@ -249,22 +260,38 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener {
 		int source = event.getSource();
 		if ((source & InputDevice.SOURCE_JOYSTICK) != 0 || (source & InputDevice.SOURCE_DPAD) != 0 || (source & InputDevice.SOURCE_GAMEPAD) != 0) {
 
-			int button = get_godot_button(keyCode);
-			int device = find_joy_device(event.getDeviceId());
+			final int button = get_godot_button(keyCode);
+			final int device = find_joy_device(event.getDeviceId());
 
-			GodotLib.joybutton(device, button, false);
+			queueEvent(new Runnable() {
+				@Override
+				public void run() {
+					GodotLib.joybutton(device, button, false);
+				}
+			});
 			return true;
 		} else {
-
-			GodotLib.key(keyCode, event.getUnicodeChar(0), false);
+			final int chr = event.getUnicodeChar(0);
+			queueEvent(new Runnable() {
+				@Override
+				public void run() {
+					GodotLib.key(keyCode, chr, false);
+				}
+			});
 		};
 		return super.onKeyUp(keyCode, event);
 	};
 
-	@Override public boolean onKeyDown(int keyCode, KeyEvent event) {
+	@Override public boolean onKeyDown(final int keyCode, KeyEvent event) {
 
 		if (keyCode == KeyEvent.KEYCODE_BACK) {
-			GodotLib.back();
+			queueEvent(new Runnable() {
+				@Override
+				public void run() {
+					GodotLib.back();
+				}
+			});
+
 			// press 'back' button should not terminate program
 			//normal handle 'back' event in game logic
 			return true;
@@ -281,16 +308,26 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener {
 
 			if (event.getRepeatCount() > 0) // ignore key echo
 				return true;
-			int button = get_godot_button(keyCode);
-			int device = find_joy_device(event.getDeviceId());
+			final int button = get_godot_button(keyCode);
+			final int device = find_joy_device(event.getDeviceId());
 
 			//Log.e(TAG, String.format("joy button down! button %x, %d, device %d", keyCode, button, device));
-
-			GodotLib.joybutton(device, button, true);
+			queueEvent(new Runnable() {
+				@Override
+				public void run() {
+					GodotLib.joybutton(device, button, true);
+				}
+			});
 			return true;
 
 		} else {
-			GodotLib.key(keyCode, event.getUnicodeChar(0), true);
+			final int chr = event.getUnicodeChar(0);
+			queueEvent(new Runnable() {
+				@Override
+				public void run() {
+					GodotLib.key(keyCode, chr, true);
+				}
+			});
 		};
 		return super.onKeyDown(keyCode, event);
 	}
@@ -299,21 +336,32 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener {
 
 		if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK && event.getAction() == MotionEvent.ACTION_MOVE) {
 
-			int device_id = find_joy_device(event.getDeviceId());
+			final int device_id = find_joy_device(event.getDeviceId());
 			joystick joy = joy_devices.get(device_id);
 
 			for (int i = 0; i < joy.axes.size(); i++) {
 				InputDevice.MotionRange range = joy.axes.get(i);
-				float value = (event.getAxisValue(range.getAxis()) - range.getMin() ) / range.getRange() * 2.0f - 1.0f;
+				final float value = (event.getAxisValue(range.getAxis()) - range.getMin() ) / range.getRange() * 2.0f - 1.0f;
 				//Log.e(TAG, String.format("axis event: %d, value %f", i, value));
-				GodotLib.joyaxis(device_id, i, value);
+				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) {
-				int hatX = Math.round(event.getAxisValue(joy.hats.get(i).getAxis()));
-				int hatY = Math.round(event.getAxisValue(joy.hats.get(i+1).getAxis()));
+				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()));
 				//Log.e(TAG, String.format("HAT EVENT %d, %d", hatX, hatY));
-				GodotLib.joyhat(device_id, hatX, hatY);
+				queueEvent(new Runnable() {
+					@Override
+					public void run() {
+						GodotLib.joyhat(device_id, hatX, hatY);
+					}
+				});
 			}
 			return true;
 		};

+ 32 - 11
platform/android/java/src/org/godotengine/godot/input/GodotTextInputWrapper.java

@@ -89,8 +89,13 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
 		//Log.d(TAG, "beforeTextChanged(" + pCharSequence + ")start: " + start + ",count: " + count + ",after: " + after);
 
 		for (int i=0;i<count;i++){
-			GodotLib.key(KeyEvent.KEYCODE_DEL, 0, true);
-			GodotLib.key(KeyEvent.KEYCODE_DEL, 0, false);
+			mView.queueEvent(new Runnable() {
+				@Override
+				public void run() {
+					GodotLib.key(KeyEvent.KEYCODE_DEL, 0, true);
+					GodotLib.key(KeyEvent.KEYCODE_DEL, 0, false);
+				}
+			});
 		}
 	}
 
@@ -99,9 +104,14 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
 		//Log.d(TAG, "onTextChanged(" + pCharSequence + ")start: " + start + ",count: " + count + ",before: " + before);
 
 		for (int i=start;i<start+count;i++){
-			int ch = pCharSequence.charAt(i);
-			GodotLib.key(0, ch, true);
-			GodotLib.key(0, ch, false);
+			final int ch = pCharSequence.charAt(i);
+			mView.queueEvent(new Runnable() {
+				@Override
+				public void run() {
+					GodotLib.key(0, ch, true);
+					GodotLib.key(0, ch, false);
+				}
+			});
 		}
 
 	}
@@ -111,8 +121,14 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
 		if (this.mEdit == pTextView && this.isFullScreenEdit()) {
 			// user press the action button, delete all old text and insert new text
 			for (int i = this.mOriginText.length(); i > 0; i--) {
-				GodotLib.key(KeyEvent.KEYCODE_DEL, 0, true);
-				GodotLib.key(KeyEvent.KEYCODE_DEL, 0, false);
+				mView.queueEvent(new Runnable() {
+					@Override
+					public void run() {
+						GodotLib.key(KeyEvent.KEYCODE_DEL, 0, true);
+						GodotLib.key(KeyEvent.KEYCODE_DEL, 0, false);
+					}
+				});
+
 				/*
 				if (BuildConfig.DEBUG) {
 					Log.d(TAG, "deleteBackward");
@@ -131,9 +147,14 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
 			}
 
 			for(int i = 0; i < text.length(); i++) {
-				int ch = text.codePointAt(i);
-				GodotLib.key(0, ch, true);
-				GodotLib.key(0, ch, false);
+				final int ch = text.codePointAt(i);
+				mView.queueEvent(new Runnable() {
+					@Override
+					public void run() {
+						GodotLib.key(0, ch, true);
+						GodotLib.key(0, ch, false);
+					}
+				});
 			}
 			/*
 			if (BuildConfig.DEBUG) {
@@ -141,7 +162,7 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
 			}
 			*/
 		}
-		
+
 		if (pActionID == EditorInfo.IME_ACTION_DONE) {
 			this.mView.requestFocus();
 		}

+ 93 - 141
platform/android/java_glue.cpp

@@ -613,8 +613,6 @@ static List<JAndroidPointerEvent> pointer_events;
 static List<Ref<InputEvent> > key_events;
 static List<OS_Android::JoypadEvent> joy_events;
 static bool initialized = false;
-static Mutex *input_mutex = NULL;
-static Mutex *suspend_mutex = NULL;
 static int step = 0;
 static bool resized = false;
 static bool resized_reload = false;
@@ -756,7 +754,7 @@ static void _alert(const String &p_message, const String &p_title) {
 	env->CallVoidMethod(_godot_instance, _alertDialog, jStrMessage, jStrTitle);
 }
 
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jobject obj, jobject activity, jboolean p_need_reload_hook, jobjectArray p_cmdline, jobject p_asset_manager) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jobject obj, jobject activity, jboolean p_need_reload_hook, jobject p_asset_manager, jboolean p_use_apk_expansion) {
 
 	__android_log_print(ANDROID_LOG_INFO, "godot", "**INIT EVENT! - %p\n", env);
 
@@ -826,36 +824,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en
 		AudioDriverAndroid::setup(gob);
 	}
 
-	const char **cmdline = NULL;
-	int cmdlen = 0;
-	bool use_apk_expansion = false;
-	if (p_cmdline) {
-		cmdlen = env->GetArrayLength(p_cmdline);
-		if (cmdlen) {
-			cmdline = (const char **)malloc((env->GetArrayLength(p_cmdline) + 1) * sizeof(const char *));
-			cmdline[cmdlen] = NULL;
-
-			for (int i = 0; i < cmdlen; i++) {
-
-				jstring string = (jstring)env->GetObjectArrayElement(p_cmdline, i);
-				const char *rawString = env->GetStringUTFChars(string, 0);
-				if (!rawString) {
-					__android_log_print(ANDROID_LOG_INFO, "godot", "cmdline arg %i is null\n", i);
-				} else {
-					//__android_log_print(ANDROID_LOG_INFO,"godot","cmdline arg %i is: %s\n",i,rawString);
-
-					if (strcmp(rawString, "--main_pack") == 0)
-						use_apk_expansion = true;
-				}
-
-				cmdline[i] = rawString;
-			}
-		}
-	}
-
-	__android_log_print(ANDROID_LOG_INFO, "godot", "CMDLINE LEN %i - APK EXPANSION %I\n", cmdlen, int(use_apk_expansion));
-
-	os_android = new OS_Android(_gfx_init_func, env, _open_uri, _get_data_dir, _get_locale, _get_model, _get_screen_dpi, _show_vk, _hide_vk, _set_screen_orient, _get_unique_id, _get_system_dir, _play_video, _is_video_playing, _pause_video, _stop_video, _set_keep_screen_on, _alert, use_apk_expansion);
+	os_android = new OS_Android(_gfx_init_func, env, _open_uri, _get_data_dir, _get_locale, _get_model, _get_screen_dpi, _show_vk, _hide_vk, _set_screen_orient, _get_unique_id, _get_system_dir, _play_video, _is_video_playing, _pause_video, _stop_video, _set_keep_screen_on, _alert, p_use_apk_expansion);
 	os_android->set_need_reload_hooks(p_need_reload_hook);
 
 	char wd[500];
@@ -864,62 +833,10 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en
 	__android_log_print(ANDROID_LOG_INFO, "godot", "test construction %i\n", tst.a);
 	__android_log_print(ANDROID_LOG_INFO, "godot", "running from dir %s\n", wd);
 
-	__android_log_print(ANDROID_LOG_INFO, "godot", "**SETUP");
-
-	Error err = Main::setup("apk", cmdlen, (char **)cmdline, false);
-
-	if (err != OK) {
-		__android_log_print(ANDROID_LOG_INFO, "godot", "*****UNABLE TO SETUP");
-
-		return; //should exit instead and print the error
-	}
-
-	__android_log_print(ANDROID_LOG_INFO, "godot", "*****SETUP OK");
-
 	//video driver is determined here, because once initialized, it can't be changed
-	String vd = ProjectSettings::get_singleton()->get("display/driver");
+	// String vd = ProjectSettings::get_singleton()->get("display/driver");
 
 	env->CallVoidMethod(_godot_instance, _on_video_init, (jboolean) true);
-
-	__android_log_print(ANDROID_LOG_INFO, "godot", "**START");
-
-	input_mutex = Mutex::create();
-	suspend_mutex = Mutex::create();
-}
-
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jobject obj, jint width, jint height, jboolean reload) {
-
-	__android_log_print(ANDROID_LOG_INFO, "godot", "^_^_^_^_^ resize %lld, %i, %i\n", Thread::get_caller_id(), width, height);
-	if (os_android)
-		os_android->set_display_size(Size2(width, height));
-
-	/*input_mutex->lock();
-	resized=true;
-	if (reload)
-		resized_reload=true;
-	new_size=Size2(width,height);
-	input_mutex->unlock();*/
-}
-
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jobject obj, bool p_32_bits) {
-
-	__android_log_print(ANDROID_LOG_INFO, "godot", "^_^_^_^_^ newcontext %lld\n", Thread::get_caller_id());
-
-	if (os_android) {
-		os_android->set_context_is_16_bits(!p_32_bits);
-	}
-
-	if (os_android && step > 0) {
-
-		os_android->reload_gfx();
-	}
-}
-
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jobject obj) {
-
-	input_mutex->lock();
-	go_back_request = true;
-	input_mutex->unlock();
 }
 
 static void _initialize_java_modules() {
@@ -985,36 +902,106 @@ static void _initialize_java_modules() {
 	}
 }
 
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jobject obj) {
-
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jobject obj, jobjectArray p_cmdline) {
 	ThreadAndroid::setup_thread();
 
-	//__android_log_print(ANDROID_LOG_INFO,"godot","**STEP EVENT! - %p-%i\n",env,Thread::get_caller_id());
+	__android_log_print(ANDROID_LOG_INFO, "godot", "**SETUP");
 
-	suspend_mutex->lock();
-	input_mutex->lock();
-	//first time step happens, initialize
-	if (step == 0) {
-		// ugly hack to initialize the rest of the engine
-		// because of the way android forces you to do everything with threads
+	const char **cmdline = NULL;
+	int cmdlen = 0;
+	bool use_apk_expansion = false;
+	if (p_cmdline) {
+		cmdlen = env->GetArrayLength(p_cmdline);
+		if (cmdlen) {
+			cmdline = (const char **)malloc((env->GetArrayLength(p_cmdline) + 1) * sizeof(const char *));
+			cmdline[cmdlen] = NULL;
+
+			for (int i = 0; i < cmdlen; i++) {
+
+				jstring string = (jstring)env->GetObjectArrayElement(p_cmdline, i);
+				const char *rawString = env->GetStringUTFChars(string, 0);
+				if (!rawString) {
+					__android_log_print(ANDROID_LOG_INFO, "godot", "cmdline arg %i is null\n", i);
+				} else {
+					//__android_log_print(ANDROID_LOG_INFO,"godot","cmdline arg %i is: %s\n",i,rawString);
 
-		java_class_wrapper = memnew(JavaClassWrapper(_godot_instance));
-		ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton("JavaClassWrapper", java_class_wrapper));
-		_initialize_java_modules();
+					if (strcmp(rawString, "-main_pack") == 0)
+						use_apk_expansion = true;
+				}
+
+				cmdline[i] = rawString;
+			}
+		}
+	}
+	__android_log_print(ANDROID_LOG_INFO, "godot", "CMDLINE LEN %i - APK EXPANSION %i\n", cmdlen, int(use_apk_expansion));
+
+#if 0
+	char *args[]={"-test","render",NULL};
+	__android_log_print(ANDROID_LOG_INFO,"godot","pre asdasd setup...");
+	Error err  = Main::setup("apk",2,args,false);
+#else
+	Error err = Main::setup("apk", cmdlen, (char **)cmdline, false);
+#endif
+	if (cmdline) {
+		free(cmdline);
+	}
+
+	if (err != OK) {
+		__android_log_print(ANDROID_LOG_INFO, "godot", "*****UNABLE TO SETUP");
+		return; //should exit instead and print the error
+	}
+	__android_log_print(ANDROID_LOG_INFO, "godot", "*****SETUP OK");
+
+	java_class_wrapper = memnew(JavaClassWrapper(_godot_instance));
+	ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton("JavaClassWrapper", java_class_wrapper));
+	_initialize_java_modules();
+}
+
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jobject obj, jint width, jint height, jboolean reload) {
+
+	__android_log_print(ANDROID_LOG_INFO, "godot", "^_^_^_^_^ resize %lld, %i, %i\n", Thread::get_caller_id(), width, height);
+	if (os_android)
+		os_android->set_display_size(Size2(width, height));
+
+	/*input_mutex->lock();
+	resized=true;
+	if (reload)
+		resized_reload=true;
+	new_size=Size2(width,height);
+	input_mutex->unlock();*/
+}
+
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jobject obj, bool p_32_bits) {
+
+	__android_log_print(ANDROID_LOG_INFO, "godot", "^_^_^_^_^ newcontext %lld\n", Thread::get_caller_id());
+
+	if (os_android) {
+		os_android->set_context_is_16_bits(!p_32_bits);
+	}
+
+	if (os_android && step > 0) {
+
+		os_android->reload_gfx();
+	}
+}
+
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jobject obj) {
+	go_back_request = true;
+}
+
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jobject obj) {
+	if (step == 0) {
+		__android_log_print(ANDROID_LOG_INFO, "godot", "**FIRST_STEP");
 
 		// Since Godot is initialized on the UI thread, _main_thread_id was set to that thread's id,
 		// but for Godot purposes, the main thread is the one running the game loop
 		Main::setup2(Thread::get_caller_id());
 		++step;
-		suspend_mutex->unlock();
-		input_mutex->unlock();
 		return;
-	};
+	}
+
 	if (step == 1) {
 		if (!Main::start()) {
-
-			input_mutex->unlock();
-			suspend_mutex->lock();
 			return; //should exit instead and print the error
 		}
 
@@ -1022,6 +1009,8 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, job
 		++step;
 	}
 
+	//__android_log_print(ANDROID_LOG_INFO,"godot","**STEP EVENT! - %p-%i\n",env,Thread::get_caller_id());
+
 	while (pointer_events.size()) {
 
 		JAndroidPointerEvent jpe = pointer_events.front()->get();
@@ -1052,8 +1041,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, job
 		go_back_request = false;
 	}
 
-	input_mutex->unlock();
-
 	os_android->process_accelerometer(accelerometer);
 
 	os_android->process_magnetometer(magnetometer);
@@ -1067,8 +1054,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, job
 		env->CallVoidMethod(_godot_instance, _finish);
 		__android_log_print(ANDROID_LOG_INFO, "godot", "**FINISH REQUEST!!! - %p-%i\n", env, Thread::get_caller_id());
 	}
-
-	suspend_mutex->unlock();
 }
 
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jobject obj, jint ev, jint pointer, jint count, jintArray positions) {
@@ -1091,11 +1076,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jo
 	jpe.points = points;
 	jpe.what = ev;
 
-	input_mutex->lock();
-
 	pointer_events.push_back(jpe);
-
-	input_mutex->unlock();
 	/*
 	if (os_android)
 		os_android->process_touch(ev,pointer,points);
@@ -1365,9 +1346,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env
 	jevent.index = p_button;
 	jevent.pressed = p_pressed;
 
-	input_mutex->lock();
 	joy_events.push_back(jevent);
-	input_mutex->unlock();
 }
 
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jobject obj, jint p_device, jint p_axis, jfloat p_value) {
@@ -1378,9 +1357,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env,
 	jevent.index = p_axis;
 	jevent.value = p_value;
 
-	input_mutex->lock();
 	joy_events.push_back(jevent);
-	input_mutex->unlock();
 }
 
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jobject obj, jint p_device, jint p_hat_x, jint p_hat_y) {
@@ -1401,9 +1378,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, j
 			hat |= InputDefault::HAT_MASK_DOWN;
 	}
 	jevent.hat = hat;
-	input_mutex->lock();
 	joy_events.push_back(jevent);
-	input_mutex->unlock();
 }
 
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged(JNIEnv *env, jobject obj, jint p_device, jboolean p_connected, jstring p_name) {
@@ -1437,54 +1412,31 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jobj
 		go_back_request = true;
 	}
 
-	input_mutex->lock();
 	key_events.push_back(ievent);
-	input_mutex->unlock();
 }
 
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_accelerometer(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z) {
-
-	input_mutex->lock();
 	accelerometer = Vector3(x, y, z);
-	input_mutex->unlock();
 }
 
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnetometer(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z) {
-
-	input_mutex->lock();
 	magnetometer = Vector3(x, y, z);
-	input_mutex->unlock();
 }
 
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gyroscope(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z) {
-
-	input_mutex->lock();
 	gyroscope = Vector3(x, y, z);
-	input_mutex->unlock();
 }
 
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, jobject obj) {
 
-	if (!suspend_mutex)
-		return;
-	suspend_mutex->lock();
-
 	if (os_android && step > 0)
 		os_android->main_loop_focusin();
-
-	suspend_mutex->unlock();
 }
 
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, jobject obj) {
 
-	if (!suspend_mutex)
-		return;
-	suspend_mutex->lock();
-
 	if (os_android && step > 0)
 		os_android->main_loop_focusout();
-
-	suspend_mutex->unlock();
 }
 
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_audio(JNIEnv *env, jobject obj) {

+ 2 - 1
platform/android/java_glue.h

@@ -36,7 +36,8 @@
 #include <jni.h>
 
 extern "C" {
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jobject obj, jobject activity, jboolean p_need_reload_hook, jobjectArray p_cmdline, jobject p_asset_manager);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jobject obj, jobject activity, jboolean p_need_reload_hook, jobject p_asset_manager, jboolean p_use_apk_expansion);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jobject obj, jobjectArray p_cmdline);
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jobject obj, jint width, jint height, jboolean reload);
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jobject obj, bool p_32_bits);
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jobject obj);