From 81a5fb10e33cd7389e1aee15343bceb1a2658aee Mon Sep 17 00:00:00 2001 From: omigamedev Date: Sun, 20 Aug 2017 00:51:59 +0100 Subject: [PATCH] android: fix directory listing, keyboard show/hide and read chars --- android/CMakeLists.txt | 7 +- android/src/main/cpp/main.cpp | 196 +++++++++++++++++++++++++++++++--- engine/app.h | 3 + engine/app_events.cpp | 11 ++ engine/asset.cpp | 13 +++ engine/node_panel_brush.cpp | 2 +- 6 files changed, 218 insertions(+), 14 deletions(-) diff --git a/android/CMakeLists.txt b/android/CMakeLists.txt index fc25afe..4a5e92b 100644 --- a/android/CMakeLists.txt +++ b/android/CMakeLists.txt @@ -24,7 +24,9 @@ add_library( ../libs/yoga/yoga/YGNodeList.c ../libs/yoga/yoga/Yoga.c ../libs/tinyxml2/tinyxml2.cpp - src/main/cpp/main.cpp + ../libs/jpeg/jpgd.cpp + ../libs/jpeg/jpge.cpp + src/main/cpp/main.cpp ../engine/pch.cpp ../engine/util.cpp ../engine/rtt.cpp @@ -36,6 +38,7 @@ add_library( ../engine/shader.cpp ../engine/shape.cpp ../engine/app.cpp + ../engine/app_dialogs.cpp ../engine/app_events.cpp ../engine/app_layout.cpp ../engine/app_shaders.cpp @@ -53,6 +56,7 @@ add_library( ../engine/node_checkbox.cpp ../engine/node_color_quad.cpp ../engine/node_dialog_open.cpp + ../engine/node_dialog_layer_rename.cpp ../engine/node_icon.cpp ../engine/node_image.cpp ../engine/node_image_texture.cpp @@ -78,6 +82,7 @@ target_include_directories(native-lib PRIVATE ../libs/tinyxml2 ../libs/yoga ../libs/stb + ../libs/jpeg ../libs/curl-android-ios/prebuilt-with-ssl/android/include ) diff --git a/android/src/main/cpp/main.cpp b/android/src/main/cpp/main.cpp index 74886bf..2769f43 100755 --- a/android/src/main/cpp/main.cpp +++ b/android/src/main/cpp/main.cpp @@ -75,6 +75,157 @@ struct engine { struct saved_state state; }; +std::string utf8chr(int cp) +{ + char c[5]={ 0x00,0x00,0x00,0x00,0x00 }; + if (cp<=0x7F) { c[0] = cp; } + else if(cp<=0x7FF) { c[0] = (cp>>6)+192; c[1] = (cp&63)+128; } + else if(0xd800<=cp && cp<=0xdfff) {} //invalid block of utf8 + else if(cp<=0xFFFF) { c[0] = (cp>>12)+224; c[1]= ((cp>>6)&63)+128; c[2]=(cp&63)+128; } + else if(cp<=0x10FFFF) { c[0] = (cp>>18)+240; c[1] = ((cp>>12)&63)+128; c[2] = ((cp>>6)&63)+128; c[3]=(cp&63)+128; } + return std::string(c); +} + +// see https://stackoverflow.com/questions/21124051/receive-complete-android-unicode-input-in-c-c +// see http://www.zedwood.com/article/cpp-utf8-char-to-codepoint +int GetUnicodeChar(struct android_app* app, int eventType, int keyCode, int metaState) +{ +#define COMBINING_ACCENT 0x80000000 +#define COMBINING_ACCENT_MASK 0x7fffffff + + JavaVM* javaVM = app->activity->vm; + JNIEnv* jniEnv = app->activity->env; + + JavaVMAttachArgs attachArgs; + attachArgs.version = JNI_VERSION_1_6; + attachArgs.name = "NativeThread"; + attachArgs.group = NULL; + + jint result = javaVM->AttachCurrentThread(&jniEnv, &attachArgs); + if(result == JNI_ERR) + { + return 0; + } + + jclass class_key_event = jniEnv->FindClass("android/view/KeyEvent"); + int unicodeKey; + + if(metaState == 0) + { + jmethodID method_get_unicode_char = jniEnv->GetMethodID(class_key_event, "getUnicodeChar", "()I"); + jmethodID eventConstructor = jniEnv->GetMethodID(class_key_event, "", "(II)V"); + jobject eventObj = jniEnv->NewObject(class_key_event, eventConstructor, eventType, keyCode); + + unicodeKey = jniEnv->CallIntMethod(eventObj, method_get_unicode_char); + } + + else + { + jmethodID method_get_unicode_char = jniEnv->GetMethodID(class_key_event, "getUnicodeChar", "(I)I"); + jmethodID eventConstructor = jniEnv->GetMethodID(class_key_event, "", "(II)V"); + jobject eventObj = jniEnv->NewObject(class_key_event, eventConstructor, eventType, keyCode); + + unicodeKey = jniEnv->CallIntMethod(eventObj, method_get_unicode_char, metaState); + } + + if ((unicodeKey & COMBINING_ACCENT) != 0) + { + unicodeKey = unicodeKey & COMBINING_ACCENT_MASK; + } + + javaVM->DetachCurrentThread(); + + LOG("Unicode key is: %d", unicodeKey); + return unicodeKey; +} + +// see https://groups.google.com/forum/#!topic/android-ndk/Tk3g00wLKhk +void displayKeyboard(android_app* mApplication, bool pShow) +{ + // Attaches the current thread to the JVM. + jint lResult; + jint lFlags = 0; + + JavaVM* lJavaVM = mApplication->activity->vm; + JNIEnv* lJNIEnv = mApplication->activity->env; + + JavaVMAttachArgs lJavaVMAttachArgs; + lJavaVMAttachArgs.version = JNI_VERSION_1_6; + lJavaVMAttachArgs.name = "NativeThread"; + lJavaVMAttachArgs.group = NULL; + + lResult=lJavaVM->AttachCurrentThread(&lJNIEnv, + &lJavaVMAttachArgs); + if (lResult == JNI_ERR) { + return; + } + + // Retrieves NativeActivity. + jobject lNativeActivity = mApplication->activity->clazz; + jclass ClassNativeActivity = lJNIEnv->GetObjectClass(lNativeActivity); + + // Retrieves Context.INPUT_METHOD_SERVICE. + jclass ClassContext = lJNIEnv->FindClass("android/content/Context"); + jfieldID FieldINPUT_METHOD_SERVICE = + lJNIEnv->GetStaticFieldID(ClassContext, "INPUT_METHOD_SERVICE", "Ljava/lang/String;"); + jobject INPUT_METHOD_SERVICE = + lJNIEnv->GetStaticObjectField(ClassContext, + FieldINPUT_METHOD_SERVICE); + //jniCheck(INPUT_METHOD_SERVICE); + + // Runs getSystemService(Context.INPUT_METHOD_SERVICE). + jclass ClassInputMethodManager = lJNIEnv->FindClass( + "android/view/inputmethod/InputMethodManager"); + jmethodID MethodGetSystemService = lJNIEnv->GetMethodID( + ClassNativeActivity, "getSystemService", + "(Ljava/lang/String;)Ljava/lang/Object;"); + jobject lInputMethodManager = lJNIEnv->CallObjectMethod( + lNativeActivity, MethodGetSystemService, + INPUT_METHOD_SERVICE); + + // Runs getWindow().getDecorView(). + jmethodID MethodGetWindow = lJNIEnv->GetMethodID( + ClassNativeActivity, "getWindow", + "()Landroid/view/Window;"); + jobject lWindow = lJNIEnv->CallObjectMethod(lNativeActivity, + MethodGetWindow); + jclass ClassWindow = lJNIEnv->FindClass( + "android/view/Window"); + jmethodID MethodGetDecorView = lJNIEnv->GetMethodID( + ClassWindow, "getDecorView", "()Landroid/view/View;"); + jobject lDecorView = lJNIEnv->CallObjectMethod(lWindow, + MethodGetDecorView); + + if (pShow) { + // Runs lInputMethodManager.showSoftInput(...). + jmethodID MethodShowSoftInput = lJNIEnv->GetMethodID( + ClassInputMethodManager, "showSoftInput", + "(Landroid/view/View;I)Z"); + jboolean lResult = lJNIEnv->CallBooleanMethod( + lInputMethodManager, MethodShowSoftInput, + lDecorView, lFlags); + } else { + // Runs lWindow.getViewToken() + jclass ClassView = lJNIEnv->FindClass( + "android/view/View"); + jmethodID MethodGetWindowToken = lJNIEnv->GetMethodID( + ClassView, "getWindowToken", "()Landroid/os/IBinder;"); + jobject lBinder = lJNIEnv->CallObjectMethod(lDecorView, + MethodGetWindowToken); + + // lInputMethodManager.hideSoftInput(...). + jmethodID MethodHideSoftInput = lJNIEnv->GetMethodID( + ClassInputMethodManager, "hideSoftInputFromWindow", + "(Landroid/os/IBinder;I)Z"); + jboolean lRes = lJNIEnv->CallBooleanMethod( + lInputMethodManager, MethodHideSoftInput, + lBinder, lFlags); + } + + // Finished with the JVM. + lJavaVM->DetachCurrentThread(); +} + /** * Initialize an EGL context for the current display. */ @@ -313,14 +464,15 @@ static int engine_init_display(struct engine* engine) { glDisable(GL_DEPTH_TEST); //glEnableClientState(GL_VERTEX_ARRAY); Asset::m_am = engine->app->activity->assetManager; + App::I.and_app = engine->app; App::I.data_path = "/sdcard/PanoPainter";// engine->app->activity->externalDataPath; App::I.width = w; App::I.height = h; + App::I.redraw = true; App::I.init(); LOG("All ready"); engine->animating = 1; - ANativeActivity_showSoftInput(engine->app->activity, ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED); return 0; } @@ -329,13 +481,10 @@ static int engine_init_display(struct engine* engine) { * Just the current frame in the display. */ static void engine_draw_frame(struct engine* engine) { - if (engine->display == NULL) + if (engine->display == NULL || !(App::I.redraw || App::I.animate)) return; - glClearColor(.1f, .1f, .1f, 1.f); - glViewport(0, 0, (GLsizei)engine->width, (GLsizei)engine->height); - glClear(GL_COLOR_BUFFER_BIT); - + App::I.clear(); App::I.update(0); eglSwapBuffers(engine->display, engine->surface); @@ -367,6 +516,7 @@ static void engine_term_display(struct engine* engine) { static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) { struct engine* engine = (struct engine*)app->userData; int32_t eventType = AInputEvent_getType(event); + App::I.redraw = true; //LOG("event type: %d", eventType); switch (eventType) { case AINPUT_EVENT_TYPE_MOTION: @@ -411,7 +561,11 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) p0.id = AMotionEvent_getPointerId(event, 0); p0.pos = {x, y}; p0.idx = index; - App::I.mouse_down(0, x, y); + int tool_type = AMotionEvent_getToolType(event, index); + float pressure = AMotionEvent_getPressure(event, 0); + kEventSource source = tool_type == AMOTION_EVENT_TOOL_TYPE_STYLUS ? + kEventSource::Stylus : kEventSource::Touch; + App::I.mouse_down(0, x, y, pressure, source); tracked = 1; //LOG("first down"); return 1; @@ -442,8 +596,12 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) float x = AMotionEvent_getX(event, 0); p0.id = -1; p1.id = -1; + int tool_type = AMotionEvent_getToolType(event, index); + float pressure = AMotionEvent_getPressure(event, 0); + kEventSource source = tool_type == AMOTION_EVENT_TOOL_TYPE_STYLUS ? + kEventSource::Stylus : kEventSource::Touch; if (tracked == 1) - App::I.mouse_up(0, x, y); + App::I.mouse_up(0, x, y, source); tracked = 0; //LOG("first up"); return 1; @@ -460,7 +618,7 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) { float y = AMotionEvent_getY(event, 0); float x = AMotionEvent_getX(event, 0); - App::I.mouse_move(x, y); + App::I.mouse_move(x, y, 0, kEventSource::Stylus); //LOG("single move"); return 1; } @@ -469,7 +627,11 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) { float y = AMotionEvent_getY(event, 0); float x = AMotionEvent_getX(event, 0); - App::I.mouse_move(x, y); + int tool_type = AMotionEvent_getToolType(event, index); + float pressure = AMotionEvent_getPressure(event, 0); + kEventSource source = tool_type == AMOTION_EVENT_TOOL_TYPE_STYLUS ? + kEventSource::Stylus : kEventSource::Touch; + App::I.mouse_move(x, y, pressure, source); //LOG("single move"); } else if (count == 2) @@ -505,15 +667,25 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) { int action = AKeyEvent_getAction(event); int32_t key_val = AKeyEvent_getKeyCode(event); + int key = AKeyEvent_getKeyCode(event); + int metaState = AKeyEvent_getMetaState(event); + int uniValue = GetUnicodeChar(app, action, key, metaState); switch (action) { + case AKEY_EVENT_ACTION_MULTIPLE: + LOG("Received key multi event: %d\n", key_val); + if (uniValue > 32 && uniValue < 127) //printable ascii range + App::I.key_char(uniValue); + break; case AKEY_EVENT_ACTION_DOWN: - LOG("Received key down event: %d\n", key_val); + LOG("Received key down event: %d\n", key_val); App::I.key_down(convert_key(key_val)); break; case AKEY_EVENT_ACTION_UP: - LOG("Received key up event: %d\n", key_val); + LOG("Received key up event: %d\n", key_val); App::I.key_up(convert_key(key_val)); + if (uniValue > 32 && uniValue < 127) //printable ascii range + App::I.key_char(uniValue); break; } return 1; diff --git a/engine/app.h b/engine/app.h index 74fee62..85b629e 100644 --- a/engine/app.h +++ b/engine/app.h @@ -66,6 +66,9 @@ public: #if defined(__IOS__) && defined(__OBJC__) GameViewController* ios_view; +#endif +#ifdef __ANDROID__ + struct android_app* and_app; #endif void showKeyboard(); void hideKeyboard(); diff --git a/engine/app_events.cpp b/engine/app_events.cpp index 23f35f9..3992360 100644 --- a/engine/app_events.cpp +++ b/engine/app_events.cpp @@ -1,6 +1,11 @@ #include "pch.h" #include "app.h" +#ifdef __ANDROID__ +void displayKeyboard(android_app* mApplication, bool pShow); +#endif + + using namespace ui; void App::resize(float w, float h) @@ -14,17 +19,23 @@ void App::resize(float w, float h) void App::showKeyboard() { + LOG("show keyboard"); redraw = true; #ifdef __IOS__ [ios_view becomeFirstResponder]; +#elif __ANDROID__ + displayKeyboard(and_app, true); #endif } void App::hideKeyboard() { + LOG("hide keyboard"); redraw = true; #ifdef __IOS__ [ios_view resignFirstResponder]; +#elif __ANDROID__ + displayKeyboard(and_app, false); #endif } diff --git a/engine/asset.cpp b/engine/asset.cpp index 11c0e53..cabc4ff 100644 --- a/engine/asset.cpp +++ b/engine/asset.cpp @@ -7,6 +7,7 @@ #endif #ifdef __ANDROID__ +#include AAssetManager* Asset::m_am; #endif @@ -49,7 +50,19 @@ std::vector Asset::list_files(std::string folder, bool is_asset, co } else { + DIR *dp; + struct dirent *ep; + dp = opendir(folder.c_str()); + if (dp != NULL) + { + while ((ep = readdir(dp))) + if (ep->d_type != DT_DIR) + names.push_back(ep->d_name); + closedir(dp); + } + else + LOG("Couldn't open the directory: %s", folder.c_str()); } #else std::string abs_path = folder; diff --git a/engine/node_panel_brush.cpp b/engine/node_panel_brush.cpp index fc5d1e4..8e78738 100644 --- a/engine/node_panel_brush.cpp +++ b/engine/node_panel_brush.cpp @@ -44,7 +44,7 @@ void NodePanelBrush::init() { init_template("tpl-panel-brushes"); //m_layers_container = find("layers-container"); - static auto icons = Asset::list_files("data/Icons/", true, ".*\\.png$"); + static auto icons = Asset::list_files("data/Icons", true, ".*\\.png$"); if ((m_container = find("brushes"))) {