summaryrefslogtreecommitdiffstats
path: root/src/android
diff options
context:
space:
mode:
authorCharles Lombardo <clombardo169@gmail.com>2023-03-07 19:19:05 +0100
committerbunnei <bunneidev@gmail.com>2023-06-03 09:05:35 +0200
commit39a65f8446d9301e48b40079707e075495336356 (patch)
tree4e0646ddb39fb86e52b322ac19f0ac2a083d7fd2 /src/android
parentandroid: Use material slider in settings dialog (diff)
downloadyuzu-39a65f8446d9301e48b40079707e075495336356.tar
yuzu-39a65f8446d9301e48b40079707e075495336356.tar.gz
yuzu-39a65f8446d9301e48b40079707e075495336356.tar.bz2
yuzu-39a65f8446d9301e48b40079707e075495336356.tar.lz
yuzu-39a65f8446d9301e48b40079707e075495336356.tar.xz
yuzu-39a65f8446d9301e48b40079707e075495336356.tar.zst
yuzu-39a65f8446d9301e48b40079707e075495336356.zip
Diffstat (limited to 'src/android')
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.java347
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt286
2 files changed, 286 insertions, 347 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.java
deleted file mode 100644
index 343bc032b..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.java
+++ /dev/null
@@ -1,347 +0,0 @@
-package org.yuzu.yuzu_emu.activities;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.Handler;
-import android.preference.PreferenceManager;
-import android.view.InputDevice;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.SeekBar;
-import android.widget.TextView;
-
-import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.AlertDialog;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.core.app.NotificationManagerCompat;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentActivity;
-import androidx.fragment.app.FragmentManager;
-
-import org.yuzu.yuzu_emu.NativeLibrary;
-import org.yuzu.yuzu_emu.R;
-import org.yuzu.yuzu_emu.fragments.EmulationFragment;
-import org.yuzu.yuzu_emu.fragments.MenuFragment;
-import org.yuzu.yuzu_emu.utils.ControllerMappingHelper;
-import org.yuzu.yuzu_emu.utils.ForegroundService;
-
-import java.lang.annotation.Retention;
-
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-public final class EmulationActivity extends AppCompatActivity {
- private static final String BACKSTACK_NAME_MENU = "menu";
-
- private static final String BACKSTACK_NAME_SUBMENU = "submenu";
-
- public static final String EXTRA_SELECTED_GAME = "SelectedGame";
- public static final String EXTRA_SELECTED_TITLE = "SelectedTitle";
- public static final int MENU_ACTION_EDIT_CONTROLS_PLACEMENT = 0;
- public static final int MENU_ACTION_TOGGLE_CONTROLS = 1;
- public static final int MENU_ACTION_ADJUST_SCALE = 2;
- public static final int MENU_ACTION_EXIT = 3;
- public static final int MENU_ACTION_SHOW_FPS = 4;
- public static final int MENU_ACTION_RESET_OVERLAY = 6;
- public static final int MENU_ACTION_SHOW_OVERLAY = 7;
- public static final int MENU_ACTION_OPEN_SETTINGS = 8;
- private static final int EMULATION_RUNNING_NOTIFICATION = 0x1000;
- private View mDecorView;
- private EmulationFragment mEmulationFragment;
- private SharedPreferences mPreferences;
- private ControllerMappingHelper mControllerMappingHelper;
- // TODO(bunnei): Disable notifications until we support app suspension.
-// private Intent foregroundService;
- private boolean activityRecreated;
- private String mSelectedTitle;
- private String mPath;
-
- private boolean mMenuVisible;
-
- public static void launch(FragmentActivity activity, String path, String title) {
- Intent launcher = new Intent(activity, EmulationActivity.class);
-
- launcher.putExtra(EXTRA_SELECTED_GAME, path);
- launcher.putExtra(EXTRA_SELECTED_TITLE, title);
- activity.startActivity(launcher);
- }
-
- public static void tryDismissRunningNotification(Activity activity) {
- // TODO(bunnei): Disable notifications until we support app suspension.
-// NotificationManagerCompat.from(activity).cancel(EMULATION_RUNNING_NOTIFICATION);
- }
-
- @Override
- protected void onDestroy() {
- // TODO(bunnei): Disable notifications until we support app suspension.
-// stopService(foregroundService);
- super.onDestroy();
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- if (savedInstanceState == null) {
- // Get params we were passed
- Intent gameToEmulate = getIntent();
- mPath = gameToEmulate.getStringExtra(EXTRA_SELECTED_GAME);
- mSelectedTitle = gameToEmulate.getStringExtra(EXTRA_SELECTED_TITLE);
- activityRecreated = false;
- } else {
- activityRecreated = true;
- restoreState(savedInstanceState);
- }
-
- mControllerMappingHelper = new ControllerMappingHelper();
-
- // Get a handle to the Window containing the UI.
- mDecorView = getWindow().getDecorView();
- mDecorView.setOnSystemUiVisibilityChangeListener(visibility ->
- {
- if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
- // Go back to immersive fullscreen mode in 3s
- Handler handler = new Handler(getMainLooper());
- handler.postDelayed(this::enableFullscreenImmersive, 3000 /* 3s */);
- }
- });
- // Set these options now so that the SurfaceView the game renders into is the right size.
- enableFullscreenImmersive();
-
- setTheme(R.style.YuzuEmulationBase);
-
- setContentView(R.layout.activity_emulation);
-
- // Find or create the EmulationFragment
- mEmulationFragment = (EmulationFragment) getSupportFragmentManager()
- .findFragmentById(R.id.frame_emulation_fragment);
- if (mEmulationFragment == null) {
- mEmulationFragment = EmulationFragment.newInstance(mPath);
- getSupportFragmentManager().beginTransaction()
- .add(R.id.frame_emulation_fragment, mEmulationFragment)
- .commit();
- }
-
- setTitle(mSelectedTitle);
-
- mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
-
- // Start a foreground service to prevent the app from getting killed in the background
- // TODO(bunnei): Disable notifications until we support app suspension.
-// foregroundService = new Intent(EmulationActivity.this, ForegroundService.class);
-// startForegroundService(foregroundService);
- }
-
- @Override
- protected void onSaveInstanceState(@NonNull Bundle outState) {
- outState.putString(EXTRA_SELECTED_GAME, mPath);
- outState.putString(EXTRA_SELECTED_TITLE, mSelectedTitle);
- super.onSaveInstanceState(outState);
- }
-
- protected void restoreState(Bundle savedInstanceState) {
- mPath = savedInstanceState.getString(EXTRA_SELECTED_GAME);
- mSelectedTitle = savedInstanceState.getString(EXTRA_SELECTED_TITLE);
-
- // If an alert prompt was in progress when state was restored, retry displaying it
- NativeLibrary.retryDisplayAlertPrompt();
- }
-
- @Override
- public void onRestart() {
- super.onRestart();
- }
-
- @Override
- public void onBackPressed() {
- toggleMenu();
- }
-
- private void enableFullscreenImmersive() {
- getWindow().getAttributes().layoutInDisplayCutoutMode=
- WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
-
- // It would be nice to use IMMERSIVE_STICKY, but that doesn't show the toolbar.
- 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);
- }
-
- public void handleMenuAction(int action) {
- switch (action) {
- case MENU_ACTION_EXIT:
- mEmulationFragment.stopEmulation();
- finish();
- break;
- }
- }
-
- private void editControlsPlacement() {
- if (mEmulationFragment.isConfiguringControls()) {
- mEmulationFragment.stopConfiguringControls();
- } else {
- mEmulationFragment.startConfiguringControls();
- }
- }
-
- private void adjustScale() {
- LayoutInflater inflater = LayoutInflater.from(this);
- View view = inflater.inflate(R.layout.dialog_seekbar, null);
-
- final SeekBar seekbar = view.findViewById(R.id.seekbar);
- final TextView value = view.findViewById(R.id.text_value);
- final TextView units = view.findViewById(R.id.text_units);
-
- seekbar.setMax(150);
- seekbar.setProgress(mPreferences.getInt("controlScale", 50));
- seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
- public void onStartTrackingTouch(SeekBar seekBar) {
- }
-
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- value.setText(String.valueOf(progress + 50));
- }
-
- public void onStopTrackingTouch(SeekBar seekBar) {
- setControlScale(seekbar.getProgress());
- }
- });
-
- value.setText(String.valueOf(seekbar.getProgress() + 50));
- units.setText("%");
-
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.emulation_control_scale);
- builder.setView(view);
- final int previousProgress = seekbar.getProgress();
- builder.setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> {
- setControlScale(previousProgress);
- });
- builder.setPositiveButton(android.R.string.ok, (dialogInterface, i) ->
- {
- setControlScale(seekbar.getProgress());
- });
- builder.setNeutralButton(R.string.slider_default, (dialogInterface, i) -> {
- setControlScale(50);
- });
-
- AlertDialog alertDialog = builder.create();
- alertDialog.show();
- }
-
- private void setControlScale(int scale) {
- SharedPreferences.Editor editor = mPreferences.edit();
- editor.putInt("controlScale", scale);
- editor.apply();
- mEmulationFragment.refreshInputOverlay();
- }
-
- private void resetOverlay() {
- new AlertDialog.Builder(this)
- .setTitle(getString(R.string.emulation_touch_overlay_reset))
- .setPositiveButton(android.R.string.yes, (dialogInterface, i) -> mEmulationFragment.resetInputOverlay())
- .setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> {
- })
- .create()
- .show();
- }
-
- private static boolean areCoordinatesOutside(@Nullable View view, float x, float y)
- {
- if (view == null)
- {
- return true;
- }
-
- Rect viewBounds = new Rect();
- view.getGlobalVisibleRect(viewBounds);
- return !viewBounds.contains(Math.round(x), Math.round(y));
- }
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent event)
- {
- if (event.getActionMasked() == MotionEvent.ACTION_DOWN)
- {
- boolean anyMenuClosed = false;
-
- Fragment submenu = getSupportFragmentManager().findFragmentById(R.id.frame_submenu);
- if (submenu != null && areCoordinatesOutside(submenu.getView(), event.getX(), event.getY()))
- {
- closeSubmenu();
- submenu = null;
- anyMenuClosed = true;
- }
-
- if (submenu == null)
- {
- Fragment menu = getSupportFragmentManager().findFragmentById(R.id.frame_menu);
- if (menu != null && areCoordinatesOutside(menu.getView(), event.getX(), event.getY()))
- {
- closeMenu();
- anyMenuClosed = true;
- }
- }
-
- if (anyMenuClosed)
- {
- return true;
- }
- }
-
- return super.dispatchTouchEvent(event);
- }
-
- public boolean isActivityRecreated() {
- return activityRecreated;
- }
-
- @Retention(SOURCE)
- @IntDef({MENU_ACTION_EDIT_CONTROLS_PLACEMENT, MENU_ACTION_TOGGLE_CONTROLS, MENU_ACTION_ADJUST_SCALE,
- MENU_ACTION_EXIT, MENU_ACTION_SHOW_FPS, MENU_ACTION_RESET_OVERLAY, MENU_ACTION_SHOW_OVERLAY, MENU_ACTION_OPEN_SETTINGS})
- public @interface MenuAction {
- }
-
- private boolean closeSubmenu()
- {
- return getSupportFragmentManager().popBackStackImmediate(BACKSTACK_NAME_SUBMENU,
- FragmentManager.POP_BACK_STACK_INCLUSIVE);
- }
-
- private boolean closeMenu()
- {
- mMenuVisible = false;
- return getSupportFragmentManager().popBackStackImmediate(BACKSTACK_NAME_MENU,
- FragmentManager.POP_BACK_STACK_INCLUSIVE);
- }
-
- private void toggleMenu()
- {
- if (!closeMenu()) {
- // Removing the menu failed, so that means it wasn't visible. Add it.
- Fragment fragment = MenuFragment.newInstance();
- getSupportFragmentManager().beginTransaction()
- .setCustomAnimations(
- R.animator.menu_slide_in_from_start,
- R.animator.menu_slide_out_to_start,
- R.animator.menu_slide_in_from_start,
- R.animator.menu_slide_out_to_start)
- .add(R.id.frame_menu, fragment)
- .addToBackStack(BACKSTACK_NAME_MENU)
- .commit();
- mMenuVisible = true;
- }
- }
-
-}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
new file mode 100644
index 000000000..bd71a3653
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
@@ -0,0 +1,286 @@
+package org.yuzu.yuzu_emu.activities
+
+import android.app.Activity
+import android.content.DialogInterface
+import android.content.Intent
+import android.graphics.Rect
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.MotionEvent
+import android.view.View
+import android.view.WindowManager
+import android.widget.TextView
+import androidx.activity.OnBackPressedCallback
+import androidx.annotation.IntDef
+import androidx.appcompat.app.AppCompatActivity
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentActivity
+import androidx.fragment.app.FragmentManager
+import androidx.preference.PreferenceManager
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.google.android.material.slider.Slider
+import com.google.android.material.slider.Slider.OnChangeListener
+import org.yuzu.yuzu_emu.NativeLibrary
+import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.features.settings.model.Settings
+import org.yuzu.yuzu_emu.fragments.EmulationFragment
+import org.yuzu.yuzu_emu.fragments.MenuFragment
+import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
+import kotlin.math.roundToInt
+
+open class EmulationActivity : AppCompatActivity() {
+ private var controllerMappingHelper: ControllerMappingHelper? = null
+
+ // TODO(bunnei): Disable notifications until we support app suspension.
+ //private Intent foregroundService;
+
+ var isActivityRecreated = false
+ private var selectedTitle: String? = null
+ private var path: String? = null
+ private var menuVisible = false
+ private var emulationFragment: EmulationFragment? = null
+
+ override fun onDestroy() {
+ // TODO(bunnei): Disable notifications until we support app suspension.
+ //stopService(foregroundService);
+ super.onDestroy()
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ if (savedInstanceState == null) {
+ // Get params we were passed
+ val gameToEmulate = intent
+ path = gameToEmulate.getStringExtra(EXTRA_SELECTED_GAME)
+ selectedTitle = gameToEmulate.getStringExtra(EXTRA_SELECTED_TITLE)
+ isActivityRecreated = false
+ } else {
+ isActivityRecreated = true
+ restoreState(savedInstanceState)
+ }
+ controllerMappingHelper = ControllerMappingHelper()
+
+ // Set these options now so that the SurfaceView the game renders into is the right size.
+ enableFullscreenImmersive()
+
+ setContentView(R.layout.activity_emulation)
+
+ // Find or create the EmulationFragment
+ var emulationFragment =
+ supportFragmentManager.findFragmentById(R.id.frame_emulation_fragment) as EmulationFragment?
+ if (emulationFragment == null) {
+ emulationFragment = EmulationFragment.newInstance(path)
+ supportFragmentManager.beginTransaction()
+ .add(R.id.frame_emulation_fragment, emulationFragment)
+ .commit()
+ }
+ title = selectedTitle
+
+ // Start a foreground service to prevent the app from getting killed in the background
+ // TODO(bunnei): Disable notifications until we support app suspension.
+ //foregroundService = new Intent(EmulationActivity.this, ForegroundService.class);
+ //startForegroundService(foregroundService);
+
+ onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
+ override fun handleOnBackPressed() {
+ toggleMenu()
+ }
+ })
+ }
+
+ override fun onSaveInstanceState(outState: Bundle) {
+ outState.putString(EXTRA_SELECTED_GAME, path)
+ outState.putString(EXTRA_SELECTED_TITLE, selectedTitle)
+ super.onSaveInstanceState(outState)
+ }
+
+ private fun restoreState(savedInstanceState: Bundle) {
+ path = savedInstanceState.getString(EXTRA_SELECTED_GAME)
+ selectedTitle = savedInstanceState.getString(EXTRA_SELECTED_TITLE)
+
+ // If an alert prompt was in progress when state was restored, retry displaying it
+ NativeLibrary.retryDisplayAlertPrompt()
+ }
+
+ private fun enableFullscreenImmersive() {
+ window.attributes.layoutInDisplayCutoutMode =
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
+
+ // It would be nice to use IMMERSIVE_STICKY, but that doesn't show the toolbar.
+ window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
+ View.SYSTEM_UI_FLAG_FULLSCREEN or
+ View.SYSTEM_UI_FLAG_IMMERSIVE
+ }
+
+ fun handleMenuAction(action: Int) {
+ when (action) {
+ MENU_ACTION_EXIT -> {
+ emulationFragment!!.stopEmulation()
+ finish()
+ }
+ }
+ }
+
+ private fun editControlsPlacement() {
+ if (emulationFragment!!.isConfiguringControls) {
+ emulationFragment!!.stopConfiguringControls()
+ } else {
+ emulationFragment!!.startConfiguringControls()
+ }
+ }
+
+ private fun adjustScale() {
+ val inflater = LayoutInflater.from(this)
+ val view = inflater.inflate(R.layout.dialog_slider, null)
+ val slider = view.findViewById<Slider>(R.id.slider)
+ val textValue = view.findViewById<TextView>(R.id.text_value)
+ val units = view.findViewById<TextView>(R.id.text_units)
+
+ slider.valueTo = 150F
+ slider.value = PreferenceManager.getDefaultSharedPreferences(applicationContext)
+ .getInt(Settings.PREF_CONTROL_SCALE, 50).toFloat()
+ slider.addOnChangeListener(OnChangeListener { _, value, _ ->
+ textValue.text = value.toString()
+ setControlScale(value.toInt())
+ })
+ textValue.text = slider.value.toString()
+ units.text = "%"
+ MaterialAlertDialogBuilder(this)
+ .setTitle(R.string.emulation_control_scale)
+ .setView(view)
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
+ setControlScale(slider.value.toInt())
+ }
+ .setNeutralButton(R.string.slider_default) { _: DialogInterface?, _: Int ->
+ setControlScale(50)
+ }
+ .show()
+ }
+
+ private fun setControlScale(scale: Int) {
+ PreferenceManager.getDefaultSharedPreferences(applicationContext).edit()
+ .putInt(Settings.PREF_CONTROL_SCALE, scale)
+ .apply()
+ emulationFragment!!.refreshInputOverlay()
+ }
+
+ private fun resetOverlay() {
+ MaterialAlertDialogBuilder(this)
+ .setTitle(getString(R.string.emulation_touch_overlay_reset))
+ .setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int -> emulationFragment!!.resetInputOverlay() }
+ .setNegativeButton(android.R.string.cancel, null)
+ .create()
+ .show()
+ }
+
+ override fun dispatchTouchEvent(event: MotionEvent): Boolean {
+ if (event.actionMasked == MotionEvent.ACTION_DOWN) {
+ var anyMenuClosed = false
+ var submenu = supportFragmentManager.findFragmentById(R.id.frame_submenu)
+ if (submenu != null && areCoordinatesOutside(submenu.view, event.x, event.y)) {
+ closeSubmenu()
+ submenu = null
+ anyMenuClosed = true
+ }
+ if (submenu == null) {
+ val menu = supportFragmentManager.findFragmentById(R.id.frame_menu)
+ if (menu != null && areCoordinatesOutside(menu.view, event.x, event.y)) {
+ closeMenu()
+ anyMenuClosed = true
+ }
+ }
+ if (anyMenuClosed) {
+ return true
+ }
+ }
+ return super.dispatchTouchEvent(event)
+ }
+
+ @Retention(AnnotationRetention.SOURCE)
+ @IntDef(
+ MENU_ACTION_EDIT_CONTROLS_PLACEMENT,
+ MENU_ACTION_TOGGLE_CONTROLS,
+ MENU_ACTION_ADJUST_SCALE,
+ MENU_ACTION_EXIT,
+ MENU_ACTION_SHOW_FPS,
+ MENU_ACTION_RESET_OVERLAY,
+ MENU_ACTION_SHOW_OVERLAY,
+ MENU_ACTION_OPEN_SETTINGS
+ )
+ annotation class MenuAction
+
+ private fun closeSubmenu(): Boolean {
+ return supportFragmentManager.popBackStackImmediate(
+ BACKSTACK_NAME_SUBMENU,
+ FragmentManager.POP_BACK_STACK_INCLUSIVE
+ )
+ }
+
+ private fun closeMenu(): Boolean {
+ menuVisible = false
+ return supportFragmentManager.popBackStackImmediate(
+ BACKSTACK_NAME_MENU,
+ FragmentManager.POP_BACK_STACK_INCLUSIVE
+ )
+ }
+
+ private fun toggleMenu() {
+ if (!closeMenu()) {
+ val fragment: Fragment = MenuFragment.newInstance()
+ supportFragmentManager.beginTransaction()
+ .setCustomAnimations(
+ R.animator.menu_slide_in_from_start,
+ R.animator.menu_slide_out_to_start,
+ R.animator.menu_slide_in_from_start,
+ R.animator.menu_slide_out_to_start
+ )
+ .add(R.id.frame_menu, fragment)
+ .addToBackStack(BACKSTACK_NAME_MENU)
+ .commit()
+ menuVisible = true
+ }
+ }
+
+ companion object {
+ private const val BACKSTACK_NAME_MENU = "menu"
+ private const val BACKSTACK_NAME_SUBMENU = "submenu"
+ const val EXTRA_SELECTED_GAME = "SelectedGame"
+ const val EXTRA_SELECTED_TITLE = "SelectedTitle"
+ const val MENU_ACTION_EDIT_CONTROLS_PLACEMENT = 0
+ const val MENU_ACTION_TOGGLE_CONTROLS = 1
+ const val MENU_ACTION_ADJUST_SCALE = 2
+ const val MENU_ACTION_EXIT = 3
+ const val MENU_ACTION_SHOW_FPS = 4
+ const val MENU_ACTION_RESET_OVERLAY = 6
+ const val MENU_ACTION_SHOW_OVERLAY = 7
+ const val MENU_ACTION_OPEN_SETTINGS = 8
+ private const val EMULATION_RUNNING_NOTIFICATION = 0x1000
+
+ @JvmStatic
+ fun launch(activity: FragmentActivity, path: String?, title: String?) {
+ val launcher = Intent(activity, EmulationActivity::class.java)
+ launcher.putExtra(EXTRA_SELECTED_GAME, path)
+ launcher.putExtra(EXTRA_SELECTED_TITLE, title)
+ activity.startActivity(launcher)
+ }
+
+ @JvmStatic
+ fun tryDismissRunningNotification(activity: Activity?) {
+ // TODO(bunnei): Disable notifications until we support app suspension.
+ //NotificationManagerCompat.from(activity).cancel(EMULATION_RUNNING_NOTIFICATION);
+ }
+
+ private fun areCoordinatesOutside(view: View?, x: Float, y: Float): Boolean {
+ if (view == null) {
+ return true
+ }
+ val viewBounds = Rect()
+ view.getGlobalVisibleRect(viewBounds)
+ return !viewBounds.contains(x.roundToInt(), y.roundToInt())
+ }
+ }
+}