Просмотр исходного кода

android: Migrate settings to navigation component

Consolidates all of the settings components to the fragment and activity with no interfaces and only the settings fragment presenter. This also includes new material animations and new viewmodel usage to prevent the fragment and activity directly interacting with one another.
Charles Lombardo 2 лет назад
Родитель
Сommit
95a939a49f
28 измененных файлов с 371 добавлено и 567 удалено
  1. 1 1
      src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
  2. 1 3
      src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
  3. 63 113
      src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
  4. 0 81
      src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt
  5. 0 38
      src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityView.kt
  6. 23 24
      src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
  7. 55 57
      src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
  8. 34 34
      src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
  9. 0 42
      src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentView.kt
  10. 8 3
      src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
  11. 16 3
      src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
  12. 47 0
      src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt
  13. 8 6
      src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
  14. 5 5
      src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt
  15. 0 16
      src/android/app/src/main/res/anim-ldrtl/anim_pop_settings_fragment_out.xml
  16. 0 16
      src/android/app/src/main/res/anim-ldrtl/anim_settings_fragment_in.xml
  17. 0 16
      src/android/app/src/main/res/anim/anim_pop_settings_fragment_out.xml
  18. 0 16
      src/android/app/src/main/res/anim/anim_settings_fragment_in.xml
  19. 0 10
      src/android/app/src/main/res/anim/anim_settings_fragment_out.xml
  20. 0 20
      src/android/app/src/main/res/animator/menu_slide_in_from_start.xml
  21. 0 21
      src/android/app/src/main/res/animator/menu_slide_out_to_start.xml
  22. 18 34
      src/android/app/src/main/res/layout/activity_settings.xml
  23. 33 6
      src/android/app/src/main/res/layout/fragment_settings.xml
  24. 0 2
      src/android/app/src/main/res/menu/menu_settings.xml
  25. 17 0
      src/android/app/src/main/res/navigation/emulation_navigation.xml
  26. 17 0
      src/android/app/src/main/res/navigation/home_navigation.xml
  27. 24 0
      src/android/app/src/main/res/navigation/settings_navigation.xml
  28. 1 0
      src/android/app/src/main/res/values/strings.xml

+ 1 - 1
src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt

@@ -46,7 +46,7 @@ class YuzuApplication : Application() {
         super.onCreate()
         super.onCreate()
         application = this
         application = this
         documentsTree = DocumentsTree()
         documentsTree = DocumentsTree()
-        DirectoryInitialization.start(applicationContext)
+        DirectoryInitialization.start()
         GpuDriverHelper.initializeDriverParameters(applicationContext)
         GpuDriverHelper.initializeDriverParameters(applicationContext)
         NativeLibrary.logDeviceInfo()
         NativeLibrary.logDeviceInfo()
 
 

+ 1 - 3
src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt

@@ -85,9 +85,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
 
 
         val navHostFragment =
         val navHostFragment =
             supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment
             supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment
-        val navController = navHostFragment.navController
-        navController
-            .setGraph(R.navigation.emulation_navigation, intent.extras)
+        navHostFragment.navController.setGraph(R.navigation.emulation_navigation, intent.extras)
 
 
         isActivityRecreated = savedInstanceState != null
         isActivityRecreated = savedInstanceState != null
 
 

+ 63 - 113
src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt

@@ -3,10 +3,7 @@
 
 
 package org.yuzu.yuzu_emu.features.settings.ui
 package org.yuzu.yuzu_emu.features.settings.ui
 
 
-import android.content.Context
-import android.content.Intent
 import android.os.Bundle
 import android.os.Bundle
-import android.view.Menu
 import android.view.View
 import android.view.View
 import android.view.ViewGroup.MarginLayoutParams
 import android.view.ViewGroup.MarginLayoutParams
 import android.widget.Toast
 import android.widget.Toast
@@ -16,20 +13,26 @@ import androidx.appcompat.app.AppCompatActivity
 import androidx.core.view.ViewCompat
 import androidx.core.view.ViewCompat
 import androidx.core.view.WindowCompat
 import androidx.core.view.WindowCompat
 import androidx.core.view.WindowInsetsCompat
 import androidx.core.view.WindowInsetsCompat
-import androidx.core.view.updatePadding
+import androidx.navigation.fragment.NavHostFragment
+import androidx.navigation.navArgs
 import com.google.android.material.color.MaterialColors
 import com.google.android.material.color.MaterialColors
+import org.yuzu.yuzu_emu.NativeLibrary
 import java.io.IOException
 import java.io.IOException
 import org.yuzu.yuzu_emu.R
 import org.yuzu.yuzu_emu.R
 import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
 import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
 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.utils.SettingsFile
 import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
+import org.yuzu.yuzu_emu.fragments.ResetSettingsDialogFragment
+import org.yuzu.yuzu_emu.model.SettingsViewModel
 import org.yuzu.yuzu_emu.utils.*
 import org.yuzu.yuzu_emu.utils.*
 
 
-class SettingsActivity : AppCompatActivity(), SettingsActivityView {
-    private val presenter = SettingsActivityPresenter(this)
-
+class SettingsActivity : AppCompatActivity() {
     private lateinit var binding: ActivitySettingsBinding
     private lateinit var binding: ActivitySettingsBinding
 
 
+    private val args by navArgs<SettingsActivityArgs>()
+
+    private val settingsViewModel: SettingsViewModel by viewModels()
+
     override fun onCreate(savedInstanceState: Bundle?) {
     override fun onCreate(savedInstanceState: Bundle?) {
         ThemeHelper.setTheme(this)
         ThemeHelper.setTheme(this)
 
 
@@ -38,16 +41,17 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
         binding = ActivitySettingsBinding.inflate(layoutInflater)
         binding = ActivitySettingsBinding.inflate(layoutInflater)
         setContentView(binding.root)
         setContentView(binding.root)
 
 
-        WindowCompat.setDecorFitsSystemWindows(window, false)
+        settingsViewModel.game = args.game
 
 
-        val launcher = intent
-        val gameID = launcher.getStringExtra(ARG_GAME_ID)
-        val menuTag = launcher.getStringExtra(ARG_MENU_TAG)
-        presenter.onCreate(savedInstanceState, menuTag!!, gameID!!)
+        val navHostFragment =
+            supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment
+        navHostFragment.navController.setGraph(R.navigation.settings_navigation, intent.extras)
 
 
-        // Show "Back" button in the action bar for navigation
-        setSupportActionBar(binding.toolbarSettings)
-        supportActionBar!!.setDisplayHomeAsUpEnabled(true)
+        WindowCompat.setDecorFitsSystemWindows(window, false)
+
+        if (savedInstanceState != null) {
+            settingsViewModel.shouldSave = savedInstanceState.getBoolean(KEY_SHOULD_SAVE)
+        }
 
 
         if (InsetsHelper.getSystemGestureType(applicationContext) !=
         if (InsetsHelper.getSystemGestureType(applicationContext) !=
             InsetsHelper.GESTURE_NAVIGATION
             InsetsHelper.GESTURE_NAVIGATION
@@ -63,6 +67,28 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
             )
             )
         }
         }
 
 
+        settingsViewModel.shouldRecreate.observe(this) {
+            if (it) {
+                settingsViewModel.setShouldRecreate(false)
+                recreate()
+            }
+        }
+        settingsViewModel.shouldNavigateBack.observe(this) {
+            if (it) {
+                settingsViewModel.setShouldNavigateBack(false)
+                navigateBack()
+            }
+        }
+        settingsViewModel.shouldShowResetSettingsDialog.observe(this) {
+            if (it) {
+                settingsViewModel.setShouldShowResetSettingsDialog(false)
+                ResetSettingsDialogFragment().show(
+                    supportFragmentManager,
+                    ResetSettingsDialogFragment.TAG
+                )
+            }
+        }
+
         onBackPressedDispatcher.addCallback(
         onBackPressedDispatcher.addCallback(
             this,
             this,
             object : OnBackPressedCallback(true) {
             object : OnBackPressedCallback(true) {
@@ -73,34 +99,28 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
         setInsets()
         setInsets()
     }
     }
 
 
-    override fun onSupportNavigateUp(): Boolean {
-        navigateBack()
-        return true
-    }
-
-    private fun navigateBack() {
-        if (supportFragmentManager.backStackEntryCount > 0) {
-            supportFragmentManager.popBackStack()
+    fun navigateBack() {
+        val navHostFragment =
+            supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment
+        if (navHostFragment.childFragmentManager.backStackEntryCount > 0) {
+            navHostFragment.navController.popBackStack()
         } else {
         } else {
             finish()
             finish()
         }
         }
     }
     }
 
 
-    override fun onCreateOptionsMenu(menu: Menu): Boolean {
-        val inflater = menuInflater
-        inflater.inflate(R.menu.menu_settings, menu)
-        return true
-    }
-
     override fun onSaveInstanceState(outState: Bundle) {
     override fun onSaveInstanceState(outState: Bundle) {
         // Critical: If super method is not called, rotations will be busted.
         // Critical: If super method is not called, rotations will be busted.
         super.onSaveInstanceState(outState)
         super.onSaveInstanceState(outState)
-        presenter.saveState(outState)
+        outState.putBoolean(KEY_SHOULD_SAVE, settingsViewModel.shouldSave)
     }
     }
 
 
     override fun onStart() {
     override fun onStart() {
         super.onStart()
         super.onStart()
-        presenter.onStart()
+        // TODO: Load custom settings contextually
+        if (!DirectoryInitialization.areDirectoriesReady) {
+            DirectoryInitialization.start()
+        }
     }
     }
 
 
     /**
     /**
@@ -110,65 +130,21 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
      */
      */
     override fun onStop() {
     override fun onStop() {
         super.onStop()
         super.onStop()
-        presenter.onStop(isFinishing)
-    }
-
-    override fun showSettingsFragment(menuTag: String, addToStack: Boolean, gameId: String) {
-        if (!addToStack && settingsFragment != null) {
-            return
-        }
-
-        val transaction = supportFragmentManager.beginTransaction()
-        if (addToStack) {
-            if (areSystemAnimationsEnabled()) {
-                transaction.setCustomAnimations(
-                    R.anim.anim_settings_fragment_in,
-                    R.anim.anim_settings_fragment_out,
-                    0,
-                    R.anim.anim_pop_settings_fragment_out
-                )
-            }
-            transaction.addToBackStack(null)
+        if (isFinishing && settingsViewModel.shouldSave) {
+            Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...")
+            Settings.saveSettings()
+            NativeLibrary.reloadSettings()
         }
         }
-        transaction.replace(
-            R.id.frame_content,
-            SettingsFragment.newInstance(menuTag, gameId),
-            FRAGMENT_TAG
-        )
-        transaction.commit()
-    }
-
-    private fun areSystemAnimationsEnabled(): Boolean {
-        val duration = android.provider.Settings.Global.getFloat(
-            contentResolver,
-            android.provider.Settings.Global.ANIMATOR_DURATION_SCALE,
-            1f
-        )
-        val transition = android.provider.Settings.Global.getFloat(
-            contentResolver,
-            android.provider.Settings.Global.TRANSITION_ANIMATION_SCALE,
-            1f
-        )
-        return duration != 0f && transition != 0f
     }
     }
 
 
-    override fun onSettingsFileLoaded() {
-        val fragment: SettingsFragmentView? = settingsFragment
-        fragment?.loadSettingsList()
-    }
-
-    override fun onSettingsFileNotFound() {
-        val fragment: SettingsFragmentView? = settingsFragment
-        fragment?.loadSettingsList()
-    }
-
-    override fun onSettingChanged() {
-        presenter.onSettingChanged()
+    override fun onDestroy() {
+        settingsViewModel.clear()
+        super.onDestroy()
     }
     }
 
 
     fun onSettingsReset() {
     fun onSettingsReset() {
         // Prevents saving to a non-existent settings file
         // Prevents saving to a non-existent settings file
-        presenter.onSettingsReset()
+        settingsViewModel.shouldSave = false
 
 
         // Delete settings file because the user may have changed values that do not exist in the UI
         // Delete settings file because the user may have changed values that do not exist in the UI
         val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG)
         val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG)
@@ -185,47 +161,21 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
         finish()
         finish()
     }
     }
 
 
-    fun setToolbarTitle(title: String) {
-        binding.toolbarSettingsLayout.title = title
-    }
-
-    private val settingsFragment: SettingsFragment?
-        get() = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as SettingsFragment?
-
     private fun setInsets() {
     private fun setInsets() {
         ViewCompat.setOnApplyWindowInsetsListener(
         ViewCompat.setOnApplyWindowInsetsListener(
-            binding.frameContent
+            binding.navigationBarShade
         ) { view: View, windowInsets: WindowInsetsCompat ->
         ) { view: View, windowInsets: WindowInsetsCompat ->
             val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
             val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
-            val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
-            view.updatePadding(
-                left = barInsets.left + cutoutInsets.left,
-                right = barInsets.right + cutoutInsets.right
-            )
 
 
-            val mlpAppBar = binding.appbarSettings.layoutParams as MarginLayoutParams
-            mlpAppBar.leftMargin = barInsets.left + cutoutInsets.left
-            mlpAppBar.rightMargin = barInsets.right + cutoutInsets.right
-            binding.appbarSettings.layoutParams = mlpAppBar
-
-            val mlpShade = binding.navigationBarShade.layoutParams as MarginLayoutParams
+            val mlpShade = view.layoutParams as MarginLayoutParams
             mlpShade.height = barInsets.bottom
             mlpShade.height = barInsets.bottom
-            binding.navigationBarShade.layoutParams = mlpShade
+            view.layoutParams = mlpShade
 
 
             windowInsets
             windowInsets
         }
         }
     }
     }
 
 
     companion object {
     companion object {
-        private const val ARG_MENU_TAG = "menu_tag"
-        private const val ARG_GAME_ID = "game_id"
-        private const val FRAGMENT_TAG = "settings"
-
-        fun launch(context: Context, menuTag: String?, gameId: String?) {
-            val settings = Intent(context, SettingsActivity::class.java)
-            settings.putExtra(ARG_MENU_TAG, menuTag)
-            settings.putExtra(ARG_GAME_ID, gameId)
-            context.startActivity(settings)
-        }
+        private const val KEY_SHOULD_SAVE = "should_save"
     }
     }
 }
 }

+ 0 - 81
src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt

@@ -1,81 +0,0 @@
-// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.yuzu.yuzu_emu.features.settings.ui
-
-import android.content.Context
-import android.os.Bundle
-import java.io.File
-import org.yuzu.yuzu_emu.NativeLibrary
-import org.yuzu.yuzu_emu.features.settings.model.Settings
-import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
-import org.yuzu.yuzu_emu.utils.DirectoryInitialization
-import org.yuzu.yuzu_emu.utils.Log
-
-class SettingsActivityPresenter(private val activityView: SettingsActivityView) {
-    private var shouldSave = false
-    private lateinit var menuTag: String
-    private lateinit var gameId: String
-
-    fun onCreate(savedInstanceState: Bundle?, menuTag: String, gameId: String) {
-        this.menuTag = menuTag
-        this.gameId = gameId
-        if (savedInstanceState != null) {
-            shouldSave = savedInstanceState.getBoolean(KEY_SHOULD_SAVE)
-        }
-    }
-
-    fun onStart() {
-        prepareDirectoriesIfNeeded()
-    }
-
-    private fun loadSettingsUI() {
-        // TODO: Load custom settings contextually
-        activityView.showSettingsFragment(menuTag, false, gameId)
-        activityView.onSettingsFileLoaded()
-    }
-
-    private fun prepareDirectoriesIfNeeded() {
-        val configFile =
-            File(
-                "${DirectoryInitialization.userDirectory}/config/" +
-                    "${SettingsFile.FILE_NAME_CONFIG}.ini"
-            )
-        if (!configFile.exists()) {
-            Log.error(
-                "${DirectoryInitialization.userDirectory}/config/" +
-                    "${SettingsFile.FILE_NAME_CONFIG}.ini"
-            )
-            Log.error("yuzu config file could not be found!")
-        }
-
-        if (!DirectoryInitialization.areDirectoriesReady) {
-            DirectoryInitialization.start(activityView as Context)
-        }
-        loadSettingsUI()
-    }
-
-    fun onStop(finishing: Boolean) {
-        if (finishing && shouldSave) {
-            Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...")
-            Settings.saveSettings()
-            NativeLibrary.reloadSettings()
-        }
-    }
-
-    fun onSettingChanged() {
-        shouldSave = true
-    }
-
-    fun onSettingsReset() {
-        shouldSave = false
-    }
-
-    fun saveState(outState: Bundle) {
-        outState.putBoolean(KEY_SHOULD_SAVE, shouldSave)
-    }
-
-    companion object {
-        private const val KEY_SHOULD_SAVE = "should_save"
-    }
-}

+ 0 - 38
src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityView.kt

@@ -1,38 +0,0 @@
-// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.yuzu.yuzu_emu.features.settings.ui
-
-/**
- * Abstraction for the Activity that manages SettingsFragments.
- */
-interface SettingsActivityView {
-    /**
-     * Show a new SettingsFragment.
-     *
-     * @param menuTag    Identifier for the settings group that should be displayed.
-     * @param addToStack Whether or not this fragment should replace a previous one.
-     */
-    fun showSettingsFragment(menuTag: String, addToStack: Boolean, gameId: String)
-
-    /**
-     * Called when a load operation completes.
-     */
-    fun onSettingsFileLoaded()
-
-    /**
-     * Called when a load operation fails.
-     */
-    fun onSettingsFileNotFound()
-
-    /**
-     * End the activity.
-     */
-    fun finish()
-
-    /**
-     * Called by a containing Fragment to tell the Activity that a setting was changed;
-     * unless this has been called, the Activity will not save to disk.
-     */
-    fun onSettingChanged()
-}

+ 23 - 24
src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt

@@ -12,7 +12,8 @@ import android.view.LayoutInflater
 import android.view.ViewGroup
 import android.view.ViewGroup
 import android.widget.TextView
 import android.widget.TextView
 import androidx.appcompat.app.AlertDialog
 import androidx.appcompat.app.AlertDialog
-import androidx.appcompat.app.AppCompatActivity
+import androidx.lifecycle.ViewModelProvider
+import androidx.navigation.findNavController
 import androidx.recyclerview.widget.RecyclerView
 import androidx.recyclerview.widget.RecyclerView
 import com.google.android.material.datepicker.MaterialDatePicker
 import com.google.android.material.datepicker.MaterialDatePicker
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
@@ -20,6 +21,7 @@ import com.google.android.material.slider.Slider
 import com.google.android.material.timepicker.MaterialTimePicker
 import com.google.android.material.timepicker.MaterialTimePicker
 import com.google.android.material.timepicker.TimeFormat
 import com.google.android.material.timepicker.TimeFormat
 import org.yuzu.yuzu_emu.R
 import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.SettingsNavigationDirections
 import org.yuzu.yuzu_emu.databinding.DialogSliderBinding
 import org.yuzu.yuzu_emu.databinding.DialogSliderBinding
 import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
 import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
 import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
 import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
@@ -30,18 +32,22 @@ import org.yuzu.yuzu_emu.features.settings.model.FloatSetting
 import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
 import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
 import org.yuzu.yuzu_emu.features.settings.model.view.*
 import org.yuzu.yuzu_emu.features.settings.model.view.*
 import org.yuzu.yuzu_emu.features.settings.ui.viewholder.*
 import org.yuzu.yuzu_emu.features.settings.ui.viewholder.*
+import org.yuzu.yuzu_emu.model.SettingsViewModel
 
 
 class SettingsAdapter(
 class SettingsAdapter(
-    private val fragmentView: SettingsFragmentView,
+    private val fragment: SettingsFragment,
     private val context: Context
     private val context: Context
 ) : RecyclerView.Adapter<SettingViewHolder?>(), DialogInterface.OnClickListener {
 ) : RecyclerView.Adapter<SettingViewHolder?>(), DialogInterface.OnClickListener {
-    private var settings: ArrayList<SettingsItem>? = null
+    private var settings = ArrayList<SettingsItem>()
     private var clickedItem: SettingsItem? = null
     private var clickedItem: SettingsItem? = null
     private var clickedPosition: Int
     private var clickedPosition: Int
     private var dialog: AlertDialog? = null
     private var dialog: AlertDialog? = null
     private var sliderProgress = 0
     private var sliderProgress = 0
     private var textSliderValue: TextView? = null
     private var textSliderValue: TextView? = null
 
 
+    private val settingsViewModel: SettingsViewModel
+        get() = ViewModelProvider(fragment.requireActivity())[SettingsViewModel::class.java]
+
     private var defaultCancelListener =
     private var defaultCancelListener =
         DialogInterface.OnClickListener { _: DialogInterface?, _: Int -> closeDialog() }
         DialogInterface.OnClickListener { _: DialogInterface?, _: Int -> closeDialog() }
 
 
@@ -91,30 +97,22 @@ class SettingsAdapter(
         holder.bind(getItem(position))
         holder.bind(getItem(position))
     }
     }
 
 
-    private fun getItem(position: Int): SettingsItem {
-        return settings!![position]
-    }
+    private fun getItem(position: Int): SettingsItem = settings[position]
 
 
-    override fun getItemCount(): Int {
-        return if (settings != null) {
-            settings!!.size
-        } else {
-            0
-        }
-    }
+    override fun getItemCount(): Int = settings.size
 
 
     override fun getItemViewType(position: Int): Int {
     override fun getItemViewType(position: Int): Int {
         return getItem(position).type
         return getItem(position).type
     }
     }
 
 
-    fun setSettingsList(settings: ArrayList<SettingsItem>?) {
+    fun setSettingsList(settings: ArrayList<SettingsItem>) {
         this.settings = settings
         this.settings = settings
         notifyDataSetChanged()
         notifyDataSetChanged()
     }
     }
 
 
     fun onBooleanClick(item: SwitchSetting, position: Int, checked: Boolean) {
     fun onBooleanClick(item: SwitchSetting, position: Int, checked: Boolean) {
         item.checked = checked
         item.checked = checked
-        fragmentView.onSettingChanged()
+        settingsViewModel.shouldSave = true
     }
     }
 
 
     private fun onSingleChoiceClick(item: SingleChoiceSetting) {
     private fun onSingleChoiceClick(item: SingleChoiceSetting) {
@@ -155,7 +153,7 @@ class SettingsAdapter(
         calendar.timeZone = TimeZone.getTimeZone("UTC")
         calendar.timeZone = TimeZone.getTimeZone("UTC")
 
 
         var timeFormat: Int = TimeFormat.CLOCK_12H
         var timeFormat: Int = TimeFormat.CLOCK_12H
-        if (DateFormat.is24HourFormat(fragmentView.activityView as AppCompatActivity)) {
+        if (DateFormat.is24HourFormat(context)) {
             timeFormat = TimeFormat.CLOCK_24H
             timeFormat = TimeFormat.CLOCK_24H
         }
         }
 
 
@@ -172,7 +170,7 @@ class SettingsAdapter(
 
 
         datePicker.addOnPositiveButtonClickListener {
         datePicker.addOnPositiveButtonClickListener {
             timePicker.show(
             timePicker.show(
-                (fragmentView.activityView as AppCompatActivity).supportFragmentManager,
+                fragment.childFragmentManager,
                 "TimePicker"
                 "TimePicker"
             )
             )
         }
         }
@@ -181,14 +179,14 @@ class SettingsAdapter(
             epochTime += timePicker.hour.toLong() * 60 * 60
             epochTime += timePicker.hour.toLong() * 60 * 60
             epochTime += timePicker.minute.toLong() * 60
             epochTime += timePicker.minute.toLong() * 60
             if (item.value != epochTime) {
             if (item.value != epochTime) {
-                fragmentView.onSettingChanged()
+                settingsViewModel.shouldSave = true
                 notifyItemChanged(clickedPosition)
                 notifyItemChanged(clickedPosition)
                 item.value = epochTime
                 item.value = epochTime
             }
             }
             clickedItem = null
             clickedItem = null
         }
         }
         datePicker.show(
         datePicker.show(
-            (fragmentView.activityView as AppCompatActivity).supportFragmentManager,
+            fragment.childFragmentManager,
             "DatePicker"
             "DatePicker"
         )
         )
     }
     }
@@ -231,7 +229,8 @@ class SettingsAdapter(
     }
     }
 
 
     fun onSubmenuClick(item: SubmenuSetting) {
     fun onSubmenuClick(item: SubmenuSetting) {
-        fragmentView.loadSubMenu(item.menuKey)
+        val action = SettingsNavigationDirections.actionGlobalSettingsFragment(item.menuKey, null)
+        fragment.view?.findNavController()?.navigate(action)
     }
     }
 
 
     override fun onClick(dialog: DialogInterface, which: Int) {
     override fun onClick(dialog: DialogInterface, which: Int) {
@@ -240,7 +239,7 @@ class SettingsAdapter(
                 val scSetting = clickedItem as SingleChoiceSetting
                 val scSetting = clickedItem as SingleChoiceSetting
                 val value = getValueForSingleChoiceSelection(scSetting, which)
                 val value = getValueForSingleChoiceSelection(scSetting, which)
                 if (scSetting.selectedValue != value) {
                 if (scSetting.selectedValue != value) {
-                    fragmentView.onSettingChanged()
+                    settingsViewModel.shouldSave = true
                 }
                 }
 
 
                 // Get the backing Setting, which may be null (if for example it was missing from the file)
                 // Get the backing Setting, which may be null (if for example it was missing from the file)
@@ -251,7 +250,7 @@ class SettingsAdapter(
             is StringSingleChoiceSetting -> {
             is StringSingleChoiceSetting -> {
                 val scSetting = clickedItem as StringSingleChoiceSetting
                 val scSetting = clickedItem as StringSingleChoiceSetting
                 val value = scSetting.getValueAt(which)
                 val value = scSetting.getValueAt(which)
-                if (scSetting.selectedValue != value) fragmentView.onSettingChanged()
+                if (scSetting.selectedValue != value) settingsViewModel.shouldSave = true
                 scSetting.selectedValue = value!!
                 scSetting.selectedValue = value!!
                 closeDialog()
                 closeDialog()
             }
             }
@@ -259,7 +258,7 @@ class SettingsAdapter(
             is SliderSetting -> {
             is SliderSetting -> {
                 val sliderSetting = clickedItem as SliderSetting
                 val sliderSetting = clickedItem as SliderSetting
                 if (sliderSetting.selectedValue != sliderProgress) {
                 if (sliderSetting.selectedValue != sliderProgress) {
-                    fragmentView.onSettingChanged()
+                    settingsViewModel.shouldSave = true
                 }
                 }
                 when (sliderSetting.setting) {
                 when (sliderSetting.setting) {
                     is ByteSetting -> {
                     is ByteSetting -> {
@@ -294,7 +293,7 @@ class SettingsAdapter(
             .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
             .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
                 setting.reset()
                 setting.reset()
                 notifyItemChanged(position)
                 notifyItemChanged(position)
-                fragmentView.onSettingChanged()
+                settingsViewModel.shouldSave = true
             }
             }
             .setNegativeButton(android.R.string.cancel, null)
             .setNegativeButton(android.R.string.cancel, null)
             .show()
             .show()

+ 55 - 57
src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt

@@ -3,39 +3,41 @@
 
 
 package org.yuzu.yuzu_emu.features.settings.ui
 package org.yuzu.yuzu_emu.features.settings.ui
 
 
-import android.content.Context
 import android.os.Bundle
 import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.LayoutInflater
 import android.view.View
 import android.view.View
 import android.view.ViewGroup
 import android.view.ViewGroup
+import android.view.ViewGroup.MarginLayoutParams
 import androidx.core.view.ViewCompat
 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.fragment.app.activityViewModels
+import androidx.navigation.fragment.navArgs
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.LinearLayoutManager
 import com.google.android.material.divider.MaterialDividerItemDecoration
 import com.google.android.material.divider.MaterialDividerItemDecoration
+import com.google.android.material.transition.MaterialSharedAxis
+import org.yuzu.yuzu_emu.R
 import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding
 import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding
-import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
+import org.yuzu.yuzu_emu.model.SettingsViewModel
 
 
-class SettingsFragment : Fragment(), SettingsFragmentView {
-    override var activityView: SettingsActivityView? = null
-
-    private val fragmentPresenter = SettingsFragmentPresenter(this)
+class SettingsFragment : Fragment() {
+    private lateinit var presenter: SettingsFragmentPresenter
     private var settingsAdapter: SettingsAdapter? = null
     private var settingsAdapter: SettingsAdapter? = null
 
 
     private var _binding: FragmentSettingsBinding? = null
     private var _binding: FragmentSettingsBinding? = null
     private val binding get() = _binding!!
     private val binding get() = _binding!!
 
 
-    override fun onAttach(context: Context) {
-        super.onAttach(context)
-        activityView = requireActivity() as SettingsActivityView
-    }
+    private val args by navArgs<SettingsFragmentArgs>()
+
+    private val settingsViewModel: SettingsViewModel by activityViewModels()
 
 
     override fun onCreate(savedInstanceState: Bundle?) {
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         super.onCreate(savedInstanceState)
-        val menuTag = requireArguments().getString(ARGUMENT_MENU_TAG)
-        val gameId = requireArguments().getString(ARGUMENT_GAME_ID)
-        fragmentPresenter.onCreate(menuTag!!, gameId!!)
+        enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
+        returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
+        reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
+        exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
     }
     }
 
 
     override fun onCreateView(
     override fun onCreateView(
@@ -48,7 +50,14 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
     }
     }
 
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
-        settingsAdapter = SettingsAdapter(this, requireActivity())
+        settingsAdapter = SettingsAdapter(this, requireContext())
+        presenter = SettingsFragmentPresenter(
+            settingsViewModel,
+            settingsAdapter!!,
+            args.menuTag,
+            args.game?.gameId ?: ""
+        )
+
         val dividerDecoration = MaterialDividerItemDecoration(
         val dividerDecoration = MaterialDividerItemDecoration(
             requireContext(),
             requireContext(),
             LinearLayoutManager.VERTICAL
             LinearLayoutManager.VERTICAL
@@ -56,63 +65,52 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
         dividerDecoration.isLastItemDecorated = false
         dividerDecoration.isLastItemDecorated = false
         binding.listSettings.apply {
         binding.listSettings.apply {
             adapter = settingsAdapter
             adapter = settingsAdapter
-            layoutManager = LinearLayoutManager(activity)
+            layoutManager = LinearLayoutManager(requireContext())
             addItemDecoration(dividerDecoration)
             addItemDecoration(dividerDecoration)
         }
         }
-        fragmentPresenter.onViewCreated()
 
 
-        setInsets()
-    }
-
-    override fun onDetach() {
-        super.onDetach()
-        activityView = null
-        if (settingsAdapter != null) {
-            settingsAdapter!!.closeDialog()
+        binding.toolbarSettings.setNavigationOnClickListener {
+            settingsViewModel.setShouldNavigateBack(true)
         }
         }
-    }
 
 
-    override fun showSettingsList(settingsList: ArrayList<SettingsItem>) {
-        settingsAdapter!!.setSettingsList(settingsList)
-    }
+        settingsViewModel.toolbarTitle.observe(viewLifecycleOwner) {
+            if (it.isNotEmpty()) binding.toolbarSettingsLayout.title = it
+        }
 
 
-    override fun loadSettingsList() {
-        fragmentPresenter.loadSettingsList()
-    }
+        presenter.onViewCreated()
 
 
-    override fun loadSubMenu(menuKey: String) {
-        activityView!!.showSettingsFragment(
-            menuKey,
-            true,
-            requireArguments().getString(ARGUMENT_GAME_ID)!!
-        )
+        setInsets()
     }
     }
 
 
-    override fun onSettingChanged() {
-        activityView!!.onSettingChanged()
+    override fun onDetach() {
+        super.onDetach()
+        settingsAdapter?.closeDialog()
     }
     }
 
 
     private fun setInsets() {
     private fun setInsets() {
         ViewCompat.setOnApplyWindowInsetsListener(
         ViewCompat.setOnApplyWindowInsetsListener(
-            binding.listSettings
-        ) { view: View, windowInsets: WindowInsetsCompat ->
-            val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
-            view.updatePadding(bottom = insets.bottom)
+            binding.root
+        ) { _: View, windowInsets: WindowInsetsCompat ->
+            val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
+            val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
+
+            val leftInsets = barInsets.left + cutoutInsets.left
+            val rightInsets = barInsets.right + cutoutInsets.right
+
+            val sideMargin = resources.getDimensionPixelSize(R.dimen.spacing_medlarge)
+            val mlpSettingsList = binding.listSettings.layoutParams as MarginLayoutParams
+            mlpSettingsList.leftMargin = sideMargin + leftInsets
+            mlpSettingsList.rightMargin = sideMargin + rightInsets
+            binding.listSettings.layoutParams = mlpSettingsList
+            binding.listSettings.updatePadding(
+                bottom = barInsets.bottom
+            )
+
+            val mlpAppBar = binding.appbarSettings.layoutParams as MarginLayoutParams
+            mlpAppBar.leftMargin = leftInsets
+            mlpAppBar.rightMargin = rightInsets
+            binding.appbarSettings.layoutParams = mlpAppBar
             windowInsets
             windowInsets
         }
         }
     }
     }
-
-    companion object {
-        private const val ARGUMENT_MENU_TAG = "menu_tag"
-        private const val ARGUMENT_GAME_ID = "game_id"
-
-        fun newInstance(menuTag: String?, gameId: String?): Fragment {
-            val fragment = SettingsFragment()
-            val arguments = Bundle()
-            arguments.putString(ARGUMENT_MENU_TAG, menuTag)
-            arguments.putString(ARGUMENT_GAME_ID, gameId)
-            fragment.arguments = arguments
-            return fragment
-        }
-    }
 }
 }

+ 34 - 34
src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt

@@ -3,6 +3,7 @@
 
 
 package org.yuzu.yuzu_emu.features.settings.ui
 package org.yuzu.yuzu_emu.features.settings.ui
 
 
+import android.content.Context
 import android.content.SharedPreferences
 import android.content.SharedPreferences
 import android.os.Build
 import android.os.Build
 import android.text.TextUtils
 import android.text.TextUtils
@@ -20,36 +21,36 @@ import org.yuzu.yuzu_emu.features.settings.model.Settings
 import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
 import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
 import org.yuzu.yuzu_emu.features.settings.model.view.*
 import org.yuzu.yuzu_emu.features.settings.model.view.*
 import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
 import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
-import org.yuzu.yuzu_emu.fragments.ResetSettingsDialogFragment
-import org.yuzu.yuzu_emu.utils.ThemeHelper
+import org.yuzu.yuzu_emu.model.SettingsViewModel
 
 
-class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) {
-    private var menuTag: String? = null
-    private lateinit var gameId: String
-    private var settingsList: ArrayList<SettingsItem>? = null
+class SettingsFragmentPresenter(
+    private val settingsViewModel: SettingsViewModel,
+    private val adapter: SettingsAdapter,
+    private var menuTag: String,
+    private var gameId: String
+) {
+    private var settingsList = ArrayList<SettingsItem>()
 
 
-    private val settingsActivity get() = fragmentView.activityView as SettingsActivity
+    private val preferences: SharedPreferences
+        get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
 
 
-    private lateinit var preferences: SharedPreferences
-
-    fun onCreate(menuTag: String, gameId: String) {
-        this.gameId = gameId
-        this.menuTag = menuTag
-    }
+    private val context: Context get() = YuzuApplication.appContext
 
 
     fun onViewCreated() {
     fun onViewCreated() {
-        preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
         loadSettingsList()
         loadSettingsList()
     }
     }
 
 
-    fun loadSettingsList() {
+    private fun loadSettingsList() {
         if (!TextUtils.isEmpty(gameId)) {
         if (!TextUtils.isEmpty(gameId)) {
-            settingsActivity.setToolbarTitle("Game Settings: $gameId")
+            settingsViewModel.setToolbarTitle(
+                context.getString(
+                    R.string.advanced_settings_game,
+                    gameId
+                )
+            )
         }
         }
+
         val sl = ArrayList<SettingsItem>()
         val sl = ArrayList<SettingsItem>()
-        if (menuTag == null) {
-            return
-        }
         when (menuTag) {
         when (menuTag) {
             SettingsFile.FILE_NAME_CONFIG -> addConfigSettings(sl)
             SettingsFile.FILE_NAME_CONFIG -> addConfigSettings(sl)
             Settings.SECTION_GENERAL -> addGeneralSettings(sl)
             Settings.SECTION_GENERAL -> addGeneralSettings(sl)
@@ -69,11 +70,11 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
             }
             }
         }
         }
         settingsList = sl
         settingsList = sl
-        fragmentView.showSettingsList(settingsList!!)
+        adapter.setSettingsList(settingsList)
     }
     }
 
 
     private fun addConfigSettings(sl: ArrayList<SettingsItem>) {
     private fun addConfigSettings(sl: ArrayList<SettingsItem>) {
-        settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.advanced_settings))
+        settingsViewModel.setToolbarTitle(context.getString(R.string.advanced_settings))
         sl.apply {
         sl.apply {
             add(SubmenuSetting(R.string.preferences_general, 0, Settings.SECTION_GENERAL))
             add(SubmenuSetting(R.string.preferences_general, 0, Settings.SECTION_GENERAL))
             add(SubmenuSetting(R.string.preferences_system, 0, Settings.SECTION_SYSTEM))
             add(SubmenuSetting(R.string.preferences_system, 0, Settings.SECTION_SYSTEM))
@@ -82,17 +83,14 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
             add(SubmenuSetting(R.string.preferences_debug, 0, Settings.SECTION_DEBUG))
             add(SubmenuSetting(R.string.preferences_debug, 0, Settings.SECTION_DEBUG))
             add(
             add(
                 RunnableSetting(R.string.reset_to_default, 0, false) {
                 RunnableSetting(R.string.reset_to_default, 0, false) {
-                    ResetSettingsDialogFragment().show(
-                        settingsActivity.supportFragmentManager,
-                        ResetSettingsDialogFragment.TAG
-                    )
+                    settingsViewModel.setShouldShowResetSettingsDialog(true)
                 }
                 }
             )
             )
         }
         }
     }
     }
 
 
     private fun addGeneralSettings(sl: ArrayList<SettingsItem>) {
     private fun addGeneralSettings(sl: ArrayList<SettingsItem>) {
-        settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_general))
+        settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_general))
         sl.apply {
         sl.apply {
             add(
             add(
                 SwitchSetting(
                 SwitchSetting(
@@ -131,7 +129,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
     }
     }
 
 
     private fun addSystemSettings(sl: ArrayList<SettingsItem>) {
     private fun addSystemSettings(sl: ArrayList<SettingsItem>) {
-        settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_system))
+        settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_system))
         sl.apply {
         sl.apply {
             add(
             add(
                 SwitchSetting(
                 SwitchSetting(
@@ -170,7 +168,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
     }
     }
 
 
     private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) {
     private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) {
-        settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_graphics))
+        settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_graphics))
         sl.apply {
         sl.apply {
             add(
             add(
                 SingleChoiceSetting(
                 SingleChoiceSetting(
@@ -267,7 +265,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
     }
     }
 
 
     private fun addAudioSettings(sl: ArrayList<SettingsItem>) {
     private fun addAudioSettings(sl: ArrayList<SettingsItem>) {
-        settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_audio))
+        settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_audio))
         sl.apply {
         sl.apply {
             add(
             add(
                 SingleChoiceSetting(
                 SingleChoiceSetting(
@@ -292,7 +290,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
     }
     }
 
 
     private fun addThemeSettings(sl: ArrayList<SettingsItem>) {
     private fun addThemeSettings(sl: ArrayList<SettingsItem>) {
-        settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_theme))
+        settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_theme))
         sl.apply {
         sl.apply {
             val theme: AbstractIntSetting = object : AbstractIntSetting {
             val theme: AbstractIntSetting = object : AbstractIntSetting {
                 override val int: Int
                 override val int: Int
@@ -302,7 +300,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
                     preferences.edit()
                     preferences.edit()
                         .putInt(Settings.PREF_THEME, value)
                         .putInt(Settings.PREF_THEME, value)
                         .apply()
                         .apply()
-                    settingsActivity.recreate()
+                    settingsViewModel.setShouldRecreate(true)
                 }
                 }
 
 
                 override val key: String? = null
                 override val key: String? = null
@@ -346,7 +344,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
                     preferences.edit()
                     preferences.edit()
                         .putInt(Settings.PREF_THEME_MODE, value)
                         .putInt(Settings.PREF_THEME_MODE, value)
                         .apply()
                         .apply()
-                    ThemeHelper.setThemeMode(settingsActivity)
+                    settingsViewModel.setShouldRecreate(true)
                 }
                 }
 
 
                 override val key: String? = null
                 override val key: String? = null
@@ -357,6 +355,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
                     preferences.edit()
                     preferences.edit()
                         .putInt(Settings.PREF_BLACK_BACKGROUNDS, defaultValue)
                         .putInt(Settings.PREF_BLACK_BACKGROUNDS, defaultValue)
                         .apply()
                         .apply()
+                    settingsViewModel.setShouldRecreate(true)
                 }
                 }
             }
             }
 
 
@@ -378,7 +377,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
                     preferences.edit()
                     preferences.edit()
                         .putBoolean(Settings.PREF_BLACK_BACKGROUNDS, value)
                         .putBoolean(Settings.PREF_BLACK_BACKGROUNDS, value)
                         .apply()
                         .apply()
-                    settingsActivity.recreate()
+                    settingsViewModel.setShouldRecreate(true)
                 }
                 }
 
 
                 override val key: String? = null
                 override val key: String? = null
@@ -389,6 +388,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
                     preferences.edit()
                     preferences.edit()
                         .putBoolean(Settings.PREF_BLACK_BACKGROUNDS, defaultValue)
                         .putBoolean(Settings.PREF_BLACK_BACKGROUNDS, defaultValue)
                         .apply()
                         .apply()
+                    settingsViewModel.setShouldRecreate(true)
                 }
                 }
             }
             }
 
 
@@ -403,7 +403,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
     }
     }
 
 
     private fun addDebugSettings(sl: ArrayList<SettingsItem>) {
     private fun addDebugSettings(sl: ArrayList<SettingsItem>) {
-        settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_debug))
+        settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_debug))
         sl.apply {
         sl.apply {
             add(HeaderSetting(R.string.gpu))
             add(HeaderSetting(R.string.gpu))
             add(
             add(

+ 0 - 42
src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentView.kt

@@ -1,42 +0,0 @@
-// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.yuzu.yuzu_emu.features.settings.ui
-
-import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
-
-/**
- * Abstraction for a screen showing a list of settings. Instances of
- * this type of view will each display a layer of the setting hierarchy.
- */
-interface SettingsFragmentView {
-    /**
-     * Pass an ArrayList to the View so that it can be displayed on screen.
-     *
-     * @param settingsList The result of converting the HashMap to an ArrayList
-     */
-    fun showSettingsList(settingsList: ArrayList<SettingsItem>)
-
-    /**
-     * Instructs the Fragment to load the settings screen.
-     */
-    fun loadSettingsList()
-
-    /**
-     * @return The Fragment's containing activity.
-     */
-    val activityView: SettingsActivityView?
-
-    /**
-     * Tell the Fragment to tell the containing Activity to show a new
-     * Fragment containing a submenu of settings.
-     *
-     * @param menuKey Identifier for the settings group that should be shown.
-     */
-    fun loadSubMenu(menuKey: String)
-
-    /**
-     * Have the fragment tell the containing Activity that a setting was modified.
-     */
-    fun onSettingChanged()
-}

+ 8 - 3
src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt

@@ -28,6 +28,7 @@ import androidx.fragment.app.Fragment
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
 import androidx.lifecycle.repeatOnLifecycle
+import androidx.navigation.findNavController
 import androidx.navigation.fragment.navArgs
 import androidx.navigation.fragment.navArgs
 import androidx.preference.PreferenceManager
 import androidx.preference.PreferenceManager
 import androidx.window.layout.FoldingFeature
 import androidx.window.layout.FoldingFeature
@@ -37,6 +38,7 @@ 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.Dispatchers
 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
@@ -45,7 +47,6 @@ import org.yuzu.yuzu_emu.databinding.DialogOverlayAdjustBinding
 import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
 import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
 import org.yuzu.yuzu_emu.features.settings.model.IntSetting
 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.utils.SettingsFile
 import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
 import org.yuzu.yuzu_emu.overlay.InputOverlay
 import org.yuzu.yuzu_emu.overlay.InputOverlay
 import org.yuzu.yuzu_emu.utils.*
 import org.yuzu.yuzu_emu.utils.*
@@ -139,7 +140,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
                 }
                 }
 
 
                 R.id.menu_settings -> {
                 R.id.menu_settings -> {
-                    SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "")
+                    val action = HomeNavigationDirections.actionGlobalSettingsActivity(
+                        null,
+                        SettingsFile.FILE_NAME_CONFIG
+                    )
+                    binding.root.findNavController().navigate(action)
                     true
                     true
                 }
                 }
 
 
@@ -211,7 +216,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
     override fun onResume() {
     override fun onResume() {
         super.onResume()
         super.onResume()
         if (!DirectoryInitialization.areDirectoriesReady) {
         if (!DirectoryInitialization.areDirectoriesReady) {
-            DirectoryInitialization.start(requireContext())
+            DirectoryInitialization.start()
         }
         }
 
 
         updateScreenLayout()
         updateScreenLayout()

+ 16 - 3
src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt

@@ -25,17 +25,18 @@ import androidx.core.view.updatePadding
 import androidx.documentfile.provider.DocumentFile
 import androidx.documentfile.provider.DocumentFile
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.activityViewModels
 import androidx.fragment.app.activityViewModels
+import androidx.navigation.findNavController
 import androidx.navigation.fragment.findNavController
 import androidx.navigation.fragment.findNavController
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.LinearLayoutManager
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import com.google.android.material.transition.MaterialSharedAxis
 import com.google.android.material.transition.MaterialSharedAxis
 import org.yuzu.yuzu_emu.BuildConfig
 import org.yuzu.yuzu_emu.BuildConfig
+import org.yuzu.yuzu_emu.HomeNavigationDirections
 import org.yuzu.yuzu_emu.R
 import org.yuzu.yuzu_emu.R
 import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter
 import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter
 import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding
 import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding
 import org.yuzu.yuzu_emu.features.DocumentProvider
 import org.yuzu.yuzu_emu.features.DocumentProvider
 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.utils.SettingsFile
 import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
 import org.yuzu.yuzu_emu.model.HomeSetting
 import org.yuzu.yuzu_emu.model.HomeSetting
 import org.yuzu.yuzu_emu.model.HomeViewModel
 import org.yuzu.yuzu_emu.model.HomeViewModel
@@ -74,7 +75,13 @@ class HomeSettingsFragment : Fragment() {
                     R.string.advanced_settings,
                     R.string.advanced_settings,
                     R.string.settings_description,
                     R.string.settings_description,
                     R.drawable.ic_settings,
                     R.drawable.ic_settings,
-                    { SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") }
+                    {
+                        val action = HomeNavigationDirections.actionGlobalSettingsActivity(
+                            null,
+                            SettingsFile.FILE_NAME_CONFIG
+                        )
+                        binding.root.findNavController().navigate(action)
+                    }
                 )
                 )
             )
             )
             add(
             add(
@@ -90,7 +97,13 @@ class HomeSettingsFragment : Fragment() {
                     R.string.preferences_theme,
                     R.string.preferences_theme,
                     R.string.theme_and_color_description,
                     R.string.theme_and_color_description,
                     R.drawable.ic_palette,
                     R.drawable.ic_palette,
-                    { SettingsActivity.launch(requireContext(), Settings.SECTION_THEME, "") }
+                    {
+                        val action = HomeNavigationDirections.actionGlobalSettingsActivity(
+                            null,
+                            Settings.SECTION_THEME
+                        )
+                        binding.root.findNavController().navigate(action)
+                    }
                 )
                 )
             )
             )
             add(
             add(

+ 47 - 0
src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt

@@ -0,0 +1,47 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.model
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+
+class SettingsViewModel : ViewModel() {
+    var game: Game? = null
+
+    var shouldSave = false
+
+    private val _toolbarTitle = MutableLiveData("")
+    val toolbarTitle: LiveData<String> get() = _toolbarTitle
+
+    private val _shouldRecreate = MutableLiveData(false)
+    val shouldRecreate: LiveData<Boolean> get() = _shouldRecreate
+
+    private val _shouldNavigateBack = MutableLiveData(false)
+    val shouldNavigateBack: LiveData<Boolean> get() = _shouldNavigateBack
+
+    private val _shouldShowResetSettingsDialog = MutableLiveData(false)
+    val shouldShowResetSettingsDialog: LiveData<Boolean> get() = _shouldShowResetSettingsDialog
+
+    fun setToolbarTitle(value: String) {
+        _toolbarTitle.value = value
+    }
+
+    fun setShouldRecreate(value: Boolean) {
+        _shouldRecreate.value = value
+    }
+
+    fun setShouldNavigateBack(value: Boolean) {
+        _shouldNavigateBack.value = value
+    }
+
+    fun setShouldShowResetSettingsDialog(value: Boolean) {
+        _shouldShowResetSettingsDialog.value = value
+    }
+
+    fun clear() {
+        game = null
+        shouldSave = false
+    }
+}

+ 8 - 6
src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt

@@ -33,13 +33,13 @@ import java.io.IOException
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 import kotlinx.coroutines.withContext
+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.activities.EmulationActivity
 import org.yuzu.yuzu_emu.activities.EmulationActivity
 import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
 import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
 import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
 import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
 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.utils.SettingsFile
 import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
 import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
 import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
 import org.yuzu.yuzu_emu.fragments.LongMessageDialogFragment
 import org.yuzu.yuzu_emu.fragments.LongMessageDialogFragment
@@ -105,11 +105,13 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
             when (it.itemId) {
             when (it.itemId) {
                 R.id.gamesFragment -> gamesViewModel.setShouldScrollToTop(true)
                 R.id.gamesFragment -> gamesViewModel.setShouldScrollToTop(true)
                 R.id.searchFragment -> gamesViewModel.setSearchFocused(true)
                 R.id.searchFragment -> gamesViewModel.setSearchFocused(true)
-                R.id.homeSettingsFragment -> SettingsActivity.launch(
-                    this,
-                    SettingsFile.FILE_NAME_CONFIG,
-                    ""
-                )
+                R.id.homeSettingsFragment -> {
+                    val action = HomeNavigationDirections.actionGlobalSettingsActivity(
+                        null,
+                        SettingsFile.FILE_NAME_CONFIG
+                    )
+                    navHostFragment.navController.navigate(action)
+                }
             }
             }
         }
         }
 
 

+ 5 - 5
src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt

@@ -3,18 +3,18 @@
 
 
 package org.yuzu.yuzu_emu.utils
 package org.yuzu.yuzu_emu.utils
 
 
-import android.content.Context
 import java.io.IOException
 import java.io.IOException
 import org.yuzu.yuzu_emu.NativeLibrary
 import org.yuzu.yuzu_emu.NativeLibrary
+import org.yuzu.yuzu_emu.YuzuApplication
 
 
 object DirectoryInitialization {
 object DirectoryInitialization {
     private var userPath: String? = null
     private var userPath: String? = null
 
 
     var areDirectoriesReady: Boolean = false
     var areDirectoriesReady: Boolean = false
 
 
-    fun start(context: Context) {
+    fun start() {
         if (!areDirectoriesReady) {
         if (!areDirectoriesReady) {
-            initializeInternalStorage(context)
+            initializeInternalStorage()
             NativeLibrary.initializeEmulation()
             NativeLibrary.initializeEmulation()
             areDirectoriesReady = true
             areDirectoriesReady = true
         }
         }
@@ -26,9 +26,9 @@ object DirectoryInitialization {
             return userPath
             return userPath
         }
         }
 
 
-    private fun initializeInternalStorage(context: Context) {
+    private fun initializeInternalStorage() {
         try {
         try {
-            userPath = context.getExternalFilesDir(null)!!.canonicalPath
+            userPath = YuzuApplication.appContext.getExternalFilesDir(null)!!.canonicalPath
             NativeLibrary.setAppDirectory(userPath!!)
             NativeLibrary.setAppDirectory(userPath!!)
         } catch (e: IOException) {
         } catch (e: IOException) {
             e.printStackTrace()
             e.printStackTrace()

+ 0 - 16
src/android/app/src/main/res/anim-ldrtl/anim_pop_settings_fragment_out.xml

@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<set xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <alpha
-        android:duration="125"
-        android:interpolator="@android:anim/decelerate_interpolator"
-        android:fromAlpha="1"
-        android:toAlpha="0" />
-
-    <translate
-        android:duration="125"
-        android:interpolator="@android:anim/decelerate_interpolator"
-        android:fromXDelta="0"
-        android:toXDelta="-75" />
-
-</set>

+ 0 - 16
src/android/app/src/main/res/anim-ldrtl/anim_settings_fragment_in.xml

@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<set xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <alpha
-        android:duration="@android:integer/config_shortAnimTime"
-        android:interpolator="@android:anim/decelerate_interpolator"
-        android:fromAlpha="0"
-        android:toAlpha="1" />
-
-    <translate
-        android:duration="@android:integer/config_shortAnimTime"
-        android:interpolator="@android:anim/decelerate_interpolator"
-        android:fromXDelta="-200"
-        android:toXDelta="0" />
-
-</set>

+ 0 - 16
src/android/app/src/main/res/anim/anim_pop_settings_fragment_out.xml

@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<set xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <alpha
-        android:duration="125"
-        android:interpolator="@android:anim/decelerate_interpolator"
-        android:fromAlpha="1"
-        android:toAlpha="0" />
-
-    <translate
-        android:duration="125"
-        android:interpolator="@android:anim/decelerate_interpolator"
-        android:fromXDelta="0"
-        android:toXDelta="75" />
-
-</set>

+ 0 - 16
src/android/app/src/main/res/anim/anim_settings_fragment_in.xml

@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<set xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <alpha
-        android:duration="@android:integer/config_shortAnimTime"
-        android:interpolator="@android:anim/decelerate_interpolator"
-        android:fromAlpha="0"
-        android:toAlpha="1" />
-
-    <translate
-        android:duration="@android:integer/config_shortAnimTime"
-        android:interpolator="@android:anim/decelerate_interpolator"
-        android:fromXDelta="200"
-        android:toXDelta="0" />
-
-</set>

+ 0 - 10
src/android/app/src/main/res/anim/anim_settings_fragment_out.xml

@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<set xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <alpha
-        android:duration="@android:integer/config_shortAnimTime"
-        android:interpolator="@android:anim/decelerate_interpolator"
-        android:fromAlpha="1"
-        android:toAlpha="0" />
-
-</set>

+ 0 - 20
src/android/app/src/main/res/animator/menu_slide_in_from_start.xml

@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<set xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <objectAnimator
-        android:propertyName="translationX"
-        android:valueType="floatType"
-        android:valueFrom="-1280dp"
-        android:valueTo="0"
-        android:interpolator="@android:interpolator/decelerate_quad"
-        android:duration="300"/>
-
-    <objectAnimator
-        android:propertyName="alpha"
-        android:valueType="floatType"
-        android:valueFrom="0"
-        android:valueTo="1"
-        android:interpolator="@android:interpolator/accelerate_quad"
-        android:duration="300"/>
-
-</set>

+ 0 - 21
src/android/app/src/main/res/animator/menu_slide_out_to_start.xml

@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<set xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <!-- This animation is used ONLY when a submenu is replaced. -->
-    <objectAnimator
-        android:propertyName="translationX"
-        android:valueType="floatType"
-        android:valueFrom="0"
-        android:valueTo="-1280dp"
-        android:interpolator="@android:interpolator/decelerate_quad"
-        android:duration="200"/>
-
-    <objectAnimator
-        android:propertyName="alpha"
-        android:valueType="floatType"
-        android:valueFrom="1"
-        android:valueTo="0"
-        android:interpolator="@android:interpolator/decelerate_quad"
-        android:duration="200"/>
-
-</set>

+ 18 - 34
src/android/app/src/main/res/layout/activity_settings.xml

@@ -1,42 +1,24 @@
 <?xml version="1.0" encoding="utf-8"?>
 <?xml version="1.0" encoding="utf-8"?>
-<androidx.coordinatorlayout.widget.CoordinatorLayout
-    android:id="@+id/coordinator_main"
+<androidx.constraintlayout.widget.ConstraintLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/constraint_settings"
     android:layout_width="match_parent"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:layout_height="match_parent"
     android:background="?attr/colorSurface">
     android:background="?attr/colorSurface">
 
 
-    <com.google.android.material.appbar.AppBarLayout
-        android:id="@+id/appbar_settings"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:fitsSystemWindows="true"
-        app:elevation="0dp">
-
-        <com.google.android.material.appbar.CollapsingToolbarLayout
-            style="?attr/collapsingToolbarLayoutMediumStyle"
-            android:id="@+id/toolbar_settings_layout"
-            android:layout_width="match_parent"
-            android:layout_height="?attr/collapsingToolbarLayoutMediumSize"
-            app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
-
-            <com.google.android.material.appbar.MaterialToolbar
-                android:id="@+id/toolbar_settings"
-                android:layout_width="match_parent"
-                android:layout_height="?attr/actionBarSize"
-                app:layout_collapseMode="pin" />
-
-        </com.google.android.material.appbar.CollapsingToolbarLayout>
-
-    </com.google.android.material.appbar.AppBarLayout>
-
-    <FrameLayout
-        android:id="@+id/frame_content"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_marginHorizontal="12dp"
-        app:layout_behavior="@string/appbar_scrolling_view_behavior" />
+    <androidx.fragment.app.FragmentContainerView
+        android:id="@+id/fragment_container"
+        android:name="androidx.navigation.fragment.NavHostFragment"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        app:defaultNavHost="true"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:layout="@layout/fragment_settings" />
 
 
     <View
     <View
         android:id="@+id/navigation_bar_shade"
         android:id="@+id/navigation_bar_shade"
@@ -45,6 +27,8 @@
         android:background="@android:color/transparent"
         android:background="@android:color/transparent"
         android:clickable="false"
         android:clickable="false"
         android:focusable="false"
         android:focusable="false"
-        android:layout_gravity="bottom|center_horizontal" />
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent" />
 
 
-</androidx.coordinatorlayout.widget.CoordinatorLayout>
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 33 - 6
src/android/app/src/main/res/layout/fragment_settings.xml

@@ -1,14 +1,41 @@
 <?xml version="1.0" encoding="utf-8"?>
 <?xml version="1.0" encoding="utf-8"?>
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/coordinator_main"
     android:layout_width="match_parent"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:background="?attr/colorSurface">
+
+    <com.google.android.material.appbar.AppBarLayout
+        android:id="@+id/appbar_settings"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:fitsSystemWindows="true"
+        app:elevation="0dp">
+
+        <com.google.android.material.appbar.CollapsingToolbarLayout
+            android:id="@+id/toolbar_settings_layout"
+            style="?attr/collapsingToolbarLayoutMediumStyle"
+            android:layout_width="match_parent"
+            android:layout_height="?attr/collapsingToolbarLayoutMediumSize"
+            app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
+
+            <com.google.android.material.appbar.MaterialToolbar
+                android:id="@+id/toolbar_settings"
+                android:layout_width="match_parent"
+                android:layout_height="?attr/actionBarSize"
+                app:layout_collapseMode="pin"
+                app:navigationIcon="@drawable/ic_back" />
+
+        </com.google.android.material.appbar.CollapsingToolbarLayout>
+
+    </com.google.android.material.appbar.AppBarLayout>
 
 
     <androidx.recyclerview.widget.RecyclerView
     <androidx.recyclerview.widget.RecyclerView
         android:id="@+id/list_settings"
         android:id="@+id/list_settings"
         android:layout_width="match_parent"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_height="match_parent"
-        android:background="?attr/colorSurface"
-        android:clipToPadding="false" />
+        android:clipToPadding="false"
+        app:layout_behavior="@string/appbar_scrolling_view_behavior" />
 
 
-</FrameLayout>
+</androidx.coordinatorlayout.widget.CoordinatorLayout>

+ 0 - 2
src/android/app/src/main/res/menu/menu_settings.xml

@@ -1,2 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu />

+ 17 - 0
src/android/app/src/main/res/navigation/emulation_navigation.xml

@@ -15,4 +15,21 @@
             app:argType="org.yuzu.yuzu_emu.model.Game" />
             app:argType="org.yuzu.yuzu_emu.model.Game" />
     </fragment>
     </fragment>
 
 
+    <activity
+        android:id="@+id/settingsActivity"
+        android:name="org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity"
+        android:label="SettingsActivity">
+        <argument
+            android:name="game"
+            app:argType="org.yuzu.yuzu_emu.model.Game"
+            app:nullable="true" />
+        <argument
+            android:name="menuTag"
+            app:argType="string" />
+    </activity>
+
+    <action
+        android:id="@+id/action_global_settingsActivity"
+        app:destination="@id/settingsActivity" />
+
 </navigation>
 </navigation>

+ 17 - 0
src/android/app/src/main/res/navigation/home_navigation.xml

@@ -70,4 +70,21 @@
         app:destination="@id/emulationActivity"
         app:destination="@id/emulationActivity"
         app:launchSingleTop="true" />
         app:launchSingleTop="true" />
 
 
+    <activity
+        android:id="@+id/settingsActivity"
+        android:name="org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity"
+        android:label="SettingsActivity">
+        <argument
+            android:name="game"
+            app:argType="org.yuzu.yuzu_emu.model.Game"
+            app:nullable="true" />
+        <argument
+            android:name="menuTag"
+            app:argType="string" />
+    </activity>
+
+    <action
+        android:id="@+id/action_global_settingsActivity"
+        app:destination="@id/settingsActivity" />
+
 </navigation>
 </navigation>

+ 24 - 0
src/android/app/src/main/res/navigation/settings_navigation.xml

@@ -0,0 +1,24 @@
+<?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"
+    android:id="@+id/settings_navigation"
+    app:startDestination="@id/settingsFragment">
+
+    <fragment
+        android:id="@+id/settingsFragment"
+        android:name="org.yuzu.yuzu_emu.features.settings.ui.SettingsFragment"
+        android:label="SettingsFragment">
+        <argument
+            android:name="menuTag"
+            app:argType="string" />
+        <argument
+            android:name="game"
+            app:argType="org.yuzu.yuzu_emu.model.Game"
+            app:nullable="true" />
+    </fragment>
+
+    <action
+        android:id="@+id/action_global_settingsFragment"
+        app:destination="@id/settingsFragment" />
+
+</navigation>

+ 1 - 0
src/android/app/src/main/res/values/strings.xml

@@ -74,6 +74,7 @@
     <string name="install_gpu_driver">Install GPU driver</string>
     <string name="install_gpu_driver">Install GPU driver</string>
     <string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string>
     <string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string>
     <string name="advanced_settings">Advanced settings</string>
     <string name="advanced_settings">Advanced settings</string>
+    <string name="advanced_settings_game">Advanced settings: %1$s</string>
     <string name="settings_description">Configure emulator settings</string>
     <string name="settings_description">Configure emulator settings</string>
     <string name="search_recently_played">Recently played</string>
     <string name="search_recently_played">Recently played</string>
     <string name="search_recently_added">Recently added</string>
     <string name="search_recently_added">Recently added</string>