diff --git a/Source/Android/app/src/main/AndroidManifest.xml b/Source/Android/app/src/main/AndroidManifest.xml
index 32233600d0..78548b4c6c 100644
--- a/Source/Android/app/src/main/AndroidManifest.xml
+++ b/Source/Android/app/src/main/AndroidManifest.xml
@@ -47,6 +47,10 @@
+
+
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
new file mode 100644
index 0000000000..de7ebb17d3
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java
@@ -0,0 +1,292 @@
+package org.dolphinemu.dolphinemu.activities;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+
+import org.dolphinemu.dolphinemu.NativeLibrary;
+import org.dolphinemu.dolphinemu.R;
+import org.dolphinemu.dolphinemu.fragments.EmulationFragment;
+import org.dolphinemu.dolphinemu.settings.input.InputConfigFragment;
+
+import java.util.List;
+
+public final class EmulationActivity extends Activity
+{
+ private View mDecorView;
+
+ /**
+ * Handlers are a way to pass a message to an Activity telling it to do something
+ * on the UI thread. This Handler responds to any message, even blank ones, by
+ * hiding the system UI.
+ */
+ private Handler mSystemUiHider = new Handler()
+ {
+ @Override
+ public void handleMessage(Message msg)
+ {
+ hideSystemUI();
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+
+ // Get a handle to the Window containing the UI.
+ mDecorView = getWindow().getDecorView();
+
+ // Set these options now so that the SurfaceView the game renders into is the right size.
+ mDecorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
+
+ // Set the ActionBar to follow the navigation/status bar's visibility changes.
+ mDecorView.setOnSystemUiVisibilityChangeListener(
+ new View.OnSystemUiVisibilityChangeListener()
+ {
+ @Override
+ public void onSystemUiVisibilityChange(int flags)
+ {
+ boolean visible = (flags & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
+
+ if (visible)
+ {
+ getActionBar().show();
+ hideSystemUiAfterDelay();
+ }
+ else
+ {
+ getActionBar().hide();
+ }
+ }
+ }
+ );
+
+ setContentView(R.layout.activity_emulation);
+
+ Intent gameToEmulate = getIntent();
+ String path = gameToEmulate.getStringExtra("SelectedGame");
+ String title = gameToEmulate.getStringExtra("SelectedTitle");
+
+ setTitle(title);
+
+ // Instantiate an EmulationFragment.
+ EmulationFragment emulationFragment = EmulationFragment.newInstance(path);
+
+ // Add fragment to the activity - this triggers all its lifecycle callbacks.
+ getFragmentManager().beginTransaction()
+ .add(R.id.frame_content, emulationFragment, EmulationFragment.FRAGMENT_TAG)
+ .commit();
+ }
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState)
+ {
+ super.onPostCreate(savedInstanceState);
+
+ // Give the user a few seconds to see what the controls look like, then hide them.
+ hideSystemUiAfterDelay();
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus)
+ {
+ super.onWindowFocusChanged(hasFocus);
+
+ if (hasFocus)
+ {
+ hideSystemUiAfterDelay();
+ }
+ else
+ {
+ // If the window loses focus (i.e. a dialog box, or a popup menu is on screen
+ // stop hiding the UI.
+ mSystemUiHider.removeMessages(0);
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu)
+ {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.menu_emulation, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item)
+ {
+ switch (item.getItemId())
+ {
+ // Enable/Disable input overlay.
+ case R.id.enableInputOverlay:
+ {
+ EmulationFragment emulationFragment = (EmulationFragment) getFragmentManager()
+ .findFragmentByTag(EmulationFragment.FRAGMENT_TAG);
+
+ emulationFragment.toggleInputOverlayVisibility();
+
+ return true;
+ }
+
+ // Screenshot capturing
+ case R.id.takeScreenshot:
+ NativeLibrary.SaveScreenShot();
+ return true;
+
+ // Save state slots
+ case R.id.saveSlot1:
+ NativeLibrary.SaveState(0);
+ return true;
+
+ case R.id.saveSlot2:
+ NativeLibrary.SaveState(1);
+ return true;
+
+ case R.id.saveSlot3:
+ NativeLibrary.SaveState(2);
+ return true;
+
+ case R.id.saveSlot4:
+ NativeLibrary.SaveState(3);
+ return true;
+
+ case R.id.saveSlot5:
+ NativeLibrary.SaveState(4);
+ return true;
+
+ // Load state slots
+ case R.id.loadSlot1:
+ NativeLibrary.LoadState(0);
+ return true;
+
+ case R.id.loadSlot2:
+ NativeLibrary.LoadState(1);
+ return true;
+
+ case R.id.loadSlot3:
+ NativeLibrary.LoadState(2);
+ return true;
+
+ case R.id.loadSlot4:
+ NativeLibrary.LoadState(3);
+ return true;
+
+ case R.id.loadSlot5:
+ NativeLibrary.LoadState(4);
+ return true;
+
+ case R.id.exitEmulation:
+ {
+ // Create a confirmation method for quitting the current emulation instance.
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.overlay_exit_emulation);
+ builder.setMessage(R.string.overlay_exit_emulation_confirm);
+ builder.setNegativeButton(R.string.no, null);
+ builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener()
+ {
+ public void onClick(DialogInterface dialog, int which)
+ {
+ onDestroy();
+ }
+ });
+ builder.show();
+ return true;
+ }
+
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ // Gets button presses
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event)
+ {
+ int action = 0;
+
+ switch (event.getAction())
+ {
+ case KeyEvent.ACTION_DOWN:
+ // Handling the case where the back button is pressed.
+ if (event.getKeyCode() == KeyEvent.KEYCODE_BACK)
+ {
+ onBackPressed();
+ return true;
+ }
+
+ // Normal key events.
+ action = NativeLibrary.ButtonState.PRESSED;
+ break;
+ case KeyEvent.ACTION_UP:
+ action = NativeLibrary.ButtonState.RELEASED;
+ break;
+ default:
+ return false;
+ }
+ InputDevice input = event.getDevice();
+ boolean handled = NativeLibrary.onGamePadEvent(InputConfigFragment.getInputDesc(input), event.getKeyCode(), action);
+ return handled;
+ }
+
+ @Override
+ public boolean dispatchGenericMotionEvent(MotionEvent event)
+ {
+ if (((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0))
+ {
+ return super.dispatchGenericMotionEvent(event);
+ }
+
+ // Don't attempt to do anything if we are disconnecting a device.
+ if (event.getActionMasked() == MotionEvent.ACTION_CANCEL)
+ return true;
+
+ InputDevice input = event.getDevice();
+ List motions = input.getMotionRanges();
+
+ for (InputDevice.MotionRange range : motions)
+ {
+ NativeLibrary.onGamePadMoveEvent(InputConfigFragment.getInputDesc(input), range.getAxis(), event.getAxisValue(range.getAxis()));
+ }
+
+ return true;
+ }
+
+ private void hideSystemUiAfterDelay()
+ {
+ // Clear any pending hide events.
+ mSystemUiHider.removeMessages(0);
+
+ // Add a new hide event, to occur 3 seconds from now.
+ mSystemUiHider.sendEmptyMessageDelayed(0, 3000);
+ }
+
+ private void hideSystemUI()
+ {
+ mDecorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
+ View.SYSTEM_UI_FLAG_FULLSCREEN |
+ View.SYSTEM_UI_FLAG_IMMERSIVE);
+ }
+
+ private void showSystemUI()
+ {
+ mDecorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameAdapter.java
index 0f964e87f4..d296c5e023 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameAdapter.java
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameAdapter.java
@@ -14,8 +14,8 @@ import android.view.ViewGroup;
import com.squareup.picasso.Picasso;
import org.dolphinemu.dolphinemu.R;
+import org.dolphinemu.dolphinemu.activities.EmulationActivity;
import org.dolphinemu.dolphinemu.dialogs.GameDetailsDialog;
-import org.dolphinemu.dolphinemu.emulation.EmulationActivity;
import org.dolphinemu.dolphinemu.model.GameDatabase;
import org.dolphinemu.dolphinemu.viewholders.GameViewHolder;
@@ -215,6 +215,7 @@ public final class GameAdapter extends RecyclerView.Adapter impl
Intent intent = new Intent(view.getContext(), EmulationActivity.class);
intent.putExtra("SelectedGame", holder.path);
+ intent.putExtra("SelectedTitle", holder.title);
view.getContext().startActivity(intent);
}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GameDetailsDialog.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GameDetailsDialog.java
index 34d40048b4..04c7253cb5 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GameDetailsDialog.java
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GameDetailsDialog.java
@@ -16,7 +16,7 @@ import com.squareup.picasso.Picasso;
import org.dolphinemu.dolphinemu.BuildConfig;
import org.dolphinemu.dolphinemu.R;
-import org.dolphinemu.dolphinemu.emulation.EmulationActivity;
+import org.dolphinemu.dolphinemu.activities.EmulationActivity;
import de.hdodenhof.circleimageview.CircleImageView;
@@ -79,6 +79,8 @@ public final class GameDetailsDialog extends DialogFragment
Intent intent = new Intent(view.getContext(), EmulationActivity.class);
intent.putExtra("SelectedGame", getArguments().getString(ARGUMENT_GAME_PATH));
+ intent.putExtra("SelectedTitle", getArguments().getString(ARGUMENT_GAME_TITLE));
+
startActivity(intent);
}
});
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
new file mode 100644
index 0000000000..7c83c9d977
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java
@@ -0,0 +1,114 @@
+package org.dolphinemu.dolphinemu.fragments;
+
+import android.app.Fragment;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.dolphinemu.dolphinemu.BuildConfig;
+import org.dolphinemu.dolphinemu.NativeLibrary;
+import org.dolphinemu.dolphinemu.R;
+import org.dolphinemu.dolphinemu.emulation.overlay.InputOverlay;
+
+
+public final class EmulationFragment extends Fragment
+{
+ public static final String FRAGMENT_TAG = BuildConfig.APPLICATION_ID + ".emulation_fragment";
+
+ private static final String ARGUMENT_GAME_PATH = BuildConfig.APPLICATION_ID + ".game_path";
+
+ private SharedPreferences mPreferences;
+
+ private InputOverlay mInputOverlay;
+
+ public static EmulationFragment newInstance(String path)
+ {
+ EmulationFragment fragment = new EmulationFragment();
+
+ Bundle arguments = new Bundle();
+ arguments.putString(ARGUMENT_GAME_PATH, path);
+ fragment.setArguments(arguments);
+
+ return fragment;
+ }
+
+ /**
+ * Initialize anything that doesn't depend on the layout / views in here.
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+
+ mPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
+ }
+
+ /**
+ * Initialize the UI and start emulation in here.
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
+ {
+ String path = getArguments().getString(ARGUMENT_GAME_PATH);
+
+ View contents = inflater.inflate(R.layout.fragment_emulation, container, false);
+
+ mInputOverlay = (InputOverlay) contents.findViewById(R.id.surface_input_overlay);
+
+ NativeLibrary.SetFilename(path);
+
+
+ // If the input overlay was previously disabled, then don't show it.
+ if (!mPreferences.getBoolean("showInputOverlay", true))
+ {
+ mInputOverlay.setVisibility(View.GONE);
+ }
+
+ return contents;
+ }
+
+ @Override
+ public void onStart()
+ {
+ super.onStart();
+ NativeLibrary.UnPauseEmulation();
+ }
+
+ @Override
+ public void onStop()
+ {
+ super.onStop();
+ NativeLibrary.PauseEmulation();
+ }
+
+ @Override
+ public void onDestroyView()
+ {
+ super.onDestroyView();
+ NativeLibrary.StopEmulation();
+ }
+
+ public void toggleInputOverlayVisibility()
+ {
+ SharedPreferences.Editor editor = mPreferences.edit();
+
+ // If the overlay is currently set to INVISIBLE
+ if (!mPreferences.getBoolean("showInputOverlay", false))
+ {
+ // Set it to VISIBLE
+ mInputOverlay.setVisibility(View.VISIBLE);
+ editor.putBoolean("showInputOverlay", true);
+ }
+ else
+ {
+ // Set it to INVISIBLE
+ mInputOverlay.setVisibility(View.GONE);
+ editor.putBoolean("showInputOverlay", false);
+ }
+
+ editor.apply();
+ }
+}
diff --git a/Source/Android/app/src/main/res/layout/activity_emulation.xml b/Source/Android/app/src/main/res/layout/activity_emulation.xml
new file mode 100644
index 0000000000..63ea99ba83
--- /dev/null
+++ b/Source/Android/app/src/main/res/layout/activity_emulation.xml
@@ -0,0 +1,5 @@
+
+
\ No newline at end of file
diff --git a/Source/Android/app/src/main/res/layout/activity_game_grid.xml b/Source/Android/app/src/main/res/layout/activity_game_grid.xml
index 8186490004..53ac5d614e 100644
--- a/Source/Android/app/src/main/res/layout/activity_game_grid.xml
+++ b/Source/Android/app/src/main/res/layout/activity_game_grid.xml
@@ -9,7 +9,7 @@
android:id="@+id/toolbar_game_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@color/dolphin_blue"
+ android:background="?android:colorPrimary"
android:minHeight="?android:attr/actionBarSize"
android:theme="@android:style/ThemeOverlay.Material.Dark.ActionBar"
android:elevation="6dp"/>
diff --git a/Source/Android/app/src/main/res/layout/fragment_emulation.xml b/Source/Android/app/src/main/res/layout/fragment_emulation.xml
new file mode 100644
index 0000000000..6a936dae64
--- /dev/null
+++ b/Source/Android/app/src/main/res/layout/fragment_emulation.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/Source/Android/app/src/main/res/menu/menu_emulation.xml b/Source/Android/app/src/main/res/menu/menu_emulation.xml
new file mode 100644
index 0000000000..627d1bc9e3
--- /dev/null
+++ b/Source/Android/app/src/main/res/menu/menu_emulation.xml
@@ -0,0 +1,77 @@
+
diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml
index 07e5ab3690..47fefbaf20 100644
--- a/Source/Android/app/src/main/res/values/strings.xml
+++ b/Source/Android/app/src/main/res/values/strings.xml
@@ -236,4 +236,7 @@
CPU Settings
Input Settings
Video Settings
+ Emulation Activity
+
+ Toggle Input Overlay
diff --git a/Source/Android/app/src/main/res/values/styles.xml b/Source/Android/app/src/main/res/values/styles.xml
index 2752458eb2..bec7fc0035 100644
--- a/Source/Android/app/src/main/res/values/styles.xml
+++ b/Source/Android/app/src/main/res/values/styles.xml
@@ -22,23 +22,19 @@
@@ -49,26 +45,39 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file