android: Convert EmulationActivity to Kotlin
This commit is contained in:
		@@ -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())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user