android: Adapt EmulationActivity to navigation component
This commit is contained in:
		| @@ -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
	
	Block a user