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 de9e864914..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(); @@ -256,6 +271,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() { @@ -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 + } + } }