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 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_MASK 0x7fffffff
@@ -128,49 +128,42 @@ void android_detach_jni()
g_engine.app->activity->vm->DetachCurrentThread();
}
void android_async_lock(struct engine* engine)
void android_async_lock()
{
mutex.lock();
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++;
}
bool android_async_trylock(struct engine* engine)
bool android_async_trylock()
{
if (!mutex.try_lock())
return false;
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++;
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--;
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();
}
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
void displayKeyboard(android_app* mApplication, bool pShow)
void displayKeyboard(bool pShow)
{
// Retrieves NativeActivity.
jobject lNativeActivity = mApplication->activity->clazz;
jobject lNativeActivity = g_engine.app->activity->clazz;
jclass ClassNativeActivity = jni->GetObjectClass(lNativeActivity);
// 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;
// Retrieves NativeActivity.
jobject lNativeActivity = mApplication->activity->clazz;
jclass ClassNativeActivity = jni->GetObjectClass(lNativeActivity);
jmethodID MethodPickFile = jni->GetMethodID(ClassNativeActivity, "pickFile", "()V");
jni->CallVoidMethod(lNativeActivity, MethodPickFile);
jclass clazz = jni->GetObjectClass(g_engine.app->activity->clazz);
jmethodID method = jni->GetMethodID(clazz, "pickFile", "()V");
jni->CallVoidMethod(g_engine.app->activity->clazz, method);
}
float get_display_density(android_app* mApplication)
float get_display_density()
{
// Retrieves NativeActivity.
jobject lNativeActivity = mApplication->activity->clazz;
jclass ClassNativeActivity = jni->GetObjectClass(lNativeActivity);
jmethodID MethodPickFile = jni->GetMethodID(ClassNativeActivity, "getDensity", "()F");
float density = jni->CallFloatMethod(lNativeActivity, MethodPickFile);
return density;
jclass clazz = jni->GetObjectClass(g_engine.app->activity->clazz);
jmethodID method = jni->GetMethodID(clazz, "getDensity", "()F");
return jni->CallFloatMethod(g_engine.app->activity->clazz, method);
}
std::string get_data_path(android_app* mApplication)
std::string get_data_path()
{
// Retrieves NativeActivity.
jobject lNativeActivity = mApplication->activity->clazz;
jclass ClassNativeActivity = jni->GetObjectClass(lNativeActivity);
jclass clazz = jni->GetObjectClass(g_engine.app->activity->clazz);
jmethodID method = jni->GetMethodID(clazz, "getDataPath", "()Ljava/lang/String;");
jstring js = (jstring)jni->CallObjectMethod(g_engine.app->activity->clazz, method);
jmethodID MethodPickFile = jni->GetMethodID(ClassNativeActivity, "getDataPath", "()Ljava/lang/String;");
jstring path = (jstring)jni->CallObjectMethod(lNativeActivity, MethodPickFile);
const char* utf = jni->GetStringUTFChars(js, nullptr);
std::string str = utf; // create a copy
jni->ReleaseStringUTFChars(js, utf);
return str;
}
const char* path_utf = jni->GetStringUTFChars(path, nullptr);
std::string file_path = path_utf; // create a copy
jni->ReleaseStringUTFChars(path, path_utf);
return file_path;
// source: https://github.com/opencollab/giws/issues/4
jstring JniStringFromUTF8(const std::string& s)
{
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");
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;
}
@@ -610,7 +646,7 @@ static int engine_init_display(struct engine* engine) {
//std::string base_path = engine->app->activity->externalDataPath ?
// engine->app->activity->externalDataPath : get_data_path(engine->app);
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());
@@ -625,7 +661,7 @@ static int engine_init_display(struct engine* engine) {
App::I->has_vr = true;
App::I->vr_only = true;
#else
float density = get_display_density(engine->app);
float density = get_display_density();
LOG("density %f", density);
App::I->zoom = density;// / 1.5;
App::I->width = w;
@@ -644,59 +680,13 @@ static int engine_init_display(struct engine* engine) {
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.
*/
static void engine_term_display(struct engine* engine) {
LOG("flush render thread");
App::I->render_sync();
mutex.lock();
if (engine->display != EGL_NO_DISPLAY) {
eglMakeCurrent(engine->display, EGL_NO_SURFACE, EGL_NO_SURFACE, 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.y = AMotionEvent_getY(event, 0);
//LOG("second down");
App::I->ui_task_async([=] {
if (tracked == 1)
App::I->ui_task_async([t=tracked, _p0=p0, _p1=p1] {
if (t == 1)
App::I->mouse_cancel(0);
App::I->gesture_start(p0.pos, p1.pos);
App::I->gesture_start(_p0.pos, _p1.pos);
});
tracked = 2;
}
@@ -875,8 +865,8 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event)
float y = AMotionEvent_getY(event, 1);
p1.pos = {x, y};
}
App::I->ui_task_async([=] {
App::I->gesture_move(p0.pos, p1.pos);
App::I->ui_task_async([_p0=p0, _p1=p1] {
App::I->gesture_move(_p0.pos, _p1.pos);
});
}
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);
int key = AKeyEvent_getKeyCode(event);
int metaState = AKeyEvent_getMetaState(event);
int uniValue = GetUnicodeChar(app, action, key, metaState);
int uniValue = GetUnicodeChar(action, key, metaState);
switch (action)
{
case AKEY_EVENT_ACTION_MULTIPLE:
@@ -945,10 +935,8 @@ static void engine_handle_cmd(struct android_app* app, int32_t cmd) {
break;
case APP_CMD_INIT_WINDOW:
// The window is being shown, get it ready.
if (engine->app->window != NULL) {
if (engine->app->window != NULL)
engine_init_display(engine);
engine_draw_frame(engine);
}
break;
case APP_CMD_TERM_WINDOW:
// 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.
engine->animating = 0;
engine_draw_frame(engine);
*/
break;
case APP_CMD_WINDOW_REDRAW_NEEDED:
@@ -1033,7 +1020,7 @@ void android_main(struct android_app* state) {
#endif
// 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
/*
@@ -1091,10 +1078,8 @@ void android_main(struct android_app* state) {
if (g_engine.display == EGL_NO_DISPLAY || ident == ALOOPER_POLL_CALLBACK)
continue;
if (ident == ALOOPER_POLL_TIMEOUT){
if (ident == ALOOPER_POLL_TIMEOUT)
App::I->redraw = true;
engine_draw_frame(&g_engine);
}
// Check if we are exiting.
if (state->destroyRequested != 0) {
@@ -1111,10 +1096,6 @@ void android_main(struct android_app* state) {
pick_file_callback_context();
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.app.NativeActivity;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Rect;
@@ -23,9 +25,12 @@ public class MainActivity extends NativeActivity {
System.loadLibrary("native-lib");
}
private ClipboardManager clipboard;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
checkPermissionReadStorage();
getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@@ -192,6 +197,32 @@ public class MainActivity extends NativeActivity {
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()
{
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);

View File

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

View File

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