android: Move JNI setup and helpers to common

This commit is contained in:
t895
2024-02-05 06:07:29 -05:00
parent 4cccbe7989
commit e7c4c8b993
13 changed files with 223 additions and 169 deletions

View File

@ -0,0 +1,65 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "android_common.h"
#include <string>
#include <string_view>
#include <jni.h>
#include "common/android/id_cache.h"
#include "common/string_util.h"
namespace Common::Android {
std::string GetJString(JNIEnv* env, jstring jstr) {
if (!jstr) {
return {};
}
const jchar* jchars = env->GetStringChars(jstr, nullptr);
const jsize length = env->GetStringLength(jstr);
const std::u16string_view string_view(reinterpret_cast<const char16_t*>(jchars),
static_cast<u32>(length));
const std::string converted_string = Common::UTF16ToUTF8(string_view);
env->ReleaseStringChars(jstr, jchars);
return converted_string;
}
jstring ToJString(JNIEnv* env, std::string_view str) {
const std::u16string converted_string = Common::UTF8ToUTF16(str);
return env->NewString(reinterpret_cast<const jchar*>(converted_string.data()),
static_cast<jint>(converted_string.size()));
}
jstring ToJString(JNIEnv* env, std::u16string_view str) {
return ToJString(env, Common::UTF16ToUTF8(str));
}
double GetJDouble(JNIEnv* env, jobject jdouble) {
return env->GetDoubleField(jdouble, GetDoubleValueField());
}
jobject ToJDouble(JNIEnv* env, double value) {
return env->NewObject(GetDoubleClass(), GetDoubleConstructor(), value);
}
s32 GetJInteger(JNIEnv* env, jobject jinteger) {
return env->GetIntField(jinteger, GetIntegerValueField());
}
jobject ToJInteger(JNIEnv* env, s32 value) {
return env->NewObject(GetIntegerClass(), GetIntegerConstructor(), value);
}
bool GetJBoolean(JNIEnv* env, jobject jboolean) {
return env->GetBooleanField(jboolean, GetBooleanValueField());
}
jobject ToJBoolean(JNIEnv* env, bool value) {
return env->NewObject(GetBooleanClass(), GetBooleanConstructor(), value);
}
} // namespace Common::Android

View File

@ -0,0 +1,26 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <string>
#include <jni.h>
#include "common/common_types.h"
namespace Common::Android {
std::string GetJString(JNIEnv* env, jstring jstr);
jstring ToJString(JNIEnv* env, std::string_view str);
jstring ToJString(JNIEnv* env, std::u16string_view str);
double GetJDouble(JNIEnv* env, jobject jdouble);
jobject ToJDouble(JNIEnv* env, double value);
s32 GetJInteger(JNIEnv* env, jobject jinteger);
jobject ToJInteger(JNIEnv* env, s32 value);
bool GetJBoolean(JNIEnv* env, jobject jboolean);
jobject ToJBoolean(JNIEnv* env, bool value);
} // namespace Common::Android

View File

@ -0,0 +1,277 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <map>
#include <thread>
#include <jni.h>
#include "common/android/android_common.h"
#include "common/android/applets/software_keyboard.h"
#include "common/android/id_cache.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/core.h"
static jclass s_software_keyboard_class;
static jclass s_keyboard_config_class;
static jclass s_keyboard_data_class;
static jmethodID s_swkbd_execute_normal;
static jmethodID s_swkbd_execute_inline;
namespace Common::Android::SoftwareKeyboard {
static jobject ToJKeyboardParams(const Core::Frontend::KeyboardInitializeParameters& config) {
JNIEnv* env = GetEnvForThread();
jobject object = env->AllocObject(s_keyboard_config_class);
env->SetObjectField(object,
env->GetFieldID(s_keyboard_config_class, "ok_text", "Ljava/lang/String;"),
ToJString(env, config.ok_text));
env->SetObjectField(
object, env->GetFieldID(s_keyboard_config_class, "header_text", "Ljava/lang/String;"),
ToJString(env, config.header_text));
env->SetObjectField(object,
env->GetFieldID(s_keyboard_config_class, "sub_text", "Ljava/lang/String;"),
ToJString(env, config.sub_text));
env->SetObjectField(
object, env->GetFieldID(s_keyboard_config_class, "guide_text", "Ljava/lang/String;"),
ToJString(env, config.guide_text));
env->SetObjectField(
object, env->GetFieldID(s_keyboard_config_class, "initial_text", "Ljava/lang/String;"),
ToJString(env, config.initial_text));
env->SetShortField(object,
env->GetFieldID(s_keyboard_config_class, "left_optional_symbol_key", "S"),
static_cast<jshort>(config.left_optional_symbol_key));
env->SetShortField(object,
env->GetFieldID(s_keyboard_config_class, "right_optional_symbol_key", "S"),
static_cast<jshort>(config.right_optional_symbol_key));
env->SetIntField(object, env->GetFieldID(s_keyboard_config_class, "max_text_length", "I"),
static_cast<jint>(config.max_text_length));
env->SetIntField(object, env->GetFieldID(s_keyboard_config_class, "min_text_length", "I"),
static_cast<jint>(config.min_text_length));
env->SetIntField(object,
env->GetFieldID(s_keyboard_config_class, "initial_cursor_position", "I"),
static_cast<jint>(config.initial_cursor_position));
env->SetIntField(object, env->GetFieldID(s_keyboard_config_class, "type", "I"),
static_cast<jint>(config.type));
env->SetIntField(object, env->GetFieldID(s_keyboard_config_class, "password_mode", "I"),
static_cast<jint>(config.password_mode));
env->SetIntField(object, env->GetFieldID(s_keyboard_config_class, "text_draw_type", "I"),
static_cast<jint>(config.text_draw_type));
env->SetIntField(object, env->GetFieldID(s_keyboard_config_class, "key_disable_flags", "I"),
static_cast<jint>(config.key_disable_flags.raw));
env->SetBooleanField(object,
env->GetFieldID(s_keyboard_config_class, "use_blur_background", "Z"),
static_cast<jboolean>(config.use_blur_background));
env->SetBooleanField(object,
env->GetFieldID(s_keyboard_config_class, "enable_backspace_button", "Z"),
static_cast<jboolean>(config.enable_backspace_button));
env->SetBooleanField(object,
env->GetFieldID(s_keyboard_config_class, "enable_return_button", "Z"),
static_cast<jboolean>(config.enable_return_button));
env->SetBooleanField(object,
env->GetFieldID(s_keyboard_config_class, "disable_cancel_button", "Z"),
static_cast<jboolean>(config.disable_cancel_button));
return object;
}
AndroidKeyboard::ResultData AndroidKeyboard::ResultData::CreateFromFrontend(jobject object) {
JNIEnv* env = GetEnvForThread();
const jstring string = reinterpret_cast<jstring>(env->GetObjectField(
object, env->GetFieldID(s_keyboard_data_class, "text", "Ljava/lang/String;")));
return ResultData{GetJString(env, string),
static_cast<Service::AM::Frontend::SwkbdResult>(env->GetIntField(
object, env->GetFieldID(s_keyboard_data_class, "result", "I")))};
}
AndroidKeyboard::~AndroidKeyboard() = default;
void AndroidKeyboard::InitializeKeyboard(
bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters,
SubmitNormalCallback submit_normal_callback_, SubmitInlineCallback submit_inline_callback_) {
if (is_inline) {
LOG_WARNING(
Frontend,
"(STUBBED) called, backend requested to initialize the inline software keyboard.");
submit_inline_callback = std::move(submit_inline_callback_);
} else {
LOG_WARNING(
Frontend,
"(STUBBED) called, backend requested to initialize the normal software keyboard.");
submit_normal_callback = std::move(submit_normal_callback_);
}
parameters = std::move(initialize_parameters);
LOG_INFO(Frontend,
"\nKeyboardInitializeParameters:"
"\nok_text={}"
"\nheader_text={}"
"\nsub_text={}"
"\nguide_text={}"
"\ninitial_text={}"
"\nmax_text_length={}"
"\nmin_text_length={}"
"\ninitial_cursor_position={}"
"\ntype={}"
"\npassword_mode={}"
"\ntext_draw_type={}"
"\nkey_disable_flags={}"
"\nuse_blur_background={}"
"\nenable_backspace_button={}"
"\nenable_return_button={}"
"\ndisable_cancel_button={}",
Common::UTF16ToUTF8(parameters.ok_text), Common::UTF16ToUTF8(parameters.header_text),
Common::UTF16ToUTF8(parameters.sub_text), Common::UTF16ToUTF8(parameters.guide_text),
Common::UTF16ToUTF8(parameters.initial_text), parameters.max_text_length,
parameters.min_text_length, parameters.initial_cursor_position, parameters.type,
parameters.password_mode, parameters.text_draw_type, parameters.key_disable_flags.raw,
parameters.use_blur_background, parameters.enable_backspace_button,
parameters.enable_return_button, parameters.disable_cancel_button);
}
void AndroidKeyboard::ShowNormalKeyboard() const {
LOG_DEBUG(Frontend, "called, backend requested to show the normal software keyboard.");
ResultData data{};
// Pivot to a new thread, as we cannot call GetEnvForThread() from a Fiber.
std::thread([&] {
data = ResultData::CreateFromFrontend(GetEnvForThread()->CallStaticObjectMethod(
s_software_keyboard_class, s_swkbd_execute_normal, ToJKeyboardParams(parameters)));
}).join();
SubmitNormalText(data);
}
void AndroidKeyboard::ShowTextCheckDialog(
Service::AM::Frontend::SwkbdTextCheckResult text_check_result,
std::u16string text_check_message) const {
LOG_WARNING(Frontend, "(STUBBED) called, backend requested to show the text check dialog.");
}
void AndroidKeyboard::ShowInlineKeyboard(
Core::Frontend::InlineAppearParameters appear_parameters) const {
LOG_WARNING(Frontend,
"(STUBBED) called, backend requested to show the inline software keyboard.");
LOG_INFO(Frontend,
"\nInlineAppearParameters:"
"\nmax_text_length={}"
"\nmin_text_length={}"
"\nkey_top_scale_x={}"
"\nkey_top_scale_y={}"
"\nkey_top_translate_x={}"
"\nkey_top_translate_y={}"
"\ntype={}"
"\nkey_disable_flags={}"
"\nkey_top_as_floating={}"
"\nenable_backspace_button={}"
"\nenable_return_button={}"
"\ndisable_cancel_button={}",
appear_parameters.max_text_length, appear_parameters.min_text_length,
appear_parameters.key_top_scale_x, appear_parameters.key_top_scale_y,
appear_parameters.key_top_translate_x, appear_parameters.key_top_translate_y,
appear_parameters.type, appear_parameters.key_disable_flags.raw,
appear_parameters.key_top_as_floating, appear_parameters.enable_backspace_button,
appear_parameters.enable_return_button, appear_parameters.disable_cancel_button);
// Pivot to a new thread, as we cannot call GetEnvForThread() from a Fiber.
m_is_inline_active = true;
std::thread([&] {
GetEnvForThread()->CallStaticVoidMethod(s_software_keyboard_class, s_swkbd_execute_inline,
ToJKeyboardParams(parameters));
}).join();
}
void AndroidKeyboard::HideInlineKeyboard() const {
LOG_WARNING(Frontend,
"(STUBBED) called, backend requested to hide the inline software keyboard.");
}
void AndroidKeyboard::InlineTextChanged(
Core::Frontend::InlineTextParameters text_parameters) const {
LOG_WARNING(Frontend,
"(STUBBED) called, backend requested to change the inline keyboard text.");
LOG_INFO(Frontend,
"\nInlineTextParameters:"
"\ninput_text={}"
"\ncursor_position={}",
Common::UTF16ToUTF8(text_parameters.input_text), text_parameters.cursor_position);
submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString,
text_parameters.input_text, text_parameters.cursor_position);
}
void AndroidKeyboard::ExitKeyboard() const {
LOG_WARNING(Frontend, "(STUBBED) called, backend requested to exit the software keyboard.");
}
void AndroidKeyboard::SubmitInlineKeyboardText(std::u16string submitted_text) {
if (!m_is_inline_active) {
return;
}
m_current_text += submitted_text;
submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text,
static_cast<int>(m_current_text.size()));
}
void AndroidKeyboard::SubmitInlineKeyboardInput(int key_code) {
static constexpr int KEYCODE_BACK = 4;
static constexpr int KEYCODE_ENTER = 66;
static constexpr int KEYCODE_DEL = 67;
if (!m_is_inline_active) {
return;
}
switch (key_code) {
case KEYCODE_BACK:
case KEYCODE_ENTER:
m_is_inline_active = false;
submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::DecidedEnter, m_current_text,
static_cast<s32>(m_current_text.size()));
break;
case KEYCODE_DEL:
m_current_text.pop_back();
submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text,
static_cast<int>(m_current_text.size()));
break;
}
}
void AndroidKeyboard::SubmitNormalText(const ResultData& data) const {
submit_normal_callback(data.result, Common::UTF8ToUTF16(data.text), true);
}
void InitJNI(JNIEnv* env) {
s_software_keyboard_class = reinterpret_cast<jclass>(
env->NewGlobalRef(env->FindClass("org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard")));
s_keyboard_config_class = reinterpret_cast<jclass>(env->NewGlobalRef(
env->FindClass("org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard$KeyboardConfig")));
s_keyboard_data_class = reinterpret_cast<jclass>(env->NewGlobalRef(
env->FindClass("org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard$KeyboardData")));
s_swkbd_execute_normal = env->GetStaticMethodID(
s_software_keyboard_class, "executeNormal",
"(Lorg/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard$KeyboardConfig;)Lorg/yuzu/yuzu_emu/"
"applets/keyboard/SoftwareKeyboard$KeyboardData;");
s_swkbd_execute_inline = env->GetStaticMethodID(
s_software_keyboard_class, "executeInline",
"(Lorg/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard$KeyboardConfig;)V");
}
void CleanupJNI(JNIEnv* env) {
env->DeleteGlobalRef(s_software_keyboard_class);
env->DeleteGlobalRef(s_keyboard_config_class);
env->DeleteGlobalRef(s_keyboard_data_class);
}
} // namespace Common::Android::SoftwareKeyboard

View File

@ -0,0 +1,78 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <jni.h>
#include "core/frontend/applets/software_keyboard.h"
namespace Common::Android::SoftwareKeyboard {
class AndroidKeyboard final : public Core::Frontend::SoftwareKeyboardApplet {
public:
~AndroidKeyboard() override;
void Close() const override {
ExitKeyboard();
}
void InitializeKeyboard(bool is_inline,
Core::Frontend::KeyboardInitializeParameters initialize_parameters,
SubmitNormalCallback submit_normal_callback_,
SubmitInlineCallback submit_inline_callback_) override;
void ShowNormalKeyboard() const override;
void ShowTextCheckDialog(Service::AM::Frontend::SwkbdTextCheckResult text_check_result,
std::u16string text_check_message) const override;
void ShowInlineKeyboard(
Core::Frontend::InlineAppearParameters appear_parameters) const override;
void HideInlineKeyboard() const override;
void InlineTextChanged(Core::Frontend::InlineTextParameters text_parameters) const override;
void ExitKeyboard() const override;
void SubmitInlineKeyboardText(std::u16string submitted_text);
void SubmitInlineKeyboardInput(int key_code);
private:
struct ResultData {
static ResultData CreateFromFrontend(jobject object);
std::string text;
Service::AM::Frontend::SwkbdResult result{};
};
void SubmitNormalText(const ResultData& result) const;
Core::Frontend::KeyboardInitializeParameters parameters{};
mutable SubmitNormalCallback submit_normal_callback;
mutable SubmitInlineCallback submit_inline_callback;
private:
mutable bool m_is_inline_active{};
std::u16string m_current_text;
};
// Should be called in JNI_Load
void InitJNI(JNIEnv* env);
// Should be called in JNI_Unload
void CleanupJNI(JNIEnv* env);
} // namespace Common::Android::SoftwareKeyboard
// Native function calls
extern "C" {
JNIEXPORT jobject JNICALL Java_org_citra_citra_1emu_applets_SoftwareKeyboard_ValidateFilters(
JNIEnv* env, jclass clazz, jstring text);
JNIEXPORT jobject JNICALL Java_org_citra_citra_1emu_applets_SoftwareKeyboard_ValidateInput(
JNIEnv* env, jclass clazz, jstring text);
}

View File

@ -0,0 +1,428 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <jni.h>
#include "applets/software_keyboard.h"
#include "common/android/id_cache.h"
#include "common/assert.h"
#include "common/fs/fs_android.h"
#include "video_core/rasterizer_interface.h"
static JavaVM* s_java_vm;
static jclass s_native_library_class;
static jclass s_disk_cache_progress_class;
static jclass s_load_callback_stage_class;
static jclass s_game_dir_class;
static jmethodID s_game_dir_constructor;
static jmethodID s_exit_emulation_activity;
static jmethodID s_disk_cache_load_progress;
static jmethodID s_on_emulation_started;
static jmethodID s_on_emulation_stopped;
static jmethodID s_on_program_changed;
static jclass s_game_class;
static jmethodID s_game_constructor;
static jfieldID s_game_title_field;
static jfieldID s_game_path_field;
static jfieldID s_game_program_id_field;
static jfieldID s_game_developer_field;
static jfieldID s_game_version_field;
static jfieldID s_game_is_homebrew_field;
static jclass s_string_class;
static jclass s_pair_class;
static jmethodID s_pair_constructor;
static jfieldID s_pair_first_field;
static jfieldID s_pair_second_field;
static jclass s_overlay_control_data_class;
static jmethodID s_overlay_control_data_constructor;
static jfieldID s_overlay_control_data_id_field;
static jfieldID s_overlay_control_data_enabled_field;
static jfieldID s_overlay_control_data_landscape_position_field;
static jfieldID s_overlay_control_data_portrait_position_field;
static jfieldID s_overlay_control_data_foldable_position_field;
static jclass s_patch_class;
static jmethodID s_patch_constructor;
static jfieldID s_patch_enabled_field;
static jfieldID s_patch_name_field;
static jfieldID s_patch_version_field;
static jfieldID s_patch_type_field;
static jfieldID s_patch_program_id_field;
static jfieldID s_patch_title_id_field;
static jclass s_double_class;
static jmethodID s_double_constructor;
static jfieldID s_double_value_field;
static jclass s_integer_class;
static jmethodID s_integer_constructor;
static jfieldID s_integer_value_field;
static jclass s_boolean_class;
static jmethodID s_boolean_constructor;
static jfieldID s_boolean_value_field;
static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
namespace Common::Android {
JNIEnv* GetEnvForThread() {
thread_local static struct OwnedEnv {
OwnedEnv() {
status = s_java_vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
if (status == JNI_EDETACHED)
s_java_vm->AttachCurrentThread(&env, nullptr);
}
~OwnedEnv() {
if (status == JNI_EDETACHED)
s_java_vm->DetachCurrentThread();
}
int status;
JNIEnv* env = nullptr;
} owned;
return owned.env;
}
jclass GetNativeLibraryClass() {
return s_native_library_class;
}
jclass GetDiskCacheProgressClass() {
return s_disk_cache_progress_class;
}
jclass GetDiskCacheLoadCallbackStageClass() {
return s_load_callback_stage_class;
}
jclass GetGameDirClass() {
return s_game_dir_class;
}
jmethodID GetGameDirConstructor() {
return s_game_dir_constructor;
}
jmethodID GetExitEmulationActivity() {
return s_exit_emulation_activity;
}
jmethodID GetDiskCacheLoadProgress() {
return s_disk_cache_load_progress;
}
jmethodID GetOnEmulationStarted() {
return s_on_emulation_started;
}
jmethodID GetOnEmulationStopped() {
return s_on_emulation_stopped;
}
jmethodID GetOnProgramChanged() {
return s_on_program_changed;
}
jclass GetGameClass() {
return s_game_class;
}
jmethodID GetGameConstructor() {
return s_game_constructor;
}
jfieldID GetGameTitleField() {
return s_game_title_field;
}
jfieldID GetGamePathField() {
return s_game_path_field;
}
jfieldID GetGameProgramIdField() {
return s_game_program_id_field;
}
jfieldID GetGameDeveloperField() {
return s_game_developer_field;
}
jfieldID GetGameVersionField() {
return s_game_version_field;
}
jfieldID GetGameIsHomebrewField() {
return s_game_is_homebrew_field;
}
jclass GetStringClass() {
return s_string_class;
}
jclass GetPairClass() {
return s_pair_class;
}
jmethodID GetPairConstructor() {
return s_pair_constructor;
}
jfieldID GetPairFirstField() {
return s_pair_first_field;
}
jfieldID GetPairSecondField() {
return s_pair_second_field;
}
jclass GetOverlayControlDataClass() {
return s_overlay_control_data_class;
}
jmethodID GetOverlayControlDataConstructor() {
return s_overlay_control_data_constructor;
}
jfieldID GetOverlayControlDataIdField() {
return s_overlay_control_data_id_field;
}
jfieldID GetOverlayControlDataEnabledField() {
return s_overlay_control_data_enabled_field;
}
jfieldID GetOverlayControlDataLandscapePositionField() {
return s_overlay_control_data_landscape_position_field;
}
jfieldID GetOverlayControlDataPortraitPositionField() {
return s_overlay_control_data_portrait_position_field;
}
jfieldID GetOverlayControlDataFoldablePositionField() {
return s_overlay_control_data_foldable_position_field;
}
jclass GetPatchClass() {
return s_patch_class;
}
jmethodID GetPatchConstructor() {
return s_patch_constructor;
}
jfieldID GetPatchEnabledField() {
return s_patch_enabled_field;
}
jfieldID GetPatchNameField() {
return s_patch_name_field;
}
jfieldID GetPatchVersionField() {
return s_patch_version_field;
}
jfieldID GetPatchTypeField() {
return s_patch_type_field;
}
jfieldID GetPatchProgramIdField() {
return s_patch_program_id_field;
}
jfieldID GetPatchTitleIdField() {
return s_patch_title_id_field;
}
jclass GetDoubleClass() {
return s_double_class;
}
jmethodID GetDoubleConstructor() {
return s_double_constructor;
}
jfieldID GetDoubleValueField() {
return s_double_value_field;
}
jclass GetIntegerClass() {
return s_integer_class;
}
jmethodID GetIntegerConstructor() {
return s_integer_constructor;
}
jfieldID GetIntegerValueField() {
return s_integer_value_field;
}
jclass GetBooleanClass() {
return s_boolean_class;
}
jmethodID GetBooleanConstructor() {
return s_boolean_constructor;
}
jfieldID GetBooleanValueField() {
return s_boolean_value_field;
}
#ifdef __cplusplus
extern "C" {
#endif
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
s_java_vm = vm;
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION) != JNI_OK)
return JNI_ERR;
// Initialize Java classes
const jclass native_library_class = env->FindClass("org/yuzu/yuzu_emu/NativeLibrary");
s_native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
s_disk_cache_progress_class = reinterpret_cast<jclass>(env->NewGlobalRef(
env->FindClass("org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress")));
s_load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(
"org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage")));
const jclass game_dir_class = env->FindClass("org/yuzu/yuzu_emu/model/GameDir");
s_game_dir_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_dir_class));
s_game_dir_constructor = env->GetMethodID(game_dir_class, "<init>", "(Ljava/lang/String;Z)V");
env->DeleteLocalRef(game_dir_class);
// Initialize methods
s_exit_emulation_activity =
env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V");
s_disk_cache_load_progress =
env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V");
s_on_emulation_started =
env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V");
s_on_emulation_stopped =
env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V");
s_on_program_changed =
env->GetStaticMethodID(s_native_library_class, "onProgramChanged", "(I)V");
const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game");
s_game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class));
s_game_constructor = env->GetMethodID(game_class, "<init>",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
"String;Ljava/lang/String;Ljava/lang/String;Z)V");
s_game_title_field = env->GetFieldID(game_class, "title", "Ljava/lang/String;");
s_game_path_field = env->GetFieldID(game_class, "path", "Ljava/lang/String;");
s_game_program_id_field = env->GetFieldID(game_class, "programId", "Ljava/lang/String;");
s_game_developer_field = env->GetFieldID(game_class, "developer", "Ljava/lang/String;");
s_game_version_field = env->GetFieldID(game_class, "version", "Ljava/lang/String;");
s_game_is_homebrew_field = env->GetFieldID(game_class, "isHomebrew", "Z");
env->DeleteLocalRef(game_class);
const jclass string_class = env->FindClass("java/lang/String");
s_string_class = reinterpret_cast<jclass>(env->NewGlobalRef(string_class));
env->DeleteLocalRef(string_class);
const jclass pair_class = env->FindClass("kotlin/Pair");
s_pair_class = reinterpret_cast<jclass>(env->NewGlobalRef(pair_class));
s_pair_constructor =
env->GetMethodID(pair_class, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;)V");
s_pair_first_field = env->GetFieldID(pair_class, "first", "Ljava/lang/Object;");
s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;");
env->DeleteLocalRef(pair_class);
const jclass overlay_control_data_class =
env->FindClass("org/yuzu/yuzu_emu/overlay/model/OverlayControlData");
s_overlay_control_data_class =
reinterpret_cast<jclass>(env->NewGlobalRef(overlay_control_data_class));
s_overlay_control_data_constructor =
env->GetMethodID(overlay_control_data_class, "<init>",
"(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;)V");
s_overlay_control_data_id_field =
env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;");
s_overlay_control_data_enabled_field =
env->GetFieldID(overlay_control_data_class, "enabled", "Z");
s_overlay_control_data_landscape_position_field =
env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;");
s_overlay_control_data_portrait_position_field =
env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;");
s_overlay_control_data_foldable_position_field =
env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
env->DeleteLocalRef(overlay_control_data_class);
const jclass patch_class = env->FindClass("org/yuzu/yuzu_emu/model/Patch");
s_patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class));
s_patch_constructor = env->GetMethodID(
patch_class, "<init>",
"(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V");
s_patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z");
s_patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;");
s_patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;");
s_patch_type_field = env->GetFieldID(patch_class, "type", "I");
s_patch_program_id_field = env->GetFieldID(patch_class, "programId", "Ljava/lang/String;");
s_patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;");
env->DeleteLocalRef(patch_class);
const jclass double_class = env->FindClass("java/lang/Double");
s_double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
s_double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
s_double_value_field = env->GetFieldID(double_class, "value", "D");
env->DeleteLocalRef(double_class);
const jclass int_class = env->FindClass("java/lang/Integer");
s_integer_class = reinterpret_cast<jclass>(env->NewGlobalRef(int_class));
s_integer_constructor = env->GetMethodID(int_class, "<init>", "(I)V");
s_integer_value_field = env->GetFieldID(int_class, "value", "I");
env->DeleteLocalRef(int_class);
const jclass boolean_class = env->FindClass("java/lang/Boolean");
s_boolean_class = reinterpret_cast<jclass>(env->NewGlobalRef(boolean_class));
s_boolean_constructor = env->GetMethodID(boolean_class, "<init>", "(Z)V");
s_boolean_value_field = env->GetFieldID(boolean_class, "value", "Z");
env->DeleteLocalRef(boolean_class);
// Initialize Android Storage
Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
// Initialize applets
Common::Android::SoftwareKeyboard::InitJNI(env);
return JNI_VERSION;
}
void JNI_OnUnload(JavaVM* vm, void* reserved) {
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION) != JNI_OK) {
return;
}
// UnInitialize Android Storage
Common::FS::Android::UnRegisterCallbacks();
env->DeleteGlobalRef(s_native_library_class);
env->DeleteGlobalRef(s_disk_cache_progress_class);
env->DeleteGlobalRef(s_load_callback_stage_class);
env->DeleteGlobalRef(s_game_dir_class);
env->DeleteGlobalRef(s_game_class);
env->DeleteGlobalRef(s_string_class);
env->DeleteGlobalRef(s_pair_class);
env->DeleteGlobalRef(s_overlay_control_data_class);
env->DeleteGlobalRef(s_patch_class);
env->DeleteGlobalRef(s_double_class);
env->DeleteGlobalRef(s_integer_class);
env->DeleteGlobalRef(s_boolean_class);
// UnInitialize applets
SoftwareKeyboard::CleanupJNI(env);
}
#ifdef __cplusplus
}
#endif
} // namespace Common::Android

View File

@ -0,0 +1,88 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <future>
#include <jni.h>
#include "video_core/rasterizer_interface.h"
namespace Common::Android {
JNIEnv* GetEnvForThread();
/**
* Starts a new thread to run JNI. Intended to be used when you must run JNI from a fiber.
* @tparam T Typename of the return value for the work param
* @param work Lambda that runs JNI code. This function will take care of attaching this thread to
* the JVM
* @return The result from the work lambda param
*/
template <typename T = void>
T RunJNIOnFiber(const std::function<T(JNIEnv*)>& work) {
std::future<T> j_result = std::async(std::launch::async, [&] {
auto env = GetEnvForThread();
return work(env);
});
return j_result.get();
}
jclass GetNativeLibraryClass();
jclass GetDiskCacheProgressClass();
jclass GetDiskCacheLoadCallbackStageClass();
jclass GetGameDirClass();
jmethodID GetGameDirConstructor();
jmethodID GetDiskCacheLoadProgress();
jmethodID GetExitEmulationActivity();
jmethodID GetOnEmulationStarted();
jmethodID GetOnEmulationStopped();
jmethodID GetOnProgramChanged();
jclass GetGameClass();
jmethodID GetGameConstructor();
jfieldID GetGameTitleField();
jfieldID GetGamePathField();
jfieldID GetGameProgramIdField();
jfieldID GetGameDeveloperField();
jfieldID GetGameVersionField();
jfieldID GetGameIsHomebrewField();
jclass GetStringClass();
jclass GetPairClass();
jmethodID GetPairConstructor();
jfieldID GetPairFirstField();
jfieldID GetPairSecondField();
jclass GetOverlayControlDataClass();
jmethodID GetOverlayControlDataConstructor();
jfieldID GetOverlayControlDataIdField();
jfieldID GetOverlayControlDataEnabledField();
jfieldID GetOverlayControlDataLandscapePositionField();
jfieldID GetOverlayControlDataPortraitPositionField();
jfieldID GetOverlayControlDataFoldablePositionField();
jclass GetPatchClass();
jmethodID GetPatchConstructor();
jfieldID GetPatchEnabledField();
jfieldID GetPatchNameField();
jfieldID GetPatchVersionField();
jfieldID GetPatchTypeField();
jfieldID GetPatchProgramIdField();
jfieldID GetPatchTitleIdField();
jclass GetDoubleClass();
jmethodID GetDoubleConstructor();
jfieldID GetDoubleValueField();
jclass GetIntegerClass();
jmethodID GetIntegerConstructor();
jfieldID GetIntegerValueField();
jclass GetBooleanClass();
jmethodID GetBooleanConstructor();
jfieldID GetBooleanValueField();
} // namespace Common::Android