summaryrefslogtreecommitdiff
path: root/src/android
diff options
context:
space:
mode:
authorGravatar Charles Lombardo2023-03-07 13:19:05 -0500
committerGravatar bunnei2023-06-03 00:05:35 -0700
commit39a65f8446d9301e48b40079707e075495336356 (patch)
tree4e0646ddb39fb86e52b322ac19f0ac2a083d7fd2 /src/android
parentandroid: Use material slider in settings dialog (diff)
downloadyuzu-39a65f8446d9301e48b40079707e075495336356.tar.gz
yuzu-39a65f8446d9301e48b40079707e075495336356.tar.xz
yuzu-39a65f8446d9301e48b40079707e075495336356.zip
android: Convert EmulationActivity to Kotlin
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 @@
1package org.yuzu.yuzu_emu.activities;
2
3import android.app.Activity;
4import android.content.Intent;
5import android.content.SharedPreferences;
6import android.graphics.Rect;
7import android.os.Bundle;
8import android.os.Handler;
9import android.preference.PreferenceManager;
10import android.view.InputDevice;
11import android.view.KeyEvent;
12import android.view.LayoutInflater;
13import android.view.MotionEvent;
14import android.view.View;
15import android.view.WindowManager;
16import android.widget.SeekBar;
17import android.widget.TextView;
18
19import androidx.annotation.IntDef;
20import androidx.annotation.NonNull;
21import androidx.annotation.Nullable;
22import androidx.appcompat.app.AlertDialog;
23import androidx.appcompat.app.AppCompatActivity;
24import androidx.core.app.NotificationManagerCompat;
25import androidx.fragment.app.Fragment;
26import androidx.fragment.app.FragmentActivity;
27import androidx.fragment.app.FragmentManager;
28
29import org.yuzu.yuzu_emu.NativeLibrary;
30import org.yuzu.yuzu_emu.R;
31import org.yuzu.yuzu_emu.fragments.EmulationFragment;
32import org.yuzu.yuzu_emu.fragments.MenuFragment;
33import org.yuzu.yuzu_emu.utils.ControllerMappingHelper;
34import org.yuzu.yuzu_emu.utils.ForegroundService;
35
36import java.lang.annotation.Retention;
37
38import static java.lang.annotation.RetentionPolicy.SOURCE;
39
40public final class EmulationActivity extends AppCompatActivity {
41 private static final String BACKSTACK_NAME_MENU = "menu";
42
43 private static final String BACKSTACK_NAME_SUBMENU = "submenu";
44
45 public static final String EXTRA_SELECTED_GAME = "SelectedGame";
46 public static final String EXTRA_SELECTED_TITLE = "SelectedTitle";
47 public static final int MENU_ACTION_EDIT_CONTROLS_PLACEMENT = 0;
48 public static final int MENU_ACTION_TOGGLE_CONTROLS = 1;
49 public static final int MENU_ACTION_ADJUST_SCALE = 2;
50 public static final int MENU_ACTION_EXIT = 3;
51 public static final int MENU_ACTION_SHOW_FPS = 4;
52 public static final int MENU_ACTION_RESET_OVERLAY = 6;
53 public static final int MENU_ACTION_SHOW_OVERLAY = 7;
54 public static final int MENU_ACTION_OPEN_SETTINGS = 8;
55 private static final int EMULATION_RUNNING_NOTIFICATION = 0x1000;
56 private View mDecorView;
57 private EmulationFragment mEmulationFragment;
58 private SharedPreferences mPreferences;
59 private ControllerMappingHelper mControllerMappingHelper;
60 // TODO(bunnei): Disable notifications until we support app suspension.
61// private Intent foregroundService;
62 private boolean activityRecreated;
63 private String mSelectedTitle;
64 private String mPath;
65
66 private boolean mMenuVisible;
67
68 public static void launch(FragmentActivity activity, String path, String title) {
69 Intent launcher = new Intent(activity, EmulationActivity.class);
70
71 launcher.putExtra(EXTRA_SELECTED_GAME, path);
72 launcher.putExtra(EXTRA_SELECTED_TITLE, title);
73 activity.startActivity(launcher);
74 }
75
76 public static void tryDismissRunningNotification(Activity activity) {
77 // TODO(bunnei): Disable notifications until we support app suspension.
78// NotificationManagerCompat.from(activity).cancel(EMULATION_RUNNING_NOTIFICATION);
79 }
80
81 @Override
82 protected void onDestroy() {
83 // TODO(bunnei): Disable notifications until we support app suspension.
84// stopService(foregroundService);
85 super.onDestroy();
86 }
87
88 @Override
89 protected void onCreate(Bundle savedInstanceState) {
90 super.onCreate(savedInstanceState);
91
92 if (savedInstanceState == null) {
93 // Get params we were passed
94 Intent gameToEmulate = getIntent();
95 mPath = gameToEmulate.getStringExtra(EXTRA_SELECTED_GAME);
96 mSelectedTitle = gameToEmulate.getStringExtra(EXTRA_SELECTED_TITLE);
97 activityRecreated = false;
98 } else {
99 activityRecreated = true;
100 restoreState(savedInstanceState);
101 }
102
103 mControllerMappingHelper = new ControllerMappingHelper();
104
105 // Get a handle to the Window containing the UI.
106 mDecorView = getWindow().getDecorView();
107 mDecorView.setOnSystemUiVisibilityChangeListener(visibility ->
108 {
109 if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
110 // Go back to immersive fullscreen mode in 3s
111 Handler handler = new Handler(getMainLooper());
112 handler.postDelayed(this::enableFullscreenImmersive, 3000 /* 3s */);
113 }
114 });
115 // Set these options now so that the SurfaceView the game renders into is the right size.
116 enableFullscreenImmersive();
117
118 setTheme(R.style.YuzuEmulationBase);
119
120 setContentView(R.layout.activity_emulation);
121
122 // Find or create the EmulationFragment
123 mEmulationFragment = (EmulationFragment) getSupportFragmentManager()
124 .findFragmentById(R.id.frame_emulation_fragment);
125 if (mEmulationFragment == null) {
126 mEmulationFragment = EmulationFragment.newInstance(mPath);
127 getSupportFragmentManager().beginTransaction()
128 .add(R.id.frame_emulation_fragment, mEmulationFragment)
129 .commit();
130 }
131
132 setTitle(mSelectedTitle);
133
134 mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
135
136 // Start a foreground service to prevent the app from getting killed in the background
137 // TODO(bunnei): Disable notifications until we support app suspension.
138// foregroundService = new Intent(EmulationActivity.this, ForegroundService.class);
139// startForegroundService(foregroundService);
140 }
141
142 @Override
143 protected void onSaveInstanceState(@NonNull Bundle outState) {
144 outState.putString(EXTRA_SELECTED_GAME, mPath);
145 outState.putString(EXTRA_SELECTED_TITLE, mSelectedTitle);
146 super.onSaveInstanceState(outState);
147 }
148
149 protected void restoreState(Bundle savedInstanceState) {
150 mPath = savedInstanceState.getString(EXTRA_SELECTED_GAME);
151 mSelectedTitle = savedInstanceState.getString(EXTRA_SELECTED_TITLE);
152
153 // If an alert prompt was in progress when state was restored, retry displaying it
154 NativeLibrary.retryDisplayAlertPrompt();
155 }
156
157 @Override
158 public void onRestart() {
159 super.onRestart();
160 }
161
162 @Override
163 public void onBackPressed() {
164 toggleMenu();
165 }
166
167 private void enableFullscreenImmersive() {
168 getWindow().getAttributes().layoutInDisplayCutoutMode=
169 WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
170
171 // It would be nice to use IMMERSIVE_STICKY, but that doesn't show the toolbar.
172 mDecorView.setSystemUiVisibility(
173 View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
174 View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
175 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
176 View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
177 View.SYSTEM_UI_FLAG_FULLSCREEN |
178 View.SYSTEM_UI_FLAG_IMMERSIVE);
179 }
180
181 public void handleMenuAction(int action) {
182 switch (action) {
183 case MENU_ACTION_EXIT:
184 mEmulationFragment.stopEmulation();
185 finish();
186 break;
187 }
188 }
189
190 private void editControlsPlacement() {
191 if (mEmulationFragment.isConfiguringControls()) {
192 mEmulationFragment.stopConfiguringControls();
193 } else {
194 mEmulationFragment.startConfiguringControls();
195 }
196 }
197
198 private void adjustScale() {
199 LayoutInflater inflater = LayoutInflater.from(this);
200 View view = inflater.inflate(R.layout.dialog_seekbar, null);
201
202 final SeekBar seekbar = view.findViewById(R.id.seekbar);
203 final TextView value = view.findViewById(R.id.text_value);
204 final TextView units = view.findViewById(R.id.text_units);
205
206 seekbar.setMax(150);
207 seekbar.setProgress(mPreferences.getInt("controlScale", 50));
208 seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
209 public void onStartTrackingTouch(SeekBar seekBar) {
210 }
211
212 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
213 value.setText(String.valueOf(progress + 50));
214 }
215
216 public void onStopTrackingTouch(SeekBar seekBar) {
217 setControlScale(seekbar.getProgress());
218 }
219 });
220
221 value.setText(String.valueOf(seekbar.getProgress() + 50));
222 units.setText("%");
223
224 AlertDialog.Builder builder = new AlertDialog.Builder(this);
225 builder.setTitle(R.string.emulation_control_scale);
226 builder.setView(view);
227 final int previousProgress = seekbar.getProgress();
228 builder.setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> {
229 setControlScale(previousProgress);
230 });
231 builder.setPositiveButton(android.R.string.ok, (dialogInterface, i) ->
232 {
233 setControlScale(seekbar.getProgress());
234 });
235 builder.setNeutralButton(R.string.slider_default, (dialogInterface, i) -> {
236 setControlScale(50);
237 });
238
239 AlertDialog alertDialog = builder.create();
240 alertDialog.show();
241 }
242
243 private void setControlScale(int scale) {
244 SharedPreferences.Editor editor = mPreferences.edit();
245 editor.putInt("controlScale", scale);
246 editor.apply();
247 mEmulationFragment.refreshInputOverlay();
248 }
249
250 private void resetOverlay() {
251 new AlertDialog.Builder(this)
252 .setTitle(getString(R.string.emulation_touch_overlay_reset))
253 .setPositiveButton(android.R.string.yes, (dialogInterface, i) -> mEmulationFragment.resetInputOverlay())
254 .setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> {
255 })
256 .create()
257 .show();
258 }
259
260 private static boolean areCoordinatesOutside(@Nullable View view, float x, float y)
261 {
262 if (view == null)
263 {
264 return true;
265 }
266
267 Rect viewBounds = new Rect();
268 view.getGlobalVisibleRect(viewBounds);
269 return !viewBounds.contains(Math.round(x), Math.round(y));
270 }
271
272 @Override
273 public boolean dispatchTouchEvent(MotionEvent event)
274 {
275 if (event.getActionMasked() == MotionEvent.ACTION_DOWN)
276 {
277 boolean anyMenuClosed = false;
278
279 Fragment submenu = getSupportFragmentManager().findFragmentById(R.id.frame_submenu);
280 if (submenu != null && areCoordinatesOutside(submenu.getView(), event.getX(), event.getY()))
281 {
282 closeSubmenu();
283 submenu = null;
284 anyMenuClosed = true;
285 }
286
287 if (submenu == null)
288 {
289 Fragment menu = getSupportFragmentManager().findFragmentById(R.id.frame_menu);
290 if (menu != null && areCoordinatesOutside(menu.getView(), event.getX(), event.getY()))
291 {
292 closeMenu();
293 anyMenuClosed = true;
294 }
295 }
296
297 if (anyMenuClosed)
298 {
299 return true;
300 }
301 }
302
303 return super.dispatchTouchEvent(event);
304 }
305
306 public boolean isActivityRecreated() {
307 return activityRecreated;
308 }
309
310 @Retention(SOURCE)
311 @IntDef({MENU_ACTION_EDIT_CONTROLS_PLACEMENT, MENU_ACTION_TOGGLE_CONTROLS, MENU_ACTION_ADJUST_SCALE,
312 MENU_ACTION_EXIT, MENU_ACTION_SHOW_FPS, MENU_ACTION_RESET_OVERLAY, MENU_ACTION_SHOW_OVERLAY, MENU_ACTION_OPEN_SETTINGS})
313 public @interface MenuAction {
314 }
315
316 private boolean closeSubmenu()
317 {
318 return getSupportFragmentManager().popBackStackImmediate(BACKSTACK_NAME_SUBMENU,
319 FragmentManager.POP_BACK_STACK_INCLUSIVE);
320 }
321
322 private boolean closeMenu()
323 {
324 mMenuVisible = false;
325 return getSupportFragmentManager().popBackStackImmediate(BACKSTACK_NAME_MENU,
326 FragmentManager.POP_BACK_STACK_INCLUSIVE);
327 }
328
329 private void toggleMenu()
330 {
331 if (!closeMenu()) {
332 // Removing the menu failed, so that means it wasn't visible. Add it.
333 Fragment fragment = MenuFragment.newInstance();
334 getSupportFragmentManager().beginTransaction()
335 .setCustomAnimations(
336 R.animator.menu_slide_in_from_start,
337 R.animator.menu_slide_out_to_start,
338 R.animator.menu_slide_in_from_start,
339 R.animator.menu_slide_out_to_start)
340 .add(R.id.frame_menu, fragment)
341 .addToBackStack(BACKSTACK_NAME_MENU)
342 .commit();
343 mMenuVisible = true;
344 }
345 }
346
347}
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 @@
1package org.yuzu.yuzu_emu.activities
2
3import android.app.Activity
4import android.content.DialogInterface
5import android.content.Intent
6import android.graphics.Rect
7import android.os.Bundle
8import android.view.LayoutInflater
9import android.view.MotionEvent
10import android.view.View
11import android.view.WindowManager
12import android.widget.TextView
13import androidx.activity.OnBackPressedCallback
14import androidx.annotation.IntDef
15import androidx.appcompat.app.AppCompatActivity
16import androidx.fragment.app.Fragment
17import androidx.fragment.app.FragmentActivity
18import androidx.fragment.app.FragmentManager
19import androidx.preference.PreferenceManager
20import com.google.android.material.dialog.MaterialAlertDialogBuilder
21import com.google.android.material.slider.Slider
22import com.google.android.material.slider.Slider.OnChangeListener
23import org.yuzu.yuzu_emu.NativeLibrary
24import org.yuzu.yuzu_emu.R
25import org.yuzu.yuzu_emu.features.settings.model.Settings
26import org.yuzu.yuzu_emu.fragments.EmulationFragment
27import org.yuzu.yuzu_emu.fragments.MenuFragment
28import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
29import kotlin.math.roundToInt
30
31open class EmulationActivity : AppCompatActivity() {
32 private var controllerMappingHelper: ControllerMappingHelper? = null
33
34 // TODO(bunnei): Disable notifications until we support app suspension.
35 //private Intent foregroundService;
36
37 var isActivityRecreated = false
38 private var selectedTitle: String? = null
39 private var path: String? = null
40 private var menuVisible = false
41 private var emulationFragment: EmulationFragment? = null
42
43 override fun onDestroy() {
44 // TODO(bunnei): Disable notifications until we support app suspension.
45 //stopService(foregroundService);
46 super.onDestroy()
47 }
48
49 override fun onCreate(savedInstanceState: Bundle?) {
50 super.onCreate(savedInstanceState)
51 if (savedInstanceState == null) {
52 // Get params we were passed
53 val gameToEmulate = intent
54 path = gameToEmulate.getStringExtra(EXTRA_SELECTED_GAME)
55 selectedTitle = gameToEmulate.getStringExtra(EXTRA_SELECTED_TITLE)
56 isActivityRecreated = false
57 } else {
58 isActivityRecreated = true
59 restoreState(savedInstanceState)
60 }
61 controllerMappingHelper = ControllerMappingHelper()
62
63 // Set these options now so that the SurfaceView the game renders into is the right size.
64 enableFullscreenImmersive()
65
66 setContentView(R.layout.activity_emulation)
67
68 // Find or create the EmulationFragment
69 var emulationFragment =
70 supportFragmentManager.findFragmentById(R.id.frame_emulation_fragment) as EmulationFragment?
71 if (emulationFragment == null) {
72 emulationFragment = EmulationFragment.newInstance(path)
73 supportFragmentManager.beginTransaction()
74 .add(R.id.frame_emulation_fragment, emulationFragment)
75 .commit()
76 }
77 title = selectedTitle
78
79 // Start a foreground service to prevent the app from getting killed in the background
80 // TODO(bunnei): Disable notifications until we support app suspension.
81 //foregroundService = new Intent(EmulationActivity.this, ForegroundService.class);
82 //startForegroundService(foregroundService);
83
84 onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
85 override fun handleOnBackPressed() {
86 toggleMenu()
87 }
88 })
89 }
90
91 override fun onSaveInstanceState(outState: Bundle) {
92 outState.putString(EXTRA_SELECTED_GAME, path)
93 outState.putString(EXTRA_SELECTED_TITLE, selectedTitle)
94 super.onSaveInstanceState(outState)
95 }
96
97 private fun restoreState(savedInstanceState: Bundle) {
98 path = savedInstanceState.getString(EXTRA_SELECTED_GAME)
99 selectedTitle = savedInstanceState.getString(EXTRA_SELECTED_TITLE)
100
101 // If an alert prompt was in progress when state was restored, retry displaying it
102 NativeLibrary.retryDisplayAlertPrompt()
103 }
104
105 private fun enableFullscreenImmersive() {
106 window.attributes.layoutInDisplayCutoutMode =
107 WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
108
109 // It would be nice to use IMMERSIVE_STICKY, but that doesn't show the toolbar.
110 window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
111 View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
112 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
113 View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
114 View.SYSTEM_UI_FLAG_FULLSCREEN or
115 View.SYSTEM_UI_FLAG_IMMERSIVE
116 }
117
118 fun handleMenuAction(action: Int) {
119 when (action) {
120 MENU_ACTION_EXIT -> {
121 emulationFragment!!.stopEmulation()
122 finish()
123 }
124 }
125 }
126
127 private fun editControlsPlacement() {
128 if (emulationFragment!!.isConfiguringControls) {
129 emulationFragment!!.stopConfiguringControls()
130 } else {
131 emulationFragment!!.startConfiguringControls()
132 }
133 }
134
135 private fun adjustScale() {
136 val inflater = LayoutInflater.from(this)
137 val view = inflater.inflate(R.layout.dialog_slider, null)
138 val slider = view.findViewById<Slider>(R.id.slider)
139 val textValue = view.findViewById<TextView>(R.id.text_value)
140 val units = view.findViewById<TextView>(R.id.text_units)
141
142 slider.valueTo = 150F
143 slider.value = PreferenceManager.getDefaultSharedPreferences(applicationContext)
144 .getInt(Settings.PREF_CONTROL_SCALE, 50).toFloat()
145 slider.addOnChangeListener(OnChangeListener { _, value, _ ->
146 textValue.text = value.toString()
147 setControlScale(value.toInt())
148 })
149 textValue.text = slider.value.toString()
150 units.text = "%"
151 MaterialAlertDialogBuilder(this)
152 .setTitle(R.string.emulation_control_scale)
153 .setView(view)
154 .setNegativeButton(android.R.string.cancel, null)
155 .setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
156 setControlScale(slider.value.toInt())
157 }
158 .setNeutralButton(R.string.slider_default) { _: DialogInterface?, _: Int ->
159 setControlScale(50)
160 }
161 .show()
162 }
163
164 private fun setControlScale(scale: Int) {
165 PreferenceManager.getDefaultSharedPreferences(applicationContext).edit()
166 .putInt(Settings.PREF_CONTROL_SCALE, scale)
167 .apply()
168 emulationFragment!!.refreshInputOverlay()
169 }
170
171 private fun resetOverlay() {
172 MaterialAlertDialogBuilder(this)
173 .setTitle(getString(R.string.emulation_touch_overlay_reset))
174 .setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int -> emulationFragment!!.resetInputOverlay() }
175 .setNegativeButton(android.R.string.cancel, null)
176 .create()
177 .show()
178 }
179
180 override fun dispatchTouchEvent(event: MotionEvent): Boolean {
181 if (event.actionMasked == MotionEvent.ACTION_DOWN) {
182 var anyMenuClosed = false
183 var submenu = supportFragmentManager.findFragmentById(R.id.frame_submenu)
184 if (submenu != null && areCoordinatesOutside(submenu.view, event.x, event.y)) {
185 closeSubmenu()
186 submenu = null
187 anyMenuClosed = true
188 }
189 if (submenu == null) {
190 val menu = supportFragmentManager.findFragmentById(R.id.frame_menu)
191 if (menu != null && areCoordinatesOutside(menu.view, event.x, event.y)) {
192 closeMenu()
193 anyMenuClosed = true
194 }
195 }
196 if (anyMenuClosed) {
197 return true
198 }
199 }
200 return super.dispatchTouchEvent(event)
201 }
202
203 @Retention(AnnotationRetention.SOURCE)
204 @IntDef(
205 MENU_ACTION_EDIT_CONTROLS_PLACEMENT,
206 MENU_ACTION_TOGGLE_CONTROLS,
207 MENU_ACTION_ADJUST_SCALE,
208 MENU_ACTION_EXIT,
209 MENU_ACTION_SHOW_FPS,
210 MENU_ACTION_RESET_OVERLAY,
211 MENU_ACTION_SHOW_OVERLAY,
212 MENU_ACTION_OPEN_SETTINGS
213 )
214 annotation class MenuAction
215
216 private fun closeSubmenu(): Boolean {
217 return supportFragmentManager.popBackStackImmediate(
218 BACKSTACK_NAME_SUBMENU,
219 FragmentManager.POP_BACK_STACK_INCLUSIVE
220 )
221 }
222
223 private fun closeMenu(): Boolean {
224 menuVisible = false
225 return supportFragmentManager.popBackStackImmediate(
226 BACKSTACK_NAME_MENU,
227 FragmentManager.POP_BACK_STACK_INCLUSIVE
228 )
229 }
230
231 private fun toggleMenu() {
232 if (!closeMenu()) {
233 val fragment: Fragment = MenuFragment.newInstance()
234 supportFragmentManager.beginTransaction()
235 .setCustomAnimations(
236 R.animator.menu_slide_in_from_start,
237 R.animator.menu_slide_out_to_start,
238 R.animator.menu_slide_in_from_start,
239 R.animator.menu_slide_out_to_start
240 )
241 .add(R.id.frame_menu, fragment)
242 .addToBackStack(BACKSTACK_NAME_MENU)
243 .commit()
244 menuVisible = true
245 }
246 }
247
248 companion object {
249 private const val BACKSTACK_NAME_MENU = "menu"
250 private const val BACKSTACK_NAME_SUBMENU = "submenu"
251 const val EXTRA_SELECTED_GAME = "SelectedGame"
252 const val EXTRA_SELECTED_TITLE = "SelectedTitle"
253 const val MENU_ACTION_EDIT_CONTROLS_PLACEMENT = 0
254 const val MENU_ACTION_TOGGLE_CONTROLS = 1
255 const val MENU_ACTION_ADJUST_SCALE = 2
256 const val MENU_ACTION_EXIT = 3
257 const val MENU_ACTION_SHOW_FPS = 4
258 const val MENU_ACTION_RESET_OVERLAY = 6
259 const val MENU_ACTION_SHOW_OVERLAY = 7
260 const val MENU_ACTION_OPEN_SETTINGS = 8
261 private const val EMULATION_RUNNING_NOTIFICATION = 0x1000
262
263 @JvmStatic
264 fun launch(activity: FragmentActivity, path: String?, title: String?) {
265 val launcher = Intent(activity, EmulationActivity::class.java)
266 launcher.putExtra(EXTRA_SELECTED_GAME, path)
267 launcher.putExtra(EXTRA_SELECTED_TITLE, title)
268 activity.startActivity(launcher)
269 }
270
271 @JvmStatic
272 fun tryDismissRunningNotification(activity: Activity?) {
273 // TODO(bunnei): Disable notifications until we support app suspension.
274 //NotificationManagerCompat.from(activity).cancel(EMULATION_RUNNING_NOTIFICATION);
275 }
276
277 private fun areCoordinatesOutside(view: View?, x: Float, y: Float): Boolean {
278 if (view == null) {
279 return true
280 }
281 val viewBounds = Rect()
282 view.getGlobalVisibleRect(viewBounds)
283 return !viewBounds.contains(x.roundToInt(), y.roundToInt())
284 }
285 }
286}