diff options
| author | 2024-02-09 11:49:25 -0500 | |
|---|---|---|
| committer | 2024-02-09 11:49:25 -0500 | |
| commit | 7ec7ff0f303504950e4270e91076a33efd0ceb17 (patch) | |
| tree | 1e8346f775550eefd491aa8280412d86000dd637 /src/common | |
| parent | Merge pull request #12927 from german77/cheat-pause (diff) | |
| parent | android: Run OnEmulationStarted frontend callback in another thread (diff) | |
| download | yuzu-7ec7ff0f303504950e4270e91076a33efd0ceb17.tar.gz yuzu-7ec7ff0f303504950e4270e91076a33efd0ceb17.tar.xz yuzu-7ec7ff0f303504950e4270e91076a33efd0ceb17.zip | |
Merge pull request #12920 from t895/jni-common
android: Move JNI setup and helpers to common
Diffstat (limited to 'src/common')
| -rw-r--r-- | src/common/CMakeLists.txt | 8 | ||||
| -rw-r--r-- | src/common/android/android_common.cpp | 65 | ||||
| -rw-r--r-- | src/common/android/android_common.h | 26 | ||||
| -rw-r--r-- | src/common/android/applets/software_keyboard.cpp | 277 | ||||
| -rw-r--r-- | src/common/android/applets/software_keyboard.h | 78 | ||||
| -rw-r--r-- | src/common/android/id_cache.cpp | 428 | ||||
| -rw-r--r-- | src/common/android/id_cache.h | 88 | ||||
| -rw-r--r-- | src/common/fs/fs_android.cpp | 167 | ||||
| -rw-r--r-- | src/common/fs/fs_android.h | 58 |
9 files changed, 1060 insertions, 135 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index c19af2ab8..779be211e 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -182,9 +182,15 @@ endif() | |||
| 182 | 182 | ||
| 183 | if(ANDROID) | 183 | if(ANDROID) |
| 184 | target_sources(common | 184 | target_sources(common |
| 185 | PRIVATE | 185 | PUBLIC |
| 186 | fs/fs_android.cpp | 186 | fs/fs_android.cpp |
| 187 | fs/fs_android.h | 187 | fs/fs_android.h |
| 188 | android/android_common.cpp | ||
| 189 | android/android_common.h | ||
| 190 | android/id_cache.cpp | ||
| 191 | android/id_cache.h | ||
| 192 | android/applets/software_keyboard.cpp | ||
| 193 | android/applets/software_keyboard.h | ||
| 188 | ) | 194 | ) |
| 189 | endif() | 195 | endif() |
| 190 | 196 | ||
diff --git a/src/common/android/android_common.cpp b/src/common/android/android_common.cpp new file mode 100644 index 000000000..e79005658 --- /dev/null +++ b/src/common/android/android_common.cpp | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "android_common.h" | ||
| 5 | |||
| 6 | #include <string> | ||
| 7 | #include <string_view> | ||
| 8 | |||
| 9 | #include <jni.h> | ||
| 10 | |||
| 11 | #include "common/android/id_cache.h" | ||
| 12 | #include "common/string_util.h" | ||
| 13 | |||
| 14 | namespace Common::Android { | ||
| 15 | |||
| 16 | std::string GetJString(JNIEnv* env, jstring jstr) { | ||
| 17 | if (!jstr) { | ||
| 18 | return {}; | ||
| 19 | } | ||
| 20 | |||
| 21 | const jchar* jchars = env->GetStringChars(jstr, nullptr); | ||
| 22 | const jsize length = env->GetStringLength(jstr); | ||
| 23 | const std::u16string_view string_view(reinterpret_cast<const char16_t*>(jchars), | ||
| 24 | static_cast<u32>(length)); | ||
| 25 | const std::string converted_string = Common::UTF16ToUTF8(string_view); | ||
| 26 | env->ReleaseStringChars(jstr, jchars); | ||
| 27 | |||
| 28 | return converted_string; | ||
| 29 | } | ||
| 30 | |||
| 31 | jstring ToJString(JNIEnv* env, std::string_view str) { | ||
| 32 | const std::u16string converted_string = Common::UTF8ToUTF16(str); | ||
| 33 | return env->NewString(reinterpret_cast<const jchar*>(converted_string.data()), | ||
| 34 | static_cast<jint>(converted_string.size())); | ||
| 35 | } | ||
| 36 | |||
| 37 | jstring ToJString(JNIEnv* env, std::u16string_view str) { | ||
| 38 | return ToJString(env, Common::UTF16ToUTF8(str)); | ||
| 39 | } | ||
| 40 | |||
| 41 | double GetJDouble(JNIEnv* env, jobject jdouble) { | ||
| 42 | return env->GetDoubleField(jdouble, GetDoubleValueField()); | ||
| 43 | } | ||
| 44 | |||
| 45 | jobject ToJDouble(JNIEnv* env, double value) { | ||
| 46 | return env->NewObject(GetDoubleClass(), GetDoubleConstructor(), value); | ||
| 47 | } | ||
| 48 | |||
| 49 | s32 GetJInteger(JNIEnv* env, jobject jinteger) { | ||
| 50 | return env->GetIntField(jinteger, GetIntegerValueField()); | ||
| 51 | } | ||
| 52 | |||
| 53 | jobject ToJInteger(JNIEnv* env, s32 value) { | ||
| 54 | return env->NewObject(GetIntegerClass(), GetIntegerConstructor(), value); | ||
| 55 | } | ||
| 56 | |||
| 57 | bool GetJBoolean(JNIEnv* env, jobject jboolean) { | ||
| 58 | return env->GetBooleanField(jboolean, GetBooleanValueField()); | ||
| 59 | } | ||
| 60 | |||
| 61 | jobject ToJBoolean(JNIEnv* env, bool value) { | ||
| 62 | return env->NewObject(GetBooleanClass(), GetBooleanConstructor(), value); | ||
| 63 | } | ||
| 64 | |||
| 65 | } // namespace Common::Android | ||
diff --git a/src/common/android/android_common.h b/src/common/android/android_common.h new file mode 100644 index 000000000..d0ccb4ec2 --- /dev/null +++ b/src/common/android/android_common.h | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <string> | ||
| 7 | |||
| 8 | #include <jni.h> | ||
| 9 | #include "common/common_types.h" | ||
| 10 | |||
| 11 | namespace Common::Android { | ||
| 12 | |||
| 13 | std::string GetJString(JNIEnv* env, jstring jstr); | ||
| 14 | jstring ToJString(JNIEnv* env, std::string_view str); | ||
| 15 | jstring ToJString(JNIEnv* env, std::u16string_view str); | ||
| 16 | |||
| 17 | double GetJDouble(JNIEnv* env, jobject jdouble); | ||
| 18 | jobject ToJDouble(JNIEnv* env, double value); | ||
| 19 | |||
| 20 | s32 GetJInteger(JNIEnv* env, jobject jinteger); | ||
| 21 | jobject ToJInteger(JNIEnv* env, s32 value); | ||
| 22 | |||
| 23 | bool GetJBoolean(JNIEnv* env, jobject jboolean); | ||
| 24 | jobject ToJBoolean(JNIEnv* env, bool value); | ||
| 25 | |||
| 26 | } // namespace Common::Android | ||
diff --git a/src/common/android/applets/software_keyboard.cpp b/src/common/android/applets/software_keyboard.cpp new file mode 100644 index 000000000..477e62b16 --- /dev/null +++ b/src/common/android/applets/software_keyboard.cpp | |||
| @@ -0,0 +1,277 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <map> | ||
| 5 | #include <thread> | ||
| 6 | |||
| 7 | #include <jni.h> | ||
| 8 | |||
| 9 | #include "common/android/android_common.h" | ||
| 10 | #include "common/android/applets/software_keyboard.h" | ||
| 11 | #include "common/android/id_cache.h" | ||
| 12 | #include "common/logging/log.h" | ||
| 13 | #include "common/string_util.h" | ||
| 14 | #include "core/core.h" | ||
| 15 | |||
| 16 | static jclass s_software_keyboard_class; | ||
| 17 | static jclass s_keyboard_config_class; | ||
| 18 | static jclass s_keyboard_data_class; | ||
| 19 | static jmethodID s_swkbd_execute_normal; | ||
| 20 | static jmethodID s_swkbd_execute_inline; | ||
| 21 | |||
| 22 | namespace Common::Android::SoftwareKeyboard { | ||
| 23 | |||
| 24 | static jobject ToJKeyboardParams(const Core::Frontend::KeyboardInitializeParameters& config) { | ||
| 25 | JNIEnv* env = GetEnvForThread(); | ||
| 26 | jobject object = env->AllocObject(s_keyboard_config_class); | ||
| 27 | |||
| 28 | env->SetObjectField(object, | ||
| 29 | env->GetFieldID(s_keyboard_config_class, "ok_text", "Ljava/lang/String;"), | ||
| 30 | ToJString(env, config.ok_text)); | ||
| 31 | env->SetObjectField( | ||
| 32 | object, env->GetFieldID(s_keyboard_config_class, "header_text", "Ljava/lang/String;"), | ||
| 33 | ToJString(env, config.header_text)); | ||
| 34 | env->SetObjectField(object, | ||
| 35 | env->GetFieldID(s_keyboard_config_class, "sub_text", "Ljava/lang/String;"), | ||
| 36 | ToJString(env, config.sub_text)); | ||
| 37 | env->SetObjectField( | ||
| 38 | object, env->GetFieldID(s_keyboard_config_class, "guide_text", "Ljava/lang/String;"), | ||
| 39 | ToJString(env, config.guide_text)); | ||
| 40 | env->SetObjectField( | ||
| 41 | object, env->GetFieldID(s_keyboard_config_class, "initial_text", "Ljava/lang/String;"), | ||
| 42 | ToJString(env, config.initial_text)); | ||
| 43 | env->SetShortField(object, | ||
| 44 | env->GetFieldID(s_keyboard_config_class, "left_optional_symbol_key", "S"), | ||
| 45 | static_cast<jshort>(config.left_optional_symbol_key)); | ||
| 46 | env->SetShortField(object, | ||
| 47 | env->GetFieldID(s_keyboard_config_class, "right_optional_symbol_key", "S"), | ||
| 48 | static_cast<jshort>(config.right_optional_symbol_key)); | ||
| 49 | env->SetIntField(object, env->GetFieldID(s_keyboard_config_class, "max_text_length", "I"), | ||
| 50 | static_cast<jint>(config.max_text_length)); | ||
| 51 | env->SetIntField(object, env->GetFieldID(s_keyboard_config_class, "min_text_length", "I"), | ||
| 52 | static_cast<jint>(config.min_text_length)); | ||
| 53 | env->SetIntField(object, | ||
| 54 | env->GetFieldID(s_keyboard_config_class, "initial_cursor_position", "I"), | ||
| 55 | static_cast<jint>(config.initial_cursor_position)); | ||
| 56 | env->SetIntField(object, env->GetFieldID(s_keyboard_config_class, "type", "I"), | ||
| 57 | static_cast<jint>(config.type)); | ||
| 58 | env->SetIntField(object, env->GetFieldID(s_keyboard_config_class, "password_mode", "I"), | ||
| 59 | static_cast<jint>(config.password_mode)); | ||
| 60 | env->SetIntField(object, env->GetFieldID(s_keyboard_config_class, "text_draw_type", "I"), | ||
| 61 | static_cast<jint>(config.text_draw_type)); | ||
| 62 | env->SetIntField(object, env->GetFieldID(s_keyboard_config_class, "key_disable_flags", "I"), | ||
| 63 | static_cast<jint>(config.key_disable_flags.raw)); | ||
| 64 | env->SetBooleanField(object, | ||
| 65 | env->GetFieldID(s_keyboard_config_class, "use_blur_background", "Z"), | ||
| 66 | static_cast<jboolean>(config.use_blur_background)); | ||
| 67 | env->SetBooleanField(object, | ||
| 68 | env->GetFieldID(s_keyboard_config_class, "enable_backspace_button", "Z"), | ||
| 69 | static_cast<jboolean>(config.enable_backspace_button)); | ||
| 70 | env->SetBooleanField(object, | ||
| 71 | env->GetFieldID(s_keyboard_config_class, "enable_return_button", "Z"), | ||
| 72 | static_cast<jboolean>(config.enable_return_button)); | ||
| 73 | env->SetBooleanField(object, | ||
| 74 | env->GetFieldID(s_keyboard_config_class, "disable_cancel_button", "Z"), | ||
| 75 | static_cast<jboolean>(config.disable_cancel_button)); | ||
| 76 | |||
| 77 | return object; | ||
| 78 | } | ||
| 79 | |||
| 80 | AndroidKeyboard::ResultData AndroidKeyboard::ResultData::CreateFromFrontend(jobject object) { | ||
| 81 | JNIEnv* env = GetEnvForThread(); | ||
| 82 | const jstring string = reinterpret_cast<jstring>(env->GetObjectField( | ||
| 83 | object, env->GetFieldID(s_keyboard_data_class, "text", "Ljava/lang/String;"))); | ||
| 84 | return ResultData{GetJString(env, string), | ||
| 85 | static_cast<Service::AM::Frontend::SwkbdResult>(env->GetIntField( | ||
| 86 | object, env->GetFieldID(s_keyboard_data_class, "result", "I")))}; | ||
| 87 | } | ||
| 88 | |||
| 89 | AndroidKeyboard::~AndroidKeyboard() = default; | ||
| 90 | |||
| 91 | void AndroidKeyboard::InitializeKeyboard( | ||
| 92 | bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters, | ||
| 93 | SubmitNormalCallback submit_normal_callback_, SubmitInlineCallback submit_inline_callback_) { | ||
| 94 | if (is_inline) { | ||
| 95 | LOG_WARNING( | ||
| 96 | Frontend, | ||
| 97 | "(STUBBED) called, backend requested to initialize the inline software keyboard."); | ||
| 98 | |||
| 99 | submit_inline_callback = std::move(submit_inline_callback_); | ||
| 100 | } else { | ||
| 101 | LOG_WARNING( | ||
| 102 | Frontend, | ||
| 103 | "(STUBBED) called, backend requested to initialize the normal software keyboard."); | ||
| 104 | |||
| 105 | submit_normal_callback = std::move(submit_normal_callback_); | ||
| 106 | } | ||
| 107 | |||
| 108 | parameters = std::move(initialize_parameters); | ||
| 109 | |||
| 110 | LOG_INFO(Frontend, | ||
| 111 | "\nKeyboardInitializeParameters:" | ||
| 112 | "\nok_text={}" | ||
| 113 | "\nheader_text={}" | ||
| 114 | "\nsub_text={}" | ||
| 115 | "\nguide_text={}" | ||
| 116 | "\ninitial_text={}" | ||
| 117 | "\nmax_text_length={}" | ||
| 118 | "\nmin_text_length={}" | ||
| 119 | "\ninitial_cursor_position={}" | ||
| 120 | "\ntype={}" | ||
| 121 | "\npassword_mode={}" | ||
| 122 | "\ntext_draw_type={}" | ||
| 123 | "\nkey_disable_flags={}" | ||
| 124 | "\nuse_blur_background={}" | ||
| 125 | "\nenable_backspace_button={}" | ||
| 126 | "\nenable_return_button={}" | ||
| 127 | "\ndisable_cancel_button={}", | ||
| 128 | Common::UTF16ToUTF8(parameters.ok_text), Common::UTF16ToUTF8(parameters.header_text), | ||
| 129 | Common::UTF16ToUTF8(parameters.sub_text), Common::UTF16ToUTF8(parameters.guide_text), | ||
| 130 | Common::UTF16ToUTF8(parameters.initial_text), parameters.max_text_length, | ||
| 131 | parameters.min_text_length, parameters.initial_cursor_position, parameters.type, | ||
| 132 | parameters.password_mode, parameters.text_draw_type, parameters.key_disable_flags.raw, | ||
| 133 | parameters.use_blur_background, parameters.enable_backspace_button, | ||
| 134 | parameters.enable_return_button, parameters.disable_cancel_button); | ||
| 135 | } | ||
| 136 | |||
| 137 | void AndroidKeyboard::ShowNormalKeyboard() const { | ||
| 138 | LOG_DEBUG(Frontend, "called, backend requested to show the normal software keyboard."); | ||
| 139 | |||
| 140 | ResultData data{}; | ||
| 141 | |||
| 142 | // Pivot to a new thread, as we cannot call GetEnvForThread() from a Fiber. | ||
| 143 | std::thread([&] { | ||
| 144 | data = ResultData::CreateFromFrontend(GetEnvForThread()->CallStaticObjectMethod( | ||
| 145 | s_software_keyboard_class, s_swkbd_execute_normal, ToJKeyboardParams(parameters))); | ||
| 146 | }).join(); | ||
| 147 | |||
| 148 | SubmitNormalText(data); | ||
| 149 | } | ||
| 150 | |||
| 151 | void AndroidKeyboard::ShowTextCheckDialog( | ||
| 152 | Service::AM::Frontend::SwkbdTextCheckResult text_check_result, | ||
| 153 | std::u16string text_check_message) const { | ||
| 154 | LOG_WARNING(Frontend, "(STUBBED) called, backend requested to show the text check dialog."); | ||
| 155 | } | ||
| 156 | |||
| 157 | void AndroidKeyboard::ShowInlineKeyboard( | ||
| 158 | Core::Frontend::InlineAppearParameters appear_parameters) const { | ||
| 159 | LOG_WARNING(Frontend, | ||
| 160 | "(STUBBED) called, backend requested to show the inline software keyboard."); | ||
| 161 | |||
| 162 | LOG_INFO(Frontend, | ||
| 163 | "\nInlineAppearParameters:" | ||
| 164 | "\nmax_text_length={}" | ||
| 165 | "\nmin_text_length={}" | ||
| 166 | "\nkey_top_scale_x={}" | ||
| 167 | "\nkey_top_scale_y={}" | ||
| 168 | "\nkey_top_translate_x={}" | ||
| 169 | "\nkey_top_translate_y={}" | ||
| 170 | "\ntype={}" | ||
| 171 | "\nkey_disable_flags={}" | ||
| 172 | "\nkey_top_as_floating={}" | ||
| 173 | "\nenable_backspace_button={}" | ||
| 174 | "\nenable_return_button={}" | ||
| 175 | "\ndisable_cancel_button={}", | ||
| 176 | appear_parameters.max_text_length, appear_parameters.min_text_length, | ||
| 177 | appear_parameters.key_top_scale_x, appear_parameters.key_top_scale_y, | ||
| 178 | appear_parameters.key_top_translate_x, appear_parameters.key_top_translate_y, | ||
| 179 | appear_parameters.type, appear_parameters.key_disable_flags.raw, | ||
| 180 | appear_parameters.key_top_as_floating, appear_parameters.enable_backspace_button, | ||
| 181 | appear_parameters.enable_return_button, appear_parameters.disable_cancel_button); | ||
| 182 | |||
| 183 | // Pivot to a new thread, as we cannot call GetEnvForThread() from a Fiber. | ||
| 184 | m_is_inline_active = true; | ||
| 185 | std::thread([&] { | ||
| 186 | GetEnvForThread()->CallStaticVoidMethod(s_software_keyboard_class, s_swkbd_execute_inline, | ||
| 187 | ToJKeyboardParams(parameters)); | ||
| 188 | }).join(); | ||
| 189 | } | ||
| 190 | |||
| 191 | void AndroidKeyboard::HideInlineKeyboard() const { | ||
| 192 | LOG_WARNING(Frontend, | ||
| 193 | "(STUBBED) called, backend requested to hide the inline software keyboard."); | ||
| 194 | } | ||
| 195 | |||
| 196 | void AndroidKeyboard::InlineTextChanged( | ||
| 197 | Core::Frontend::InlineTextParameters text_parameters) const { | ||
| 198 | LOG_WARNING(Frontend, | ||
| 199 | "(STUBBED) called, backend requested to change the inline keyboard text."); | ||
| 200 | |||
| 201 | LOG_INFO(Frontend, | ||
| 202 | "\nInlineTextParameters:" | ||
| 203 | "\ninput_text={}" | ||
| 204 | "\ncursor_position={}", | ||
| 205 | Common::UTF16ToUTF8(text_parameters.input_text), text_parameters.cursor_position); | ||
| 206 | |||
| 207 | submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, | ||
| 208 | text_parameters.input_text, text_parameters.cursor_position); | ||
| 209 | } | ||
| 210 | |||
| 211 | void AndroidKeyboard::ExitKeyboard() const { | ||
| 212 | LOG_WARNING(Frontend, "(STUBBED) called, backend requested to exit the software keyboard."); | ||
| 213 | } | ||
| 214 | |||
| 215 | void AndroidKeyboard::SubmitInlineKeyboardText(std::u16string submitted_text) { | ||
| 216 | if (!m_is_inline_active) { | ||
| 217 | return; | ||
| 218 | } | ||
| 219 | |||
| 220 | m_current_text += submitted_text; | ||
| 221 | |||
| 222 | submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text, | ||
| 223 | static_cast<int>(m_current_text.size())); | ||
| 224 | } | ||
| 225 | |||
| 226 | void AndroidKeyboard::SubmitInlineKeyboardInput(int key_code) { | ||
| 227 | static constexpr int KEYCODE_BACK = 4; | ||
| 228 | static constexpr int KEYCODE_ENTER = 66; | ||
| 229 | static constexpr int KEYCODE_DEL = 67; | ||
| 230 | |||
| 231 | if (!m_is_inline_active) { | ||
| 232 | return; | ||
| 233 | } | ||
| 234 | |||
| 235 | switch (key_code) { | ||
| 236 | case KEYCODE_BACK: | ||
| 237 | case KEYCODE_ENTER: | ||
| 238 | m_is_inline_active = false; | ||
| 239 | submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::DecidedEnter, m_current_text, | ||
| 240 | static_cast<s32>(m_current_text.size())); | ||
| 241 | break; | ||
| 242 | case KEYCODE_DEL: | ||
| 243 | m_current_text.pop_back(); | ||
| 244 | submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text, | ||
| 245 | static_cast<int>(m_current_text.size())); | ||
| 246 | break; | ||
| 247 | } | ||
| 248 | } | ||
| 249 | |||
| 250 | void AndroidKeyboard::SubmitNormalText(const ResultData& data) const { | ||
| 251 | submit_normal_callback(data.result, Common::UTF8ToUTF16(data.text), true); | ||
| 252 | } | ||
| 253 | |||
| 254 | void InitJNI(JNIEnv* env) { | ||
| 255 | s_software_keyboard_class = reinterpret_cast<jclass>( | ||
| 256 | env->NewGlobalRef(env->FindClass("org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard"))); | ||
| 257 | s_keyboard_config_class = reinterpret_cast<jclass>(env->NewGlobalRef( | ||
| 258 | env->FindClass("org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard$KeyboardConfig"))); | ||
| 259 | s_keyboard_data_class = reinterpret_cast<jclass>(env->NewGlobalRef( | ||
| 260 | env->FindClass("org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard$KeyboardData"))); | ||
| 261 | |||
| 262 | s_swkbd_execute_normal = env->GetStaticMethodID( | ||
| 263 | s_software_keyboard_class, "executeNormal", | ||
| 264 | "(Lorg/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard$KeyboardConfig;)Lorg/yuzu/yuzu_emu/" | ||
| 265 | "applets/keyboard/SoftwareKeyboard$KeyboardData;"); | ||
| 266 | s_swkbd_execute_inline = env->GetStaticMethodID( | ||
| 267 | s_software_keyboard_class, "executeInline", | ||
| 268 | "(Lorg/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard$KeyboardConfig;)V"); | ||
| 269 | } | ||
| 270 | |||
| 271 | void CleanupJNI(JNIEnv* env) { | ||
| 272 | env->DeleteGlobalRef(s_software_keyboard_class); | ||
| 273 | env->DeleteGlobalRef(s_keyboard_config_class); | ||
| 274 | env->DeleteGlobalRef(s_keyboard_data_class); | ||
| 275 | } | ||
| 276 | |||
| 277 | } // namespace Common::Android::SoftwareKeyboard | ||
diff --git a/src/common/android/applets/software_keyboard.h b/src/common/android/applets/software_keyboard.h new file mode 100644 index 000000000..9fd09d27c --- /dev/null +++ b/src/common/android/applets/software_keyboard.h | |||
| @@ -0,0 +1,78 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <jni.h> | ||
| 7 | |||
| 8 | #include "core/frontend/applets/software_keyboard.h" | ||
| 9 | |||
| 10 | namespace Common::Android::SoftwareKeyboard { | ||
| 11 | |||
| 12 | class AndroidKeyboard final : public Core::Frontend::SoftwareKeyboardApplet { | ||
| 13 | public: | ||
| 14 | ~AndroidKeyboard() override; | ||
| 15 | |||
| 16 | void Close() const override { | ||
| 17 | ExitKeyboard(); | ||
| 18 | } | ||
| 19 | |||
| 20 | void InitializeKeyboard(bool is_inline, | ||
| 21 | Core::Frontend::KeyboardInitializeParameters initialize_parameters, | ||
| 22 | SubmitNormalCallback submit_normal_callback_, | ||
| 23 | SubmitInlineCallback submit_inline_callback_) override; | ||
| 24 | |||
| 25 | void ShowNormalKeyboard() const override; | ||
| 26 | |||
| 27 | void ShowTextCheckDialog(Service::AM::Frontend::SwkbdTextCheckResult text_check_result, | ||
| 28 | std::u16string text_check_message) const override; | ||
| 29 | |||
| 30 | void ShowInlineKeyboard( | ||
| 31 | Core::Frontend::InlineAppearParameters appear_parameters) const override; | ||
| 32 | |||
| 33 | void HideInlineKeyboard() const override; | ||
| 34 | |||
| 35 | void InlineTextChanged(Core::Frontend::InlineTextParameters text_parameters) const override; | ||
| 36 | |||
| 37 | void ExitKeyboard() const override; | ||
| 38 | |||
| 39 | void SubmitInlineKeyboardText(std::u16string submitted_text); | ||
| 40 | |||
| 41 | void SubmitInlineKeyboardInput(int key_code); | ||
| 42 | |||
| 43 | private: | ||
| 44 | struct ResultData { | ||
| 45 | static ResultData CreateFromFrontend(jobject object); | ||
| 46 | |||
| 47 | std::string text; | ||
| 48 | Service::AM::Frontend::SwkbdResult result{}; | ||
| 49 | }; | ||
| 50 | |||
| 51 | void SubmitNormalText(const ResultData& result) const; | ||
| 52 | |||
| 53 | Core::Frontend::KeyboardInitializeParameters parameters{}; | ||
| 54 | |||
| 55 | mutable SubmitNormalCallback submit_normal_callback; | ||
| 56 | mutable SubmitInlineCallback submit_inline_callback; | ||
| 57 | |||
| 58 | private: | ||
| 59 | mutable bool m_is_inline_active{}; | ||
| 60 | std::u16string m_current_text; | ||
| 61 | }; | ||
| 62 | |||
| 63 | // Should be called in JNI_Load | ||
| 64 | void InitJNI(JNIEnv* env); | ||
| 65 | |||
| 66 | // Should be called in JNI_Unload | ||
| 67 | void CleanupJNI(JNIEnv* env); | ||
| 68 | |||
| 69 | } // namespace Common::Android::SoftwareKeyboard | ||
| 70 | |||
| 71 | // Native function calls | ||
| 72 | extern "C" { | ||
| 73 | JNIEXPORT jobject JNICALL Java_org_citra_citra_1emu_applets_SoftwareKeyboard_ValidateFilters( | ||
| 74 | JNIEnv* env, jclass clazz, jstring text); | ||
| 75 | |||
| 76 | JNIEXPORT jobject JNICALL Java_org_citra_citra_1emu_applets_SoftwareKeyboard_ValidateInput( | ||
| 77 | JNIEnv* env, jclass clazz, jstring text); | ||
| 78 | } | ||
diff --git a/src/common/android/id_cache.cpp b/src/common/android/id_cache.cpp new file mode 100644 index 000000000..f39262db9 --- /dev/null +++ b/src/common/android/id_cache.cpp | |||
| @@ -0,0 +1,428 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <jni.h> | ||
| 5 | |||
| 6 | #include "applets/software_keyboard.h" | ||
| 7 | #include "common/android/id_cache.h" | ||
| 8 | #include "common/assert.h" | ||
| 9 | #include "common/fs/fs_android.h" | ||
| 10 | #include "video_core/rasterizer_interface.h" | ||
| 11 | |||
| 12 | static JavaVM* s_java_vm; | ||
| 13 | static jclass s_native_library_class; | ||
| 14 | static jclass s_disk_cache_progress_class; | ||
| 15 | static jclass s_load_callback_stage_class; | ||
| 16 | static jclass s_game_dir_class; | ||
| 17 | static jmethodID s_game_dir_constructor; | ||
| 18 | static jmethodID s_exit_emulation_activity; | ||
| 19 | static jmethodID s_disk_cache_load_progress; | ||
| 20 | static jmethodID s_on_emulation_started; | ||
| 21 | static jmethodID s_on_emulation_stopped; | ||
| 22 | static jmethodID s_on_program_changed; | ||
| 23 | |||
| 24 | static jclass s_game_class; | ||
| 25 | static jmethodID s_game_constructor; | ||
| 26 | static jfieldID s_game_title_field; | ||
| 27 | static jfieldID s_game_path_field; | ||
| 28 | static jfieldID s_game_program_id_field; | ||
| 29 | static jfieldID s_game_developer_field; | ||
| 30 | static jfieldID s_game_version_field; | ||
| 31 | static jfieldID s_game_is_homebrew_field; | ||
| 32 | |||
| 33 | static jclass s_string_class; | ||
| 34 | static jclass s_pair_class; | ||
| 35 | static jmethodID s_pair_constructor; | ||
| 36 | static jfieldID s_pair_first_field; | ||
| 37 | static jfieldID s_pair_second_field; | ||
| 38 | |||
| 39 | static jclass s_overlay_control_data_class; | ||
| 40 | static jmethodID s_overlay_control_data_constructor; | ||
| 41 | static jfieldID s_overlay_control_data_id_field; | ||
| 42 | static jfieldID s_overlay_control_data_enabled_field; | ||
| 43 | static jfieldID s_overlay_control_data_landscape_position_field; | ||
| 44 | static jfieldID s_overlay_control_data_portrait_position_field; | ||
| 45 | static jfieldID s_overlay_control_data_foldable_position_field; | ||
| 46 | |||
| 47 | static jclass s_patch_class; | ||
| 48 | static jmethodID s_patch_constructor; | ||
| 49 | static jfieldID s_patch_enabled_field; | ||
| 50 | static jfieldID s_patch_name_field; | ||
| 51 | static jfieldID s_patch_version_field; | ||
| 52 | static jfieldID s_patch_type_field; | ||
| 53 | static jfieldID s_patch_program_id_field; | ||
| 54 | static jfieldID s_patch_title_id_field; | ||
| 55 | |||
| 56 | static jclass s_double_class; | ||
| 57 | static jmethodID s_double_constructor; | ||
| 58 | static jfieldID s_double_value_field; | ||
| 59 | |||
| 60 | static jclass s_integer_class; | ||
| 61 | static jmethodID s_integer_constructor; | ||
| 62 | static jfieldID s_integer_value_field; | ||
| 63 | |||
| 64 | static jclass s_boolean_class; | ||
| 65 | static jmethodID s_boolean_constructor; | ||
| 66 | static jfieldID s_boolean_value_field; | ||
| 67 | |||
| 68 | static constexpr jint JNI_VERSION = JNI_VERSION_1_6; | ||
| 69 | |||
| 70 | namespace Common::Android { | ||
| 71 | |||
| 72 | JNIEnv* GetEnvForThread() { | ||
| 73 | thread_local static struct OwnedEnv { | ||
| 74 | OwnedEnv() { | ||
| 75 | status = s_java_vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6); | ||
| 76 | if (status == JNI_EDETACHED) | ||
| 77 | s_java_vm->AttachCurrentThread(&env, nullptr); | ||
| 78 | } | ||
| 79 | |||
| 80 | ~OwnedEnv() { | ||
| 81 | if (status == JNI_EDETACHED) | ||
| 82 | s_java_vm->DetachCurrentThread(); | ||
| 83 | } | ||
| 84 | |||
| 85 | int status; | ||
| 86 | JNIEnv* env = nullptr; | ||
| 87 | } owned; | ||
| 88 | return owned.env; | ||
| 89 | } | ||
| 90 | |||
| 91 | jclass GetNativeLibraryClass() { | ||
| 92 | return s_native_library_class; | ||
| 93 | } | ||
| 94 | |||
| 95 | jclass GetDiskCacheProgressClass() { | ||
| 96 | return s_disk_cache_progress_class; | ||
| 97 | } | ||
| 98 | |||
| 99 | jclass GetDiskCacheLoadCallbackStageClass() { | ||
| 100 | return s_load_callback_stage_class; | ||
| 101 | } | ||
| 102 | |||
| 103 | jclass GetGameDirClass() { | ||
| 104 | return s_game_dir_class; | ||
| 105 | } | ||
| 106 | |||
| 107 | jmethodID GetGameDirConstructor() { | ||
| 108 | return s_game_dir_constructor; | ||
| 109 | } | ||
| 110 | |||
| 111 | jmethodID GetExitEmulationActivity() { | ||
| 112 | return s_exit_emulation_activity; | ||
| 113 | } | ||
| 114 | |||
| 115 | jmethodID GetDiskCacheLoadProgress() { | ||
| 116 | return s_disk_cache_load_progress; | ||
| 117 | } | ||
| 118 | |||
| 119 | jmethodID GetOnEmulationStarted() { | ||
| 120 | return s_on_emulation_started; | ||
| 121 | } | ||
| 122 | |||
| 123 | jmethodID GetOnEmulationStopped() { | ||
| 124 | return s_on_emulation_stopped; | ||
| 125 | } | ||
| 126 | |||
| 127 | jmethodID GetOnProgramChanged() { | ||
| 128 | return s_on_program_changed; | ||
| 129 | } | ||
| 130 | |||
| 131 | jclass GetGameClass() { | ||
| 132 | return s_game_class; | ||
| 133 | } | ||
| 134 | |||
| 135 | jmethodID GetGameConstructor() { | ||
| 136 | return s_game_constructor; | ||
| 137 | } | ||
| 138 | |||
| 139 | jfieldID GetGameTitleField() { | ||
| 140 | return s_game_title_field; | ||
| 141 | } | ||
| 142 | |||
| 143 | jfieldID GetGamePathField() { | ||
| 144 | return s_game_path_field; | ||
| 145 | } | ||
| 146 | |||
| 147 | jfieldID GetGameProgramIdField() { | ||
| 148 | return s_game_program_id_field; | ||
| 149 | } | ||
| 150 | |||
| 151 | jfieldID GetGameDeveloperField() { | ||
| 152 | return s_game_developer_field; | ||
| 153 | } | ||
| 154 | |||
| 155 | jfieldID GetGameVersionField() { | ||
| 156 | return s_game_version_field; | ||
| 157 | } | ||
| 158 | |||
| 159 | jfieldID GetGameIsHomebrewField() { | ||
| 160 | return s_game_is_homebrew_field; | ||
| 161 | } | ||
| 162 | |||
| 163 | jclass GetStringClass() { | ||
| 164 | return s_string_class; | ||
| 165 | } | ||
| 166 | |||
| 167 | jclass GetPairClass() { | ||
| 168 | return s_pair_class; | ||
| 169 | } | ||
| 170 | |||
| 171 | jmethodID GetPairConstructor() { | ||
| 172 | return s_pair_constructor; | ||
| 173 | } | ||
| 174 | |||
| 175 | jfieldID GetPairFirstField() { | ||
| 176 | return s_pair_first_field; | ||
| 177 | } | ||
| 178 | |||
| 179 | jfieldID GetPairSecondField() { | ||
| 180 | return s_pair_second_field; | ||
| 181 | } | ||
| 182 | |||
| 183 | jclass GetOverlayControlDataClass() { | ||
| 184 | return s_overlay_control_data_class; | ||
| 185 | } | ||
| 186 | |||
| 187 | jmethodID GetOverlayControlDataConstructor() { | ||
| 188 | return s_overlay_control_data_constructor; | ||
| 189 | } | ||
| 190 | |||
| 191 | jfieldID GetOverlayControlDataIdField() { | ||
| 192 | return s_overlay_control_data_id_field; | ||
| 193 | } | ||
| 194 | |||
| 195 | jfieldID GetOverlayControlDataEnabledField() { | ||
| 196 | return s_overlay_control_data_enabled_field; | ||
| 197 | } | ||
| 198 | |||
| 199 | jfieldID GetOverlayControlDataLandscapePositionField() { | ||
| 200 | return s_overlay_control_data_landscape_position_field; | ||
| 201 | } | ||
| 202 | |||
| 203 | jfieldID GetOverlayControlDataPortraitPositionField() { | ||
| 204 | return s_overlay_control_data_portrait_position_field; | ||
| 205 | } | ||
| 206 | |||
| 207 | jfieldID GetOverlayControlDataFoldablePositionField() { | ||
| 208 | return s_overlay_control_data_foldable_position_field; | ||
| 209 | } | ||
| 210 | |||
| 211 | jclass GetPatchClass() { | ||
| 212 | return s_patch_class; | ||
| 213 | } | ||
| 214 | |||
| 215 | jmethodID GetPatchConstructor() { | ||
| 216 | return s_patch_constructor; | ||
| 217 | } | ||
| 218 | |||
| 219 | jfieldID GetPatchEnabledField() { | ||
| 220 | return s_patch_enabled_field; | ||
| 221 | } | ||
| 222 | |||
| 223 | jfieldID GetPatchNameField() { | ||
| 224 | return s_patch_name_field; | ||
| 225 | } | ||
| 226 | |||
| 227 | jfieldID GetPatchVersionField() { | ||
| 228 | return s_patch_version_field; | ||
| 229 | } | ||
| 230 | |||
| 231 | jfieldID GetPatchTypeField() { | ||
| 232 | return s_patch_type_field; | ||
| 233 | } | ||
| 234 | |||
| 235 | jfieldID GetPatchProgramIdField() { | ||
| 236 | return s_patch_program_id_field; | ||
| 237 | } | ||
| 238 | |||
| 239 | jfieldID GetPatchTitleIdField() { | ||
| 240 | return s_patch_title_id_field; | ||
| 241 | } | ||
| 242 | |||
| 243 | jclass GetDoubleClass() { | ||
| 244 | return s_double_class; | ||
| 245 | } | ||
| 246 | |||
| 247 | jmethodID GetDoubleConstructor() { | ||
| 248 | return s_double_constructor; | ||
| 249 | } | ||
| 250 | |||
| 251 | jfieldID GetDoubleValueField() { | ||
| 252 | return s_double_value_field; | ||
| 253 | } | ||
| 254 | |||
| 255 | jclass GetIntegerClass() { | ||
| 256 | return s_integer_class; | ||
| 257 | } | ||
| 258 | |||
| 259 | jmethodID GetIntegerConstructor() { | ||
| 260 | return s_integer_constructor; | ||
| 261 | } | ||
| 262 | |||
| 263 | jfieldID GetIntegerValueField() { | ||
| 264 | return s_integer_value_field; | ||
| 265 | } | ||
| 266 | |||
| 267 | jclass GetBooleanClass() { | ||
| 268 | return s_boolean_class; | ||
| 269 | } | ||
| 270 | |||
| 271 | jmethodID GetBooleanConstructor() { | ||
| 272 | return s_boolean_constructor; | ||
| 273 | } | ||
| 274 | |||
| 275 | jfieldID GetBooleanValueField() { | ||
| 276 | return s_boolean_value_field; | ||
| 277 | } | ||
| 278 | |||
| 279 | #ifdef __cplusplus | ||
| 280 | extern "C" { | ||
| 281 | #endif | ||
| 282 | |||
| 283 | jint JNI_OnLoad(JavaVM* vm, void* reserved) { | ||
| 284 | s_java_vm = vm; | ||
| 285 | |||
| 286 | JNIEnv* env; | ||
| 287 | if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION) != JNI_OK) | ||
| 288 | return JNI_ERR; | ||
| 289 | |||
| 290 | // Initialize Java classes | ||
| 291 | const jclass native_library_class = env->FindClass("org/yuzu/yuzu_emu/NativeLibrary"); | ||
| 292 | s_native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class)); | ||
| 293 | s_disk_cache_progress_class = reinterpret_cast<jclass>(env->NewGlobalRef( | ||
| 294 | env->FindClass("org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress"))); | ||
| 295 | s_load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass( | ||
| 296 | "org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage"))); | ||
| 297 | |||
| 298 | const jclass game_dir_class = env->FindClass("org/yuzu/yuzu_emu/model/GameDir"); | ||
| 299 | s_game_dir_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_dir_class)); | ||
| 300 | s_game_dir_constructor = env->GetMethodID(game_dir_class, "<init>", "(Ljava/lang/String;Z)V"); | ||
| 301 | env->DeleteLocalRef(game_dir_class); | ||
| 302 | |||
| 303 | // Initialize methods | ||
| 304 | s_exit_emulation_activity = | ||
| 305 | env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V"); | ||
| 306 | s_disk_cache_load_progress = | ||
| 307 | env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V"); | ||
| 308 | s_on_emulation_started = | ||
| 309 | env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V"); | ||
| 310 | s_on_emulation_stopped = | ||
| 311 | env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V"); | ||
| 312 | s_on_program_changed = | ||
| 313 | env->GetStaticMethodID(s_native_library_class, "onProgramChanged", "(I)V"); | ||
| 314 | |||
| 315 | const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game"); | ||
| 316 | s_game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class)); | ||
| 317 | s_game_constructor = env->GetMethodID(game_class, "<init>", | ||
| 318 | "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/" | ||
| 319 | "String;Ljava/lang/String;Ljava/lang/String;Z)V"); | ||
| 320 | s_game_title_field = env->GetFieldID(game_class, "title", "Ljava/lang/String;"); | ||
| 321 | s_game_path_field = env->GetFieldID(game_class, "path", "Ljava/lang/String;"); | ||
| 322 | s_game_program_id_field = env->GetFieldID(game_class, "programId", "Ljava/lang/String;"); | ||
| 323 | s_game_developer_field = env->GetFieldID(game_class, "developer", "Ljava/lang/String;"); | ||
| 324 | s_game_version_field = env->GetFieldID(game_class, "version", "Ljava/lang/String;"); | ||
| 325 | s_game_is_homebrew_field = env->GetFieldID(game_class, "isHomebrew", "Z"); | ||
| 326 | env->DeleteLocalRef(game_class); | ||
| 327 | |||
| 328 | const jclass string_class = env->FindClass("java/lang/String"); | ||
| 329 | s_string_class = reinterpret_cast<jclass>(env->NewGlobalRef(string_class)); | ||
| 330 | env->DeleteLocalRef(string_class); | ||
| 331 | |||
| 332 | const jclass pair_class = env->FindClass("kotlin/Pair"); | ||
| 333 | s_pair_class = reinterpret_cast<jclass>(env->NewGlobalRef(pair_class)); | ||
| 334 | s_pair_constructor = | ||
| 335 | env->GetMethodID(pair_class, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;)V"); | ||
| 336 | s_pair_first_field = env->GetFieldID(pair_class, "first", "Ljava/lang/Object;"); | ||
| 337 | s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;"); | ||
| 338 | env->DeleteLocalRef(pair_class); | ||
| 339 | |||
| 340 | const jclass overlay_control_data_class = | ||
| 341 | env->FindClass("org/yuzu/yuzu_emu/overlay/model/OverlayControlData"); | ||
| 342 | s_overlay_control_data_class = | ||
| 343 | reinterpret_cast<jclass>(env->NewGlobalRef(overlay_control_data_class)); | ||
| 344 | s_overlay_control_data_constructor = | ||
| 345 | env->GetMethodID(overlay_control_data_class, "<init>", | ||
| 346 | "(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;)V"); | ||
| 347 | s_overlay_control_data_id_field = | ||
| 348 | env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;"); | ||
| 349 | s_overlay_control_data_enabled_field = | ||
| 350 | env->GetFieldID(overlay_control_data_class, "enabled", "Z"); | ||
| 351 | s_overlay_control_data_landscape_position_field = | ||
| 352 | env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;"); | ||
| 353 | s_overlay_control_data_portrait_position_field = | ||
| 354 | env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;"); | ||
| 355 | s_overlay_control_data_foldable_position_field = | ||
| 356 | env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;"); | ||
| 357 | env->DeleteLocalRef(overlay_control_data_class); | ||
| 358 | |||
| 359 | const jclass patch_class = env->FindClass("org/yuzu/yuzu_emu/model/Patch"); | ||
| 360 | s_patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class)); | ||
| 361 | s_patch_constructor = env->GetMethodID( | ||
| 362 | patch_class, "<init>", | ||
| 363 | "(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V"); | ||
| 364 | s_patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z"); | ||
| 365 | s_patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;"); | ||
| 366 | s_patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;"); | ||
| 367 | s_patch_type_field = env->GetFieldID(patch_class, "type", "I"); | ||
| 368 | s_patch_program_id_field = env->GetFieldID(patch_class, "programId", "Ljava/lang/String;"); | ||
| 369 | s_patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;"); | ||
| 370 | env->DeleteLocalRef(patch_class); | ||
| 371 | |||
| 372 | const jclass double_class = env->FindClass("java/lang/Double"); | ||
| 373 | s_double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class)); | ||
| 374 | s_double_constructor = env->GetMethodID(double_class, "<init>", "(D)V"); | ||
| 375 | s_double_value_field = env->GetFieldID(double_class, "value", "D"); | ||
| 376 | env->DeleteLocalRef(double_class); | ||
| 377 | |||
| 378 | const jclass int_class = env->FindClass("java/lang/Integer"); | ||
| 379 | s_integer_class = reinterpret_cast<jclass>(env->NewGlobalRef(int_class)); | ||
| 380 | s_integer_constructor = env->GetMethodID(int_class, "<init>", "(I)V"); | ||
| 381 | s_integer_value_field = env->GetFieldID(int_class, "value", "I"); | ||
| 382 | env->DeleteLocalRef(int_class); | ||
| 383 | |||
| 384 | const jclass boolean_class = env->FindClass("java/lang/Boolean"); | ||
| 385 | s_boolean_class = reinterpret_cast<jclass>(env->NewGlobalRef(boolean_class)); | ||
| 386 | s_boolean_constructor = env->GetMethodID(boolean_class, "<init>", "(Z)V"); | ||
| 387 | s_boolean_value_field = env->GetFieldID(boolean_class, "value", "Z"); | ||
| 388 | env->DeleteLocalRef(boolean_class); | ||
| 389 | |||
| 390 | // Initialize Android Storage | ||
| 391 | Common::FS::Android::RegisterCallbacks(env, s_native_library_class); | ||
| 392 | |||
| 393 | // Initialize applets | ||
| 394 | Common::Android::SoftwareKeyboard::InitJNI(env); | ||
| 395 | |||
| 396 | return JNI_VERSION; | ||
| 397 | } | ||
| 398 | |||
| 399 | void JNI_OnUnload(JavaVM* vm, void* reserved) { | ||
| 400 | JNIEnv* env; | ||
| 401 | if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION) != JNI_OK) { | ||
| 402 | return; | ||
| 403 | } | ||
| 404 | |||
| 405 | // UnInitialize Android Storage | ||
| 406 | Common::FS::Android::UnRegisterCallbacks(); | ||
| 407 | env->DeleteGlobalRef(s_native_library_class); | ||
| 408 | env->DeleteGlobalRef(s_disk_cache_progress_class); | ||
| 409 | env->DeleteGlobalRef(s_load_callback_stage_class); | ||
| 410 | env->DeleteGlobalRef(s_game_dir_class); | ||
| 411 | env->DeleteGlobalRef(s_game_class); | ||
| 412 | env->DeleteGlobalRef(s_string_class); | ||
| 413 | env->DeleteGlobalRef(s_pair_class); | ||
| 414 | env->DeleteGlobalRef(s_overlay_control_data_class); | ||
| 415 | env->DeleteGlobalRef(s_patch_class); | ||
| 416 | env->DeleteGlobalRef(s_double_class); | ||
| 417 | env->DeleteGlobalRef(s_integer_class); | ||
| 418 | env->DeleteGlobalRef(s_boolean_class); | ||
| 419 | |||
| 420 | // UnInitialize applets | ||
| 421 | SoftwareKeyboard::CleanupJNI(env); | ||
| 422 | } | ||
| 423 | |||
| 424 | #ifdef __cplusplus | ||
| 425 | } | ||
| 426 | #endif | ||
| 427 | |||
| 428 | } // namespace Common::Android | ||
diff --git a/src/common/android/id_cache.h b/src/common/android/id_cache.h new file mode 100644 index 000000000..47802f96c --- /dev/null +++ b/src/common/android/id_cache.h | |||
| @@ -0,0 +1,88 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <future> | ||
| 7 | #include <jni.h> | ||
| 8 | |||
| 9 | #include "video_core/rasterizer_interface.h" | ||
| 10 | |||
| 11 | namespace Common::Android { | ||
| 12 | |||
| 13 | JNIEnv* GetEnvForThread(); | ||
| 14 | |||
| 15 | /** | ||
| 16 | * Starts a new thread to run JNI. Intended to be used when you must run JNI from a fiber. | ||
| 17 | * @tparam T Typename of the return value for the work param | ||
| 18 | * @param work Lambda that runs JNI code. This function will take care of attaching this thread to | ||
| 19 | * the JVM | ||
| 20 | * @return The result from the work lambda param | ||
| 21 | */ | ||
| 22 | template <typename T = void> | ||
| 23 | T RunJNIOnFiber(const std::function<T(JNIEnv*)>& work) { | ||
| 24 | std::future<T> j_result = std::async(std::launch::async, [&] { | ||
| 25 | auto env = GetEnvForThread(); | ||
| 26 | return work(env); | ||
| 27 | }); | ||
| 28 | return j_result.get(); | ||
| 29 | } | ||
| 30 | |||
| 31 | jclass GetNativeLibraryClass(); | ||
| 32 | |||
| 33 | jclass GetDiskCacheProgressClass(); | ||
| 34 | jclass GetDiskCacheLoadCallbackStageClass(); | ||
| 35 | jclass GetGameDirClass(); | ||
| 36 | jmethodID GetGameDirConstructor(); | ||
| 37 | jmethodID GetDiskCacheLoadProgress(); | ||
| 38 | |||
| 39 | jmethodID GetExitEmulationActivity(); | ||
| 40 | jmethodID GetOnEmulationStarted(); | ||
| 41 | jmethodID GetOnEmulationStopped(); | ||
| 42 | jmethodID GetOnProgramChanged(); | ||
| 43 | |||
| 44 | jclass GetGameClass(); | ||
| 45 | jmethodID GetGameConstructor(); | ||
| 46 | jfieldID GetGameTitleField(); | ||
| 47 | jfieldID GetGamePathField(); | ||
| 48 | jfieldID GetGameProgramIdField(); | ||
| 49 | jfieldID GetGameDeveloperField(); | ||
| 50 | jfieldID GetGameVersionField(); | ||
| 51 | jfieldID GetGameIsHomebrewField(); | ||
| 52 | |||
| 53 | jclass GetStringClass(); | ||
| 54 | jclass GetPairClass(); | ||
| 55 | jmethodID GetPairConstructor(); | ||
| 56 | jfieldID GetPairFirstField(); | ||
| 57 | jfieldID GetPairSecondField(); | ||
| 58 | |||
| 59 | jclass GetOverlayControlDataClass(); | ||
| 60 | jmethodID GetOverlayControlDataConstructor(); | ||
| 61 | jfieldID GetOverlayControlDataIdField(); | ||
| 62 | jfieldID GetOverlayControlDataEnabledField(); | ||
| 63 | jfieldID GetOverlayControlDataLandscapePositionField(); | ||
| 64 | jfieldID GetOverlayControlDataPortraitPositionField(); | ||
| 65 | jfieldID GetOverlayControlDataFoldablePositionField(); | ||
| 66 | |||
| 67 | jclass GetPatchClass(); | ||
| 68 | jmethodID GetPatchConstructor(); | ||
| 69 | jfieldID GetPatchEnabledField(); | ||
| 70 | jfieldID GetPatchNameField(); | ||
| 71 | jfieldID GetPatchVersionField(); | ||
| 72 | jfieldID GetPatchTypeField(); | ||
| 73 | jfieldID GetPatchProgramIdField(); | ||
| 74 | jfieldID GetPatchTitleIdField(); | ||
| 75 | |||
| 76 | jclass GetDoubleClass(); | ||
| 77 | jmethodID GetDoubleConstructor(); | ||
| 78 | jfieldID GetDoubleValueField(); | ||
| 79 | |||
| 80 | jclass GetIntegerClass(); | ||
| 81 | jmethodID GetIntegerConstructor(); | ||
| 82 | jfieldID GetIntegerValueField(); | ||
| 83 | |||
| 84 | jclass GetBooleanClass(); | ||
| 85 | jmethodID GetBooleanConstructor(); | ||
| 86 | jfieldID GetBooleanValueField(); | ||
| 87 | |||
| 88 | } // namespace Common::Android | ||
diff --git a/src/common/fs/fs_android.cpp b/src/common/fs/fs_android.cpp index 1dd826a4a..9a8053222 100644 --- a/src/common/fs/fs_android.cpp +++ b/src/common/fs/fs_android.cpp | |||
| @@ -1,63 +1,38 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "common/android/android_common.h" | ||
| 5 | #include "common/android/id_cache.h" | ||
| 6 | #include "common/assert.h" | ||
| 4 | #include "common/fs/fs_android.h" | 7 | #include "common/fs/fs_android.h" |
| 5 | #include "common/string_util.h" | 8 | #include "common/string_util.h" |
| 6 | 9 | ||
| 7 | namespace Common::FS::Android { | 10 | namespace Common::FS::Android { |
| 8 | 11 | ||
| 9 | JNIEnv* GetEnvForThread() { | ||
| 10 | thread_local static struct OwnedEnv { | ||
| 11 | OwnedEnv() { | ||
| 12 | status = g_jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6); | ||
| 13 | if (status == JNI_EDETACHED) | ||
| 14 | g_jvm->AttachCurrentThread(&env, nullptr); | ||
| 15 | } | ||
| 16 | |||
| 17 | ~OwnedEnv() { | ||
| 18 | if (status == JNI_EDETACHED) | ||
| 19 | g_jvm->DetachCurrentThread(); | ||
| 20 | } | ||
| 21 | |||
| 22 | int status; | ||
| 23 | JNIEnv* env = nullptr; | ||
| 24 | } owned; | ||
| 25 | return owned.env; | ||
| 26 | } | ||
| 27 | |||
| 28 | void RegisterCallbacks(JNIEnv* env, jclass clazz) { | 12 | void RegisterCallbacks(JNIEnv* env, jclass clazz) { |
| 29 | env->GetJavaVM(&g_jvm); | 13 | env->GetJavaVM(&g_jvm); |
| 30 | native_library = clazz; | 14 | native_library = clazz; |
| 31 | 15 | ||
| 32 | #define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) \ | 16 | s_get_parent_directory = env->GetStaticMethodID(native_library, "getParentDirectory", |
| 33 | F(JMethodID, JMethodName, Signature) | 17 | "(Ljava/lang/String;)Ljava/lang/String;"); |
| 34 | #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \ | 18 | s_get_filename = env->GetStaticMethodID(native_library, "getFilename", |
| 35 | F(JMethodID, JMethodName, Signature) | 19 | "(Ljava/lang/String;)Ljava/lang/String;"); |
| 36 | #define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \ | 20 | s_get_size = env->GetStaticMethodID(native_library, "getSize", "(Ljava/lang/String;)J"); |
| 37 | F(JMethodID, JMethodName, Signature) | 21 | s_is_directory = env->GetStaticMethodID(native_library, "isDirectory", "(Ljava/lang/String;)Z"); |
| 38 | #define F(JMethodID, JMethodName, Signature) \ | 22 | s_file_exists = env->GetStaticMethodID(native_library, "exists", "(Ljava/lang/String;)Z"); |
| 39 | JMethodID = env->GetStaticMethodID(native_library, JMethodName, Signature); | 23 | s_open_content_uri = env->GetStaticMethodID(native_library, "openContentUri", |
| 40 | ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) | 24 | "(Ljava/lang/String;Ljava/lang/String;)I"); |
| 41 | ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) | ||
| 42 | ANDROID_STORAGE_FUNCTIONS(FS) | ||
| 43 | #undef F | ||
| 44 | #undef FS | ||
| 45 | #undef FR | ||
| 46 | #undef FH | ||
| 47 | } | 25 | } |
| 48 | 26 | ||
| 49 | void UnRegisterCallbacks() { | 27 | void UnRegisterCallbacks() { |
| 50 | #define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID) | 28 | s_get_parent_directory = nullptr; |
| 51 | #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID) | 29 | s_get_filename = nullptr; |
| 52 | #define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID) | 30 | |
| 53 | #define F(JMethodID) JMethodID = nullptr; | 31 | s_get_size = nullptr; |
| 54 | ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) | 32 | s_is_directory = nullptr; |
| 55 | ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) | 33 | s_file_exists = nullptr; |
| 56 | ANDROID_STORAGE_FUNCTIONS(FS) | 34 | |
| 57 | #undef F | 35 | s_open_content_uri = nullptr; |
| 58 | #undef FS | ||
| 59 | #undef FR | ||
| 60 | #undef FH | ||
| 61 | } | 36 | } |
| 62 | 37 | ||
| 63 | bool IsContentUri(const std::string& path) { | 38 | bool IsContentUri(const std::string& path) { |
| @@ -69,8 +44,8 @@ bool IsContentUri(const std::string& path) { | |||
| 69 | return path.find(prefix) == 0; | 44 | return path.find(prefix) == 0; |
| 70 | } | 45 | } |
| 71 | 46 | ||
| 72 | int OpenContentUri(const std::string& filepath, OpenMode openmode) { | 47 | s32 OpenContentUri(const std::string& filepath, OpenMode openmode) { |
| 73 | if (open_content_uri == nullptr) | 48 | if (s_open_content_uri == nullptr) |
| 74 | return -1; | 49 | return -1; |
| 75 | 50 | ||
| 76 | const char* mode = ""; | 51 | const char* mode = ""; |
| @@ -82,50 +57,66 @@ int OpenContentUri(const std::string& filepath, OpenMode openmode) { | |||
| 82 | UNIMPLEMENTED(); | 57 | UNIMPLEMENTED(); |
| 83 | return -1; | 58 | return -1; |
| 84 | } | 59 | } |
| 85 | auto env = GetEnvForThread(); | 60 | auto env = Common::Android::GetEnvForThread(); |
| 86 | jstring j_filepath = env->NewStringUTF(filepath.c_str()); | 61 | jstring j_filepath = Common::Android::ToJString(env, filepath); |
| 87 | jstring j_mode = env->NewStringUTF(mode); | 62 | jstring j_mode = Common::Android::ToJString(env, mode); |
| 88 | return env->CallStaticIntMethod(native_library, open_content_uri, j_filepath, j_mode); | 63 | return env->CallStaticIntMethod(native_library, s_open_content_uri, j_filepath, j_mode); |
| 64 | } | ||
| 65 | |||
| 66 | u64 GetSize(const std::string& filepath) { | ||
| 67 | if (s_get_size == nullptr) { | ||
| 68 | return 0; | ||
| 69 | } | ||
| 70 | auto env = Common::Android::GetEnvForThread(); | ||
| 71 | return static_cast<u64>(env->CallStaticLongMethod( | ||
| 72 | native_library, s_get_size, | ||
| 73 | Common::Android::ToJString(Common::Android::GetEnvForThread(), filepath))); | ||
| 74 | } | ||
| 75 | |||
| 76 | bool IsDirectory(const std::string& filepath) { | ||
| 77 | if (s_is_directory == nullptr) { | ||
| 78 | return 0; | ||
| 79 | } | ||
| 80 | auto env = Common::Android::GetEnvForThread(); | ||
| 81 | return env->CallStaticBooleanMethod( | ||
| 82 | native_library, s_is_directory, | ||
| 83 | Common::Android::ToJString(Common::Android::GetEnvForThread(), filepath)); | ||
| 89 | } | 84 | } |
| 90 | 85 | ||
| 91 | #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \ | 86 | bool Exists(const std::string& filepath) { |
| 92 | F(FunctionName, ReturnValue, JMethodID, Caller) | 87 | if (s_file_exists == nullptr) { |
| 93 | #define F(FunctionName, ReturnValue, JMethodID, Caller) \ | 88 | return 0; |
| 94 | ReturnValue FunctionName(const std::string& filepath) { \ | ||
| 95 | if (JMethodID == nullptr) { \ | ||
| 96 | return 0; \ | ||
| 97 | } \ | ||
| 98 | auto env = GetEnvForThread(); \ | ||
| 99 | jstring j_filepath = env->NewStringUTF(filepath.c_str()); \ | ||
| 100 | return env->Caller(native_library, JMethodID, j_filepath); \ | ||
| 101 | } | 89 | } |
| 102 | ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) | 90 | auto env = Common::Android::GetEnvForThread(); |
| 103 | #undef F | 91 | return env->CallStaticBooleanMethod( |
| 104 | #undef FR | 92 | native_library, s_file_exists, |
| 105 | 93 | Common::Android::ToJString(Common::Android::GetEnvForThread(), filepath)); | |
| 106 | #define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) \ | 94 | } |
| 107 | F(FunctionName, JMethodID, Caller) | 95 | |
| 108 | #define F(FunctionName, JMethodID, Caller) \ | 96 | std::string GetParentDirectory(const std::string& filepath) { |
| 109 | std::string FunctionName(const std::string& filepath) { \ | 97 | if (s_get_parent_directory == nullptr) { |
| 110 | if (JMethodID == nullptr) { \ | 98 | return 0; |
| 111 | return 0; \ | ||
| 112 | } \ | ||
| 113 | auto env = GetEnvForThread(); \ | ||
| 114 | jstring j_filepath = env->NewStringUTF(filepath.c_str()); \ | ||
| 115 | jstring j_return = \ | ||
| 116 | static_cast<jstring>(env->Caller(native_library, JMethodID, j_filepath)); \ | ||
| 117 | if (!j_return) { \ | ||
| 118 | return {}; \ | ||
| 119 | } \ | ||
| 120 | const jchar* jchars = env->GetStringChars(j_return, nullptr); \ | ||
| 121 | const jsize length = env->GetStringLength(j_return); \ | ||
| 122 | const std::u16string_view string_view(reinterpret_cast<const char16_t*>(jchars), length); \ | ||
| 123 | const std::string converted_string = Common::UTF16ToUTF8(string_view); \ | ||
| 124 | env->ReleaseStringChars(j_return, jchars); \ | ||
| 125 | return converted_string; \ | ||
| 126 | } | 99 | } |
| 127 | ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) | 100 | auto env = Common::Android::GetEnvForThread(); |
| 128 | #undef F | 101 | jstring j_return = static_cast<jstring>(env->CallStaticObjectMethod( |
| 129 | #undef FH | 102 | native_library, s_get_parent_directory, Common::Android::ToJString(env, filepath))); |
| 103 | if (!j_return) { | ||
| 104 | return {}; | ||
| 105 | } | ||
| 106 | return Common::Android::GetJString(env, j_return); | ||
| 107 | } | ||
| 108 | |||
| 109 | std::string GetFilename(const std::string& filepath) { | ||
| 110 | if (s_get_filename == nullptr) { | ||
| 111 | return 0; | ||
| 112 | } | ||
| 113 | auto env = Common::Android::GetEnvForThread(); | ||
| 114 | jstring j_return = static_cast<jstring>(env->CallStaticObjectMethod( | ||
| 115 | native_library, s_get_filename, Common::Android::ToJString(env, filepath))); | ||
| 116 | if (!j_return) { | ||
| 117 | return {}; | ||
| 118 | } | ||
| 119 | return Common::Android::GetJString(env, j_return); | ||
| 120 | } | ||
| 130 | 121 | ||
| 131 | } // namespace Common::FS::Android | 122 | } // namespace Common::FS::Android |
diff --git a/src/common/fs/fs_android.h b/src/common/fs/fs_android.h index 2c9234313..b33f4beb8 100644 --- a/src/common/fs/fs_android.h +++ b/src/common/fs/fs_android.h | |||
| @@ -7,38 +7,17 @@ | |||
| 7 | #include <vector> | 7 | #include <vector> |
| 8 | #include <jni.h> | 8 | #include <jni.h> |
| 9 | 9 | ||
| 10 | #define ANDROID_STORAGE_FUNCTIONS(V) \ | ||
| 11 | V(OpenContentUri, int, (const std::string& filepath, OpenMode openmode), open_content_uri, \ | ||
| 12 | "openContentUri", "(Ljava/lang/String;Ljava/lang/String;)I") | ||
| 13 | |||
| 14 | #define ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(V) \ | ||
| 15 | V(GetSize, std::uint64_t, get_size, CallStaticLongMethod, "getSize", "(Ljava/lang/String;)J") \ | ||
| 16 | V(IsDirectory, bool, is_directory, CallStaticBooleanMethod, "isDirectory", \ | ||
| 17 | "(Ljava/lang/String;)Z") \ | ||
| 18 | V(Exists, bool, file_exists, CallStaticBooleanMethod, "exists", "(Ljava/lang/String;)Z") | ||
| 19 | |||
| 20 | #define ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(V) \ | ||
| 21 | V(GetParentDirectory, get_parent_directory, CallStaticObjectMethod, "getParentDirectory", \ | ||
| 22 | "(Ljava/lang/String;)Ljava/lang/String;") \ | ||
| 23 | V(GetFilename, get_filename, CallStaticObjectMethod, "getFilename", \ | ||
| 24 | "(Ljava/lang/String;)Ljava/lang/String;") | ||
| 25 | |||
| 26 | namespace Common::FS::Android { | 10 | namespace Common::FS::Android { |
| 27 | 11 | ||
| 28 | static JavaVM* g_jvm = nullptr; | 12 | static JavaVM* g_jvm = nullptr; |
| 29 | static jclass native_library = nullptr; | 13 | static jclass native_library = nullptr; |
| 30 | 14 | ||
| 31 | #define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID) | 15 | static jmethodID s_get_parent_directory; |
| 32 | #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID) | 16 | static jmethodID s_get_filename; |
| 33 | #define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID) | 17 | static jmethodID s_get_size; |
| 34 | #define F(JMethodID) static jmethodID JMethodID = nullptr; | 18 | static jmethodID s_is_directory; |
| 35 | ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) | 19 | static jmethodID s_file_exists; |
| 36 | ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) | 20 | static jmethodID s_open_content_uri; |
| 37 | ANDROID_STORAGE_FUNCTIONS(FS) | ||
| 38 | #undef F | ||
| 39 | #undef FS | ||
| 40 | #undef FR | ||
| 41 | #undef FH | ||
| 42 | 21 | ||
| 43 | enum class OpenMode { | 22 | enum class OpenMode { |
| 44 | Read, | 23 | Read, |
| @@ -57,24 +36,11 @@ void UnRegisterCallbacks(); | |||
| 57 | 36 | ||
| 58 | bool IsContentUri(const std::string& path); | 37 | bool IsContentUri(const std::string& path); |
| 59 | 38 | ||
| 60 | #define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \ | 39 | int OpenContentUri(const std::string& filepath, OpenMode openmode); |
| 61 | F(FunctionName, Parameters, ReturnValue) | 40 | std::uint64_t GetSize(const std::string& filepath); |
| 62 | #define F(FunctionName, Parameters, ReturnValue) ReturnValue FunctionName Parameters; | 41 | bool IsDirectory(const std::string& filepath); |
| 63 | ANDROID_STORAGE_FUNCTIONS(FS) | 42 | bool Exists(const std::string& filepath); |
| 64 | #undef F | 43 | std::string GetParentDirectory(const std::string& filepath); |
| 65 | #undef FS | 44 | std::string GetFilename(const std::string& filepath); |
| 66 | |||
| 67 | #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \ | ||
| 68 | F(FunctionName, ReturnValue) | ||
| 69 | #define F(FunctionName, ReturnValue) ReturnValue FunctionName(const std::string& filepath); | ||
| 70 | ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) | ||
| 71 | #undef F | ||
| 72 | #undef FR | ||
| 73 | |||
| 74 | #define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(FunctionName) | ||
| 75 | #define F(FunctionName) std::string FunctionName(const std::string& filepath); | ||
| 76 | ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) | ||
| 77 | #undef F | ||
| 78 | #undef FH | ||
| 79 | 45 | ||
| 80 | } // namespace Common::FS::Android | 46 | } // namespace Common::FS::Android |