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:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user