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