android: Add savestates UI
A simple menu with savestates info.
This commit is contained in:
parent
8a31ffaf22
commit
cf84342e35
@ -16,6 +16,7 @@ import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
@ -25,6 +26,7 @@ import org.citra.citra_emu.utils.Log;
|
||||
import org.citra.citra_emu.utils.PermissionsHandler;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Date;
|
||||
|
||||
import static android.Manifest.permission.CAMERA;
|
||||
import static android.Manifest.permission.RECORD_AUDIO;
|
||||
@ -487,6 +489,19 @@ public final class NativeLibrary {
|
||||
|
||||
public static native void InstallCIAS(String[] path);
|
||||
|
||||
public static final int SAVESTATE_SLOT_COUNT = 10;
|
||||
|
||||
public static final class SavestateInfo {
|
||||
public int slot;
|
||||
public Date time;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static native SavestateInfo[] GetSavestateInfo();
|
||||
|
||||
public static native void SaveState(int slot);
|
||||
public static native void LoadState(int slot);
|
||||
|
||||
/**
|
||||
* Button type for use in onTouchEvent
|
||||
*/
|
||||
|
@ -14,6 +14,7 @@ import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.SubMenu;
|
||||
import android.view.View;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
@ -303,6 +304,45 @@ public final class EmulationActivity extends AppCompatActivity {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
super.onPrepareOptionsMenu(menu);
|
||||
|
||||
final NativeLibrary.SavestateInfo[] savestates = NativeLibrary.GetSavestateInfo();
|
||||
if (savestates == null) {
|
||||
menu.findItem(R.id.menu_emulation_save_state).setVisible(false);
|
||||
menu.findItem(R.id.menu_emulation_load_state).setVisible(false);
|
||||
return true;
|
||||
}
|
||||
menu.findItem(R.id.menu_emulation_save_state).setVisible(true);
|
||||
menu.findItem(R.id.menu_emulation_load_state).setVisible(true);
|
||||
|
||||
final SubMenu saveStateMenu = menu.findItem(R.id.menu_emulation_save_state).getSubMenu();
|
||||
final SubMenu loadStateMenu = menu.findItem(R.id.menu_emulation_load_state).getSubMenu();
|
||||
saveStateMenu.clear();
|
||||
loadStateMenu.clear();
|
||||
|
||||
// Update savestates information
|
||||
for (int i = 0; i < NativeLibrary.SAVESTATE_SLOT_COUNT; ++i) {
|
||||
final int slot = i + 1;
|
||||
final String text = getString(R.string.emulation_empty_state_slot, slot);
|
||||
saveStateMenu.add(text).setEnabled(true).setOnMenuItemClickListener((item) -> {
|
||||
NativeLibrary.SaveState(slot);
|
||||
return true;
|
||||
});
|
||||
loadStateMenu.add(text).setEnabled(false).setOnMenuItemClickListener((item) -> {
|
||||
NativeLibrary.LoadState(slot);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
for (final NativeLibrary.SavestateInfo info : savestates) {
|
||||
final String text = getString(R.string.emulation_occupied_state_slot, info.slot, info.time);
|
||||
saveStateMenu.getItem(info.slot - 1).setTitle(text);
|
||||
loadStateMenu.getItem(info.slot - 1).setTitle(text).setEnabled(true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("WrongConstant")
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
|
@ -128,7 +128,7 @@ void Config::ReadValues() {
|
||||
// Work around to map Android setting for enabling the frame limiter to the format Citra expects
|
||||
if (sdl2_config->GetBoolean("Renderer", "use_frame_limit", true)) {
|
||||
Settings::values.frame_limit =
|
||||
static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100));
|
||||
static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100));
|
||||
} else {
|
||||
Settings::values.frame_limit = 0;
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
|
||||
static JavaVM* s_java_vm;
|
||||
|
||||
static jclass s_native_library_class;
|
||||
static jclass s_savestate_info_class;
|
||||
static jmethodID s_display_alert_msg;
|
||||
static jmethodID s_display_alert_prompt;
|
||||
static jmethodID s_alert_prompt_button;
|
||||
@ -53,6 +54,10 @@ jclass GetNativeLibraryClass() {
|
||||
return s_native_library_class;
|
||||
}
|
||||
|
||||
jclass GetSavestateInfoClass() {
|
||||
return s_savestate_info_class;
|
||||
}
|
||||
|
||||
jmethodID GetDisplayAlertMsg() {
|
||||
return s_display_alert_msg;
|
||||
}
|
||||
@ -111,6 +116,8 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
// Initialize Java methods
|
||||
const jclass native_library_class = env->FindClass("org/citra/citra_emu/NativeLibrary");
|
||||
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_display_alert_msg = env->GetStaticMethodID(s_native_library_class, "displayAlertMsg",
|
||||
"(Ljava/lang/String;Ljava/lang/String;Z)Z");
|
||||
s_display_alert_prompt =
|
||||
@ -142,6 +149,7 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) {
|
||||
}
|
||||
|
||||
env->DeleteGlobalRef(s_native_library_class);
|
||||
env->DeleteGlobalRef(s_savestate_info_class);
|
||||
MiiSelector::CleanupJNI(env);
|
||||
SoftwareKeyboard::CleanupJNI(env);
|
||||
Camera::StillImage::CleanupJNI(env);
|
||||
|
@ -12,6 +12,7 @@ namespace IDCache {
|
||||
|
||||
JNIEnv* GetEnvForThread();
|
||||
jclass GetNativeLibraryClass();
|
||||
jclass GetSavestateInfoClass();
|
||||
jmethodID GetDisplayAlertMsg();
|
||||
jmethodID GetDisplayAlertPrompt();
|
||||
jmethodID GetAlertPromptButton();
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "core/frontend/scope_acquire_context.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/nfc/nfc.h"
|
||||
#include "core/savestate.h"
|
||||
#include "core/settings.h"
|
||||
#include "jni/applets/mii_selector.h"
|
||||
#include "jni/applets/swkbd.h"
|
||||
@ -620,4 +621,47 @@ void Java_org_citra_citra_1emu_NativeLibrary_InstallCIAS(JNIEnv* env, [[maybe_un
|
||||
thread.join();
|
||||
}
|
||||
|
||||
jobjectArray Java_org_citra_citra_1emu_NativeLibrary_GetSavestateInfo(
|
||||
JNIEnv* env, [[maybe_unused]] jclass clazz) {
|
||||
const jclass date_class = env->FindClass("java/util/Date");
|
||||
const auto date_constructor = env->GetMethodID(date_class, "<init>", "(J)V");
|
||||
|
||||
const jclass savestate_info_class = IDCache::GetSavestateInfoClass();
|
||||
const auto slot_field = env->GetFieldID(savestate_info_class, "slot", "I");
|
||||
const auto date_field = env->GetFieldID(savestate_info_class, "time", "Ljava/util/Date;");
|
||||
|
||||
const Core::System& system{Core::System::GetInstance()};
|
||||
if (!system.IsPoweredOn()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u64 title_id;
|
||||
if (system.GetAppLoader().ReadProgramId(title_id) != Loader::ResultStatus::Success) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto savestates = Core::ListSaveStates(title_id);
|
||||
const jobjectArray array =
|
||||
env->NewObjectArray(static_cast<jsize>(savestates.size()), savestate_info_class, nullptr);
|
||||
for (std::size_t i = 0; i < savestates.size(); ++i) {
|
||||
const jobject object = env->AllocObject(savestate_info_class);
|
||||
env->SetIntField(object, slot_field, static_cast<jint>(savestates[i].slot));
|
||||
env->SetObjectField(object, date_field,
|
||||
env->NewObject(date_class, date_constructor,
|
||||
static_cast<jlong>(savestates[i].time * 1000)));
|
||||
|
||||
env->SetObjectArrayElement(array, i, object);
|
||||
}
|
||||
LOG_CRITICAL(Frontend, "Called");
|
||||
return array;
|
||||
}
|
||||
|
||||
void Java_org_citra_citra_1emu_NativeLibrary_SaveState(JNIEnv* env, jclass clazz, jint slot) {
|
||||
Core::System::GetInstance().SendSignal(Core::System::Signal::Save, slot);
|
||||
}
|
||||
|
||||
void Java_org_citra_citra_1emu_NativeLibrary_LoadState(JNIEnv* env, jclass clazz, jint slot) {
|
||||
Core::System::GetInstance().SendSignal(Core::System::Signal::Load, slot);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
@ -146,6 +146,15 @@ JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_InstallCIAS(JNIEn
|
||||
jclass clazz,
|
||||
jobjectArray path);
|
||||
|
||||
JNIEXPORT jobjectArray JNICALL
|
||||
Java_org_citra_citra_1emu_NativeLibrary_GetSavestateInfo(JNIEnv* env, jclass clazz);
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_SaveState(JNIEnv* env, jclass clazz,
|
||||
jint slot);
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_LoadState(JNIEnv* env, jclass clazz,
|
||||
jint slot);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -3,6 +3,18 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
tools:context="org.citra.citra_emu.activities.EmulationActivity">
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_emulation_save_state"
|
||||
android:title="@string/emulation_save_state">
|
||||
<menu/>
|
||||
</item>
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_emulation_load_state"
|
||||
android:title="@string/emulation_load_state">
|
||||
<menu/>
|
||||
</item>
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_emulation_configure_controls"
|
||||
android:title="@string/emulation_configure_controls">
|
||||
|
@ -143,6 +143,10 @@
|
||||
<string name="loader_error_invalid_format">Invalid ROM format</string>
|
||||
|
||||
<!-- Emulation Menu -->
|
||||
<string name="emulation_save_state">Save State</string>
|
||||
<string name="emulation_load_state">Load State</string>
|
||||
<string name="emulation_empty_state_slot">Slot %1$d</string>
|
||||
<string name="emulation_occupied_state_slot">Slot %1$d - %2$tF %2$tR</string>
|
||||
<string name="emulation_show_fps">Show FPS</string>
|
||||
<string name="emulation_configure_controls">Configure Controls</string>
|
||||
<string name="emulation_edit_layout">Edit Layout</string>
|
||||
|
Loading…
x
Reference in New Issue
Block a user