diff --git a/android/app/CMakeLists.txt b/android/app/CMakeLists.txt index 912f74f96..ee535e134 100644 --- a/android/app/CMakeLists.txt +++ b/android/app/CMakeLists.txt @@ -229,7 +229,6 @@ add_library(classicube SHARED ) target_link_libraries(classicube - android EGL GLESv2 log) diff --git a/android/app/src/main/java/com/classicube/MainActivity.java b/android/app/src/main/java/com/classicube/MainActivity.java index 4d77de4c5..53d2761d2 100644 --- a/android/app/src/main/java/com/classicube/MainActivity.java +++ b/android/app/src/main/java/com/classicube/MainActivity.java @@ -6,8 +6,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.URL; import java.security.KeyStore; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; @@ -25,6 +23,8 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.res.Configuration; import android.database.Cursor; +import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.PixelFormat; import android.graphics.Rect; import android.net.Uri; @@ -913,118 +913,6 @@ public class MainActivity extends Activity } - // ====================================================================== - // -------------------------------- HTTP -------------------------------- - // ====================================================================== - // Implements java Android side of the Android HTTP backend (See Http.c) - static HttpURLConnection conn; - static InputStream src; - static byte[] readCache = new byte[8192]; - - public static int httpInit(String url, String method) { - // newer android versions block cleartext traffic by default - if (android.os.Build.VERSION.SDK_INT >= 26) { - url = url.replace("http://", "https://"); - } - - try { - conn = (HttpURLConnection)new URL(url).openConnection(); - conn.setDoInput(true); - conn.setRequestMethod(method); - conn.setInstanceFollowRedirects(true); - - httpAddMethodHeaders(method); - return 0; - } catch (Exception ex) { - return httpOnError(ex); - } - } - - static void httpAddMethodHeaders(String method) { - if (!method.equals("HEAD")) return; - - // Ever since dropbox switched to to chunked transfer encoding, - // sending a HEAD request to dropbox always seems to result in the - // next GET request failing with 'Unexpected status line' ProtocolException - // Seems to be a known issue: https://github.com/square/okhttp/issues/3689 - // Simplest workaround is to ask for connection to be closed after HEAD request - httpSetHeader("connection", "close"); - } - - public static void httpSetHeader(String name, String value) { - conn.setRequestProperty(name, value); - } - - public static int httpSetData(byte[] data) { - try { - conn.setDoOutput(true); - conn.getOutputStream().write(data); - conn.getOutputStream().flush(); - return 0; - } catch (Exception ex) { - return httpOnError(ex); - } - } - - public static int httpPerform() { - int len; - try { - conn.connect(); - // Some implementations also provide this as getHeaderField(0), but some don't - httpParseHeader("HTTP/1.1 " + conn.getResponseCode() + " MSG"); - - // Legitimate webservers aren't going to reply with over 200 headers - for (int i = 0; i < 200; i++) { - String key = conn.getHeaderFieldKey(i); - String val = conn.getHeaderField(i); - if (key == null && val == null) break; - - if (key == null) { - httpParseHeader(val); - } else { - httpParseHeader(key + ":" + val); - } - } - - src = conn.getInputStream(); - while ((len = src.read(readCache)) > 0) { - httpAppendData(readCache, len); - } - - httpFinish(); - return 0; - } catch (Exception ex) { - return httpOnError(ex); - } - } - - static void httpFinish() { - conn = null; - try { - src.close(); - } catch (Exception ex) { } - src = null; - } - - // TODO: Should we prune this list? - static List errors = new ArrayList(); - - static int httpOnError(Exception ex) { - ex.printStackTrace(); - httpFinish(); - errors.add(ex.getMessage()); - return -errors.size(); // don't want 0 as an error code - } - - public static String httpDescribeError(int res) { - res = -res - 1; - return res >= 0 && res < errors.size() ? errors.get(res) : null; - } - - native static void httpParseHeader(String header); - native static void httpAppendData(byte[] data, int len); - - // ====================================================================== // -------------------------------- SSL --------------------------------- // ====================================================================== @@ -1087,4 +975,39 @@ public class MainActivity extends Activity chain.clear(); return result; } + + + // ====================================================================== + // --------------------------- Legacy window ---------------------------- + // ====================================================================== + Canvas cur_canvas; + public int[] legacy_lockCanvas(int l, int t, int r, int b) { + CCView surface = (CCView)curView; + Rect rect = new Rect(l, t, r, b); + cur_canvas = surface.getHolder().lockCanvas(rect); + + int w = (r - l); + int h = (b - t); + return new int[w * h]; + } + + public void legacy_unlockCanvas(int l, int t, int r, int b, int[] buf) { + CCView surface = (CCView)curView; + int w = (r - l); + int h = (b - t); + + // TODO avoid need to swap R/B + for (int i = 0; i < buf.length; i++) + { + int color = buf[i]; + int R = color & 0x00FF0000; + int B = color & 0x000000FF; + + buf[i] = (color & 0xFF00FF00) | (B << 16) | (R >> 16); + } + + cur_canvas.drawBitmap(buf, 0, w, l, t, w, h, false, null); + surface.getHolder().unlockCanvasAndPost(cur_canvas); + cur_canvas = null; + } } diff --git a/src/android/Window_Android.c b/src/android/Window_Android.c index b66c331d4..58a7e3217 100644 --- a/src/android/Window_Android.c +++ b/src/android/Window_Android.c @@ -14,6 +14,7 @@ static ANativeWindow* win_handle; static cc_bool winCreated; + static jmethodID JAVA_openKeyboard, JAVA_setKeyboardText, JAVA_closeKeyboard; static jmethodID JAVA_getWindowState, JAVA_enterFullscreen, JAVA_exitFullscreen; static jmethodID JAVA_showAlert, JAVA_setRequestedOri; @@ -22,6 +23,12 @@ static jmethodID JAVA_processedSurfaceDestroyed, JAVA_processEvents; static jmethodID JAVA_getDpiX, JAVA_getDpiY, JAVA_setupForGame; static jmethodID JAVA_getClipboardText, JAVA_setClipboardText; +static int32_t (*_ANativeWindow_lock)(ANativeWindow* window, ANativeWindow_Buffer* oBuffer, ARect* ioDirtyBounds); +static int32_t (*_ANativeWindow_unlockAndPost)(ANativeWindow* window); + +static ANativeWindow* (*_ANativeWindow_fromSurface)(JNIEnv* env, jobject surface); +static void (*_ANativeWindow_release)(ANativeWindow* window); + static void SetWindowBounds(int width, int height) { Window_Main.Width = width; Window_Main.Height = height; @@ -176,7 +183,7 @@ static void JNICALL java_processJoystickR(JNIEnv* env, jobject o, jint x, jint y static void JNICALL java_processSurfaceCreated(JNIEnv* env, jobject o, jobject surface, jint w, jint h) { Platform_LogConst("WIN - CREATED"); - win_handle = ANativeWindow_fromSurface(env, surface); + win_handle = _ANativeWindow_fromSurface(env, surface); winCreated = true; Window_Main.Handle.ptr = win_handle; @@ -187,7 +194,7 @@ static void JNICALL java_processSurfaceCreated(JNIEnv* env, jobject o, jobject s static void JNICALL java_processSurfaceDestroyed(JNIEnv* env, jobject o) { Platform_LogConst("WIN - DESTROYED"); - if (win_handle) ANativeWindow_release(win_handle); + if (win_handle) _ANativeWindow_release(win_handle); win_handle = NULL; Window_Main.Handle.ptr = NULL; @@ -254,89 +261,6 @@ static void JNICALL java_onLowMemory(JNIEnv* env, jobject o) { static void JNICALL java_processOFDResult(JNIEnv* env, jobject o, jstring str); -static const JNINativeMethod methods[] = { - { "processKeyDown", "(I)V", java_processKeyDown }, - { "processKeyUp", "(I)V", java_processKeyUp }, - { "processKeyChar", "(I)V", java_processKeyChar }, - { "processKeyText", "(Ljava/lang/String;)V", java_processKeyText }, - - { "processPointerDown", "(IIII)V", java_processPointerDown }, - { "processPointerUp", "(IIII)V", java_processPointerUp }, - { "processPointerMove", "(IIII)V", java_processPointerMove }, - - { "processJoystickL", "(II)V", java_processJoystickL }, - { "processJoystickR", "(II)V", java_processJoystickR }, - - { "processSurfaceCreated", "(Landroid/view/Surface;II)V", java_processSurfaceCreated }, - { "processSurfaceDestroyed", "()V", java_processSurfaceDestroyed }, - { "processSurfaceResized", "(II)V", java_processSurfaceResized }, - { "processSurfaceRedrawNeeded", "()V", java_processSurfaceRedrawNeeded }, - - { "processOnStart", "()V", java_onStart }, - { "processOnStop", "()V", java_onStop }, - { "processOnResume", "()V", java_onResume }, - { "processOnPause", "()V", java_onPause }, - { "processOnDestroy", "()V", java_onDestroy }, - - { "processOnGotFocus", "()V", java_onGotFocus }, - { "processOnLostFocus", "()V", java_onLostFocus }, - { "processOnLowMemory", "()V", java_onLowMemory }, - - { "processOFDResult", "(Ljava/lang/String;)V", java_processOFDResult }, -}; - -static void CacheJavaMethodRefs(JNIEnv* env) { - JAVA_openKeyboard = Java_GetIMethod(env, "openKeyboard", "(Ljava/lang/String;I)V"); - JAVA_setKeyboardText = Java_GetIMethod(env, "setKeyboardText", "(Ljava/lang/String;)V"); - JAVA_closeKeyboard = Java_GetIMethod(env, "closeKeyboard", "()V"); - - JAVA_getWindowState = Java_GetIMethod(env, "getWindowState", "()I"); - JAVA_enterFullscreen = Java_GetIMethod(env, "enterFullscreen", "()V"); - JAVA_exitFullscreen = Java_GetIMethod(env, "exitFullscreen", "()V"); - - JAVA_getDpiX = Java_GetIMethod(env, "getDpiX", "()F"); - JAVA_getDpiY = Java_GetIMethod(env, "getDpiY", "()F"); - JAVA_setupForGame = Java_GetIMethod(env, "setupForGame", "()V"); - - JAVA_processedSurfaceDestroyed = Java_GetIMethod(env, "processedSurfaceDestroyed", "()V"); - JAVA_processEvents = Java_GetIMethod(env, "processEvents", "()V"); - - JAVA_showAlert = Java_GetIMethod(env, "showAlert", "(Ljava/lang/String;Ljava/lang/String;)V"); - JAVA_setRequestedOri = Java_GetIMethod(env, "setRequestedOrientation", "(I)V"); - JAVA_openFileDialog = Java_GetIMethod(env, "openFileDialog", "(Ljava/lang/String;)I"); - JAVA_saveFileDialog = Java_GetIMethod(env, "saveFileDialog", "(Ljava/lang/String;Ljava/lang/String;)I"); - - JAVA_getClipboardText = Java_GetIMethod(env, "getClipboardText", "()Ljava/lang/String;"); - JAVA_setClipboardText = Java_GetIMethod(env, "setClipboardText", "(Ljava/lang/String;)V"); -} - -void Window_PreInit(void) { - JNIEnv* env; - Java_GetCurrentEnv(env); - Java_RegisterNatives(env, methods); - CacheJavaMethodRefs(env); - - DisplayInfo.CursorVisible = true; -} - -// TODO move to bottom of file? -void Window_Init(void) { - JNIEnv* env; - /* TODO: ANativeActivity_setWindowFlags(app->activity, AWINDOW_FLAG_FULLSCREEN, 0); */ - Java_GetCurrentEnv(env); - - Window_Main.SoftKeyboard = SOFT_KEYBOARD_RESIZE; - Input_SetTouchMode(true); - Gui_SetTouchUI(true); - Input.Sources = INPUT_SOURCE_NORMAL; - - DisplayInfo.Depth = 32; - DisplayInfo.ScaleX = Java_ICall_Float(env, JAVA_getDpiX, NULL); - DisplayInfo.ScaleY = Java_ICall_Float(env, JAVA_getDpiY, NULL); -} - -void Window_Free(void) { } - static void RemakeWindowSurface(void) { JNIEnv* env; Java_GetCurrentEnv(env); @@ -547,7 +471,7 @@ void Window_DrawFramebuffer(Rect2D r, struct Bitmap* bmp) { b.top = r.y; b.bottom = r.y + r.height; /* Platform_Log4("DIRTY: %i,%i - %i,%i", &b.left, &b.top, &b.right, &b.bottom); */ - res = ANativeWindow_lock(win_handle, &buffer, &b); + res = _ANativeWindow_lock(win_handle, &buffer, &b); if (res) Process_Abort2(res, "Locking window pixels"); /* Platform_Log4("ADJUS: %i,%i - %i,%i", &b.left, &b.top, &b.right, &b.bottom); */ @@ -565,7 +489,7 @@ void Window_DrawFramebuffer(Rect2D r, struct Bitmap* bmp) { { Mem_Copy(dst + y * buffer.stride, src + y * bmp->width, size); } - res = ANativeWindow_unlockAndPost(win_handle); + res = _ANativeWindow_unlockAndPost(win_handle); if (res) Process_Abort2(res, "Unlocking window pixels"); } @@ -617,3 +541,169 @@ void Window_LockLandscapeOrientation(cc_bool lock) { void Window_EnableRawMouse(void) { DefaultEnableRawMouse(); } void Window_UpdateRawMouse(void) { } void Window_DisableRawMouse(void) { DefaultDisableRawMouse(); } + + +/*########################################################################################################################* +*------------------------------------------------------libandroid---------------------------------------------------------* +*#########################################################################################################################*/ +static const cc_string droidLib = String_FromConst("libandroid.so"); + +static cc_bool LoadDroidFuncs(void) { + static const struct DynamicLibSym funcs[] = { + DynamicLib_ReqSym(ANativeWindow_lock), + DynamicLib_ReqSym(ANativeWindow_unlockAndPost), + DynamicLib_ReqSym(ANativeWindow_fromSurface), + DynamicLib_ReqSym(ANativeWindow_release), + }; + void* lib; + + return DynamicLib_LoadAll(&droidLib, funcs, Array_Elems(funcs), &lib); +} + +static ANativeWindow* fallback_fromSurface(JNIEnv* env, jobject surface) { + return (void*)1; +} + +static void fallback_release(ANativeWindow* window) { +} + +static jobject canvas_arr; +static uint32_t* canvas_ptr; +static ARect canvas_rect; + +static int32_t fallback_lock(ANativeWindow* window, ANativeWindow_Buffer* oBuffer, ARect* ioDirtyBounds) { + JNIEnv* env; + jvalue args[4]; + Java_GetCurrentEnv(env); + + args[0].i = ioDirtyBounds->left; + args[1].i = ioDirtyBounds->top; + args[2].i = ioDirtyBounds->right; + args[3].i = ioDirtyBounds->bottom; + + jmethodID method = Java_GetIMethod(env, "legacy_lockCanvas", "(IIII)[I"); + canvas_arr = Java_ICall_Obj(env, method, args); + canvas_ptr = (*env)->GetIntArrayElements(env, canvas_arr, NULL); + + oBuffer->stride = ioDirtyBounds->right - ioDirtyBounds->left; + // Counteract pointer adjustment done in Window_DrawFramebuffer + oBuffer->bits = canvas_ptr - (ioDirtyBounds->top * oBuffer->stride) - ioDirtyBounds->left; + canvas_rect = *ioDirtyBounds; + + if (!canvas_ptr) Process_Abort("Out of memory for canvas"); + return 0; +} + +static int32_t fallback_unlockAndPost(ANativeWindow* window) { + JNIEnv* env; + jvalue args[5]; + Java_GetCurrentEnv(env); + (*env)->ReleaseIntArrayElements(env, canvas_arr, canvas_ptr, 0); + + args[0].i = canvas_rect.left; + args[1].i = canvas_rect.top; + args[2].i = canvas_rect.right; + args[3].i = canvas_rect.bottom; + args[4].l = canvas_arr; + + jmethodID method = Java_GetIMethod(env, "legacy_unlockCanvas", "(IIII[I)V"); + Java_ICall_Void(env, method, args); + + (*env)->DeleteLocalRef(env, canvas_arr); + canvas_arr = NULL; + return 0; +} + +static void UseFallbackDroidFuncs(void) { + _ANativeWindow_fromSurface = fallback_fromSurface; + _ANativeWindow_release = fallback_release; + _ANativeWindow_lock = fallback_lock; + _ANativeWindow_unlockAndPost = fallback_unlockAndPost; +} + + +/*########################################################################################################################* +*----------------------------------------------------Initialisation-------------------------------------------------------* +*#########################################################################################################################*/ +static const JNINativeMethod methods[] = { + { "processKeyDown", "(I)V", java_processKeyDown }, + { "processKeyUp", "(I)V", java_processKeyUp }, + { "processKeyChar", "(I)V", java_processKeyChar }, + { "processKeyText", "(Ljava/lang/String;)V", java_processKeyText }, + + { "processPointerDown", "(IIII)V", java_processPointerDown }, + { "processPointerUp", "(IIII)V", java_processPointerUp }, + { "processPointerMove", "(IIII)V", java_processPointerMove }, + + { "processJoystickL", "(II)V", java_processJoystickL }, + { "processJoystickR", "(II)V", java_processJoystickR }, + + { "processSurfaceCreated", "(Landroid/view/Surface;II)V", java_processSurfaceCreated }, + { "processSurfaceDestroyed", "()V", java_processSurfaceDestroyed }, + { "processSurfaceResized", "(II)V", java_processSurfaceResized }, + { "processSurfaceRedrawNeeded", "()V", java_processSurfaceRedrawNeeded }, + + { "processOnStart", "()V", java_onStart }, + { "processOnStop", "()V", java_onStop }, + { "processOnResume", "()V", java_onResume }, + { "processOnPause", "()V", java_onPause }, + { "processOnDestroy", "()V", java_onDestroy }, + + { "processOnGotFocus", "()V", java_onGotFocus }, + { "processOnLostFocus", "()V", java_onLostFocus }, + { "processOnLowMemory", "()V", java_onLowMemory }, + + { "processOFDResult", "(Ljava/lang/String;)V", java_processOFDResult }, +}; + +static void CacheJavaMethodRefs(JNIEnv* env) { + JAVA_openKeyboard = Java_GetIMethod(env, "openKeyboard", "(Ljava/lang/String;I)V"); + JAVA_setKeyboardText = Java_GetIMethod(env, "setKeyboardText", "(Ljava/lang/String;)V"); + JAVA_closeKeyboard = Java_GetIMethod(env, "closeKeyboard", "()V"); + + JAVA_getWindowState = Java_GetIMethod(env, "getWindowState", "()I"); + JAVA_enterFullscreen = Java_GetIMethod(env, "enterFullscreen", "()V"); + JAVA_exitFullscreen = Java_GetIMethod(env, "exitFullscreen", "()V"); + + JAVA_getDpiX = Java_GetIMethod(env, "getDpiX", "()F"); + JAVA_getDpiY = Java_GetIMethod(env, "getDpiY", "()F"); + JAVA_setupForGame = Java_GetIMethod(env, "setupForGame", "()V"); + + JAVA_processedSurfaceDestroyed = Java_GetIMethod(env, "processedSurfaceDestroyed", "()V"); + JAVA_processEvents = Java_GetIMethod(env, "processEvents", "()V"); + + JAVA_showAlert = Java_GetIMethod(env, "showAlert", "(Ljava/lang/String;Ljava/lang/String;)V"); + JAVA_setRequestedOri = Java_GetIMethod(env, "setRequestedOrientation", "(I)V"); + JAVA_openFileDialog = Java_GetIMethod(env, "openFileDialog", "(Ljava/lang/String;)I"); + JAVA_saveFileDialog = Java_GetIMethod(env, "saveFileDialog", "(Ljava/lang/String;Ljava/lang/String;)I"); + + JAVA_getClipboardText = Java_GetIMethod(env, "getClipboardText", "()Ljava/lang/String;"); + JAVA_setClipboardText = Java_GetIMethod(env, "setClipboardText", "(Ljava/lang/String;)V"); +} + +void Window_PreInit(void) { + JNIEnv* env; + Java_GetCurrentEnv(env); + Java_RegisterNatives(env, methods); + CacheJavaMethodRefs(env); + + DisplayInfo.CursorVisible = true; + if (!LoadDroidFuncs()) UseFallbackDroidFuncs(); +} + +void Window_Init(void) { + JNIEnv* env; + /* TODO: ANativeActivity_setWindowFlags(app->activity, AWINDOW_FLAG_FULLSCREEN, 0); */ + Java_GetCurrentEnv(env); + + Window_Main.SoftKeyboard = SOFT_KEYBOARD_RESIZE; + Input_SetTouchMode(true); + Gui_SetTouchUI(true); + Input.Sources = INPUT_SOURCE_NORMAL; + + DisplayInfo.Depth = 32; + DisplayInfo.ScaleX = Java_ICall_Float(env, JAVA_getDpiX, NULL); + DisplayInfo.ScaleY = Java_ICall_Float(env, JAVA_getDpiY, NULL); +} + +void Window_Free(void) { } \ No newline at end of file