289 lines
9.0 KiB
C++
289 lines
9.0 KiB
C++
// Copyright (C) 2002-2007 Nikolaus Gebhardt
|
|
// Copyright (C) 2007-2011 Christian Stehno
|
|
// Copyright (C) 2016-2017 Dawid Gan
|
|
// This file is part of the "Irrlicht Engine".
|
|
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
|
|
|
#ifdef ANDROID
|
|
#include <irrString.h>
|
|
#include <atomic>
|
|
#include <jni.h>
|
|
#include <SDL_system.h>
|
|
#include <string>
|
|
#include <vector>
|
|
#include "../../../../src/utils/utf8/unchecked.h"
|
|
#include "../../../../src/guiengine/message_queue.hpp"
|
|
|
|
using namespace irr;
|
|
|
|
// Call when android keyboard is opened or close, and save its height for
|
|
// moving screen
|
|
std::atomic<int> g_keyboard_height(0);
|
|
std::atomic<int> g_moved_height(0);
|
|
std::atomic<int> g_disable_padding(0);
|
|
extern "C" int Android_getKeyboardHeight()
|
|
{
|
|
return g_keyboard_height.load();
|
|
}
|
|
|
|
extern "C" int Android_getMovedHeight()
|
|
{
|
|
return g_moved_height.load();
|
|
}
|
|
|
|
extern "C" int Android_disablePadding()
|
|
{
|
|
return g_disable_padding.load();
|
|
}
|
|
|
|
#define MAKE_DEBUG_MSG_CALLBACK(x) JNIEXPORT void JNICALL Java_ ## x##_SuperTuxKartActivity_debugMsg(JNIEnv* env, jclass cls, jstring msg)
|
|
#define ANDROID_DEBUG_MSG_CALLBACK(PKG_NAME) MAKE_DEBUG_MSG_CALLBACK(PKG_NAME)
|
|
|
|
extern "C"
|
|
ANDROID_DEBUG_MSG_CALLBACK(ANDROID_PACKAGE_CALLBACK_NAME)
|
|
{
|
|
if (msg == NULL)
|
|
return;
|
|
const uint16_t* utf16_text =
|
|
(const uint16_t*)env->GetStringChars(msg, NULL);
|
|
if (utf16_text == NULL)
|
|
return;
|
|
const size_t str_len = env->GetStringLength(msg);
|
|
std::u32string tmp;
|
|
utf8::unchecked::utf16to32(
|
|
utf16_text, utf16_text + str_len, std::back_inserter(tmp));
|
|
env->ReleaseStringChars(msg, utf16_text);
|
|
core::stringw message = (wchar_t*)tmp.c_str();
|
|
MessageQueue::add(MessageQueue::MT_GENERIC, message);
|
|
}
|
|
|
|
#define MAKE_ANDROID_SAVE_KBD_HEIGHT_CALLBACK(x) JNIEXPORT void JNICALL Java_ ## x##_SuperTuxKartActivity_saveKeyboardHeight(JNIEnv* env, jclass cls, jint height)
|
|
#define ANDROID_SAVE_KBD_HEIGHT_CALLBACK(PKG_NAME) MAKE_ANDROID_SAVE_KBD_HEIGHT_CALLBACK(PKG_NAME)
|
|
|
|
extern "C"
|
|
ANDROID_SAVE_KBD_HEIGHT_CALLBACK(ANDROID_PACKAGE_CALLBACK_NAME)
|
|
{
|
|
g_keyboard_height.store((int)height);
|
|
}
|
|
|
|
#define MAKE_ANDROID_SAVE_MOVED_HEIGHT_CALLBACK(x) JNIEXPORT void JNICALL Java_ ## x##_SuperTuxKartActivity_saveMovedHeight(JNIEnv* env, jclass cls, jint height)
|
|
#define ANDROID_SAVE_MOVED_HEIGHT_CALLBACK(PKG_NAME) MAKE_ANDROID_SAVE_MOVED_HEIGHT_CALLBACK(PKG_NAME)
|
|
|
|
extern "C"
|
|
ANDROID_SAVE_MOVED_HEIGHT_CALLBACK(ANDROID_PACKAGE_CALLBACK_NAME)
|
|
{
|
|
g_moved_height.store((int)height);
|
|
}
|
|
|
|
#define MAKE_ANDROID_HANDLE_PADDING_CALLBACK(x) JNIEXPORT void JNICALL Java_ ## x##_SuperTuxKartActivity_handlePadding(JNIEnv* env, jclass cls, jboolean val)
|
|
#define ANDROID_HANDLE_PADDING_CALLBACK(PKG_NAME) MAKE_ANDROID_HANDLE_PADDING_CALLBACK(PKG_NAME)
|
|
|
|
extern "C"
|
|
ANDROID_HANDLE_PADDING_CALLBACK(ANDROID_PACKAGE_CALLBACK_NAME)
|
|
{
|
|
g_disable_padding.store((int)val);
|
|
}
|
|
|
|
extern "C" void Android_initDisplayCutout(float* top, float* bottom,
|
|
float* left, float* right,
|
|
int* initial_orientation)
|
|
{
|
|
JNIEnv* env = NULL;
|
|
jobject activity = NULL;
|
|
jclass class_native_activity = NULL;
|
|
jmethodID top_method = NULL;
|
|
jmethodID bottom_method = NULL;
|
|
jmethodID left_method = NULL;
|
|
jmethodID right_method = NULL;
|
|
jmethodID initial_orientation_method = NULL;
|
|
|
|
env = (JNIEnv*)SDL_AndroidGetJNIEnv();
|
|
if (!env)
|
|
goto exit;
|
|
|
|
activity = (jobject)SDL_AndroidGetActivity();
|
|
if (!activity)
|
|
goto exit;
|
|
|
|
class_native_activity = env->GetObjectClass(activity);
|
|
if (class_native_activity == NULL)
|
|
goto exit;
|
|
|
|
top_method = env->GetMethodID(class_native_activity, "getTopPadding", "()F");
|
|
if (top_method == NULL)
|
|
goto exit;
|
|
*top = env->CallFloatMethod(activity, top_method);
|
|
|
|
bottom_method = env->GetMethodID(class_native_activity, "getBottomPadding", "()F");
|
|
if (bottom_method == NULL)
|
|
goto exit;
|
|
*bottom = env->CallFloatMethod(activity, bottom_method);
|
|
|
|
left_method = env->GetMethodID(class_native_activity, "getLeftPadding", "()F");
|
|
if (left_method == NULL)
|
|
goto exit;
|
|
*left = env->CallFloatMethod(activity, left_method);
|
|
|
|
right_method = env->GetMethodID(class_native_activity, "getRightPadding", "()F");
|
|
if (right_method == NULL)
|
|
goto exit;
|
|
*right = env->CallFloatMethod(activity, right_method);
|
|
|
|
initial_orientation_method = env->GetMethodID(class_native_activity, "getInitialOrientation", "()I");
|
|
if (initial_orientation_method == NULL)
|
|
goto exit;
|
|
*initial_orientation = env->CallIntMethod(activity, initial_orientation_method);
|
|
exit:
|
|
if (!env)
|
|
return;
|
|
env->DeleteLocalRef(class_native_activity);
|
|
env->DeleteLocalRef(activity);
|
|
}
|
|
|
|
bool Android_isHardwareKeyboardConnected()
|
|
{
|
|
JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv();
|
|
if (!env)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
jobject activity = (jobject)SDL_AndroidGetActivity();
|
|
if (!activity)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
jclass class_native_activity = env->GetObjectClass(activity);
|
|
if (class_native_activity == NULL)
|
|
{
|
|
env->DeleteLocalRef(activity);
|
|
return false;
|
|
}
|
|
|
|
jmethodID method_id = env->GetMethodID(class_native_activity, "isHardwareKeyboardConnected", "()Z");
|
|
if (method_id == NULL)
|
|
{
|
|
env->DeleteLocalRef(class_native_activity);
|
|
env->DeleteLocalRef(activity);
|
|
return false;
|
|
}
|
|
|
|
bool ret = env->CallBooleanMethod(activity, method_id);
|
|
env->DeleteLocalRef(class_native_activity);
|
|
env->DeleteLocalRef(activity);
|
|
return ret;
|
|
}
|
|
|
|
void Android_toggleOnScreenKeyboard(bool show, int type, int y)
|
|
{
|
|
JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv();
|
|
if (!env)
|
|
{
|
|
return;
|
|
}
|
|
|
|
jobject activity = (jobject)SDL_AndroidGetActivity();
|
|
if (!activity)
|
|
{
|
|
return;
|
|
}
|
|
|
|
jclass class_native_activity = env->GetObjectClass(activity);
|
|
if (class_native_activity == NULL)
|
|
{
|
|
env->DeleteLocalRef(activity);
|
|
return;
|
|
}
|
|
|
|
jmethodID method_id = NULL;
|
|
if (show)
|
|
method_id = env->GetMethodID(class_native_activity, "showKeyboard", "(II)V");
|
|
else
|
|
method_id = env->GetMethodID(class_native_activity, "hideKeyboard", "(Z)V");
|
|
|
|
if (method_id == NULL)
|
|
{
|
|
env->DeleteLocalRef(class_native_activity);
|
|
env->DeleteLocalRef(activity);
|
|
return;
|
|
}
|
|
|
|
if (show)
|
|
env->CallVoidMethod(activity, method_id, (jint)type, (jint)y);
|
|
else
|
|
env->CallVoidMethod(activity, method_id, (jboolean)(type != 0));
|
|
env->DeleteLocalRef(class_native_activity);
|
|
env->DeleteLocalRef(activity);
|
|
}
|
|
|
|
void Android_fromSTKEditBox(int widget_id, const core::stringw& text, int selection_start, int selection_end, int type)
|
|
{
|
|
JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv();
|
|
if (!env)
|
|
{
|
|
return;
|
|
}
|
|
|
|
jobject activity = (jobject)SDL_AndroidGetActivity();
|
|
if (!activity)
|
|
{
|
|
return;
|
|
}
|
|
|
|
jclass class_native_activity = env->GetObjectClass(activity);
|
|
if (class_native_activity == NULL)
|
|
{
|
|
env->DeleteLocalRef(activity);
|
|
return;
|
|
}
|
|
|
|
jmethodID method_id = env->GetMethodID(class_native_activity, "fromSTKEditBox", "(ILjava/lang/String;III)V");
|
|
if (method_id == NULL)
|
|
{
|
|
env->DeleteLocalRef(class_native_activity);
|
|
env->DeleteLocalRef(activity);
|
|
return;
|
|
}
|
|
|
|
// Android use 32bit wchar_t and java use utf16 string
|
|
// We should not use the modified utf8 from java as it fails for emoji
|
|
// because it's larger than 16bit
|
|
|
|
std::vector<uint16_t> utf16;
|
|
// Use utf32 for emoji later
|
|
static_assert(sizeof(wchar_t) == sizeof(uint32_t), "wchar_t is not 32bit");
|
|
const uint32_t* chars = (const uint32_t*)text.c_str();
|
|
utf8::unchecked::utf32to16(chars, chars + text.size(), back_inserter(utf16));
|
|
|
|
std::vector<int> mappings;
|
|
int pos = 0;
|
|
mappings.push_back(pos++);
|
|
for (unsigned i = 0; i < utf16.size(); i++)
|
|
{
|
|
if (utf8::internal::is_lead_surrogate(utf16[i]))
|
|
{
|
|
pos++;
|
|
mappings.push_back(pos++);
|
|
i++;
|
|
}
|
|
else
|
|
mappings.push_back(pos++);
|
|
}
|
|
|
|
// Correct start / end position for utf16
|
|
if (selection_start < (int)mappings.size())
|
|
selection_start = mappings[selection_start];
|
|
if (selection_end < (int)mappings.size())
|
|
selection_end = mappings[selection_end];
|
|
|
|
jstring jstring_text = env->NewString((const jchar*)utf16.data(), utf16.size());
|
|
|
|
env->CallVoidMethod(activity, method_id, (jint)widget_id, jstring_text, (jint)selection_start, (jint)selection_end, (jint)type);
|
|
env->DeleteLocalRef(jstring_text);
|
|
env->DeleteLocalRef(class_native_activity);
|
|
env->DeleteLocalRef(activity);
|
|
}
|
|
|
|
#endif
|