From 987d24fe87c5679d055576b8458ee5d2263fc7ab Mon Sep 17 00:00:00 2001 From: Mike <7153163+hackbar@users.noreply.github.com> Date: Wed, 1 Nov 2017 21:59:30 -0700 Subject: [PATCH 1/2] Android: Use the newInstance pattern for EmulationFragment. --- .../activities/EmulationActivity.java | 13 ++++++++++-- .../fragments/EmulationFragment.java | 20 ++++++++++++++----- .../layout-television/activity_emulation.xml | 5 ++--- .../main/res/layout/activity_emulation.xml | 5 ++--- 4 files changed, 30 insertions(+), 13 deletions(-) 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 2753735200..27ec8cd794 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 @@ -42,6 +42,7 @@ import org.dolphinemu.dolphinemu.ui.platform.Platform; import org.dolphinemu.dolphinemu.utils.Animations; 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; @@ -226,8 +227,17 @@ public final class EmulationActivity extends AppCompatActivity setContentView(R.layout.activity_emulation); mImageView = (ImageView) findViewById(R.id.image_screenshot); + + // Find or create the EmulationFragment mEmulationFragment = (EmulationFragment) getSupportFragmentManager() - .findFragmentById(R.id.fragment_emulation); + .findFragmentById(R.id.frame_emulation_fragment); + if (mEmulationFragment == null) + { + mEmulationFragment = EmulationFragment.newInstance(path); + getSupportFragmentManager().beginTransaction() + .add(R.id.frame_emulation_fragment, mEmulationFragment) + .commit(); + } if (savedInstanceState == null) { @@ -266,7 +276,6 @@ public final class EmulationActivity extends AppCompatActivity } }); - mEmulationFragment.setGamePath(path); mEmulationFragment.startEmulation(); } else 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 0572d048f4..393790cf14 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 @@ -21,6 +21,8 @@ import org.dolphinemu.dolphinemu.utils.Log; public final class EmulationFragment extends Fragment implements SurfaceHolder.Callback { + private static final String KEY_GAMEPATH = "gamepath"; + private SharedPreferences mPreferences; private Surface mSurface; @@ -32,6 +34,17 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C private String mGamePath; private final EmulationState mEmulationState = new EmulationState(); + public static EmulationFragment newInstance(String gamePath) + { + + Bundle args = new Bundle(); + args.putString(KEY_GAMEPATH, gamePath); + + EmulationFragment fragment = new EmulationFragment(); + fragment.setArguments(args); + return fragment; + } + @Override public void onAttach(Context context) { @@ -59,6 +72,8 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C setRetainInstance(true); mPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); + + mGamePath = getArguments().getString(KEY_GAMEPATH); } /** @@ -114,11 +129,6 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C super.onDetach(); } - public void setGamePath(String gamePath) - { - mGamePath = gamePath; - } - public void toggleInputOverlayVisibility() { SharedPreferences.Editor editor = mPreferences.edit(); diff --git a/Source/Android/app/src/main/res/layout-television/activity_emulation.xml b/Source/Android/app/src/main/res/layout-television/activity_emulation.xml index 3009f40e84..835af1b39b 100644 --- a/Source/Android/app/src/main/res/layout-television/activity_emulation.xml +++ b/Source/Android/app/src/main/res/layout-television/activity_emulation.xml @@ -5,9 +5,8 @@ xmlns:tools="http://schemas.android.com/tools" android:id="@+id/frame_content"> - diff --git a/Source/Android/app/src/main/res/layout/activity_emulation.xml b/Source/Android/app/src/main/res/layout/activity_emulation.xml index 2562d5a44b..7ebaceee22 100644 --- a/Source/Android/app/src/main/res/layout/activity_emulation.xml +++ b/Source/Android/app/src/main/res/layout/activity_emulation.xml @@ -3,9 +3,8 @@ android:layout_height="match_parent" android:id="@+id/frame_content"> - From 18cb68eb3cfe656a4eb22229a5441c665a54bf3d Mon Sep 17 00:00:00 2001 From: Mike <7153163+hackbar@users.noreply.github.com> Date: Wed, 25 Oct 2017 12:45:03 -0700 Subject: [PATCH 2/2] Android: Destroy the surface in EmulationFragment onStop. Emulation needs to be running when the surface is destroyed, but we want to pause in onStop. So call the surfaceDestroyed callback, as this accomplished both. --- .../activities/EmulationActivity.java | 2 - .../fragments/EmulationFragment.java | 253 ++++++++++-------- 2 files changed, 145 insertions(+), 110 deletions(-) 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 27ec8cd794..8e59d07cca 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 @@ -275,8 +275,6 @@ public final class EmulationActivity extends AppCompatActivity mImageView.setVisibility(View.GONE); } }); - - mEmulationFragment.startEmulation(); } else { 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 393790cf14..263cd597c1 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,14 +25,9 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C private SharedPreferences mPreferences; - private Surface mSurface; - private InputOverlay mInputOverlay; - private Thread mEmulationThread; - - private String mGamePath; - private final EmulationState mEmulationState = new EmulationState(); + private EmulationState mEmulationState; public static EmulationFragment newInstance(String gamePath) { @@ -73,7 +68,8 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C mPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); - mGamePath = getArguments().getString(KEY_GAMEPATH); + String gamePath = getArguments().getString(KEY_GAMEPATH); + mEmulationState = new EmulationState(gamePath); } /** @@ -116,10 +112,17 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C } @Override - public void onStop() + public void onResume() { - pauseEmulation(); - super.onStop(); + super.onResume(); + mEmulationState.run(); + } + + @Override + public void onPause() + { + mEmulationState.pause(); + super.onPause(); } @Override @@ -158,101 +161,28 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C @Override public void surfaceCreated(SurfaceHolder holder) { - Log.debug("[EmulationFragment] Surface created."); + // We purposely don't do anything here. + // All work is done in surfaceChanged, which we are guaranteed to get even for surface creation. } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.debug("[EmulationFragment] Surface changed. Resolution: " + width + "x" + height); - - if (mEmulationState.isPaused()) - { - NativeLibrary.UnPauseEmulation(); - } - - mSurface = holder.getSurface(); - NativeLibrary.SurfaceChanged(mSurface); + mEmulationState.newSurface(holder.getSurface()); } @Override public void surfaceDestroyed(SurfaceHolder holder) { - Log.debug("[EmulationFragment] Surface destroyed."); - NativeLibrary.SurfaceDestroyed(); - - if (mEmulationState.isRunning()) - { - pauseEmulation(); - } + mEmulationState.clearSurface(); } - public void startEmulation() + public void stopEmulation() { - synchronized (mEmulationState) - { - if (mEmulationState.isStopped()) - { - Log.debug("[EmulationFragment] Starting emulation thread."); - - mEmulationThread = new Thread(mEmulationRunner, "NativeEmulation"); - mEmulationThread.start(); - // The thread will call mEmulationState.run() - } - else if (mEmulationState.isPaused()) - { - Log.debug("[EmulationFragment] Resuming emulation."); - NativeLibrary.UnPauseEmulation(); - mEmulationState.run(); - } - else - { - Log.debug("[EmulationFragment] Bug, startEmulation called while running."); - } - } + mEmulationState.stop(); } - public void stopEmulation() { - synchronized (mEmulationState) - { - if (!mEmulationState.isStopped()) - { - NativeLibrary.StopEmulation(); - mEmulationState.stop(); - } - } - } - - private void pauseEmulation() - { - synchronized (mEmulationState) - { - Log.debug("[EmulationFragment] Pausing emulation."); - - NativeLibrary.PauseEmulation(); - mEmulationState.pause(); - } - } - - private Runnable mEmulationRunner = new Runnable() - { - @Override - public void run() - { - // Busy-wait for surface to be set - while (mSurface == null) {} - - synchronized (mEmulationState) - { - Log.info("[EmulationFragment] Starting emulation: " + mSurface); - - mEmulationState.run(); - } - // Start emulation using the provided Surface. - NativeLibrary.Run(mGamePath); - } - }; - public void startConfiguringControls() { getView().findViewById(R.id.done_control_config).setVisibility(View.VISIBLE); @@ -277,42 +207,149 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C STOPPED, RUNNING, PAUSED } + private final String mGamePath; + private Thread mEmulationThread; private State state; + private Surface mSurface; + private boolean mRunWhenSurfaceIsValid; - EmulationState() + EmulationState(String gamePath) { + mGamePath = gamePath; // Starting state is stopped. state = State.STOPPED; } - public boolean isStopped() + // Getters for the current state + + public synchronized boolean isStopped() { return state == State.STOPPED; } - public boolean isRunning() - { - return state == State.RUNNING; - } - - public boolean isPaused() + public synchronized boolean isPaused() { return state == State.PAUSED; } - public void run() + public synchronized boolean isRunning() { + return state == State.RUNNING; + } + + // State changing methods + + public synchronized void stop() + { + if (state != State.STOPPED) + { + state = State.STOPPED; + NativeLibrary.StopEmulation(); + } + else + { + Log.warning("[EmulationFragment] Stop called while already stopped."); + } + } + + public synchronized void pause() + { + if (state != State.PAUSED) + { + state = State.PAUSED; + Log.debug("[EmulationFragment] Pausing emulation."); + + // Release the surface before pausing, since emulation has to be running for that. + mSurface = null; + NativeLibrary.SurfaceDestroyed(); + NativeLibrary.PauseEmulation(); + } + else + { + Log.warning("[EmulationFragment] Pause called while already paused."); + } + } + + public synchronized void run() + { + // If the surface is set, run now. Otherwise, wait for it to get set. + if (mSurface != null) + { + runWithValidSurface(); + } + else + { + mRunWhenSurfaceIsValid = true; + } + } + + // Surface callbacks + public synchronized void newSurface(Surface surface) + { + mSurface = surface; + if (mRunWhenSurfaceIsValid) + { + runWithValidSurface(); + } + } + + public synchronized void clearSurface() + { + if (mSurface == null) + { + Log.warning("[EmulationFragment] clearSurface called, but surface already null."); + } + else + { + mSurface = null; + Log.debug("[EmulationFragment] Surface destroyed."); + + if (state == State.RUNNING) + { + NativeLibrary.SurfaceDestroyed(); + state = State.PAUSED; + } + else if (state == State.PAUSED) + { + Log.warning("[EmulationFragment] Surface cleared while emulation paused."); + } + else + { + Log.warning("[EmulationFragment] Surface cleared while emulation stopped."); + } + } + } + + private void runWithValidSurface() + { + mRunWhenSurfaceIsValid = false; + if (state == State.STOPPED) + { + Log.debug("[EmulationFragment] Starting emulation thread."); + + mEmulationThread = new Thread(new Runnable() + { + @Override + public void run() + { + NativeLibrary.SurfaceChanged(mSurface); + NativeLibrary.Run(mGamePath); + }}, + "NativeEmulation"); + mEmulationThread.start(); + + } + else if (state == State.PAUSED) + { + Log.debug("[EmulationFragment] Resuming emulation."); + NativeLibrary.UnPauseEmulation(); + NativeLibrary.SurfaceChanged(mSurface); + } + else + { + Log.debug("[EmulationFragment] Bug, run called while already running."); + } state = State.RUNNING; } - - public void pause() - { - state = State.PAUSED; - } - - public void stop() - { - state = State.STOPPED; - } } }