summaryrefslogtreecommitdiff
path: root/Source/Android/app/src/main/java
diff options
context:
space:
mode:
authorMarkus Wick <degasus@users.noreply.github.com>2018-01-05 13:45:36 +0100
committerGitHub <noreply@github.com>2018-01-05 13:45:36 +0100
commite705d433126e00e3dc39e7a25a182fcd3b555a2b (patch)
treed362f74f96adb576fe0bfbff3e8b11cf4236b100 /Source/Android/app/src/main/java
parente31f8f8ecb901a81cdf6075ca305032a6ba3366b (diff)
parent136e835fb489f2b0412c760739dd5c668055fb56 (diff)
Merge pull request #6270 from mahdihijazi/suppport_restore_state
[Android] Support restore emulator state
Diffstat (limited to 'Source/Android/app/src/main/java')
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java28
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java81
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java76
3 files changed, 155 insertions, 30 deletions
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java
index 8d193e75ec..e82de7fa01 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java
@@ -294,8 +294,19 @@ public final class NativeLibrary
* Saves a game state to the slot number.
*
* @param slot The slot location to save state to.
+ * @param wait If false, returns as early as possible.
+ * If true, returns once the savestate has been written to disk.
*/
- public static native void SaveState(int slot);
+ public static native void SaveState(int slot, boolean wait);
+
+ /**
+ * Saves a game state to the specified path.
+ *
+ * @param path The path to save state to.
+ * @param wait If false, returns as early as possible.
+ * If true, returns once the savestate has been written to disk.
+ */
+ public static native void SaveStateAs(String path, boolean wait);
/**
* Loads a game state from the slot number.
@@ -305,6 +316,13 @@ public final class NativeLibrary
public static native void LoadState(int slot);
/**
+ * Loads a game state from the specified path.
+ *
+ * @param path The path to load state from.
+ */
+ public static native void LoadStateAs(String path);
+
+ /**
* Sets the current working user directory
* If not set, it auto-detects a location
*/
@@ -322,6 +340,11 @@ public final class NativeLibrary
*/
public static native void Run(String path);
+ /**
+ * Begins emulation from the specified savestate.
+ */
+ public static native void Run(String path, String savestatePath, boolean deleteSavestate);
+
// Surface Handling
public static native void SurfaceChanged(Surface surf);
public static native void SurfaceDestroyed();
@@ -335,6 +358,9 @@ public final class NativeLibrary
/** Stops emulation. */
public static native void StopEmulation();
+ /** Returns true if emulation is running (or is paused). */
+ public static native boolean IsRunning();
+
/**
* Enables or disables CPU block profiling
* @param enable
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java
index 56873cab76..80972f0c7f 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java
@@ -2,7 +2,6 @@ package org.dolphinemu.dolphinemu.activities;
import android.app.AlertDialog;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.hardware.usb.UsbManager;
@@ -42,7 +41,6 @@ import org.dolphinemu.dolphinemu.utils.Animations;
import org.dolphinemu.dolphinemu.utils.ControllerMappingHelper;
import org.dolphinemu.dolphinemu.utils.Java_GCAdapter;
import org.dolphinemu.dolphinemu.utils.Java_WiimoteAdapter;
-import org.dolphinemu.dolphinemu.utils.Log;
import java.lang.annotation.Retention;
import java.util.List;
@@ -68,8 +66,15 @@ public final class EmulationActivity extends AppCompatActivity
private static boolean sIsGameCubeGame;
+ private boolean activityRecreated;
private String mScreenPath;
private String mSelectedTitle;
+ private String mPath;
+
+ public static final String EXTRA_SELECTED_GAME = "SelectedGame";
+ public static final String EXTRA_SELECTED_TITLE = "SelectedTitle";
+ public static final String EXTRA_SCREEN_PATH = "ScreenPath";
+ public static final String EXTRA_GRID_POSITION = "GridPosition";
@Retention(SOURCE)
@IntDef({MENU_ACTION_EDIT_CONTROLS_PLACEMENT, MENU_ACTION_TOGGLE_CONTROLS, MENU_ACTION_ADJUST_SCALE,
@@ -138,10 +143,10 @@ public final class EmulationActivity extends AppCompatActivity
{
Intent launcher = new Intent(activity, EmulationActivity.class);
- launcher.putExtra("SelectedGame", path);
- launcher.putExtra("SelectedTitle", title);
- launcher.putExtra("ScreenPath", screenshotPath);
- launcher.putExtra("GridPosition", position);
+ launcher.putExtra(EXTRA_SELECTED_GAME, path);
+ launcher.putExtra(EXTRA_SELECTED_TITLE, title);
+ launcher.putExtra(EXTRA_SCREEN_PATH, screenshotPath);
+ launcher.putExtra(EXTRA_GRID_POSITION, position);
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(
activity,
@@ -158,13 +163,23 @@ public final class EmulationActivity extends AppCompatActivity
{
super.onCreate(savedInstanceState);
- // Get params we were passed
- Intent gameToEmulate = getIntent();
- String path = gameToEmulate.getStringExtra("SelectedGame");
- sIsGameCubeGame = Platform.fromNativeInt(NativeLibrary.GetPlatform(path)) == Platform.GAMECUBE;
- mSelectedTitle = gameToEmulate.getStringExtra("SelectedTitle");
- mScreenPath = gameToEmulate.getStringExtra("ScreenPath");
- mPosition = gameToEmulate.getIntExtra("GridPosition", -1);
+ if (savedInstanceState == null)
+ {
+ // Get params we were passed
+ Intent gameToEmulate = getIntent();
+ mPath = gameToEmulate.getStringExtra(EXTRA_SELECTED_GAME);
+ mSelectedTitle = gameToEmulate.getStringExtra(EXTRA_SELECTED_TITLE);
+ mScreenPath = gameToEmulate.getStringExtra(EXTRA_SCREEN_PATH);
+ mPosition = gameToEmulate.getIntExtra(EXTRA_GRID_POSITION, -1);
+ activityRecreated = false;
+ }
+ else
+ {
+ activityRecreated = true;
+ restoreState(savedInstanceState);
+ }
+
+ sIsGameCubeGame = Platform.fromNativeInt(NativeLibrary.GetPlatform(mPath)) == Platform.GAMECUBE;
mDeviceHasTouchScreen = getPackageManager().hasSystemFeature("android.hardware.touchscreen");
mControllerMappingHelper = new ControllerMappingHelper();
@@ -206,7 +221,7 @@ public final class EmulationActivity extends AppCompatActivity
.findFragmentById(R.id.frame_emulation_fragment);
if (mEmulationFragment == null)
{
- mEmulationFragment = EmulationFragment.newInstance(path);
+ mEmulationFragment = EmulationFragment.newInstance(mPath);
getSupportFragmentManager().beginTransaction()
.add(R.id.frame_emulation_fragment, mEmulationFragment)
.commit();
@@ -257,6 +272,25 @@ public final class EmulationActivity extends AppCompatActivity
}
@Override
+ protected void onSaveInstanceState(Bundle outState)
+ {
+ mEmulationFragment.saveTemporaryState();
+ outState.putString(EXTRA_SELECTED_GAME, mPath);
+ outState.putString(EXTRA_SELECTED_TITLE, mSelectedTitle);
+ outState.putString(EXTRA_SCREEN_PATH, mScreenPath);
+ outState.putInt(EXTRA_GRID_POSITION, mPosition);
+ super.onSaveInstanceState(outState);
+ }
+
+ protected void restoreState(Bundle savedInstanceState)
+ {
+ mPath = savedInstanceState.getString(EXTRA_SELECTED_GAME);
+ mSelectedTitle = savedInstanceState.getString(EXTRA_SELECTED_TITLE);
+ mScreenPath = savedInstanceState.getString(EXTRA_SCREEN_PATH);
+ mPosition = savedInstanceState.getInt(EXTRA_GRID_POSITION);
+ }
+
+ @Override
public void onBackPressed()
{
if (!mDeviceHasTouchScreen)
@@ -412,7 +446,7 @@ public final class EmulationActivity extends AppCompatActivity
// Quick save / load
case MENU_ACTION_QUICK_SAVE:
- NativeLibrary.SaveState(9);
+ NativeLibrary.SaveState(9, false);
return;
case MENU_ACTION_QUICK_LOAD:
@@ -436,27 +470,27 @@ public final class EmulationActivity extends AppCompatActivity
// Save state slots
case MENU_ACTION_SAVE_SLOT1:
- NativeLibrary.SaveState(0);
+ NativeLibrary.SaveState(0, false);
return;
case MENU_ACTION_SAVE_SLOT2:
- NativeLibrary.SaveState(1);
+ NativeLibrary.SaveState(1, false);
return;
case MENU_ACTION_SAVE_SLOT3:
- NativeLibrary.SaveState(2);
+ NativeLibrary.SaveState(2, false);
return;
case MENU_ACTION_SAVE_SLOT4:
- NativeLibrary.SaveState(3);
+ NativeLibrary.SaveState(3, false);
return;
case MENU_ACTION_SAVE_SLOT5:
- NativeLibrary.SaveState(4);
+ NativeLibrary.SaveState(4, false);
return;
case MENU_ACTION_SAVE_SLOT6:
- NativeLibrary.SaveState(5);
+ NativeLibrary.SaveState(5, false);
return;
// Load state slots
@@ -716,4 +750,9 @@ public final class EmulationActivity extends AppCompatActivity
{
return sIsGameCubeGame;
}
+
+ public boolean isActivityRecreated()
+ {
+ return activityRecreated;
+ }
}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java
index 27d32db0d5..1d6a67e6c4 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java
@@ -25,6 +25,8 @@ import org.dolphinemu.dolphinemu.services.DirectoryInitializationService.Directo
import org.dolphinemu.dolphinemu.utils.DirectoryStateReceiver;
import org.dolphinemu.dolphinemu.utils.Log;
+import java.io.File;
+
import rx.functions.Action1;
public final class EmulationFragment extends Fragment implements SurfaceHolder.Callback
@@ -39,6 +41,8 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
private DirectoryStateReceiver directoryStateReceiver;
+ private EmulationActivity activity;
+
public static EmulationFragment newInstance(String gamePath)
{
@@ -57,6 +61,7 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
if (context instanceof EmulationActivity)
{
+ activity = (EmulationActivity)context;
NativeLibrary.setEmulationActivity((EmulationActivity) context);
}
else
@@ -79,7 +84,7 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
mPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
String gamePath = getArguments().getString(KEY_GAMEPATH);
- mEmulationState = new EmulationState(gamePath);
+ mEmulationState = new EmulationState(gamePath, getTemporaryStateFilePath());
}
/**
@@ -120,7 +125,7 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
super.onResume();
if (DirectoryInitializationService.areDolphinDirectoriesReady())
{
- mEmulationState.run();
+ mEmulationState.run(activity.isActivityRecreated());
}
else
{
@@ -155,7 +160,7 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
directoryStateReceiver =
new DirectoryStateReceiver(directoryInitializationState -> {
if (directoryInitializationState == DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED) {
- mEmulationState.run();
+ mEmulationState.run(activity.isActivityRecreated());
} else if (directoryInitializationState == DirectoryInitializationState.EXTERNAL_STORAGE_PERMISSION_NEEDED) {
Toast.makeText(getContext(), R.string.write_permission_needed, Toast.LENGTH_SHORT)
.show();
@@ -249,10 +254,13 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
private State state;
private Surface mSurface;
private boolean mRunWhenSurfaceIsValid;
+ private boolean loadPreviousTemporaryState;
+ private final String temporaryStatePath;
- EmulationState(String gamePath)
+ EmulationState(String gamePath, String temporaryStatePath)
{
mGamePath = gamePath;
+ this.temporaryStatePath = temporaryStatePath;
// Starting state is stopped.
state = State.STOPPED;
}
@@ -280,6 +288,7 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
{
if (state != State.STOPPED)
{
+ Log.debug("[EmulationFragment] Stopping emulation.");
state = State.STOPPED;
NativeLibrary.StopEmulation();
}
@@ -307,8 +316,29 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
}
}
- public synchronized void run()
+ public synchronized void run(boolean isActivityRecreated)
{
+ if (isActivityRecreated)
+ {
+ if (NativeLibrary.IsRunning())
+ {
+ loadPreviousTemporaryState = false;
+ state = State.PAUSED;
+ deleteFile(temporaryStatePath);
+ }
+ else
+ {
+ loadPreviousTemporaryState = true;
+ }
+ }
+ else
+ {
+ Log.debug("[EmulationFragment] activity resumed or fresh start");
+ loadPreviousTemporaryState = false;
+ // activity resumed without being killed or this is the first run
+ deleteFile(temporaryStatePath);
+ }
+
// If the surface is set, run now. Otherwise, wait for it to get set.
if (mSurface != null)
{
@@ -362,12 +392,19 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
mRunWhenSurfaceIsValid = false;
if (state == State.STOPPED)
{
- Log.debug("[EmulationFragment] Starting emulation thread.");
-
mEmulationThread = new Thread(() ->
{
NativeLibrary.SurfaceChanged(mSurface);
- NativeLibrary.Run(mGamePath);
+ if (loadPreviousTemporaryState)
+ {
+ Log.debug("[EmulationFragment] Starting emulation thread from previous state.");
+ NativeLibrary.Run(mGamePath, temporaryStatePath, true);
+ }
+ else
+ {
+ Log.debug("[EmulationFragment] Starting emulation thread.");
+ NativeLibrary.Run(mGamePath);
+ }
}, "NativeEmulation");
mEmulationThread.start();
@@ -385,4 +422,27 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
state = State.RUNNING;
}
}
+
+ public void saveTemporaryState()
+ {
+ NativeLibrary.SaveStateAs(getTemporaryStateFilePath(), true);
+ }
+
+ private String getTemporaryStateFilePath()
+ {
+ return getContext().getFilesDir() + File.separator + "temp.sav";
+ }
+
+ private static void deleteFile(String path)
+ {
+ try
+ {
+ File file = new File(path);
+ file.delete();
+ }
+ catch (Exception ex)
+ {
+ // fail safely
+ }
+ }
}