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