// 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 #include #include #include #include #include #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 g_keyboard_height(0); std::atomic g_moved_height(0); std::atomic 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 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 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