committed by
bunnei
2 changed files with 286 additions and 347 deletions
-
347src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.java
-
286src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
@ -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; |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -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()) |
|||
} |
|||
} |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue