android: Handle core errors
The errors are handled in a similar manner to the Qt frontend: an AlertDialog will pop up, prompting the user to select 'Abort' or 'Continue'. Error messages are translatable as string values.
This commit is contained in:
parent
a2b52b2a71
commit
ba7f896518
@ -211,6 +211,84 @@ public final class NativeLibrary {
|
||||
*/
|
||||
public static native void SwapScreens(boolean swap_screens, int rotation);
|
||||
|
||||
public enum CoreError {
|
||||
ErrorSystemFiles,
|
||||
ErrorSavestate,
|
||||
ErrorUnknown,
|
||||
}
|
||||
|
||||
private static boolean coreErrorAlertResult = false;
|
||||
|
||||
/**
|
||||
* Handles a core error.
|
||||
* @return true: continue; false: abort
|
||||
*/
|
||||
public static boolean OnCoreError(CoreError error, String details) {
|
||||
final EmulationActivity emulationActivity = sEmulationActivity.get();
|
||||
if (emulationActivity == null) {
|
||||
Log.error("[NativeLibrary] EmulationActivity not present");
|
||||
return false;
|
||||
}
|
||||
|
||||
String title, message;
|
||||
switch (error) {
|
||||
case ErrorSystemFiles: {
|
||||
title = emulationActivity.getString(R.string.system_archive_not_found);
|
||||
message = emulationActivity.getString(R.string.system_archive_not_found_message, details.isEmpty() ? emulationActivity.getString(R.string.system_archive_general) : details);
|
||||
break;
|
||||
}
|
||||
case ErrorSavestate: {
|
||||
title = emulationActivity.getString(R.string.save_load_error);
|
||||
message = details;
|
||||
break;
|
||||
}
|
||||
case ErrorUnknown: {
|
||||
title = emulationActivity.getString(R.string.fatal_error);
|
||||
message = emulationActivity.getString(R.string.fatal_error_message);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Create object used for waiting.
|
||||
final Object lock = new Object();
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity)
|
||||
.setTitle(title)
|
||||
.setMessage(message)
|
||||
.setPositiveButton(R.string.continue_button, (dialog, which) -> {
|
||||
coreErrorAlertResult = true;
|
||||
synchronized (lock) {
|
||||
lock.notify();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.abort_button, (dialog, which) -> {
|
||||
coreErrorAlertResult = false;
|
||||
synchronized (lock) {
|
||||
lock.notify();
|
||||
}
|
||||
}).setOnDismissListener(dialog -> {
|
||||
coreErrorAlertResult = true;
|
||||
synchronized (lock) {
|
||||
lock.notify();
|
||||
}
|
||||
});
|
||||
|
||||
// Show the AlertDialog on the main thread.
|
||||
emulationActivity.runOnUiThread(builder::show);
|
||||
|
||||
// Wait for the lock to notify that it is complete.
|
||||
synchronized (lock) {
|
||||
try {
|
||||
lock.wait();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
return coreErrorAlertResult;
|
||||
}
|
||||
|
||||
public static boolean isPortraitMode() {
|
||||
return CitraApplication.getAppContext().getResources().getConfiguration().orientation ==
|
||||
Configuration.ORIENTATION_PORTRAIT;
|
||||
|
@ -19,7 +19,9 @@ static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
|
||||
static JavaVM* s_java_vm;
|
||||
|
||||
static jclass s_native_library_class;
|
||||
static jclass s_core_error_class;
|
||||
static jclass s_savestate_info_class;
|
||||
static jmethodID s_on_core_error;
|
||||
static jmethodID s_display_alert_msg;
|
||||
static jmethodID s_display_alert_prompt;
|
||||
static jmethodID s_alert_prompt_button;
|
||||
@ -54,10 +56,18 @@ jclass GetNativeLibraryClass() {
|
||||
return s_native_library_class;
|
||||
}
|
||||
|
||||
jclass GetCoreErrorClass() {
|
||||
return s_core_error_class;
|
||||
}
|
||||
|
||||
jclass GetSavestateInfoClass() {
|
||||
return s_savestate_info_class;
|
||||
}
|
||||
|
||||
jmethodID GetOnCoreError() {
|
||||
return s_on_core_error;
|
||||
}
|
||||
|
||||
jmethodID GetDisplayAlertMsg() {
|
||||
return s_display_alert_msg;
|
||||
}
|
||||
@ -118,6 +128,11 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
s_native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
|
||||
s_savestate_info_class = reinterpret_cast<jclass>(
|
||||
env->NewGlobalRef(env->FindClass("org/citra/citra_emu/NativeLibrary$SavestateInfo")));
|
||||
s_core_error_class = reinterpret_cast<jclass>(
|
||||
env->NewGlobalRef(env->FindClass("org/citra/citra_emu/NativeLibrary$CoreError")));
|
||||
s_on_core_error = env->GetStaticMethodID(
|
||||
s_native_library_class, "OnCoreError",
|
||||
"(Lorg/citra/citra_emu/NativeLibrary$CoreError;Ljava/lang/String;)Z");
|
||||
s_display_alert_msg = env->GetStaticMethodID(s_native_library_class, "displayAlertMsg",
|
||||
"(Ljava/lang/String;Ljava/lang/String;Z)Z");
|
||||
s_display_alert_prompt =
|
||||
@ -150,6 +165,7 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) {
|
||||
|
||||
env->DeleteGlobalRef(s_native_library_class);
|
||||
env->DeleteGlobalRef(s_savestate_info_class);
|
||||
env->DeleteGlobalRef(s_core_error_class);
|
||||
MiiSelector::CleanupJNI(env);
|
||||
SoftwareKeyboard::CleanupJNI(env);
|
||||
Camera::StillImage::CleanupJNI(env);
|
||||
|
@ -12,7 +12,9 @@ namespace IDCache {
|
||||
|
||||
JNIEnv* GetEnvForThread();
|
||||
jclass GetNativeLibraryClass();
|
||||
jclass GetCoreErrorClass();
|
||||
jclass GetSavestateInfoClass();
|
||||
jmethodID GetOnCoreError();
|
||||
jmethodID GetDisplayAlertMsg();
|
||||
jmethodID GetDisplayAlertPrompt();
|
||||
jmethodID GetAlertPromptButton();
|
||||
|
@ -96,6 +96,29 @@ static int AlertPromptButton() {
|
||||
IDCache::GetAlertPromptButton()));
|
||||
}
|
||||
|
||||
static jobject ToJavaCoreError(Core::System::ResultStatus result) {
|
||||
static const std::map<Core::System::ResultStatus, const char*> CoreErrorNameMap{
|
||||
{Core::System::ResultStatus::ErrorSystemFiles, "ErrorSystemFiles"},
|
||||
{Core::System::ResultStatus::ErrorSavestate, "ErrorSavestate"},
|
||||
{Core::System::ResultStatus::ErrorUnknown, "ErrorUnknown"},
|
||||
};
|
||||
|
||||
const auto name = CoreErrorNameMap.count(result) ? CoreErrorNameMap.at(result) : "ErrorUnknown";
|
||||
|
||||
JNIEnv* env = IDCache::GetEnvForThread();
|
||||
const jclass core_error_class = IDCache::GetCoreErrorClass();
|
||||
return env->GetStaticObjectField(
|
||||
core_error_class, env->GetStaticFieldID(core_error_class, name,
|
||||
"Lorg/citra/citra_emu/NativeLibrary$CoreError;"));
|
||||
}
|
||||
|
||||
static bool HandleCoreError(Core::System::ResultStatus result, const std::string& details) {
|
||||
JNIEnv* env = IDCache::GetEnvForThread();
|
||||
return env->CallStaticBooleanMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnCoreError(),
|
||||
ToJavaCoreError(result),
|
||||
env->NewStringUTF(details.c_str())) != JNI_FALSE;
|
||||
}
|
||||
|
||||
static Camera::NDK::Factory* g_ndk_factory{};
|
||||
|
||||
static void TryShutdown() {
|
||||
@ -187,7 +210,20 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
|
||||
// Start running emulation
|
||||
while (is_running) {
|
||||
if (!pause_emulation) {
|
||||
system.RunLoop();
|
||||
const auto result = system.RunLoop();
|
||||
if (result == Core::System::ResultStatus::Success) {
|
||||
continue;
|
||||
}
|
||||
if (result == Core::System::ResultStatus::ShutdownRequested) {
|
||||
return result; // This also exits the emulation activity
|
||||
} else {
|
||||
InputManager::NDKMotionHandler()->DisableSensors();
|
||||
if (!HandleCoreError(result, system.GetStatusDetails())) {
|
||||
// Frontend requests us to abort
|
||||
return result;
|
||||
}
|
||||
InputManager::NDKMotionHandler()->EnableSensors();
|
||||
}
|
||||
} else {
|
||||
// Ensure no audio bleeds out while game is paused
|
||||
const float volume = Settings::values.volume;
|
||||
|
@ -201,4 +201,14 @@
|
||||
<!-- Microphone -->
|
||||
<string name="microphone">Microphone</string>
|
||||
<string name="microphone_permission_needed">Citra needs to access your microphone to emulate the 3DS\'s microphone.\n\nAlternatively, you can also change \"Audio Input Device\" in Audio Settings.</string>
|
||||
|
||||
<!-- Core Errors -->
|
||||
<string name="abort_button">Abort</string>
|
||||
<string name="continue_button">Continue</string>
|
||||
<string name="system_archive_not_found">System Archive Not Found</string>
|
||||
<string name="system_archive_not_found_message">%s is missing. Please dump your system archives.\nContinuing emulation may result in crashes and bugs.</string>
|
||||
<string name="system_archive_general">A system archive</string>
|
||||
<string name="save_load_error">Save/Load Error</string>
|
||||
<string name="fatal_error">Fatal Error</string>
|
||||
<string name="fatal_error_message">A fatal error occurred. Check the log for details.\nContinuing emulation may result in crashes and bugs.</string>
|
||||
</resources>
|
||||
|
Loading…
x
Reference in New Issue
Block a user