From b79c993328f943285dc119c8ebfdd21b18fc747f Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Sun, 11 Jun 2023 19:54:54 -0400 Subject: [PATCH] android: Adapt EmulationActivity to navigation component --- src/android/app/build.gradle.kts | 1 + src/android/app/src/main/AndroidManifest.xml | 1 - .../yuzu_emu/activities/EmulationActivity.kt | 64 +++++-------------- .../org/yuzu/yuzu_emu/adapters/GameAdapter.kt | 5 +- .../yuzu_emu/fragments/EmulationFragment.kt | 32 ++++++---- .../main/res/layout/activity_emulation.xml | 16 ++--- .../res/navigation/emulation_navigation.xml | 18 ++++++ .../main/res/navigation/home_navigation.xml | 14 ++++ src/android/build.gradle.kts | 9 +++ 9 files changed, 86 insertions(+), 74 deletions(-) create mode 100644 src/android/app/src/main/res/navigation/emulation_navigation.xml diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts index fe613d339b..a637db78ac 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts @@ -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") } /** diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml index 55f62b4b91..b474ddb0bd 100644 --- a/src/android/app/src/main/AndroidManifest.xml +++ b/src/android/app/src/main/AndroidManifest.xml @@ -53,7 +53,6 @@ SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt index 20a0394f54..caf6603487 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt @@ -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) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt index 7f9e2e2d47..83d08841bc 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt @@ -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) : diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt index 9523381cd3..02bfcdb1eb 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt @@ -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() 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(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 - } } } diff --git a/src/android/app/src/main/res/layout/activity_emulation.xml b/src/android/app/src/main/res/layout/activity_emulation.xml index f6360a65b1..139065d3d6 100644 --- a/src/android/app/src/main/res/layout/activity_emulation.xml +++ b/src/android/app/src/main/res/layout/activity_emulation.xml @@ -1,13 +1,9 @@ - - - - - + android:keepScreenOn="true" + app:defaultNavHost="true" /> diff --git a/src/android/app/src/main/res/navigation/emulation_navigation.xml b/src/android/app/src/main/res/navigation/emulation_navigation.xml new file mode 100644 index 0000000000..8208f4c2c4 --- /dev/null +++ b/src/android/app/src/main/res/navigation/emulation_navigation.xml @@ -0,0 +1,18 @@ + + + + + + + + diff --git a/src/android/app/src/main/res/navigation/home_navigation.xml b/src/android/app/src/main/res/navigation/home_navigation.xml index 48072683ea..fcebba7266 100644 --- a/src/android/app/src/main/res/navigation/home_navigation.xml +++ b/src/android/app/src/main/res/navigation/home_navigation.xml @@ -56,4 +56,18 @@ android:name="org.yuzu.yuzu_emu.fragments.LicensesFragment" android:label="LicensesFragment" /> + + + + + + diff --git a/src/android/build.gradle.kts b/src/android/build.gradle.kts index e19e8ce58e..80f370c164 100644 --- a/src/android/build.gradle.kts +++ b/src/android/build.gradle.kts @@ -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") + } +}