Merge pull request #10726 from t895/emulation-nav-component
android: Adapt EmulationActivity to navigation component
This commit is contained in:
		@@ -9,6 +9,7 @@ plugins {
 | 
			
		||||
    id("org.jetbrains.kotlin.android")
 | 
			
		||||
    id("kotlin-parcelize")
 | 
			
		||||
    kotlin("plugin.serialization") version "1.8.21"
 | 
			
		||||
    id("androidx.navigation.safeargs.kotlin")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 
 | 
			
		||||
@@ -53,7 +53,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name="org.yuzu.yuzu_emu.activities.EmulationActivity"
 | 
			
		||||
            android:theme="@style/Theme.Yuzu.Main"
 | 
			
		||||
            android:launchMode="singleTop"
 | 
			
		||||
            android:screenOrientation="userLandscape"
 | 
			
		||||
            android:exported="true">
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -23,30 +23,25 @@ import androidx.appcompat.app.AppCompatActivity
 | 
			
		||||
import androidx.core.view.WindowCompat
 | 
			
		||||
import androidx.core.view.WindowInsetsCompat
 | 
			
		||||
import androidx.core.view.WindowInsetsControllerCompat
 | 
			
		||||
import androidx.lifecycle.Lifecycle
 | 
			
		||||
import androidx.lifecycle.lifecycleScope
 | 
			
		||||
import androidx.lifecycle.repeatOnLifecycle
 | 
			
		||||
import androidx.window.layout.WindowInfoTracker
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
import androidx.navigation.fragment.NavHostFragment
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary
 | 
			
		||||
import org.yuzu.yuzu_emu.R
 | 
			
		||||
import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
 | 
			
		||||
import org.yuzu.yuzu_emu.fragments.EmulationFragment
 | 
			
		||||
import org.yuzu.yuzu_emu.model.Game
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.ForegroundService
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.InputHandler
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.NfcReader
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.ThemeHelper
 | 
			
		||||
import kotlin.math.roundToInt
 | 
			
		||||
 | 
			
		||||
class EmulationActivity : AppCompatActivity(), SensorEventListener {
 | 
			
		||||
    private lateinit var binding: ActivityEmulationBinding
 | 
			
		||||
 | 
			
		||||
    private var controllerMappingHelper: ControllerMappingHelper? = null
 | 
			
		||||
 | 
			
		||||
    var isActivityRecreated = false
 | 
			
		||||
    private var emulationFragment: EmulationFragment? = null
 | 
			
		||||
    private lateinit var nfcReader: NfcReader
 | 
			
		||||
    private lateinit var inputHandler: InputHandler
 | 
			
		||||
 | 
			
		||||
@@ -55,8 +50,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
 | 
			
		||||
    private var motionTimestamp: Long = 0
 | 
			
		||||
    private var flipMotionOrientation: Boolean = false
 | 
			
		||||
 | 
			
		||||
    private lateinit var game: Game
 | 
			
		||||
 | 
			
		||||
    private val settingsViewModel: SettingsViewModel by viewModels()
 | 
			
		||||
 | 
			
		||||
    override fun onDestroy() {
 | 
			
		||||
@@ -70,47 +63,31 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
 | 
			
		||||
        settingsViewModel.settings.loadSettings()
 | 
			
		||||
 | 
			
		||||
        super.onCreate(savedInstanceState)
 | 
			
		||||
        if (savedInstanceState == null) {
 | 
			
		||||
            // Get params we were passed
 | 
			
		||||
            game = intent.parcelable(EXTRA_SELECTED_GAME)!!
 | 
			
		||||
            isActivityRecreated = false
 | 
			
		||||
        } else {
 | 
			
		||||
            isActivityRecreated = true
 | 
			
		||||
            restoreState(savedInstanceState)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        binding = ActivityEmulationBinding.inflate(layoutInflater)
 | 
			
		||||
        setContentView(binding.root)
 | 
			
		||||
 | 
			
		||||
        val navHostFragment =
 | 
			
		||||
            supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment
 | 
			
		||||
        val navController = navHostFragment.navController
 | 
			
		||||
        navController
 | 
			
		||||
            .setGraph(R.navigation.emulation_navigation, intent.extras)
 | 
			
		||||
 | 
			
		||||
        isActivityRecreated = savedInstanceState != null
 | 
			
		||||
 | 
			
		||||
        controllerMappingHelper = ControllerMappingHelper()
 | 
			
		||||
 | 
			
		||||
        // Set these options now so that the SurfaceView the game renders into is the right size.
 | 
			
		||||
        enableFullscreenImmersive()
 | 
			
		||||
 | 
			
		||||
        setContentView(R.layout.activity_emulation)
 | 
			
		||||
        window.decorView.setBackgroundColor(getColor(android.R.color.black))
 | 
			
		||||
 | 
			
		||||
        // Find or create the EmulationFragment
 | 
			
		||||
        emulationFragment =
 | 
			
		||||
            supportFragmentManager.findFragmentById(R.id.frame_emulation_fragment) as EmulationFragment?
 | 
			
		||||
        if (emulationFragment == null) {
 | 
			
		||||
            emulationFragment = EmulationFragment.newInstance(game)
 | 
			
		||||
            supportFragmentManager.beginTransaction()
 | 
			
		||||
                .add(R.id.frame_emulation_fragment, emulationFragment!!)
 | 
			
		||||
                .commit()
 | 
			
		||||
        }
 | 
			
		||||
        title = game.title
 | 
			
		||||
 | 
			
		||||
        nfcReader = NfcReader(this)
 | 
			
		||||
        nfcReader.initialize()
 | 
			
		||||
 | 
			
		||||
        inputHandler = InputHandler()
 | 
			
		||||
        inputHandler.initialize()
 | 
			
		||||
 | 
			
		||||
        lifecycleScope.launch(Dispatchers.Main) {
 | 
			
		||||
            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
 | 
			
		||||
                WindowInfoTracker.getOrCreate(this@EmulationActivity)
 | 
			
		||||
                    .windowLayoutInfo(this@EmulationActivity)
 | 
			
		||||
                    .collect { emulationFragment?.updateCurrentLayout(this@EmulationActivity, it) }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Start a foreground service to prevent the app from getting killed in the background
 | 
			
		||||
        val startIntent = Intent(this, ForegroundService::class.java)
 | 
			
		||||
        startForegroundService(startIntent)
 | 
			
		||||
@@ -157,11 +134,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
 | 
			
		||||
        nfcReader.onNewIntent(intent)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onSaveInstanceState(outState: Bundle) {
 | 
			
		||||
        outState.putParcelable(EXTRA_SELECTED_GAME, game)
 | 
			
		||||
        super.onSaveInstanceState(outState)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun dispatchKeyEvent(event: KeyEvent): Boolean {
 | 
			
		||||
        if (event.source and InputDevice.SOURCE_JOYSTICK != InputDevice.SOURCE_JOYSTICK &&
 | 
			
		||||
            event.source and InputDevice.SOURCE_GAMEPAD != InputDevice.SOURCE_GAMEPAD
 | 
			
		||||
@@ -248,10 +220,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
 | 
			
		||||
 | 
			
		||||
    override fun onAccuracyChanged(sensor: Sensor, i: Int) {}
 | 
			
		||||
 | 
			
		||||
    private fun restoreState(savedInstanceState: Bundle) {
 | 
			
		||||
        game = savedInstanceState.parcelable(EXTRA_SELECTED_GAME)!!
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun enableFullscreenImmersive() {
 | 
			
		||||
        WindowCompat.setDecorFitsSystemWindows(window, false)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,7 @@ import androidx.appcompat.app.AppCompatActivity
 | 
			
		||||
import androidx.documentfile.provider.DocumentFile
 | 
			
		||||
import androidx.lifecycle.ViewModelProvider
 | 
			
		||||
import androidx.lifecycle.lifecycleScope
 | 
			
		||||
import androidx.navigation.findNavController
 | 
			
		||||
import androidx.preference.PreferenceManager
 | 
			
		||||
import androidx.recyclerview.widget.AsyncDifferConfig
 | 
			
		||||
import androidx.recyclerview.widget.DiffUtil
 | 
			
		||||
@@ -23,6 +24,7 @@ import androidx.recyclerview.widget.ListAdapter
 | 
			
		||||
import androidx.recyclerview.widget.RecyclerView
 | 
			
		||||
import coil.load
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
import org.yuzu.yuzu_emu.HomeNavigationDirections
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary
 | 
			
		||||
import org.yuzu.yuzu_emu.R
 | 
			
		||||
import org.yuzu.yuzu_emu.YuzuApplication
 | 
			
		||||
@@ -78,7 +80,8 @@ class GameAdapter(private val activity: AppCompatActivity) :
 | 
			
		||||
            )
 | 
			
		||||
            .apply()
 | 
			
		||||
 | 
			
		||||
        EmulationActivity.launch(activity, holder.game)
 | 
			
		||||
        val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game)
 | 
			
		||||
        view.findNavController().navigate(action)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inner class GameViewHolder(val binding: CardGameBinding) :
 | 
			
		||||
 
 | 
			
		||||
@@ -26,11 +26,18 @@ import androidx.core.view.ViewCompat
 | 
			
		||||
import androidx.core.view.WindowInsetsCompat
 | 
			
		||||
import androidx.core.view.updatePadding
 | 
			
		||||
import androidx.fragment.app.Fragment
 | 
			
		||||
import androidx.lifecycle.Lifecycle
 | 
			
		||||
import androidx.lifecycle.lifecycleScope
 | 
			
		||||
import androidx.lifecycle.repeatOnLifecycle
 | 
			
		||||
import androidx.navigation.fragment.navArgs
 | 
			
		||||
import androidx.preference.PreferenceManager
 | 
			
		||||
import androidx.window.layout.FoldingFeature
 | 
			
		||||
import androidx.window.layout.WindowInfoTracker
 | 
			
		||||
import androidx.window.layout.WindowLayoutInfo
 | 
			
		||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
 | 
			
		||||
import com.google.android.material.slider.Slider
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary
 | 
			
		||||
import org.yuzu.yuzu_emu.R
 | 
			
		||||
import org.yuzu.yuzu_emu.YuzuApplication
 | 
			
		||||
@@ -41,9 +48,7 @@ import org.yuzu.yuzu_emu.features.settings.model.IntSetting
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
 | 
			
		||||
import org.yuzu.yuzu_emu.model.Game
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.*
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable
 | 
			
		||||
 | 
			
		||||
class EmulationFragment : Fragment(), SurfaceHolder.Callback {
 | 
			
		||||
    private lateinit var preferences: SharedPreferences
 | 
			
		||||
@@ -54,7 +59,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
 | 
			
		||||
    private var _binding: FragmentEmulationBinding? = null
 | 
			
		||||
    private val binding get() = _binding!!
 | 
			
		||||
 | 
			
		||||
    private lateinit var game: Game
 | 
			
		||||
    val args by navArgs<EmulationFragmentArgs>()
 | 
			
		||||
 | 
			
		||||
    override fun onAttach(context: Context) {
 | 
			
		||||
        super.onAttach(context)
 | 
			
		||||
@@ -75,8 +80,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
 | 
			
		||||
        // So this fragment doesn't restart on configuration changes; i.e. rotation.
 | 
			
		||||
        retainInstance = true
 | 
			
		||||
        preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
 | 
			
		||||
        game = requireArguments().parcelable(EmulationActivity.EXTRA_SELECTED_GAME)!!
 | 
			
		||||
        emulationState = EmulationState(game.path)
 | 
			
		||||
        emulationState = EmulationState(args.game.path)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -100,7 +104,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
 | 
			
		||||
        updateShowFpsOverlay()
 | 
			
		||||
 | 
			
		||||
        binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text =
 | 
			
		||||
            game.title
 | 
			
		||||
            args.game.title
 | 
			
		||||
        binding.inGameMenu.setNavigationItemSelectedListener {
 | 
			
		||||
            when (it.itemId) {
 | 
			
		||||
                R.id.menu_pause_emulation -> {
 | 
			
		||||
@@ -153,6 +157,14 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
 | 
			
		||||
                    if (binding.drawerLayout.isOpen) binding.drawerLayout.close() else binding.drawerLayout.open()
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
        viewLifecycleOwner.lifecycleScope.launch(Dispatchers.Main) {
 | 
			
		||||
            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
 | 
			
		||||
                WindowInfoTracker.getOrCreate(requireContext())
 | 
			
		||||
                    .windowLayoutInfo(requireActivity())
 | 
			
		||||
                    .collect { updateCurrentLayout(requireActivity() as EmulationActivity, it) }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onResume() {
 | 
			
		||||
@@ -601,13 +613,5 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        private val perfStatsUpdateHandler = Handler(Looper.myLooper()!!)
 | 
			
		||||
 | 
			
		||||
        fun newInstance(game: Game): EmulationFragment {
 | 
			
		||||
            val args = Bundle()
 | 
			
		||||
            args.putParcelable(EmulationActivity.EXTRA_SELECTED_GAME, game)
 | 
			
		||||
            val fragment = EmulationFragment()
 | 
			
		||||
            fragment.arguments = args
 | 
			
		||||
            return fragment
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,9 @@
 | 
			
		||||
<FrameLayout
 | 
			
		||||
<androidx.fragment.app.FragmentContainerView
 | 
			
		||||
    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:id="@+id/frame_content"
 | 
			
		||||
    xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
			
		||||
    android:id="@+id/fragment_container"
 | 
			
		||||
    android:name="androidx.navigation.fragment.NavHostFragment"
 | 
			
		||||
    android:layout_width="match_parent"
 | 
			
		||||
    android:layout_height="match_parent"
 | 
			
		||||
    android:keepScreenOn="true">
 | 
			
		||||
 | 
			
		||||
    <FrameLayout
 | 
			
		||||
        android:id="@+id/frame_emulation_fragment"
 | 
			
		||||
        android:layout_width="match_parent"
 | 
			
		||||
        android:layout_height="match_parent" />
 | 
			
		||||
 | 
			
		||||
</FrameLayout>
 | 
			
		||||
    android:keepScreenOn="true"
 | 
			
		||||
    app:defaultNavHost="true" />
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,18 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
			
		||||
    xmlns:tools="http://schemas.android.com/tools"
 | 
			
		||||
    android:id="@+id/emulation_navigation"
 | 
			
		||||
    app:startDestination="@id/emulationFragment">
 | 
			
		||||
 | 
			
		||||
    <fragment
 | 
			
		||||
        android:id="@+id/emulationFragment"
 | 
			
		||||
        android:name="org.yuzu.yuzu_emu.fragments.EmulationFragment"
 | 
			
		||||
        android:label="fragment_emulation"
 | 
			
		||||
        tools:layout="@layout/fragment_emulation" >
 | 
			
		||||
        <argument
 | 
			
		||||
            android:name="game"
 | 
			
		||||
            app:argType="org.yuzu.yuzu_emu.model.Game" />
 | 
			
		||||
    </fragment>
 | 
			
		||||
 | 
			
		||||
</navigation>
 | 
			
		||||
@@ -56,4 +56,18 @@
 | 
			
		||||
        android:name="org.yuzu.yuzu_emu.fragments.LicensesFragment"
 | 
			
		||||
        android:label="LicensesFragment" />
 | 
			
		||||
 | 
			
		||||
    <activity
 | 
			
		||||
        android:id="@+id/emulationActivity"
 | 
			
		||||
        android:name="org.yuzu.yuzu_emu.activities.EmulationActivity"
 | 
			
		||||
        android:label="EmulationActivity">
 | 
			
		||||
        <argument
 | 
			
		||||
            android:name="game"
 | 
			
		||||
            app:argType="org.yuzu.yuzu_emu.model.Game" />
 | 
			
		||||
    </activity>
 | 
			
		||||
 | 
			
		||||
    <action
 | 
			
		||||
        android:id="@+id/action_global_emulationActivity"
 | 
			
		||||
        app:destination="@id/emulationActivity"
 | 
			
		||||
        app:launchSingleTop="true" />
 | 
			
		||||
 | 
			
		||||
</navigation>
 | 
			
		||||
 
 | 
			
		||||
@@ -11,3 +11,12 @@ plugins {
 | 
			
		||||
tasks.register("clean").configure {
 | 
			
		||||
    delete(rootProject.buildDir)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
buildscript {
 | 
			
		||||
    repositories {
 | 
			
		||||
        google()
 | 
			
		||||
    }
 | 
			
		||||
    dependencies {
 | 
			
		||||
        classpath("androidx.navigation:navigation-safe-args-gradle-plugin:2.6.0")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user