mirror of
https://github.com/yuzu-emu/yuzu.git
synced 2024-07-04 23:31:19 +01:00
android: Convert EmulationActivity to Kotlin
This commit is contained in:
parent
7cd72a7c6d
commit
39a65f8446
2 changed files with 286 additions and 347 deletions
|
@ -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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue