fix some lambda ref, use global android_app variable instead of passing as argument, fix clipboard utf issue by using byte array

This commit is contained in:
2019-07-25 08:11:48 +02:00
parent 14e0716696
commit 5fc27ebb14
4 changed files with 143 additions and 125 deletions

View File

@@ -83,7 +83,7 @@ std::string utf8chr(int cp)
// see https://stackoverflow.com/questions/21124051/receive-complete-android-unicode-input-in-c-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 // see http://www.zedwood.com/article/cpp-utf8-char-to-codepoint
int GetUnicodeChar(struct android_app* app, int eventType, int keyCode, int metaState) int GetUnicodeChar(int eventType, int keyCode, int metaState)
{ {
#define COMBINING_ACCENT 0x80000000 #define COMBINING_ACCENT 0x80000000
#define COMBINING_ACCENT_MASK 0x7fffffff #define COMBINING_ACCENT_MASK 0x7fffffff
@@ -128,49 +128,42 @@ void android_detach_jni()
g_engine.app->activity->vm->DetachCurrentThread(); g_engine.app->activity->vm->DetachCurrentThread();
} }
void android_async_lock(struct engine* engine) void android_async_lock()
{ {
mutex.lock(); mutex.lock();
if (mutex_count == 0) if (mutex_count == 0)
eglMakeCurrent(engine->display, engine->surface, engine->surface, engine->context); eglMakeCurrent(g_engine.display, g_engine.surface, g_engine.surface, g_engine.context);
mutex_count++; mutex_count++;
} }
bool android_async_trylock(struct engine* engine) bool android_async_trylock()
{ {
if (!mutex.try_lock()) if (!mutex.try_lock())
return false; return false;
if (mutex_count == 0) if (mutex_count == 0)
eglMakeCurrent(engine->display, engine->surface, engine->surface, engine->context); eglMakeCurrent(g_engine.display, g_engine.surface, g_engine.surface, g_engine.context);
mutex_count++; mutex_count++;
return true; return true;
} }
void android_async_swap(struct engine* engine) void android_async_swap()
{ {
eglSwapBuffers(engine->display, engine->surface); eglSwapBuffers(g_engine.display, g_engine.surface);
} }
void android_async_unlock(struct engine* engine) void android_async_unlock()
{ {
mutex_count--; mutex_count--;
if (mutex_count == 0) if (mutex_count == 0)
eglMakeCurrent(engine->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglMakeCurrent(g_engine.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
mutex.unlock(); mutex.unlock();
} }
struct locker
{
struct engine* e;
locker(struct engine* _e){ e = _e; android_async_lock(e); }
~locker(){ android_async_unlock(e); }
};
// see https://groups.google.com/forum/#!topic/android-ndk/Tk3g00wLKhk // see https://groups.google.com/forum/#!topic/android-ndk/Tk3g00wLKhk
void displayKeyboard(android_app* mApplication, bool pShow) void displayKeyboard(bool pShow)
{ {
// Retrieves NativeActivity. // Retrieves NativeActivity.
jobject lNativeActivity = mApplication->activity->clazz; jobject lNativeActivity = g_engine.app->activity->clazz;
jclass ClassNativeActivity = jni->GetObjectClass(lNativeActivity); jclass ClassNativeActivity = jni->GetObjectClass(lNativeActivity);
// Retrieves Context.INPUT_METHOD_SERVICE. // Retrieves Context.INPUT_METHOD_SERVICE.
@@ -284,42 +277,82 @@ JNIEXPORT void JNICALL Java_com_omixlab_panopainter_MainActivity_contentRectChan
} }
} }
void android_pick_file(android_app* mApplication, std::function<void(std::string)> callback) void android_pick_file(std::function<void(std::string)> callback)
{ {
pick_file_callback = callback; pick_file_callback = callback;
jclass clazz = jni->GetObjectClass(g_engine.app->activity->clazz);
// Retrieves NativeActivity. jmethodID method = jni->GetMethodID(clazz, "pickFile", "()V");
jobject lNativeActivity = mApplication->activity->clazz; jni->CallVoidMethod(g_engine.app->activity->clazz, method);
jclass ClassNativeActivity = jni->GetObjectClass(lNativeActivity);
jmethodID MethodPickFile = jni->GetMethodID(ClassNativeActivity, "pickFile", "()V");
jni->CallVoidMethod(lNativeActivity, MethodPickFile);
} }
float get_display_density(android_app* mApplication) float get_display_density()
{ {
// Retrieves NativeActivity. jclass clazz = jni->GetObjectClass(g_engine.app->activity->clazz);
jobject lNativeActivity = mApplication->activity->clazz; jmethodID method = jni->GetMethodID(clazz, "getDensity", "()F");
jclass ClassNativeActivity = jni->GetObjectClass(lNativeActivity); return jni->CallFloatMethod(g_engine.app->activity->clazz, method);
jmethodID MethodPickFile = jni->GetMethodID(ClassNativeActivity, "getDensity", "()F");
float density = jni->CallFloatMethod(lNativeActivity, MethodPickFile);
return density;
} }
std::string get_data_path(android_app* mApplication) std::string get_data_path()
{ {
// Retrieves NativeActivity. jclass clazz = jni->GetObjectClass(g_engine.app->activity->clazz);
jobject lNativeActivity = mApplication->activity->clazz; jmethodID method = jni->GetMethodID(clazz, "getDataPath", "()Ljava/lang/String;");
jclass ClassNativeActivity = jni->GetObjectClass(lNativeActivity); jstring js = (jstring)jni->CallObjectMethod(g_engine.app->activity->clazz, method);
jmethodID MethodPickFile = jni->GetMethodID(ClassNativeActivity, "getDataPath", "()Ljava/lang/String;"); const char* utf = jni->GetStringUTFChars(js, nullptr);
jstring path = (jstring)jni->CallObjectMethod(lNativeActivity, MethodPickFile); std::string str = utf; // create a copy
jni->ReleaseStringUTFChars(js, utf);
return str;
}
const char* path_utf = jni->GetStringUTFChars(path, nullptr); // source: https://github.com/opencollab/giws/issues/4
std::string file_path = path_utf; // create a copy jstring JniStringFromUTF8(const std::string& s)
jni->ReleaseStringUTFChars(path, path_utf); {
return file_path; int len = s.size();
jbyteArray bytes = jni->NewByteArray(len);
if (bytes == 0)
{
LOG("jni->NewByteArray failed");
return 0;
}
jni->SetByteArrayRegion(bytes, 0, len, (jbyte *)s.c_str());
jclass string_class = jni->FindClass("java/lang/String");
jmethodID stringConstructor = jni->GetMethodID(string_class, "<init>", "([B)V" );
if (stringConstructor == NULL)
{
LOG("JniStringFromUTF8 new String(byte[]) failed");
jni->DeleteLocalRef(bytes);
return 0;
}
jstring result = (jstring)jni->NewObject(string_class, stringConstructor, bytes);
jni->DeleteLocalRef(bytes);
return result; //NOTE: jstring must be freed using: curEnv->DeleteLocalRef(result);
}
std::string android_get_clipboard()
{
jclass clazz = jni->GetObjectClass(g_engine.app->activity->clazz);
jmethodID method = jni->GetMethodID(clazz, "getClipboardText", "()Ljava/lang/String;");
jstring js = (jstring)jni->CallObjectMethod(g_engine.app->activity->clazz, method);
const char* utf = jni->GetStringUTFChars(js, nullptr);
std::string str = utf; // create a copy
jni->ReleaseStringUTFChars(js, utf);
return str;
}
bool android_set_clipboard(const std::string& s)
{
jclass clazz = jni->GetObjectClass(g_engine.app->activity->clazz);
jmethodID method = jni->GetMethodID(clazz, "setClipboardText", "(Ljava/lang/String;)Z");
jstring js = JniStringFromUTF8(s);
if (!js) return false;
jboolean success = jni->CallBooleanMethod(g_engine.app->activity->clazz, method, js);
jni->DeleteLocalRef(js);
return success;
} }
/** /**
@@ -511,6 +544,9 @@ static int engine_init_display(struct engine* engine) {
{ {
LOG("RESUME APP"); LOG("RESUME APP");
App::I->and_app = engine->app; App::I->and_app = engine->app;
LOG("release egl context");
eglMakeCurrent(engine->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
mutex.unlock();
return 0; return 0;
} }
@@ -610,7 +646,7 @@ static int engine_init_display(struct engine* engine) {
//std::string base_path = engine->app->activity->externalDataPath ? //std::string base_path = engine->app->activity->externalDataPath ?
// engine->app->activity->externalDataPath : get_data_path(engine->app); // engine->app->activity->externalDataPath : get_data_path(engine->app);
if (App::I->data_path.empty() || App::I->data_path == ".") if (App::I->data_path.empty() || App::I->data_path == ".")
App::I->data_path = get_data_path(engine->app); App::I->data_path = get_data_path();
LOG("data_path %s", App::I->data_path.c_str()); LOG("data_path %s", App::I->data_path.c_str());
@@ -625,7 +661,7 @@ static int engine_init_display(struct engine* engine) {
App::I->has_vr = true; App::I->has_vr = true;
App::I->vr_only = true; App::I->vr_only = true;
#else #else
float density = get_display_density(engine->app); float density = get_display_density();
LOG("density %f", density); LOG("density %f", density);
App::I->zoom = density;// / 1.5; App::I->zoom = density;// / 1.5;
App::I->width = w; App::I->width = w;
@@ -644,59 +680,13 @@ static int engine_init_display(struct engine* engine) {
return 0; return 0;
} }
/**
* Just the current frame in the display.
*/
static void engine_draw_frame(struct engine* engine) {
return;
static auto start = std::chrono::high_resolution_clock::now();
static float elapsed = 0;
static float elapsed_1s = 0;
static int rendered_frames = 0;
locker _lock(engine);
if (engine->display == EGL_NO_DISPLAY)
return;
auto now = std::chrono::high_resolution_clock::now();
auto dt = std::chrono::duration<float>(now - start);
start = now;
elapsed += dt.count();
elapsed_1s += dt.count();
App::I->tick(dt.count());
if (elapsed_1s > 1.f)
{
//LOG("vr %d fps", rendered_frames);
elapsed_1s = 0;
rendered_frames = 0;
}
const float fps60 = 1.f / 60.f;
if (elapsed < fps60)
return;
rendered_frames++;
#ifdef __QUEST__
App::I->update(elapsed);
oculus_draw(dt.count());
#else
if (!(App::I->redraw || App::I->animate))
return;
App::I->clear();
App::I->update(elapsed);
eglSwapBuffers(engine->display, engine->surface);
#endif
elapsed = 0;
}
/** /**
* Tear down the EGL context currently associated with the display. * Tear down the EGL context currently associated with the display.
*/ */
static void engine_term_display(struct engine* engine) { static void engine_term_display(struct engine* engine) {
LOG("flush render thread");
App::I->render_sync();
mutex.lock();
if (engine->display != EGL_NO_DISPLAY) { if (engine->display != EGL_NO_DISPLAY) {
eglMakeCurrent(engine->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglMakeCurrent(engine->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
// if (engine->context != EGL_NO_CONTEXT) { // if (engine->context != EGL_NO_CONTEXT) {
@@ -794,10 +784,10 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event)
p0.pos.x = AMotionEvent_getX(event, 0); p0.pos.x = AMotionEvent_getX(event, 0);
p0.pos.y = AMotionEvent_getY(event, 0); p0.pos.y = AMotionEvent_getY(event, 0);
//LOG("second down"); //LOG("second down");
App::I->ui_task_async([=] { App::I->ui_task_async([t=tracked, _p0=p0, _p1=p1] {
if (tracked == 1) if (t == 1)
App::I->mouse_cancel(0); App::I->mouse_cancel(0);
App::I->gesture_start(p0.pos, p1.pos); App::I->gesture_start(_p0.pos, _p1.pos);
}); });
tracked = 2; tracked = 2;
} }
@@ -875,8 +865,8 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event)
float y = AMotionEvent_getY(event, 1); float y = AMotionEvent_getY(event, 1);
p1.pos = {x, y}; p1.pos = {x, y};
} }
App::I->ui_task_async([=] { App::I->ui_task_async([_p0=p0, _p1=p1] {
App::I->gesture_move(p0.pos, p1.pos); App::I->gesture_move(_p0.pos, _p1.pos);
}); });
} }
return 1; return 1;
@@ -894,7 +884,7 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event)
int32_t key_val = AKeyEvent_getKeyCode(event); int32_t key_val = AKeyEvent_getKeyCode(event);
int key = AKeyEvent_getKeyCode(event); int key = AKeyEvent_getKeyCode(event);
int metaState = AKeyEvent_getMetaState(event); int metaState = AKeyEvent_getMetaState(event);
int uniValue = GetUnicodeChar(app, action, key, metaState); int uniValue = GetUnicodeChar(action, key, metaState);
switch (action) switch (action)
{ {
case AKEY_EVENT_ACTION_MULTIPLE: case AKEY_EVENT_ACTION_MULTIPLE:
@@ -945,10 +935,8 @@ static void engine_handle_cmd(struct android_app* app, int32_t cmd) {
break; break;
case APP_CMD_INIT_WINDOW: case APP_CMD_INIT_WINDOW:
// The window is being shown, get it ready. // The window is being shown, get it ready.
if (engine->app->window != NULL) { if (engine->app->window != NULL)
engine_init_display(engine); engine_init_display(engine);
engine_draw_frame(engine);
}
break; break;
case APP_CMD_TERM_WINDOW: case APP_CMD_TERM_WINDOW:
// The window is being hidden or closed, clean it up. // The window is being hidden or closed, clean it up.
@@ -980,7 +968,6 @@ static void engine_handle_cmd(struct android_app* app, int32_t cmd) {
} }
// Also stop animating. // Also stop animating.
engine->animating = 0; engine->animating = 0;
engine_draw_frame(engine);
*/ */
break; break;
case APP_CMD_WINDOW_REDRAW_NEEDED: case APP_CMD_WINDOW_REDRAW_NEEDED:
@@ -1033,7 +1020,7 @@ void android_main(struct android_app* state) {
#endif #endif
// Note that AttachCurrentThread will reset the thread name. // Note that AttachCurrentThread will reset the thread name.
prctl(PR_SET_NAME, (long)"PanoPainter Main", 0, 0, 0); prctl(PR_SET_NAME, (long)"PP Main", 0, 0, 0);
// Prepare to monitor accelerometer // Prepare to monitor accelerometer
/* /*
@@ -1091,10 +1078,8 @@ void android_main(struct android_app* state) {
if (g_engine.display == EGL_NO_DISPLAY || ident == ALOOPER_POLL_CALLBACK) if (g_engine.display == EGL_NO_DISPLAY || ident == ALOOPER_POLL_CALLBACK)
continue; continue;
if (ident == ALOOPER_POLL_TIMEOUT){ if (ident == ALOOPER_POLL_TIMEOUT)
App::I->redraw = true; App::I->redraw = true;
engine_draw_frame(&g_engine);
}
// Check if we are exiting. // Check if we are exiting.
if (state->destroyRequested != 0) { if (state->destroyRequested != 0) {
@@ -1111,10 +1096,6 @@ void android_main(struct android_app* state) {
pick_file_callback_context(); pick_file_callback_context();
pick_file_callback_context = nullptr; pick_file_callback_context = nullptr;
} }
// Drawing is throttled to the screen update rate, so there
// is no need to do timing here.
engine_draw_frame(&g_engine);
} }
} }
} }

View File

@@ -2,6 +2,8 @@ package com.omixlab.panopainter;
import android.Manifest; import android.Manifest;
import android.app.NativeActivity; import android.app.NativeActivity;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.Rect; import android.graphics.Rect;
@@ -23,9 +25,12 @@ public class MainActivity extends NativeActivity {
System.loadLibrary("native-lib"); System.loadLibrary("native-lib");
} }
private ClipboardManager clipboard;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
checkPermissionReadStorage(); checkPermissionReadStorage();
getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@@ -192,6 +197,32 @@ public class MainActivity extends NativeActivity {
return getResources().getDisplayMetrics().density; return getResources().getDisplayMetrics().density;
} }
public String getClipboardText()
{
try
{
return clipboard.hasPrimaryClip() ? clipboard.getPrimaryClip().getItemAt(0).getText().toString() : "";
}
catch (Exception e)
{
return "";
}
}
public boolean setClipboardText(String s)
{
try
{
clipboard.setPrimaryClip(ClipData.newPlainText("PanoPainter text", s));
return true;
}
catch (Exception e)
{
// fail silently
return false;
}
}
public void pickFile() public void pickFile()
{ {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT); Intent intent = new Intent(Intent.ACTION_GET_CONTENT);

View File

@@ -12,9 +12,9 @@
#include "settings.h" #include "settings.h"
#ifdef __ANDROID__ #ifdef __ANDROID__
void android_async_lock(struct engine* engine); void android_async_lock();
void android_async_swap(struct engine* engine); void android_async_swap();
void android_async_unlock(struct engine* engine); void android_async_unlock();
void android_attach_jni(); void android_attach_jni();
void android_detach_jni(); void android_detach_jni();
#elif _WIN32 #elif _WIN32
@@ -418,7 +418,7 @@ void App::async_start()
#elif __IOS__ #elif __IOS__
[ios_view async_lock]; [ios_view async_lock];
#elif __ANDROID__ #elif __ANDROID__
android_async_lock(and_engine); android_async_lock();
#elif _WIN32 #elif _WIN32
async_lock(); async_lock();
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
@@ -438,7 +438,7 @@ void App::async_end()
#elif __IOS__ #elif __IOS__
[ios_view async_unlock]; [ios_view async_unlock];
#elif __ANDROID__ #elif __ANDROID__
android_async_unlock(and_engine); android_async_unlock();
#elif _WIN32 #elif _WIN32
async_unlock(); async_unlock();
#endif #endif
@@ -451,7 +451,7 @@ void App::async_swap()
#elif __IOS__ #elif __IOS__
[ios_view async_swap]; [ios_view async_swap];
#elif __ANDROID__ #elif __ANDROID__
android_async_swap(and_engine); android_async_swap();
#elif _WIN32 #elif _WIN32
win32_async_swap(); win32_async_swap();
#endif #endif

View File

@@ -2,8 +2,10 @@
#include "app.h" #include "app.h"
#ifdef __ANDROID__ #ifdef __ANDROID__
void displayKeyboard(android_app* mApplication, bool pShow); void displayKeyboard(bool pShow);
void android_pick_file(android_app* mApplication, std::function<void(std::string)> callback); void android_pick_file(std::function<void(std::string)> callback);
std::string android_get_clipboard();
bool android_set_clipboard(const std::string& s);
#elif _WIN32 #elif _WIN32
std::string win32_open_file(const char* filter); std::string win32_open_file(const char* filter);
std::string win32_open_dir(); std::string win32_open_dir();
@@ -21,6 +23,8 @@ std::string App::clipboard_get_text()
return [ios_view clipboard_get_string]; return [ios_view clipboard_get_string];
#elif __OSX__ #elif __OSX__
return [osx_view clipboard_get_string]; return [osx_view clipboard_get_string];
#elif __ANDROID__
return android_get_clipboard();
#endif #endif
} }
@@ -32,6 +36,8 @@ bool App::clipboard_set_text(const std::string& s)
return [ios_view clipboard_set_string:s]; return [ios_view clipboard_set_string:s];
#elif __OSX__ #elif __OSX__
return [osx_view clipboard_set_string:s]; return [osx_view clipboard_set_string:s];
#elif __ANDROID__
return android_set_clipboard(s);
#endif #endif
} }
@@ -93,7 +99,7 @@ void App::showKeyboard()
[ios_view becomeFirstResponder]; [ios_view becomeFirstResponder];
}); });
#elif __ANDROID__ #elif __ANDROID__
displayKeyboard(and_app, true); displayKeyboard(true);
#endif #endif
} }
@@ -107,7 +113,7 @@ void App::hideKeyboard()
// [ios_view unregisterForKeyboardNotifications]; // [ios_view unregisterForKeyboardNotifications];
}); });
#elif __ANDROID__ #elif __ANDROID__
displayKeyboard(and_app, false); displayKeyboard(false);
#endif #endif
} }
@@ -126,7 +132,7 @@ void App::pick_image(std::function<void(std::string path)> callback)
callback(path); callback(path);
}); });
#elif __ANDROID__ #elif __ANDROID__
android_pick_file(and_app, callback); android_pick_file(callback);
#elif _WIN32 #elif _WIN32
std::string path = win32_open_file("Image Files (*.jpg, *.png)\0*.jpg;*.png"); std::string path = win32_open_file("Image Files (*.jpg, *.png)\0*.jpg;*.png");
if (!path.empty()) if (!path.empty())
@@ -154,7 +160,7 @@ void App::pick_file(std::vector<std::string> types, std::function<void (std::str
callback(path); callback(path);
}); });
#elif __ANDROID__ #elif __ANDROID__
android_pick_file(and_app, callback); android_pick_file(callback);
#elif _WIN32 #elif _WIN32
std::string filter = "Supported Files ("; std::string filter = "Supported Files (";
bool first_type = true; bool first_type = true;