Skip to content
Snippets Groups Projects
Commit b79c9933 authored by Charles Lombardo's avatar Charles Lombardo
Browse files

android: Adapt EmulationActivity to navigation component

parent 278336af
No related branches found
No related tags found
No related merge requests found
...@@ -9,6 +9,7 @@ plugins { ...@@ -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 ...@@ -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 ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 ...@@ -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 ...@@ -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) : ...@@ -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 ...@@ -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 ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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
}
} }
} }
<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>
<?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 @@ ...@@ -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 { ...@@ -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")
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment