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 7d55a89e2d..3cbbf3299a 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 @@ -399,20 +399,6 @@ public final class NativeLibrary } } - public static void endEmulationActivity() - { - Log.verbose("[NativeLibrary] Ending EmulationActivity."); - EmulationActivity emulationActivity = sEmulationActivity.get(); - if (emulationActivity != null) - { - emulationActivity.exitWithAnimation(); - } - else - { - Log.warning("[NativeLibrary] EmulationActivity is null, can't end."); - } - } - public static void setEmulationActivity(EmulationActivity emulationActivity) { Log.verbose("[NativeLibrary] Registering EmulationActivity."); 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 0748a34004..14d0e2c9be 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 @@ -23,7 +23,6 @@ import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; -import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.SeekBar; @@ -55,8 +54,8 @@ public final class EmulationActivity extends AppCompatActivity private static final String FRAGMENT_SUBMENU_TAG = "submenu"; private View mDecorView; private ImageView mImageView; + private EmulationFragment mEmulationFragment; - private FrameLayout mFrameEmulation; private LinearLayout mMenuLayout; private SharedPreferences mPreferences; @@ -85,7 +84,6 @@ public final class EmulationActivity extends AppCompatActivity } }; private String mScreenPath; - private FrameLayout mFrameContent; private String mSelectedTitle; @Retention(SOURCE) @@ -219,12 +217,13 @@ public final class EmulationActivity extends AppCompatActivity Java_GCAdapter.manager = (UsbManager) getSystemService(Context.USB_SERVICE); Java_WiimoteAdapter.manager = (UsbManager) getSystemService(Context.USB_SERVICE); + setContentView(R.layout.activity_emulation); mImageView = (ImageView) findViewById(R.id.image_screenshot); - mFrameContent = (FrameLayout) findViewById(R.id.frame_content); - mFrameEmulation = (FrameLayout) findViewById(R.id.frame_emulation_fragment); mMenuLayout = (LinearLayout) findViewById(R.id.layout_ingame_menu); + mEmulationFragment = (EmulationFragment) getSupportFragmentManager() + .findFragmentById(R.id.fragment_emulation); Intent gameToEmulate = getIntent(); String path = gameToEmulate.getStringExtra("SelectedGame"); @@ -260,14 +259,6 @@ public final class EmulationActivity extends AppCompatActivity Animations.fadeViewOut(mImageView) .setStartDelay(2000) - .withStartAction(new Runnable() - { - @Override - public void run() - { - mFrameEmulation.setVisibility(View.VISIBLE); - } - }) .withEndAction(new Runnable() { @Override @@ -277,18 +268,12 @@ public final class EmulationActivity extends AppCompatActivity } }); - // Instantiate an EmulationFragment. - EmulationFragment emulationFragment = EmulationFragment.newInstance(path); - - // Add fragment to the activity - this triggers all its lifecycle callbacks. - getSupportFragmentManager().beginTransaction() - .add(R.id.frame_emulation_fragment, emulationFragment, EmulationFragment.FRAGMENT_TAG) - .commit(); + mEmulationFragment.setGamePath(path); + mEmulationFragment.startEmulation(); } else { mImageView.setVisibility(View.GONE); - mFrameEmulation.setVisibility(View.VISIBLE); } if (mDeviceHasTouchScreen) @@ -311,23 +296,6 @@ public final class EmulationActivity extends AppCompatActivity mIsGameCubeGame = Platform.fromNativeInt(NativeLibrary.GetPlatform(path)) == Platform.GAMECUBE; } - @Override - protected void onStart() - { - super.onStart(); - Log.debug("[EmulationActivity] EmulationActivity starting."); - NativeLibrary.setEmulationActivity(this); - } - - @Override - protected void onStop() - { - super.onStop(); - Log.debug("[EmulationActivity] EmulationActivity stopping."); - - NativeLibrary.clearEmulationActivity(); - } - @Override protected void onPostCreate(Bundle savedInstanceState) { @@ -376,7 +344,8 @@ public final class EmulationActivity extends AppCompatActivity } else { - stopEmulation(); + mEmulationFragment.stopEmulation(); + exitWithAnimation(); } } @@ -406,15 +375,6 @@ public final class EmulationActivity extends AppCompatActivity } } - private void stopEmulation() - { - EmulationFragment fragment = (EmulationFragment) getSupportFragmentManager() - .findFragmentByTag(EmulationFragment.FRAGMENT_TAG); - fragment.notifyEmulationStopped(); - - NativeLibrary.StopEmulation(); - } - public void exitWithAnimation() { runOnUiThread(new Runnable() @@ -458,7 +418,6 @@ public final class EmulationActivity extends AppCompatActivity @Override public void run() { - mFrameContent.removeView(mFrameEmulation); setResult(mPosition); finishAfterTransition(); } @@ -599,20 +558,23 @@ public final class EmulationActivity extends AppCompatActivity return; case MENU_ACTION_EXIT: - toggleMenu(); - stopEmulation(); + toggleMenu(); // Hide the menu (it will be showing since we just clicked it) + mEmulationFragment.stopEmulation(); + exitWithAnimation(); return; } } - private void editControlsPlacement() { - EmulationFragment emulationFragment = (EmulationFragment) getSupportFragmentManager() - .findFragmentById(R.id.frame_emulation_fragment); - if (emulationFragment.isConfiguringControls()) { - emulationFragment.stopConfiguringControls(); - } else { - emulationFragment.startConfiguringControls(); + private void editControlsPlacement() + { + if (mEmulationFragment.isConfiguringControls()) + { + mEmulationFragment.stopConfiguringControls(); + } + else + { + mEmulationFragment.startConfiguringControls(); } } @@ -701,20 +663,18 @@ public final class EmulationActivity extends AppCompatActivity } builder.setNeutralButton(getString(R.string.emulation_toggle_all), new DialogInterface.OnClickListener() { @Override - public void onClick(DialogInterface dialogInterface, int i) { - EmulationFragment emulationFragment = (EmulationFragment) getSupportFragmentManager() - .findFragmentByTag(EmulationFragment.FRAGMENT_TAG); - emulationFragment.toggleInputOverlayVisibility(); + public void onClick(DialogInterface dialogInterface, int i) + { + mEmulationFragment.toggleInputOverlayVisibility(); } }); builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() { @Override - public void onClick(DialogInterface dialogInterface, int i) { + public void onClick(DialogInterface dialogInterface, int i) + { editor.apply(); - EmulationFragment emulationFragment = (EmulationFragment) getSupportFragmentManager() - .findFragmentByTag(EmulationFragment.FRAGMENT_TAG); - emulationFragment.refreshInputOverlay(); + mEmulationFragment.refreshInputOverlay(); } }); @@ -759,9 +719,7 @@ public final class EmulationActivity extends AppCompatActivity editor.putInt("controlScale", seekbar.getProgress()); editor.apply(); - EmulationFragment emulationFragment = (EmulationFragment) getSupportFragmentManager() - .findFragmentByTag(EmulationFragment.FRAGMENT_TAG); - emulationFragment.refreshInputOverlay(); + mEmulationFragment.refreshInputOverlay(); } }); @@ -788,9 +746,7 @@ public final class EmulationActivity extends AppCompatActivity public void onClick(DialogInterface dialogInterface, int i) { editor.apply(); - EmulationFragment emulationFragment = (EmulationFragment) getSupportFragmentManager() - .findFragmentByTag(EmulationFragment.FRAGMENT_TAG); - emulationFragment.refreshInputOverlay(); + mEmulationFragment.refreshInputOverlay(); Toast.makeText(getApplication(), R.string.emulation_controller_changed, Toast.LENGTH_SHORT).show(); } 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 f80487b938..4ba035f950 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 @@ -1,5 +1,6 @@ package org.dolphinemu.dolphinemu.fragments; +import android.content.Context; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; @@ -14,15 +15,12 @@ import android.widget.Button; import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.R; +import org.dolphinemu.dolphinemu.activities.EmulationActivity; import org.dolphinemu.dolphinemu.overlay.InputOverlay; import org.dolphinemu.dolphinemu.utils.Log; public final class EmulationFragment extends Fragment implements SurfaceHolder.Callback { - public static final String FRAGMENT_TAG = "emulation_fragment"; - - private static final String ARG_GAME_PATH = "game_path"; - private SharedPreferences mPreferences; private Surface mSurface; @@ -31,18 +29,22 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C private Thread mEmulationThread; - private boolean mEmulationStarted; - private boolean mEmulationRunning; + private String mGamePath; + private final EmulationState mEmulationState = new EmulationState(); - public static EmulationFragment newInstance(String path) + @Override + public void onAttach(Context context) { - EmulationFragment fragment = new EmulationFragment(); + super.onAttach(context); - Bundle arguments = new Bundle(); - arguments.putString(ARG_GAME_PATH, path); - fragment.setArguments(arguments); - - return fragment; + if (context instanceof EmulationActivity) + { + NativeLibrary.setEmulationActivity((EmulationActivity) context); + } + else + { + throw new IllegalStateException("EmulationFragment must have EmulationActivity parent"); + } } /** @@ -67,38 +69,20 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C { View contents = inflater.inflate(R.layout.fragment_emulation, container, false); - SurfaceView surfaceView = (SurfaceView) contents.findViewById(R.id.surface_emulation); - mInputOverlay = (InputOverlay) contents.findViewById(R.id.surface_input_overlay); - + SurfaceView surfaceView = contents.findViewById(R.id.surface_emulation); surfaceView.getHolder().addCallback(this); - // If the input overlay was previously disabled, then don't show it. + mInputOverlay = contents.findViewById(R.id.surface_input_overlay); if (mInputOverlay != null) { + // If the input overlay was previously disabled, then don't show it. if (!mPreferences.getBoolean("showInputOverlay", true)) { mInputOverlay.setVisibility(View.GONE); } } - if (savedInstanceState == null) - { - mEmulationThread = new Thread(mEmulationRunner); - } - else - { - // Likely a rotation occurred. - // TODO Pass native code the Surface, which will have been recreated, from surfaceChanged() - // TODO Also, write the native code that will get the video backend to accept the new Surface as one of its own. - } - - return contents; - } - - @Override - public void onViewCreated(View view, Bundle savedInstanceState) - { - Button doneButton = (Button) view.findViewById(R.id.done_control_config); + Button doneButton = contents.findViewById(R.id.done_control_config); if (doneButton != null) { doneButton.setOnClickListener(new View.OnClickListener() @@ -110,29 +94,29 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C } }); } - } - @Override - public void onStart() - { - super.onStart(); - startEmulation(); + // The new Surface created here will get passed to the native code via onSurfaceChanged. + + return contents; } @Override public void onStop() { + pauseEmulation(); super.onStop(); } @Override - public void onDestroyView() + public void onDetach() { - super.onDestroyView(); - if (getActivity().isFinishing() && mEmulationStarted) - { - NativeLibrary.StopEmulation(); - } + NativeLibrary.clearEmulationActivity(); + super.onDetach(); + } + + public void setGamePath(String setPath) + { + this.mGamePath = setPath; } public void toggleInputOverlayVisibility() @@ -171,6 +155,12 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C 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); } @@ -181,45 +171,57 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C Log.debug("[EmulationFragment] Surface destroyed."); NativeLibrary.SurfaceDestroyed(); - if (mEmulationRunning) + if (mEmulationState.isRunning()) { pauseEmulation(); } } - private void startEmulation() + public void startEmulation() { - if (!mEmulationStarted) + synchronized (mEmulationState) { - Log.debug("[EmulationFragment] Starting emulation thread."); + if (mEmulationState.isStopped()) + { + Log.debug("[EmulationFragment] Starting emulation thread."); - mEmulationThread.start(); + 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."); + } } - else + } + + public void stopEmulation() { + synchronized (mEmulationState) { - Log.debug("[EmulationFragment] Resuming emulation."); - NativeLibrary.UnPauseEmulation(); + if (!mEmulationState.isStopped()) + { + NativeLibrary.StopEmulation(); + mEmulationState.stop(); + } } - - mEmulationRunning = true; } private void pauseEmulation() { - Log.debug("[EmulationFragment] Pausing emulation."); + synchronized (mEmulationState) + { + Log.debug("[EmulationFragment] Pausing emulation."); - NativeLibrary.PauseEmulation(); - mEmulationRunning = false; - } - - /** - * Called by containing activity to tell the Fragment emulation is already stopping, - * so it doesn't try to stop emulation on its way to the garbage collector. - */ - public void notifyEmulationStopped() - { - mEmulationStarted = false; - mEmulationRunning = false; + NativeLibrary.PauseEmulation(); + mEmulationState.pause(); + } } private Runnable mEmulationRunner = new Runnable() @@ -227,18 +229,17 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C @Override public void run() { - mEmulationRunning = true; - mEmulationStarted = true; + // Busy-wait for surface to be set + while (mSurface == null) {} - while (mSurface == null) - if (!mEmulationRunning) - return; - - Log.info("[EmulationFragment] Starting emulation: " + mSurface); + synchronized (mEmulationState) + { + Log.info("[EmulationFragment] Starting emulation: " + mSurface); + mEmulationState.run(); + } // Start emulation using the provided Surface. - String path = getArguments().getString(ARG_GAME_PATH); - NativeLibrary.Run(path); + NativeLibrary.Run(mGamePath); } }; @@ -258,4 +259,50 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C { return mInputOverlay.isInEditMode(); } + + private static class EmulationState + { + private enum State + { + STOPPED, RUNNING, PAUSED + } + + private State state; + + EmulationState() + { + // Starting state is stopped. + state = State.STOPPED; + } + + public boolean isStopped() + { + return state == State.STOPPED; + } + + public boolean isRunning() + { + return state == State.RUNNING; + } + + public boolean isPaused() + { + return state == State.PAUSED; + } + + public void run() + { + state = State.RUNNING; + } + + public void pause() + { + state = State.PAUSED; + } + + public void stop() + { + state = State.STOPPED; + } + } } 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 59e1ba02ec..ad67bcb05e 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,11 +5,11 @@ xmlns:tools="http://schemas.android.com/tools" android:id="@+id/frame_content"> - + android:layout_height="match_parent"/> - + android:layout_height="match_parent"/> GetStaticMethodID(s_jni_class, "displayAlertMsg", "(Ljava/lang/String;)V"); - s_jni_method_end = env->GetStaticMethodID(s_jni_class, "endEmulationActivity", "()V"); } // Surface Handling @@ -816,9 +814,6 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run(JNIEnv* ANativeWindow_release(s_surf); s_surf = nullptr; } - - // Execute the Java method. - env->CallStaticVoidMethod(s_jni_class, s_jni_method_end); } #ifdef __cplusplus