summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt11
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt53
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt53
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt45
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt113
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt32
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt46
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt16
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt16
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt44
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt44
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeSetting.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt28
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt57
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt27
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt64
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt23
-rw-r--r--src/android/app/src/main/res/navigation/home_navigation.xml2
-rw-r--r--src/android/app/src/main/res/navigation/settings_navigation.xml2
22 files changed, 406 insertions, 295 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
index 8d87d3bd7..1675627a1 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
@@ -10,8 +10,12 @@ import android.view.ViewGroup
10import androidx.appcompat.app.AppCompatActivity 10import androidx.appcompat.app.AppCompatActivity
11import androidx.core.content.ContextCompat 11import androidx.core.content.ContextCompat
12import androidx.core.content.res.ResourcesCompat 12import androidx.core.content.res.ResourcesCompat
13import androidx.lifecycle.Lifecycle
13import androidx.lifecycle.LifecycleOwner 14import androidx.lifecycle.LifecycleOwner
15import androidx.lifecycle.lifecycleScope
16import androidx.lifecycle.repeatOnLifecycle
14import androidx.recyclerview.widget.RecyclerView 17import androidx.recyclerview.widget.RecyclerView
18import kotlinx.coroutines.launch
15import org.yuzu.yuzu_emu.R 19import org.yuzu.yuzu_emu.R
16import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding 20import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding
17import org.yuzu.yuzu_emu.fragments.MessageDialogFragment 21import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
@@ -86,7 +90,11 @@ class HomeSettingAdapter(
86 binding.optionIcon.alpha = 0.5f 90 binding.optionIcon.alpha = 0.5f
87 } 91 }
88 92
89 option.details.observe(viewLifecycle) { updateOptionDetails(it) } 93 viewLifecycle.lifecycleScope.launch {
94 viewLifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) {
95 option.details.collect { updateOptionDetails(it) }
96 }
97 }
90 binding.optionDetail.postDelayed( 98 binding.optionDetail.postDelayed(
91 { 99 {
92 binding.optionDetail.ellipsize = TextUtils.TruncateAt.MARQUEE 100 binding.optionDetail.ellipsize = TextUtils.TruncateAt.MARQUEE
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
index 0702236e8..08e2a973d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
@@ -80,6 +80,17 @@ object Settings {
80 const val SECTION_THEME = "Theme" 80 const val SECTION_THEME = "Theme"
81 const val SECTION_DEBUG = "Debug" 81 const val SECTION_DEBUG = "Debug"
82 82
83 enum class MenuTag(val titleId: Int) {
84 SECTION_ROOT(R.string.advanced_settings),
85 SECTION_GENERAL(R.string.preferences_general),
86 SECTION_SYSTEM(R.string.preferences_system),
87 SECTION_RENDERER(R.string.preferences_graphics),
88 SECTION_AUDIO(R.string.preferences_audio),
89 SECTION_CPU(R.string.cpu),
90 SECTION_THEME(R.string.preferences_theme),
91 SECTION_DEBUG(R.string.preferences_debug);
92 }
93
83 const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown" 94 const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown"
84 95
85 const val PREF_OVERLAY_VERSION = "OverlayVersion" 96 const val PREF_OVERLAY_VERSION = "OverlayVersion"
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt
index 91c273964..b343e527e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt
@@ -3,10 +3,12 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model.view 4package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6import org.yuzu.yuzu_emu.features.settings.model.Settings
7
6class SubmenuSetting( 8class SubmenuSetting(
7 titleId: Int, 9 titleId: Int,
8 descriptionId: Int, 10 descriptionId: Int,
9 val menuKey: String 11 val menuKey: Settings.MenuTag
10) : SettingsItem(emptySetting, titleId, descriptionId) { 12) : SettingsItem(emptySetting, titleId, descriptionId) {
11 override val type = TYPE_SUBMENU 13 override val type = TYPE_SUBMENU
12} 14}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
index 908c01265..4d2f2f604 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
@@ -13,9 +13,14 @@ import androidx.appcompat.app.AppCompatActivity
13import androidx.core.view.ViewCompat 13import androidx.core.view.ViewCompat
14import androidx.core.view.WindowCompat 14import androidx.core.view.WindowCompat
15import androidx.core.view.WindowInsetsCompat 15import androidx.core.view.WindowInsetsCompat
16import androidx.lifecycle.Lifecycle
17import androidx.lifecycle.lifecycleScope
18import androidx.lifecycle.repeatOnLifecycle
16import androidx.navigation.fragment.NavHostFragment 19import androidx.navigation.fragment.NavHostFragment
17import androidx.navigation.navArgs 20import androidx.navigation.navArgs
18import com.google.android.material.color.MaterialColors 21import com.google.android.material.color.MaterialColors
22import kotlinx.coroutines.flow.collectLatest
23import kotlinx.coroutines.launch
19import java.io.IOException 24import java.io.IOException
20import org.yuzu.yuzu_emu.R 25import org.yuzu.yuzu_emu.R
21import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding 26import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
@@ -66,25 +71,39 @@ class SettingsActivity : AppCompatActivity() {
66 ) 71 )
67 } 72 }
68 73
69 settingsViewModel.shouldRecreate.observe(this) { 74 lifecycleScope.apply {
70 if (it) { 75 launch {
71 settingsViewModel.setShouldRecreate(false) 76 repeatOnLifecycle(Lifecycle.State.CREATED) {
72 recreate() 77 settingsViewModel.shouldRecreate.collectLatest {
78 if (it) {
79 settingsViewModel.setShouldRecreate(false)
80 recreate()
81 }
82 }
83 }
73 } 84 }
74 } 85 launch {
75 settingsViewModel.shouldNavigateBack.observe(this) { 86 repeatOnLifecycle(Lifecycle.State.CREATED) {
76 if (it) { 87 settingsViewModel.shouldNavigateBack.collectLatest {
77 settingsViewModel.setShouldNavigateBack(false) 88 if (it) {
78 navigateBack() 89 settingsViewModel.setShouldNavigateBack(false)
90 navigateBack()
91 }
92 }
93 }
79 } 94 }
80 } 95 launch {
81 settingsViewModel.shouldShowResetSettingsDialog.observe(this) { 96 repeatOnLifecycle(Lifecycle.State.CREATED) {
82 if (it) { 97 settingsViewModel.shouldShowResetSettingsDialog.collectLatest {
83 settingsViewModel.setShouldShowResetSettingsDialog(false) 98 if (it) {
84 ResetSettingsDialogFragment().show( 99 settingsViewModel.setShouldShowResetSettingsDialog(false)
85 supportFragmentManager, 100 ResetSettingsDialogFragment().show(
86 ResetSettingsDialogFragment.TAG 101 supportFragmentManager,
87 ) 102 ResetSettingsDialogFragment.TAG
103 )
104 }
105 }
106 }
88 } 107 }
89 } 108 }
90 109
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
index bc319714c..70d8ec14b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
@@ -3,6 +3,7 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.ui 4package org.yuzu.yuzu_emu.features.settings.ui
5 5
6import android.annotation.SuppressLint
6import android.os.Bundle 7import android.os.Bundle
7import android.view.LayoutInflater 8import android.view.LayoutInflater
8import android.view.View 9import android.view.View
@@ -13,14 +14,19 @@ import androidx.core.view.WindowInsetsCompat
13import androidx.core.view.updatePadding 14import androidx.core.view.updatePadding
14import androidx.fragment.app.Fragment 15import androidx.fragment.app.Fragment
15import androidx.fragment.app.activityViewModels 16import androidx.fragment.app.activityViewModels
17import androidx.lifecycle.Lifecycle
18import androidx.lifecycle.lifecycleScope
19import androidx.lifecycle.repeatOnLifecycle
16import androidx.navigation.findNavController 20import androidx.navigation.findNavController
17import androidx.navigation.fragment.navArgs 21import androidx.navigation.fragment.navArgs
18import androidx.recyclerview.widget.LinearLayoutManager 22import androidx.recyclerview.widget.LinearLayoutManager
19import com.google.android.material.divider.MaterialDividerItemDecoration 23import com.google.android.material.divider.MaterialDividerItemDecoration
20import com.google.android.material.transition.MaterialSharedAxis 24import com.google.android.material.transition.MaterialSharedAxis
25import kotlinx.coroutines.flow.collectLatest
26import kotlinx.coroutines.launch
21import org.yuzu.yuzu_emu.R 27import org.yuzu.yuzu_emu.R
22import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding 28import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding
23import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 29import org.yuzu.yuzu_emu.features.settings.model.Settings
24import org.yuzu.yuzu_emu.model.SettingsViewModel 30import org.yuzu.yuzu_emu.model.SettingsViewModel
25 31
26class SettingsFragment : Fragment() { 32class SettingsFragment : Fragment() {
@@ -51,15 +57,17 @@ class SettingsFragment : Fragment() {
51 return binding.root 57 return binding.root
52 } 58 }
53 59
60 // This is using the correct scope, lint is just acting up
61 @SuppressLint("UnsafeRepeatOnLifecycleDetector")
54 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 62 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
55 settingsAdapter = SettingsAdapter(this, requireContext()) 63 settingsAdapter = SettingsAdapter(this, requireContext())
56 presenter = SettingsFragmentPresenter( 64 presenter = SettingsFragmentPresenter(
57 settingsViewModel, 65 settingsViewModel,
58 settingsAdapter!!, 66 settingsAdapter!!,
59 args.menuTag, 67 args.menuTag
60 args.game?.gameId ?: ""
61 ) 68 )
62 69
70 binding.toolbarSettingsLayout.title = getString(args.menuTag.titleId)
63 val dividerDecoration = MaterialDividerItemDecoration( 71 val dividerDecoration = MaterialDividerItemDecoration(
64 requireContext(), 72 requireContext(),
65 LinearLayoutManager.VERTICAL 73 LinearLayoutManager.VERTICAL
@@ -75,28 +83,31 @@ class SettingsFragment : Fragment() {
75 settingsViewModel.setShouldNavigateBack(true) 83 settingsViewModel.setShouldNavigateBack(true)
76 } 84 }
77 85
78 settingsViewModel.toolbarTitle.observe(viewLifecycleOwner) { 86 viewLifecycleOwner.lifecycleScope.apply {
79 if (it.isNotEmpty()) binding.toolbarSettingsLayout.title = it 87 launch {
80 } 88 repeatOnLifecycle(Lifecycle.State.CREATED) {
81 89 settingsViewModel.shouldReloadSettingsList.collectLatest {
82 settingsViewModel.shouldReloadSettingsList.observe(viewLifecycleOwner) { 90 if (it) {
83 if (it) { 91 settingsViewModel.setShouldReloadSettingsList(false)
84 settingsViewModel.setShouldReloadSettingsList(false) 92 presenter.loadSettingsList()
85 presenter.loadSettingsList() 93 }
94 }
95 }
86 } 96 }
87 } 97 launch {
88 98 settingsViewModel.isUsingSearch.collectLatest {
89 settingsViewModel.isUsingSearch.observe(viewLifecycleOwner) { 99 if (it) {
90 if (it) { 100 reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
91 reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true) 101 exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
92 exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false) 102 } else {
93 } else { 103 reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
94 reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) 104 exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
95 exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) 105 }
106 }
96 } 107 }
97 } 108 }
98 109
99 if (args.menuTag == SettingsFile.FILE_NAME_CONFIG) { 110 if (args.menuTag == Settings.MenuTag.SECTION_ROOT) {
100 binding.toolbarSettings.inflateMenu(R.menu.menu_settings) 111 binding.toolbarSettings.inflateMenu(R.menu.menu_settings)
101 binding.toolbarSettings.setOnMenuItemClickListener { 112 binding.toolbarSettings.setOnMenuItemClickListener {
102 when (it.itemId) { 113 when (it.itemId) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
index 22a529b1b..766414a6c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
@@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.features.settings.ui
6import android.content.Context 6import android.content.Context
7import android.content.SharedPreferences 7import android.content.SharedPreferences
8import android.os.Build 8import android.os.Build
9import android.text.TextUtils
10import android.widget.Toast 9import android.widget.Toast
11import androidx.preference.PreferenceManager 10import androidx.preference.PreferenceManager
12import org.yuzu.yuzu_emu.R 11import org.yuzu.yuzu_emu.R
@@ -20,15 +19,13 @@ import org.yuzu.yuzu_emu.features.settings.model.LongSetting
20import org.yuzu.yuzu_emu.features.settings.model.Settings 19import org.yuzu.yuzu_emu.features.settings.model.Settings
21import org.yuzu.yuzu_emu.features.settings.model.ShortSetting 20import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
22import org.yuzu.yuzu_emu.features.settings.model.view.* 21import org.yuzu.yuzu_emu.features.settings.model.view.*
23import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
24import org.yuzu.yuzu_emu.model.SettingsViewModel 22import org.yuzu.yuzu_emu.model.SettingsViewModel
25import org.yuzu.yuzu_emu.utils.NativeConfig 23import org.yuzu.yuzu_emu.utils.NativeConfig
26 24
27class SettingsFragmentPresenter( 25class SettingsFragmentPresenter(
28 private val settingsViewModel: SettingsViewModel, 26 private val settingsViewModel: SettingsViewModel,
29 private val adapter: SettingsAdapter, 27 private val adapter: SettingsAdapter,
30 private var menuTag: String, 28 private var menuTag: Settings.MenuTag
31 private var gameId: String
32) { 29) {
33 private var settingsList = ArrayList<SettingsItem>() 30 private var settingsList = ArrayList<SettingsItem>()
34 31
@@ -53,24 +50,15 @@ class SettingsFragmentPresenter(
53 } 50 }
54 51
55 fun loadSettingsList() { 52 fun loadSettingsList() {
56 if (!TextUtils.isEmpty(gameId)) {
57 settingsViewModel.setToolbarTitle(
58 context.getString(
59 R.string.advanced_settings_game,
60 gameId
61 )
62 )
63 }
64
65 val sl = ArrayList<SettingsItem>() 53 val sl = ArrayList<SettingsItem>()
66 when (menuTag) { 54 when (menuTag) {
67 SettingsFile.FILE_NAME_CONFIG -> addConfigSettings(sl) 55 Settings.MenuTag.SECTION_ROOT -> addConfigSettings(sl)
68 Settings.SECTION_GENERAL -> addGeneralSettings(sl) 56 Settings.MenuTag.SECTION_GENERAL -> addGeneralSettings(sl)
69 Settings.SECTION_SYSTEM -> addSystemSettings(sl) 57 Settings.MenuTag.SECTION_SYSTEM -> addSystemSettings(sl)
70 Settings.SECTION_RENDERER -> addGraphicsSettings(sl) 58 Settings.MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl)
71 Settings.SECTION_AUDIO -> addAudioSettings(sl) 59 Settings.MenuTag.SECTION_AUDIO -> addAudioSettings(sl)
72 Settings.SECTION_THEME -> addThemeSettings(sl) 60 Settings.MenuTag.SECTION_THEME -> addThemeSettings(sl)
73 Settings.SECTION_DEBUG -> addDebugSettings(sl) 61 Settings.MenuTag.SECTION_DEBUG -> addDebugSettings(sl)
74 else -> { 62 else -> {
75 val context = YuzuApplication.appContext 63 val context = YuzuApplication.appContext
76 Toast.makeText( 64 Toast.makeText(
@@ -86,13 +74,12 @@ class SettingsFragmentPresenter(
86 } 74 }
87 75
88 private fun addConfigSettings(sl: ArrayList<SettingsItem>) { 76 private fun addConfigSettings(sl: ArrayList<SettingsItem>) {
89 settingsViewModel.setToolbarTitle(context.getString(R.string.advanced_settings))
90 sl.apply { 77 sl.apply {
91 add(SubmenuSetting(R.string.preferences_general, 0, Settings.SECTION_GENERAL)) 78 add(SubmenuSetting(R.string.preferences_general, 0, Settings.MenuTag.SECTION_GENERAL))
92 add(SubmenuSetting(R.string.preferences_system, 0, Settings.SECTION_SYSTEM)) 79 add(SubmenuSetting(R.string.preferences_system, 0, Settings.MenuTag.SECTION_SYSTEM))
93 add(SubmenuSetting(R.string.preferences_graphics, 0, Settings.SECTION_RENDERER)) 80 add(SubmenuSetting(R.string.preferences_graphics, 0, Settings.MenuTag.SECTION_RENDERER))
94 add(SubmenuSetting(R.string.preferences_audio, 0, Settings.SECTION_AUDIO)) 81 add(SubmenuSetting(R.string.preferences_audio, 0, Settings.MenuTag.SECTION_AUDIO))
95 add(SubmenuSetting(R.string.preferences_debug, 0, Settings.SECTION_DEBUG)) 82 add(SubmenuSetting(R.string.preferences_debug, 0, Settings.MenuTag.SECTION_DEBUG))
96 add( 83 add(
97 RunnableSetting(R.string.reset_to_default, 0, false) { 84 RunnableSetting(R.string.reset_to_default, 0, false) {
98 settingsViewModel.setShouldShowResetSettingsDialog(true) 85 settingsViewModel.setShouldShowResetSettingsDialog(true)
@@ -102,7 +89,6 @@ class SettingsFragmentPresenter(
102 } 89 }
103 90
104 private fun addGeneralSettings(sl: ArrayList<SettingsItem>) { 91 private fun addGeneralSettings(sl: ArrayList<SettingsItem>) {
105 settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_general))
106 sl.apply { 92 sl.apply {
107 add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key) 93 add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key)
108 add(ShortSetting.RENDERER_SPEED_LIMIT.key) 94 add(ShortSetting.RENDERER_SPEED_LIMIT.key)
@@ -112,7 +98,6 @@ class SettingsFragmentPresenter(
112 } 98 }
113 99
114 private fun addSystemSettings(sl: ArrayList<SettingsItem>) { 100 private fun addSystemSettings(sl: ArrayList<SettingsItem>) {
115 settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_system))
116 sl.apply { 101 sl.apply {
117 add(BooleanSetting.USE_DOCKED_MODE.key) 102 add(BooleanSetting.USE_DOCKED_MODE.key)
118 add(IntSetting.REGION_INDEX.key) 103 add(IntSetting.REGION_INDEX.key)
@@ -123,7 +108,6 @@ class SettingsFragmentPresenter(
123 } 108 }
124 109
125 private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) { 110 private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) {
126 settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_graphics))
127 sl.apply { 111 sl.apply {
128 add(IntSetting.RENDERER_ACCURACY.key) 112 add(IntSetting.RENDERER_ACCURACY.key)
129 add(IntSetting.RENDERER_RESOLUTION.key) 113 add(IntSetting.RENDERER_RESOLUTION.key)
@@ -140,7 +124,6 @@ class SettingsFragmentPresenter(
140 } 124 }
141 125
142 private fun addAudioSettings(sl: ArrayList<SettingsItem>) { 126 private fun addAudioSettings(sl: ArrayList<SettingsItem>) {
143 settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_audio))
144 sl.apply { 127 sl.apply {
145 add(IntSetting.AUDIO_OUTPUT_ENGINE.key) 128 add(IntSetting.AUDIO_OUTPUT_ENGINE.key)
146 add(ByteSetting.AUDIO_VOLUME.key) 129 add(ByteSetting.AUDIO_VOLUME.key)
@@ -148,7 +131,6 @@ class SettingsFragmentPresenter(
148 } 131 }
149 132
150 private fun addThemeSettings(sl: ArrayList<SettingsItem>) { 133 private fun addThemeSettings(sl: ArrayList<SettingsItem>) {
151 settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_theme))
152 sl.apply { 134 sl.apply {
153 val theme: AbstractIntSetting = object : AbstractIntSetting { 135 val theme: AbstractIntSetting = object : AbstractIntSetting {
154 override val int: Int 136 override val int: Int
@@ -261,7 +243,6 @@ class SettingsFragmentPresenter(
261 } 243 }
262 244
263 private fun addDebugSettings(sl: ArrayList<SettingsItem>) { 245 private fun addDebugSettings(sl: ArrayList<SettingsItem>) {
264 settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_debug))
265 sl.apply { 246 sl.apply {
266 add(HeaderSetting(R.string.gpu)) 247 add(HeaderSetting(R.string.gpu))
267 add(IntSetting.RENDERER_BACKEND.key) 248 add(IntSetting.RENDERER_BACKEND.key)
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 944ae652e..3e6c157c7 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
@@ -39,6 +39,7 @@ import androidx.window.layout.WindowLayoutInfo
39import com.google.android.material.dialog.MaterialAlertDialogBuilder 39import com.google.android.material.dialog.MaterialAlertDialogBuilder
40import com.google.android.material.slider.Slider 40import com.google.android.material.slider.Slider
41import kotlinx.coroutines.Dispatchers 41import kotlinx.coroutines.Dispatchers
42import kotlinx.coroutines.flow.collectLatest
42import kotlinx.coroutines.launch 43import kotlinx.coroutines.launch
43import org.yuzu.yuzu_emu.HomeNavigationDirections 44import org.yuzu.yuzu_emu.HomeNavigationDirections
44import org.yuzu.yuzu_emu.NativeLibrary 45import org.yuzu.yuzu_emu.NativeLibrary
@@ -49,7 +50,6 @@ import org.yuzu.yuzu_emu.databinding.DialogOverlayAdjustBinding
49import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding 50import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
50import org.yuzu.yuzu_emu.features.settings.model.IntSetting 51import org.yuzu.yuzu_emu.features.settings.model.IntSetting
51import org.yuzu.yuzu_emu.features.settings.model.Settings 52import org.yuzu.yuzu_emu.features.settings.model.Settings
52import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
53import org.yuzu.yuzu_emu.model.Game 53import org.yuzu.yuzu_emu.model.Game
54import org.yuzu.yuzu_emu.model.EmulationViewModel 54import org.yuzu.yuzu_emu.model.EmulationViewModel
55import org.yuzu.yuzu_emu.overlay.InputOverlay 55import org.yuzu.yuzu_emu.overlay.InputOverlay
@@ -129,6 +129,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
129 return binding.root 129 return binding.root
130 } 130 }
131 131
132 // This is using the correct scope, lint is just acting up
133 @SuppressLint("UnsafeRepeatOnLifecycleDetector")
132 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 134 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
133 binding.surfaceEmulation.holder.addCallback(this) 135 binding.surfaceEmulation.holder.addCallback(this)
134 binding.showFpsText.setTextColor(Color.YELLOW) 136 binding.showFpsText.setTextColor(Color.YELLOW)
@@ -163,7 +165,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
163 R.id.menu_settings -> { 165 R.id.menu_settings -> {
164 val action = HomeNavigationDirections.actionGlobalSettingsActivity( 166 val action = HomeNavigationDirections.actionGlobalSettingsActivity(
165 null, 167 null,
166 SettingsFile.FILE_NAME_CONFIG 168 Settings.MenuTag.SECTION_ROOT
167 ) 169 )
168 binding.root.findNavController().navigate(action) 170 binding.root.findNavController().navigate(action)
169 true 171 true
@@ -205,59 +207,80 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
205 } 207 }
206 ) 208 )
207 209
208 viewLifecycleOwner.lifecycleScope.launch(Dispatchers.Main) {
209 lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
210 WindowInfoTracker.getOrCreate(requireContext())
211 .windowLayoutInfo(requireActivity())
212 .collect { updateFoldableLayout(requireActivity() as EmulationActivity, it) }
213 }
214 }
215
216 GameIconUtils.loadGameIcon(game, binding.loadingImage) 210 GameIconUtils.loadGameIcon(game, binding.loadingImage)
217 binding.loadingTitle.text = game.title 211 binding.loadingTitle.text = game.title
218 binding.loadingTitle.isSelected = true 212 binding.loadingTitle.isSelected = true
219 binding.loadingText.isSelected = true 213 binding.loadingText.isSelected = true
220 214
221 emulationViewModel.shaderProgress.observe(viewLifecycleOwner) { 215 viewLifecycleOwner.lifecycleScope.apply {
222 if (it > 0 && it != emulationViewModel.totalShaders.value!!) { 216 launch {
223 binding.loadingProgressIndicator.isIndeterminate = false 217 repeatOnLifecycle(Lifecycle.State.STARTED) {
224 218 WindowInfoTracker.getOrCreate(requireContext())
225 if (it < binding.loadingProgressIndicator.max) { 219 .windowLayoutInfo(requireActivity())
226 binding.loadingProgressIndicator.progress = it 220 .collect {
221 updateFoldableLayout(requireActivity() as EmulationActivity, it)
222 }
227 } 223 }
228 } 224 }
225 launch {
226 repeatOnLifecycle(Lifecycle.State.CREATED) {
227 emulationViewModel.shaderProgress.collectLatest {
228 if (it > 0 && it != emulationViewModel.totalShaders.value) {
229 binding.loadingProgressIndicator.isIndeterminate = false
230
231 if (it < binding.loadingProgressIndicator.max) {
232 binding.loadingProgressIndicator.progress = it
233 }
234 }
229 235
230 if (it == emulationViewModel.totalShaders.value!!) { 236 if (it == emulationViewModel.totalShaders.value) {
231 binding.loadingText.setText(R.string.loading) 237 binding.loadingText.setText(R.string.loading)
232 binding.loadingProgressIndicator.isIndeterminate = true 238 binding.loadingProgressIndicator.isIndeterminate = true
239 }
240 }
241 }
233 } 242 }
234 } 243 launch {
235 emulationViewModel.totalShaders.observe(viewLifecycleOwner) { 244 repeatOnLifecycle(Lifecycle.State.CREATED) {
236 binding.loadingProgressIndicator.max = it 245 emulationViewModel.totalShaders.collectLatest {
237 } 246 binding.loadingProgressIndicator.max = it
238 emulationViewModel.shaderMessage.observe(viewLifecycleOwner) { 247 }
239 if (it.isNotEmpty()) { 248 }
240 binding.loadingText.text = it
241 } 249 }
242 } 250 launch {
243 251 repeatOnLifecycle(Lifecycle.State.CREATED) {
244 emulationViewModel.emulationStarted.observe(viewLifecycleOwner) { started -> 252 emulationViewModel.shaderMessage.collectLatest {
245 if (started) { 253 if (it.isNotEmpty()) {
246 binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED) 254 binding.loadingText.text = it
247 ViewUtils.showView(binding.surfaceInputOverlay) 255 }
248 ViewUtils.hideView(binding.loadingIndicator) 256 }
249 257 }
250 // Setup overlay
251 updateShowFpsOverlay()
252 } 258 }
253 } 259 launch {
260 repeatOnLifecycle(Lifecycle.State.CREATED) {
261 emulationViewModel.emulationStarted.collectLatest {
262 if (it) {
263 binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
264 ViewUtils.showView(binding.surfaceInputOverlay)
265 ViewUtils.hideView(binding.loadingIndicator)
254 266
255 emulationViewModel.isEmulationStopping.observe(viewLifecycleOwner) { 267 // Setup overlay
256 if (it) { 268 updateShowFpsOverlay()
257 binding.loadingText.setText(R.string.shutting_down) 269 }
258 ViewUtils.showView(binding.loadingIndicator) 270 }
259 ViewUtils.hideView(binding.inputContainer) 271 }
260 ViewUtils.hideView(binding.showFpsText) 272 }
273 launch {
274 repeatOnLifecycle(Lifecycle.State.CREATED) {
275 emulationViewModel.isEmulationStopping.collectLatest {
276 if (it) {
277 binding.loadingText.setText(R.string.shutting_down)
278 ViewUtils.showView(binding.loadingIndicator)
279 ViewUtils.hideView(binding.inputContainer)
280 ViewUtils.hideView(binding.showFpsText)
281 }
282 }
283 }
261 } 284 }
262 } 285 }
263 } 286 }
@@ -274,9 +297,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
274 } 297 }
275 } 298 }
276 } else { 299 } else {
277 if (EmulationMenuSettings.showOverlay && 300 if (EmulationMenuSettings.showOverlay && emulationViewModel.emulationStarted.value) {
278 emulationViewModel.emulationStarted.value == true
279 ) {
280 binding.surfaceInputOverlay.post { 301 binding.surfaceInputOverlay.post {
281 binding.surfaceInputOverlay.visibility = View.VISIBLE 302 binding.surfaceInputOverlay.visibility = View.VISIBLE
282 } 303 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
index cbbe14d22..c119e69c9 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
@@ -37,7 +37,6 @@ import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter
37import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding 37import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding
38import org.yuzu.yuzu_emu.features.DocumentProvider 38import org.yuzu.yuzu_emu.features.DocumentProvider
39import org.yuzu.yuzu_emu.features.settings.model.Settings 39import org.yuzu.yuzu_emu.features.settings.model.Settings
40import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
41import org.yuzu.yuzu_emu.model.HomeSetting 40import org.yuzu.yuzu_emu.model.HomeSetting
42import org.yuzu.yuzu_emu.model.HomeViewModel 41import org.yuzu.yuzu_emu.model.HomeViewModel
43import org.yuzu.yuzu_emu.ui.main.MainActivity 42import org.yuzu.yuzu_emu.ui.main.MainActivity
@@ -78,7 +77,7 @@ class HomeSettingsFragment : Fragment() {
78 { 77 {
79 val action = HomeNavigationDirections.actionGlobalSettingsActivity( 78 val action = HomeNavigationDirections.actionGlobalSettingsActivity(
80 null, 79 null,
81 SettingsFile.FILE_NAME_CONFIG 80 Settings.MenuTag.SECTION_ROOT
82 ) 81 )
83 binding.root.findNavController().navigate(action) 82 binding.root.findNavController().navigate(action)
84 } 83 }
@@ -100,7 +99,7 @@ class HomeSettingsFragment : Fragment() {
100 { 99 {
101 val action = HomeNavigationDirections.actionGlobalSettingsActivity( 100 val action = HomeNavigationDirections.actionGlobalSettingsActivity(
102 null, 101 null,
103 Settings.SECTION_THEME 102 Settings.MenuTag.SECTION_THEME
104 ) 103 )
105 binding.root.findNavController().navigate(action) 104 binding.root.findNavController().navigate(action)
106 } 105 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt
index 181bd983a..ea8eb073a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt
@@ -9,8 +9,12 @@ import android.widget.Toast
9import androidx.appcompat.app.AppCompatActivity 9import androidx.appcompat.app.AppCompatActivity
10import androidx.fragment.app.DialogFragment 10import androidx.fragment.app.DialogFragment
11import androidx.fragment.app.activityViewModels 11import androidx.fragment.app.activityViewModels
12import androidx.lifecycle.Lifecycle
12import androidx.lifecycle.ViewModelProvider 13import androidx.lifecycle.ViewModelProvider
14import androidx.lifecycle.lifecycleScope
15import androidx.lifecycle.repeatOnLifecycle
13import com.google.android.material.dialog.MaterialAlertDialogBuilder 16import com.google.android.material.dialog.MaterialAlertDialogBuilder
17import kotlinx.coroutines.launch
14import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding 18import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
15import org.yuzu.yuzu_emu.model.TaskViewModel 19import org.yuzu.yuzu_emu.model.TaskViewModel
16 20
@@ -28,21 +32,27 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
28 .create() 32 .create()
29 dialog.setCanceledOnTouchOutside(false) 33 dialog.setCanceledOnTouchOutside(false)
30 34
31 taskViewModel.isComplete.observe(this) { complete -> 35 viewLifecycleOwner.lifecycleScope.launch {
32 if (complete) { 36 repeatOnLifecycle(Lifecycle.State.CREATED) {
33 dialog.dismiss() 37 taskViewModel.isComplete.collect {
34 when (val result = taskViewModel.result.value) { 38 if (it) {
35 is String -> Toast.makeText(requireContext(), result, Toast.LENGTH_LONG).show() 39 dialog.dismiss()
36 is MessageDialogFragment -> result.show( 40 when (val result = taskViewModel.result.value) {
37 requireActivity().supportFragmentManager, 41 is String -> Toast.makeText(requireContext(), result, Toast.LENGTH_LONG)
38 MessageDialogFragment.TAG 42 .show()
39 ) 43
44 is MessageDialogFragment -> result.show(
45 requireActivity().supportFragmentManager,
46 MessageDialogFragment.TAG
47 )
48 }
49 taskViewModel.clear()
50 }
40 } 51 }
41 taskViewModel.clear()
42 } 52 }
43 } 53 }
44 54
45 if (taskViewModel.isRunning.value == false) { 55 if (!taskViewModel.isRunning.value) {
46 taskViewModel.runTask() 56 taskViewModel.runTask()
47 } 57 }
48 return dialog 58 return dialog
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
index f54dccc69..2dbca76a5 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
@@ -3,6 +3,7 @@
3 3
4package org.yuzu.yuzu_emu.fragments 4package org.yuzu.yuzu_emu.fragments
5 5
6import android.annotation.SuppressLint
6import android.content.Context 7import android.content.Context
7import android.content.SharedPreferences 8import android.content.SharedPreferences
8import android.os.Bundle 9import android.os.Bundle
@@ -17,9 +18,13 @@ import androidx.core.view.updatePadding
17import androidx.core.widget.doOnTextChanged 18import androidx.core.widget.doOnTextChanged
18import androidx.fragment.app.Fragment 19import androidx.fragment.app.Fragment
19import androidx.fragment.app.activityViewModels 20import androidx.fragment.app.activityViewModels
21import androidx.lifecycle.Lifecycle
22import androidx.lifecycle.lifecycleScope
23import androidx.lifecycle.repeatOnLifecycle
20import androidx.preference.PreferenceManager 24import androidx.preference.PreferenceManager
21import info.debatty.java.stringsimilarity.Jaccard 25import info.debatty.java.stringsimilarity.Jaccard
22import info.debatty.java.stringsimilarity.JaroWinkler 26import info.debatty.java.stringsimilarity.JaroWinkler
27import kotlinx.coroutines.launch
23import java.util.Locale 28import java.util.Locale
24import org.yuzu.yuzu_emu.R 29import org.yuzu.yuzu_emu.R
25import org.yuzu.yuzu_emu.YuzuApplication 30import org.yuzu.yuzu_emu.YuzuApplication
@@ -52,6 +57,8 @@ class SearchFragment : Fragment() {
52 return binding.root 57 return binding.root
53 } 58 }
54 59
60 // This is using the correct scope, lint is just acting up
61 @SuppressLint("UnsafeRepeatOnLifecycleDetector")
55 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 62 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
56 homeViewModel.setNavigationVisibility(visible = true, animated = false) 63 homeViewModel.setNavigationVisibility(visible = true, animated = false)
57 preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) 64 preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
@@ -79,21 +86,32 @@ class SearchFragment : Fragment() {
79 filterAndSearch() 86 filterAndSearch()
80 } 87 }
81 88
82 gamesViewModel.apply { 89 viewLifecycleOwner.lifecycleScope.apply {
83 searchFocused.observe(viewLifecycleOwner) { searchFocused -> 90 launch {
84 if (searchFocused) { 91 repeatOnLifecycle(Lifecycle.State.CREATED) {
85 focusSearch() 92 gamesViewModel.searchFocused.collect {
86 gamesViewModel.setSearchFocused(false) 93 if (it) {
94 focusSearch()
95 gamesViewModel.setSearchFocused(false)
96 }
97 }
87 } 98 }
88 } 99 }
89 100 launch {
90 games.observe(viewLifecycleOwner) { filterAndSearch() } 101 repeatOnLifecycle(Lifecycle.State.CREATED) {
91 searchedGames.observe(viewLifecycleOwner) { 102 gamesViewModel.games.collect { filterAndSearch() }
92 (binding.gridGamesSearch.adapter as GameAdapter).submitList(it) 103 }
93 if (it.isEmpty()) { 104 }
94 binding.noResultsView.visibility = View.VISIBLE 105 launch {
95 } else { 106 repeatOnLifecycle(Lifecycle.State.CREATED) {
96 binding.noResultsView.visibility = View.GONE 107 gamesViewModel.searchedGames.collect {
108 (binding.gridGamesSearch.adapter as GameAdapter).submitList(it)
109 if (it.isEmpty()) {
110 binding.noResultsView.visibility = View.VISIBLE
111 } else {
112 binding.noResultsView.visibility = View.GONE
113 }
114 }
97 } 115 }
98 } 116 }
99 } 117 }
@@ -109,7 +127,7 @@ class SearchFragment : Fragment() {
109 private inner class ScoredGame(val score: Double, val item: Game) 127 private inner class ScoredGame(val score: Double, val item: Game)
110 128
111 private fun filterAndSearch() { 129 private fun filterAndSearch() {
112 val baseList = gamesViewModel.games.value!! 130 val baseList = gamesViewModel.games.value
113 val filteredList: List<Game> = when (binding.chipGroup.checkedChipId) { 131 val filteredList: List<Game> = when (binding.chipGroup.checkedChipId) {
114 R.id.chip_recently_played -> { 132 R.id.chip_recently_played -> {
115 baseList.filter { 133 baseList.filter {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt
index 55b6a0367..9d0594c6e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt
@@ -15,10 +15,14 @@ import androidx.core.view.updatePadding
15import androidx.core.widget.doOnTextChanged 15import androidx.core.widget.doOnTextChanged
16import androidx.fragment.app.Fragment 16import androidx.fragment.app.Fragment
17import androidx.fragment.app.activityViewModels 17import androidx.fragment.app.activityViewModels
18import androidx.lifecycle.Lifecycle
19import androidx.lifecycle.lifecycleScope
20import androidx.lifecycle.repeatOnLifecycle
18import androidx.recyclerview.widget.LinearLayoutManager 21import androidx.recyclerview.widget.LinearLayoutManager
19import com.google.android.material.divider.MaterialDividerItemDecoration 22import com.google.android.material.divider.MaterialDividerItemDecoration
20import com.google.android.material.transition.MaterialSharedAxis 23import com.google.android.material.transition.MaterialSharedAxis
21import info.debatty.java.stringsimilarity.Cosine 24import info.debatty.java.stringsimilarity.Cosine
25import kotlinx.coroutines.launch
22import org.yuzu.yuzu_emu.R 26import org.yuzu.yuzu_emu.R
23import org.yuzu.yuzu_emu.databinding.FragmentSettingsSearchBinding 27import org.yuzu.yuzu_emu.databinding.FragmentSettingsSearchBinding
24import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem 28import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
@@ -79,10 +83,14 @@ class SettingsSearchFragment : Fragment() {
79 search() 83 search()
80 binding.settingsList.smoothScrollToPosition(0) 84 binding.settingsList.smoothScrollToPosition(0)
81 } 85 }
82 settingsViewModel.shouldReloadSettingsList.observe(viewLifecycleOwner) { 86 viewLifecycleOwner.lifecycleScope.launch {
83 if (it) { 87 repeatOnLifecycle(Lifecycle.State.CREATED) {
84 settingsViewModel.setShouldReloadSettingsList(false) 88 settingsViewModel.shouldReloadSettingsList.collect {
85 search() 89 if (it) {
90 settingsViewModel.setShouldReloadSettingsList(false)
91 search()
92 }
93 }
86 } 94 }
87 } 95 }
88 96
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
index d50c421a0..fbb2f6e18 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
@@ -22,10 +22,14 @@ import androidx.core.view.isVisible
22import androidx.core.view.updatePadding 22import androidx.core.view.updatePadding
23import androidx.fragment.app.Fragment 23import androidx.fragment.app.Fragment
24import androidx.fragment.app.activityViewModels 24import androidx.fragment.app.activityViewModels
25import androidx.lifecycle.Lifecycle
26import androidx.lifecycle.lifecycleScope
27import androidx.lifecycle.repeatOnLifecycle
25import androidx.navigation.findNavController 28import androidx.navigation.findNavController
26import androidx.preference.PreferenceManager 29import androidx.preference.PreferenceManager
27import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback 30import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
28import com.google.android.material.transition.MaterialFadeThrough 31import com.google.android.material.transition.MaterialFadeThrough
32import kotlinx.coroutines.launch
29import java.io.File 33import java.io.File
30import org.yuzu.yuzu_emu.R 34import org.yuzu.yuzu_emu.R
31import org.yuzu.yuzu_emu.YuzuApplication 35import org.yuzu.yuzu_emu.YuzuApplication
@@ -206,10 +210,14 @@ class SetupFragment : Fragment() {
206 ) 210 )
207 } 211 }
208 212
209 homeViewModel.shouldPageForward.observe(viewLifecycleOwner) { 213 viewLifecycleOwner.lifecycleScope.launch {
210 if (it) { 214 repeatOnLifecycle(Lifecycle.State.CREATED) {
211 pageForward() 215 homeViewModel.shouldPageForward.collect {
212 homeViewModel.setShouldPageForward(false) 216 if (it) {
217 pageForward()
218 homeViewModel.setShouldPageForward(false)
219 }
220 }
213 } 221 }
214 } 222 }
215 223
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt
index e35f51bc3..f34870c2d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt
@@ -3,28 +3,28 @@
3 3
4package org.yuzu.yuzu_emu.model 4package org.yuzu.yuzu_emu.model
5 5
6import androidx.lifecycle.LiveData
7import androidx.lifecycle.MutableLiveData
8import androidx.lifecycle.ViewModel 6import androidx.lifecycle.ViewModel
7import kotlinx.coroutines.flow.MutableStateFlow
8import kotlinx.coroutines.flow.StateFlow
9 9
10class EmulationViewModel : ViewModel() { 10class EmulationViewModel : ViewModel() {
11 private val _emulationStarted = MutableLiveData(false) 11 val emulationStarted: StateFlow<Boolean> get() = _emulationStarted
12 val emulationStarted: LiveData<Boolean> get() = _emulationStarted 12 private val _emulationStarted = MutableStateFlow(false)
13 13
14 private val _isEmulationStopping = MutableLiveData(false) 14 val isEmulationStopping: StateFlow<Boolean> get() = _isEmulationStopping
15 val isEmulationStopping: LiveData<Boolean> get() = _isEmulationStopping 15 private val _isEmulationStopping = MutableStateFlow(false)
16 16
17 private val _shaderProgress = MutableLiveData(0) 17 val shaderProgress: StateFlow<Int> get() = _shaderProgress
18 val shaderProgress: LiveData<Int> get() = _shaderProgress 18 private val _shaderProgress = MutableStateFlow(0)
19 19
20 private val _totalShaders = MutableLiveData(0) 20 val totalShaders: StateFlow<Int> get() = _totalShaders
21 val totalShaders: LiveData<Int> get() = _totalShaders 21 private val _totalShaders = MutableStateFlow(0)
22 22
23 private val _shaderMessage = MutableLiveData("") 23 val shaderMessage: StateFlow<String> get() = _shaderMessage
24 val shaderMessage: LiveData<String> get() = _shaderMessage 24 private val _shaderMessage = MutableStateFlow("")
25 25
26 fun setEmulationStarted(started: Boolean) { 26 fun setEmulationStarted(started: Boolean) {
27 _emulationStarted.postValue(started) 27 _emulationStarted.value = started
28 } 28 }
29 29
30 fun setIsEmulationStopping(value: Boolean) { 30 fun setIsEmulationStopping(value: Boolean) {
@@ -50,10 +50,18 @@ class EmulationViewModel : ViewModel() {
50 } 50 }
51 51
52 fun clear() { 52 fun clear() {
53 _emulationStarted.value = false 53 setEmulationStarted(false)
54 _isEmulationStopping.value = false 54 setIsEmulationStopping(false)
55 _shaderProgress.value = 0 55 setShaderProgress(0)
56 _totalShaders.value = 0 56 setTotalShaders(0)
57 _shaderMessage.value = "" 57 setShaderMessage("")
58 }
59
60 companion object {
61 const val KEY_EMULATION_STARTED = "EmulationStarted"
62 const val KEY_IS_EMULATION_STOPPING = "IsEmulationStarting"
63 const val KEY_SHADER_PROGRESS = "ShaderProgress"
64 const val KEY_TOTAL_SHADERS = "TotalShaders"
65 const val KEY_SHADER_MESSAGE = "ShaderMessage"
58 } 66 }
59} 67}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
index 1fe42f922..6e09fa81d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
@@ -5,13 +5,13 @@ package org.yuzu.yuzu_emu.model
5 5
6import android.net.Uri 6import android.net.Uri
7import androidx.documentfile.provider.DocumentFile 7import androidx.documentfile.provider.DocumentFile
8import androidx.lifecycle.LiveData
9import androidx.lifecycle.MutableLiveData
10import androidx.lifecycle.ViewModel 8import androidx.lifecycle.ViewModel
11import androidx.lifecycle.viewModelScope 9import androidx.lifecycle.viewModelScope
12import androidx.preference.PreferenceManager 10import androidx.preference.PreferenceManager
13import java.util.Locale 11import java.util.Locale
14import kotlinx.coroutines.Dispatchers 12import kotlinx.coroutines.Dispatchers
13import kotlinx.coroutines.flow.MutableStateFlow
14import kotlinx.coroutines.flow.StateFlow
15import kotlinx.coroutines.launch 15import kotlinx.coroutines.launch
16import kotlinx.coroutines.withContext 16import kotlinx.coroutines.withContext
17import kotlinx.serialization.ExperimentalSerializationApi 17import kotlinx.serialization.ExperimentalSerializationApi
@@ -24,23 +24,23 @@ import org.yuzu.yuzu_emu.utils.GameHelper
24 24
25@OptIn(ExperimentalSerializationApi::class) 25@OptIn(ExperimentalSerializationApi::class)
26class GamesViewModel : ViewModel() { 26class GamesViewModel : ViewModel() {
27 private val _games = MutableLiveData<List<Game>>(emptyList()) 27 val games: StateFlow<List<Game>> get() = _games
28 val games: LiveData<List<Game>> get() = _games 28 private val _games = MutableStateFlow(emptyList<Game>())
29 29
30 private val _searchedGames = MutableLiveData<List<Game>>(emptyList()) 30 val searchedGames: StateFlow<List<Game>> get() = _searchedGames
31 val searchedGames: LiveData<List<Game>> get() = _searchedGames 31 private val _searchedGames = MutableStateFlow(emptyList<Game>())
32 32
33 private val _isReloading = MutableLiveData(false) 33 val isReloading: StateFlow<Boolean> get() = _isReloading
34 val isReloading: LiveData<Boolean> get() = _isReloading 34 private val _isReloading = MutableStateFlow(false)
35 35
36 private val _shouldSwapData = MutableLiveData(false) 36 val shouldSwapData: StateFlow<Boolean> get() = _shouldSwapData
37 val shouldSwapData: LiveData<Boolean> get() = _shouldSwapData 37 private val _shouldSwapData = MutableStateFlow(false)
38 38
39 private val _shouldScrollToTop = MutableLiveData(false) 39 val shouldScrollToTop: StateFlow<Boolean> get() = _shouldScrollToTop
40 val shouldScrollToTop: LiveData<Boolean> get() = _shouldScrollToTop 40 private val _shouldScrollToTop = MutableStateFlow(false)
41 41
42 private val _searchFocused = MutableLiveData(false) 42 val searchFocused: StateFlow<Boolean> get() = _searchFocused
43 val searchFocused: LiveData<Boolean> get() = _searchFocused 43 private val _searchFocused = MutableStateFlow(false)
44 44
45 init { 45 init {
46 // Ensure keys are loaded so that ROM metadata can be decrypted. 46 // Ensure keys are loaded so that ROM metadata can be decrypted.
@@ -79,36 +79,36 @@ class GamesViewModel : ViewModel() {
79 ) 79 )
80 ) 80 )
81 81
82 _games.postValue(sortedList) 82 _games.value = sortedList
83 } 83 }
84 84
85 fun setSearchedGames(games: List<Game>) { 85 fun setSearchedGames(games: List<Game>) {
86 _searchedGames.postValue(games) 86 _searchedGames.value = games
87 } 87 }
88 88
89 fun setShouldSwapData(shouldSwap: Boolean) { 89 fun setShouldSwapData(shouldSwap: Boolean) {
90 _shouldSwapData.postValue(shouldSwap) 90 _shouldSwapData.value = shouldSwap
91 } 91 }
92 92
93 fun setShouldScrollToTop(shouldScroll: Boolean) { 93 fun setShouldScrollToTop(shouldScroll: Boolean) {
94 _shouldScrollToTop.postValue(shouldScroll) 94 _shouldScrollToTop.value = shouldScroll
95 } 95 }
96 96
97 fun setSearchFocused(searchFocused: Boolean) { 97 fun setSearchFocused(searchFocused: Boolean) {
98 _searchFocused.postValue(searchFocused) 98 _searchFocused.value = searchFocused
99 } 99 }
100 100
101 fun reloadGames(directoryChanged: Boolean) { 101 fun reloadGames(directoryChanged: Boolean) {
102 if (isReloading.value == true) { 102 if (isReloading.value) {
103 return 103 return
104 } 104 }
105 _isReloading.postValue(true) 105 _isReloading.value = true
106 106
107 viewModelScope.launch { 107 viewModelScope.launch {
108 withContext(Dispatchers.IO) { 108 withContext(Dispatchers.IO) {
109 NativeLibrary.resetRomMetadata() 109 NativeLibrary.resetRomMetadata()
110 setGames(GameHelper.getGames()) 110 setGames(GameHelper.getGames())
111 _isReloading.postValue(false) 111 _isReloading.value = false
112 112
113 if (directoryChanged) { 113 if (directoryChanged) {
114 setShouldSwapData(true) 114 setShouldSwapData(true)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeSetting.kt
index 498c222fa..b32e19373 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeSetting.kt
@@ -3,8 +3,8 @@
3 3
4package org.yuzu.yuzu_emu.model 4package org.yuzu.yuzu_emu.model
5 5
6import androidx.lifecycle.LiveData 6import kotlinx.coroutines.flow.MutableStateFlow
7import androidx.lifecycle.MutableLiveData 7import kotlinx.coroutines.flow.StateFlow
8 8
9data class HomeSetting( 9data class HomeSetting(
10 val titleId: Int, 10 val titleId: Int,
@@ -14,5 +14,5 @@ data class HomeSetting(
14 val isEnabled: () -> Boolean = { true }, 14 val isEnabled: () -> Boolean = { true },
15 val disabledTitleId: Int = 0, 15 val disabledTitleId: Int = 0,
16 val disabledMessageId: Int = 0, 16 val disabledMessageId: Int = 0,
17 val details: LiveData<String> = MutableLiveData("") 17 val details: StateFlow<String> = MutableStateFlow("")
18) 18)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
index a48ef7a88..756f76721 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
@@ -5,47 +5,43 @@ package org.yuzu.yuzu_emu.model
5 5
6import android.net.Uri 6import android.net.Uri
7import androidx.fragment.app.FragmentActivity 7import androidx.fragment.app.FragmentActivity
8import androidx.lifecycle.LiveData
9import androidx.lifecycle.MutableLiveData
10import androidx.lifecycle.ViewModel 8import androidx.lifecycle.ViewModel
11import androidx.lifecycle.ViewModelProvider 9import androidx.lifecycle.ViewModelProvider
12import androidx.preference.PreferenceManager 10import androidx.preference.PreferenceManager
11import kotlinx.coroutines.flow.MutableStateFlow
12import kotlinx.coroutines.flow.StateFlow
13import org.yuzu.yuzu_emu.YuzuApplication 13import org.yuzu.yuzu_emu.YuzuApplication
14import org.yuzu.yuzu_emu.utils.GameHelper 14import org.yuzu.yuzu_emu.utils.GameHelper
15 15
16class HomeViewModel : ViewModel() { 16class HomeViewModel : ViewModel() {
17 private val _navigationVisible = MutableLiveData<Pair<Boolean, Boolean>>() 17 val navigationVisible: StateFlow<Pair<Boolean, Boolean>> get() = _navigationVisible
18 val navigationVisible: LiveData<Pair<Boolean, Boolean>> get() = _navigationVisible 18 private val _navigationVisible = MutableStateFlow(Pair(false, false))
19 19
20 private val _statusBarShadeVisible = MutableLiveData(true) 20 val statusBarShadeVisible: StateFlow<Boolean> get() = _statusBarShadeVisible
21 val statusBarShadeVisible: LiveData<Boolean> get() = _statusBarShadeVisible 21 private val _statusBarShadeVisible = MutableStateFlow(true)
22 22
23 private val _shouldPageForward = MutableLiveData(false) 23 val shouldPageForward: StateFlow<Boolean> get() = _shouldPageForward
24 val shouldPageForward: LiveData<Boolean> get() = _shouldPageForward 24 private val _shouldPageForward = MutableStateFlow(false)
25 25
26 private val _gamesDir = MutableLiveData( 26 val gamesDir: StateFlow<String> get() = _gamesDir
27 private val _gamesDir = MutableStateFlow(
27 Uri.parse( 28 Uri.parse(
28 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) 29 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
29 .getString(GameHelper.KEY_GAME_PATH, "") 30 .getString(GameHelper.KEY_GAME_PATH, "")
30 ).path ?: "" 31 ).path ?: ""
31 ) 32 )
32 val gamesDir: LiveData<String> get() = _gamesDir
33 33
34 var navigatedToSetup = false 34 var navigatedToSetup = false
35 35
36 init {
37 _navigationVisible.value = Pair(false, false)
38 }
39
40 fun setNavigationVisibility(visible: Boolean, animated: Boolean) { 36 fun setNavigationVisibility(visible: Boolean, animated: Boolean) {
41 if (_navigationVisible.value?.first == visible) { 37 if (navigationVisible.value.first == visible) {
42 return 38 return
43 } 39 }
44 _navigationVisible.value = Pair(visible, animated) 40 _navigationVisible.value = Pair(visible, animated)
45 } 41 }
46 42
47 fun setStatusBarShadeVisibility(visible: Boolean) { 43 fun setStatusBarShadeVisibility(visible: Boolean) {
48 if (_statusBarShadeVisible.value == visible) { 44 if (statusBarShadeVisible.value == visible) {
49 return 45 return
50 } 46 }
51 _statusBarShadeVisible.value = visible 47 _statusBarShadeVisible.value = visible
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt
index d16d15fa6..53fa7a8de 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt
@@ -3,48 +3,43 @@
3 3
4package org.yuzu.yuzu_emu.model 4package org.yuzu.yuzu_emu.model
5 5
6import androidx.lifecycle.LiveData
7import androidx.lifecycle.MutableLiveData
8import androidx.lifecycle.SavedStateHandle
9import androidx.lifecycle.ViewModel 6import androidx.lifecycle.ViewModel
7import kotlinx.coroutines.flow.MutableStateFlow
8import kotlinx.coroutines.flow.StateFlow
10import org.yuzu.yuzu_emu.R 9import org.yuzu.yuzu_emu.R
11import org.yuzu.yuzu_emu.YuzuApplication 10import org.yuzu.yuzu_emu.YuzuApplication
12import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem 11import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
13 12
14class SettingsViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() { 13class SettingsViewModel : ViewModel() {
15 var game: Game? = null 14 var game: Game? = null
16 15
17 var shouldSave = false 16 var shouldSave = false
18 17
19 var clickedItem: SettingsItem? = null 18 var clickedItem: SettingsItem? = null
20 19
21 private val _toolbarTitle = MutableLiveData("") 20 val shouldRecreate: StateFlow<Boolean> get() = _shouldRecreate
22 val toolbarTitle: LiveData<String> get() = _toolbarTitle 21 private val _shouldRecreate = MutableStateFlow(false)
23 22
24 private val _shouldRecreate = MutableLiveData(false) 23 val shouldNavigateBack: StateFlow<Boolean> get() = _shouldNavigateBack
25 val shouldRecreate: LiveData<Boolean> get() = _shouldRecreate 24 private val _shouldNavigateBack = MutableStateFlow(false)
26 25
27 private val _shouldNavigateBack = MutableLiveData(false) 26 val shouldShowResetSettingsDialog: StateFlow<Boolean> get() = _shouldShowResetSettingsDialog
28 val shouldNavigateBack: LiveData<Boolean> get() = _shouldNavigateBack 27 private val _shouldShowResetSettingsDialog = MutableStateFlow(false)
29 28
30 private val _shouldShowResetSettingsDialog = MutableLiveData(false) 29 val shouldReloadSettingsList: StateFlow<Boolean> get() = _shouldReloadSettingsList
31 val shouldShowResetSettingsDialog: LiveData<Boolean> get() = _shouldShowResetSettingsDialog 30 private val _shouldReloadSettingsList = MutableStateFlow(false)
32 31
33 private val _shouldReloadSettingsList = MutableLiveData(false) 32 val isUsingSearch: StateFlow<Boolean> get() = _isUsingSearch
34 val shouldReloadSettingsList: LiveData<Boolean> get() = _shouldReloadSettingsList 33 private val _isUsingSearch = MutableStateFlow(false)
35 34
36 private val _isUsingSearch = MutableLiveData(false) 35 val sliderProgress: StateFlow<Int> get() = _sliderProgress
37 val isUsingSearch: LiveData<Boolean> get() = _isUsingSearch 36 private val _sliderProgress = MutableStateFlow(-1)
38 37
39 val sliderProgress = savedStateHandle.getStateFlow(KEY_SLIDER_PROGRESS, -1) 38 val sliderTextValue: StateFlow<String> get() = _sliderTextValue
39 private val _sliderTextValue = MutableStateFlow("")
40 40
41 val sliderTextValue = savedStateHandle.getStateFlow(KEY_SLIDER_TEXT_VALUE, "") 41 val adapterItemChanged: StateFlow<Int> get() = _adapterItemChanged
42 42 private val _adapterItemChanged = MutableStateFlow(-1)
43 val adapterItemChanged = savedStateHandle.getStateFlow(KEY_ADAPTER_ITEM_CHANGED, -1)
44
45 fun setToolbarTitle(value: String) {
46 _toolbarTitle.value = value
47 }
48 43
49 fun setShouldRecreate(value: Boolean) { 44 fun setShouldRecreate(value: Boolean) {
50 _shouldRecreate.value = value 45 _shouldRecreate.value = value
@@ -67,8 +62,8 @@ class SettingsViewModel(private val savedStateHandle: SavedStateHandle) : ViewMo
67 } 62 }
68 63
69 fun setSliderTextValue(value: Float, units: String) { 64 fun setSliderTextValue(value: Float, units: String) {
70 savedStateHandle[KEY_SLIDER_PROGRESS] = value 65 _sliderProgress.value = value.toInt()
71 savedStateHandle[KEY_SLIDER_TEXT_VALUE] = String.format( 66 _sliderTextValue.value = String.format(
72 YuzuApplication.appContext.getString(R.string.value_with_units), 67 YuzuApplication.appContext.getString(R.string.value_with_units),
73 value.toInt().toString(), 68 value.toInt().toString(),
74 units 69 units
@@ -76,21 +71,15 @@ class SettingsViewModel(private val savedStateHandle: SavedStateHandle) : ViewMo
76 } 71 }
77 72
78 fun setSliderProgress(value: Float) { 73 fun setSliderProgress(value: Float) {
79 savedStateHandle[KEY_SLIDER_PROGRESS] = value 74 _sliderProgress.value = value.toInt()
80 } 75 }
81 76
82 fun setAdapterItemChanged(value: Int) { 77 fun setAdapterItemChanged(value: Int) {
83 savedStateHandle[KEY_ADAPTER_ITEM_CHANGED] = value 78 _adapterItemChanged.value = value
84 } 79 }
85 80
86 fun clear() { 81 fun clear() {
87 game = null 82 game = null
88 shouldSave = false 83 shouldSave = false
89 } 84 }
90
91 companion object {
92 const val KEY_SLIDER_TEXT_VALUE = "SliderTextValue"
93 const val KEY_SLIDER_PROGRESS = "SliderProgress"
94 const val KEY_ADAPTER_ITEM_CHANGED = "AdapterItemChanged"
95 }
96} 85}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt
index 27ea725a5..531c2aaf0 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt
@@ -3,29 +3,25 @@
3 3
4package org.yuzu.yuzu_emu.model 4package org.yuzu.yuzu_emu.model
5 5
6import androidx.lifecycle.LiveData
7import androidx.lifecycle.MutableLiveData
8import androidx.lifecycle.ViewModel 6import androidx.lifecycle.ViewModel
9import androidx.lifecycle.viewModelScope 7import androidx.lifecycle.viewModelScope
10import kotlinx.coroutines.Dispatchers 8import kotlinx.coroutines.Dispatchers
9import kotlinx.coroutines.flow.MutableStateFlow
10import kotlinx.coroutines.flow.StateFlow
11import kotlinx.coroutines.launch 11import kotlinx.coroutines.launch
12 12
13class TaskViewModel : ViewModel() { 13class TaskViewModel : ViewModel() {
14 private val _result = MutableLiveData<Any>() 14 val result: StateFlow<Any> get() = _result
15 val result: LiveData<Any> = _result 15 private val _result = MutableStateFlow(Any())
16 16
17 private val _isComplete = MutableLiveData<Boolean>() 17 val isComplete: StateFlow<Boolean> get() = _isComplete
18 val isComplete: LiveData<Boolean> = _isComplete 18 private val _isComplete = MutableStateFlow(false)
19 19
20 private val _isRunning = MutableLiveData<Boolean>() 20 val isRunning: StateFlow<Boolean> get() = _isRunning
21 val isRunning: LiveData<Boolean> = _isRunning 21 private val _isRunning = MutableStateFlow(false)
22 22
23 lateinit var task: () -> Any 23 lateinit var task: () -> Any
24 24
25 init {
26 clear()
27 }
28
29 fun clear() { 25 fun clear() {
30 _result.value = Any() 26 _result.value = Any()
31 _isComplete.value = false 27 _isComplete.value = false
@@ -33,15 +29,16 @@ class TaskViewModel : ViewModel() {
33 } 29 }
34 30
35 fun runTask() { 31 fun runTask() {
36 if (_isRunning.value == true) { 32 if (isRunning.value) {
37 return 33 return
38 } 34 }
39 _isRunning.value = true 35 _isRunning.value = true
40 36
41 viewModelScope.launch(Dispatchers.IO) { 37 viewModelScope.launch(Dispatchers.IO) {
42 val res = task() 38 val res = task()
43 _result.postValue(res) 39 _result.value = res
44 _isComplete.postValue(true) 40 _isComplete.value = true
41 _isRunning.value = false
45 } 42 }
46 } 43 }
47} 44}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
index b0156dca5..35e365458 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
@@ -3,6 +3,7 @@
3 3
4package org.yuzu.yuzu_emu.ui 4package org.yuzu.yuzu_emu.ui
5 5
6import android.annotation.SuppressLint
6import android.os.Bundle 7import android.os.Bundle
7import android.view.LayoutInflater 8import android.view.LayoutInflater
8import android.view.View 9import android.view.View
@@ -14,8 +15,12 @@ import androidx.core.view.WindowInsetsCompat
14import androidx.core.view.updatePadding 15import androidx.core.view.updatePadding
15import androidx.fragment.app.Fragment 16import androidx.fragment.app.Fragment
16import androidx.fragment.app.activityViewModels 17import androidx.fragment.app.activityViewModels
18import androidx.lifecycle.Lifecycle
19import androidx.lifecycle.lifecycleScope
20import androidx.lifecycle.repeatOnLifecycle
17import com.google.android.material.color.MaterialColors 21import com.google.android.material.color.MaterialColors
18import com.google.android.material.transition.MaterialFadeThrough 22import com.google.android.material.transition.MaterialFadeThrough
23import kotlinx.coroutines.launch
19import org.yuzu.yuzu_emu.R 24import org.yuzu.yuzu_emu.R
20import org.yuzu.yuzu_emu.adapters.GameAdapter 25import org.yuzu.yuzu_emu.adapters.GameAdapter
21import org.yuzu.yuzu_emu.databinding.FragmentGamesBinding 26import org.yuzu.yuzu_emu.databinding.FragmentGamesBinding
@@ -44,6 +49,8 @@ class GamesFragment : Fragment() {
44 return binding.root 49 return binding.root
45 } 50 }
46 51
52 // This is using the correct scope, lint is just acting up
53 @SuppressLint("UnsafeRepeatOnLifecycleDetector")
47 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 54 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
48 homeViewModel.setNavigationVisibility(visible = true, animated = false) 55 homeViewModel.setNavigationVisibility(visible = true, animated = false)
49 56
@@ -80,37 +87,48 @@ class GamesFragment : Fragment() {
80 if (_binding == null) { 87 if (_binding == null) {
81 return@post 88 return@post
82 } 89 }
83 binding.swipeRefresh.isRefreshing = gamesViewModel.isReloading.value!! 90 binding.swipeRefresh.isRefreshing = gamesViewModel.isReloading.value
84 } 91 }
85 } 92 }
86 93
87 gamesViewModel.apply { 94 viewLifecycleOwner.lifecycleScope.apply {
88 // Watch for when we get updates to any of our games lists 95 launch {
89 isReloading.observe(viewLifecycleOwner) { isReloading -> 96 repeatOnLifecycle(Lifecycle.State.CREATED) {
90 binding.swipeRefresh.isRefreshing = isReloading 97 gamesViewModel.isReloading.collect { binding.swipeRefresh.isRefreshing = it }
98 }
91 } 99 }
92 games.observe(viewLifecycleOwner) { 100 launch {
93 (binding.gridGames.adapter as GameAdapter).submitList(it) 101 repeatOnLifecycle(Lifecycle.State.CREATED) {
94 if (it.isEmpty()) { 102 gamesViewModel.games.collect {
95 binding.noticeText.visibility = View.VISIBLE 103 (binding.gridGames.adapter as GameAdapter).submitList(it)
96 } else { 104 if (it.isEmpty()) {
97 binding.noticeText.visibility = View.GONE 105 binding.noticeText.visibility = View.VISIBLE
106 } else {
107 binding.noticeText.visibility = View.GONE
108 }
109 }
98 } 110 }
99 } 111 }
100 shouldSwapData.observe(viewLifecycleOwner) { shouldSwapData -> 112 launch {
101 if (shouldSwapData) { 113 repeatOnLifecycle(Lifecycle.State.CREATED) {
102 (binding.gridGames.adapter as GameAdapter).submitList( 114 gamesViewModel.shouldSwapData.collect {
103 gamesViewModel.games.value!! 115 if (it) {
104 ) 116 (binding.gridGames.adapter as GameAdapter).submitList(
105 gamesViewModel.setShouldSwapData(false) 117 gamesViewModel.games.value
118 )
119 gamesViewModel.setShouldSwapData(false)
120 }
121 }
106 } 122 }
107 } 123 }
108 124 launch {
109 // Check if the user reselected the games menu item and then scroll to top of the list 125 repeatOnLifecycle(Lifecycle.State.CREATED) {
110 shouldScrollToTop.observe(viewLifecycleOwner) { shouldScroll -> 126 gamesViewModel.shouldScrollToTop.collect {
111 if (shouldScroll) { 127 if (it) {
112 scrollToTop() 128 scrollToTop()
113 gamesViewModel.setShouldScrollToTop(false) 129 gamesViewModel.setShouldScrollToTop(false)
130 }
131 }
114 } 132 }
115 } 133 }
116 } 134 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
index 7d8e06ad8..b6b6c6c17 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
@@ -19,7 +19,9 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
19import androidx.core.view.ViewCompat 19import androidx.core.view.ViewCompat
20import androidx.core.view.WindowCompat 20import androidx.core.view.WindowCompat
21import androidx.core.view.WindowInsetsCompat 21import androidx.core.view.WindowInsetsCompat
22import androidx.lifecycle.Lifecycle
22import androidx.lifecycle.lifecycleScope 23import androidx.lifecycle.lifecycleScope
24import androidx.lifecycle.repeatOnLifecycle
23import androidx.navigation.NavController 25import androidx.navigation.NavController
24import androidx.navigation.fragment.NavHostFragment 26import androidx.navigation.fragment.NavHostFragment
25import androidx.navigation.ui.setupWithNavController 27import androidx.navigation.ui.setupWithNavController
@@ -40,7 +42,6 @@ import org.yuzu.yuzu_emu.activities.EmulationActivity
40import org.yuzu.yuzu_emu.databinding.ActivityMainBinding 42import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
41import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding 43import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
42import org.yuzu.yuzu_emu.features.settings.model.Settings 44import org.yuzu.yuzu_emu.features.settings.model.Settings
43import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
44import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment 45import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
45import org.yuzu.yuzu_emu.fragments.MessageDialogFragment 46import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
46import org.yuzu.yuzu_emu.model.GamesViewModel 47import org.yuzu.yuzu_emu.model.GamesViewModel
@@ -107,7 +108,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
107 R.id.homeSettingsFragment -> { 108 R.id.homeSettingsFragment -> {
108 val action = HomeNavigationDirections.actionGlobalSettingsActivity( 109 val action = HomeNavigationDirections.actionGlobalSettingsActivity(
109 null, 110 null,
110 SettingsFile.FILE_NAME_CONFIG 111 Settings.MenuTag.SECTION_ROOT
111 ) 112 )
112 navHostFragment.navController.navigate(action) 113 navHostFragment.navController.navigate(action)
113 } 114 }
@@ -115,16 +116,22 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
115 } 116 }
116 117
117 // Prevents navigation from being drawn for a short time on recreation if set to hidden 118 // Prevents navigation from being drawn for a short time on recreation if set to hidden
118 if (!homeViewModel.navigationVisible.value?.first!!) { 119 if (!homeViewModel.navigationVisible.value.first) {
119 binding.navigationView.visibility = View.INVISIBLE 120 binding.navigationView.visibility = View.INVISIBLE
120 binding.statusBarShade.visibility = View.INVISIBLE 121 binding.statusBarShade.visibility = View.INVISIBLE
121 } 122 }
122 123
123 homeViewModel.navigationVisible.observe(this) { 124 lifecycleScope.apply {
124 showNavigation(it.first, it.second) 125 launch {
125 } 126 repeatOnLifecycle(Lifecycle.State.CREATED) {
126 homeViewModel.statusBarShadeVisible.observe(this) { visible -> 127 homeViewModel.navigationVisible.collect { showNavigation(it.first, it.second) }
127 showStatusBarShade(visible) 128 }
129 }
130 launch {
131 repeatOnLifecycle(Lifecycle.State.CREATED) {
132 homeViewModel.statusBarShadeVisible.collect { showStatusBarShade(it) }
133 }
134 }
128 } 135 }
129 136
130 // Dismiss previous notifications (should not happen unless a crash occurred) 137 // Dismiss previous notifications (should not happen unless a crash occurred)
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 2085430bf..2e0ce7a3d 100644
--- a/src/android/app/src/main/res/navigation/home_navigation.xml
+++ b/src/android/app/src/main/res/navigation/home_navigation.xml
@@ -82,7 +82,7 @@
82 app:nullable="true" /> 82 app:nullable="true" />
83 <argument 83 <argument
84 android:name="menuTag" 84 android:name="menuTag"
85 app:argType="string" /> 85 app:argType="org.yuzu.yuzu_emu.features.settings.model.Settings$MenuTag" />
86 </activity> 86 </activity>
87 87
88 <action 88 <action
diff --git a/src/android/app/src/main/res/navigation/settings_navigation.xml b/src/android/app/src/main/res/navigation/settings_navigation.xml
index 88e1b4587..1d87d36b3 100644
--- a/src/android/app/src/main/res/navigation/settings_navigation.xml
+++ b/src/android/app/src/main/res/navigation/settings_navigation.xml
@@ -10,7 +10,7 @@
10 android:label="SettingsFragment"> 10 android:label="SettingsFragment">
11 <argument 11 <argument
12 android:name="menuTag" 12 android:name="menuTag"
13 app:argType="string" /> 13 app:argType="org.yuzu.yuzu_emu.features.settings.model.Settings$MenuTag" />
14 <argument 14 <argument
15 android:name="game" 15 android:name="game"
16 app:argType="org.yuzu.yuzu_emu.model.Game" 16 app:argType="org.yuzu.yuzu_emu.model.Game"