summaryrefslogtreecommitdiff
path: root/src/android
diff options
context:
space:
mode:
authorGravatar t8952024-02-17 21:23:38 -0500
committerGravatar t8952024-02-17 23:09:09 -0500
commit35a3c7226a796ae7205753c13924fe0becd8fe60 (patch)
tree9b705b44d686b2ca2feac0057ac92404cab4b3e6 /src/android
parentMerge pull request #13050 from t895/marquee-helper (diff)
downloadyuzu-35a3c7226a796ae7205753c13924fe0becd8fe60.tar.gz
yuzu-35a3c7226a796ae7205753c13924fe0becd8fe60.tar.xz
yuzu-35a3c7226a796ae7205753c13924fe0becd8fe60.zip
android: Create lifecycle utility to simplify common StateFlow operations
Diffstat (limited to 'src/android')
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt11
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt11
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileDialogFragment.kt39
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt55
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt21
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt99
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsSearchFragment.kt17
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt91
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt18
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriversLoadingDialogFragment.kt13
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt193
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt12
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt36
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt16
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt99
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt41
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt37
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt69
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt45
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/LifecycleUtils.kt38
20 files changed, 331 insertions, 630 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt
index 017306875..7366e2c77 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt
@@ -6,11 +6,7 @@ package org.yuzu.yuzu_emu.adapters
6import android.view.LayoutInflater 6import android.view.LayoutInflater
7import android.view.ViewGroup 7import android.view.ViewGroup
8import androidx.core.content.res.ResourcesCompat 8import androidx.core.content.res.ResourcesCompat
9import androidx.lifecycle.Lifecycle
10import androidx.lifecycle.LifecycleOwner 9import androidx.lifecycle.LifecycleOwner
11import androidx.lifecycle.lifecycleScope
12import androidx.lifecycle.repeatOnLifecycle
13import kotlinx.coroutines.launch
14import org.yuzu.yuzu_emu.databinding.CardInstallableIconBinding 10import org.yuzu.yuzu_emu.databinding.CardInstallableIconBinding
15import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding 11import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding
16import org.yuzu.yuzu_emu.model.GameProperty 12import org.yuzu.yuzu_emu.model.GameProperty
@@ -18,6 +14,7 @@ import org.yuzu.yuzu_emu.model.InstallableProperty
18import org.yuzu.yuzu_emu.model.SubmenuProperty 14import org.yuzu.yuzu_emu.model.SubmenuProperty
19import org.yuzu.yuzu_emu.utils.ViewUtils.marquee 15import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
20import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible 16import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
17import org.yuzu.yuzu_emu.utils.collect
21import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder 18import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
22 19
23class GamePropertiesAdapter( 20class GamePropertiesAdapter(
@@ -82,11 +79,7 @@ class GamePropertiesAdapter(
82 binding.details.text = submenuProperty.details.invoke() 79 binding.details.text = submenuProperty.details.invoke()
83 } else if (submenuProperty.detailsFlow != null) { 80 } else if (submenuProperty.detailsFlow != null) {
84 binding.details.setVisible(true) 81 binding.details.setVisible(true)
85 viewLifecycle.lifecycleScope.launch { 82 submenuProperty.detailsFlow.collect(viewLifecycle) { binding.details.text = it }
86 viewLifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
87 submenuProperty.detailsFlow.collect { binding.details.text = it }
88 }
89 }
90 } else { 83 } else {
91 binding.details.setVisible(false) 84 binding.details.setVisible(false)
92 } 85 }
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 9234a4901..0bd196673 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
@@ -8,17 +8,14 @@ import android.view.ViewGroup
8import androidx.appcompat.app.AppCompatActivity 8import androidx.appcompat.app.AppCompatActivity
9import androidx.core.content.ContextCompat 9import androidx.core.content.ContextCompat
10import androidx.core.content.res.ResourcesCompat 10import androidx.core.content.res.ResourcesCompat
11import androidx.lifecycle.Lifecycle
12import androidx.lifecycle.LifecycleOwner 11import androidx.lifecycle.LifecycleOwner
13import androidx.lifecycle.lifecycleScope
14import androidx.lifecycle.repeatOnLifecycle
15import kotlinx.coroutines.launch
16import org.yuzu.yuzu_emu.R 12import org.yuzu.yuzu_emu.R
17import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding 13import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding
18import org.yuzu.yuzu_emu.fragments.MessageDialogFragment 14import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
19import org.yuzu.yuzu_emu.model.HomeSetting 15import org.yuzu.yuzu_emu.model.HomeSetting
20import org.yuzu.yuzu_emu.utils.ViewUtils.marquee 16import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
21import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible 17import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
18import org.yuzu.yuzu_emu.utils.collect
22import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder 19import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
23 20
24class HomeSettingAdapter( 21class HomeSettingAdapter(
@@ -59,11 +56,7 @@ class HomeSettingAdapter(
59 binding.optionIcon.alpha = 0.5f 56 binding.optionIcon.alpha = 0.5f
60 } 57 }
61 58
62 viewLifecycle.lifecycleScope.launch { 59 model.details.collect(viewLifecycle) { updateOptionDetails(it) }
63 viewLifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) {
64 model.details.collect { updateOptionDetails(it) }
65 }
66 }
67 binding.optionDetail.marquee() 60 binding.optionDetail.marquee()
68 61
69 binding.root.setOnClickListener { onClick(model) } 62 binding.root.setOnClickListener { onClick(model) }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileDialogFragment.kt
index 9b24d41c1..1bae593ae 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileDialogFragment.kt
@@ -11,16 +11,13 @@ import android.view.ViewGroup
11import android.widget.Toast 11import android.widget.Toast
12import androidx.fragment.app.DialogFragment 12import androidx.fragment.app.DialogFragment
13import androidx.fragment.app.activityViewModels 13import androidx.fragment.app.activityViewModels
14import androidx.lifecycle.Lifecycle
15import androidx.lifecycle.lifecycleScope
16import androidx.lifecycle.repeatOnLifecycle
17import androidx.recyclerview.widget.LinearLayoutManager 14import androidx.recyclerview.widget.LinearLayoutManager
18import com.google.android.material.dialog.MaterialAlertDialogBuilder 15import com.google.android.material.dialog.MaterialAlertDialogBuilder
19import kotlinx.coroutines.launch
20import org.yuzu.yuzu_emu.R 16import org.yuzu.yuzu_emu.R
21import org.yuzu.yuzu_emu.databinding.DialogInputProfilesBinding 17import org.yuzu.yuzu_emu.databinding.DialogInputProfilesBinding
22import org.yuzu.yuzu_emu.features.settings.model.view.InputProfileSetting 18import org.yuzu.yuzu_emu.features.settings.model.view.InputProfileSetting
23import org.yuzu.yuzu_emu.fragments.MessageDialogFragment 19import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
20import org.yuzu.yuzu_emu.utils.collect
24 21
25class InputProfileDialogFragment : DialogFragment() { 22class InputProfileDialogFragment : DialogFragment() {
26 private var position = 0 23 private var position = 0
@@ -110,25 +107,21 @@ class InputProfileDialogFragment : DialogFragment() {
110 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 107 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
111 super.onViewCreated(view, savedInstanceState) 108 super.onViewCreated(view, savedInstanceState)
112 109
113 viewLifecycleOwner.lifecycleScope.launch { 110 settingsViewModel.shouldShowDeleteProfileDialog.collect(viewLifecycleOwner) {
114 repeatOnLifecycle(Lifecycle.State.CREATED) { 111 if (it.isNotEmpty()) {
115 settingsViewModel.shouldShowDeleteProfileDialog.collect { 112 MessageDialogFragment.newInstance(
116 if (it.isNotEmpty()) { 113 activity = requireActivity(),
117 MessageDialogFragment.newInstance( 114 titleId = R.string.delete_input_profile,
118 activity = requireActivity(), 115 descriptionId = R.string.delete_input_profile_description,
119 titleId = R.string.delete_input_profile, 116 positiveAction = {
120 descriptionId = R.string.delete_input_profile_description, 117 setting.deleteProfile(it)
121 positiveAction = { 118 settingsViewModel.setReloadListAndNotifyDataset(true)
122 setting.deleteProfile(it) 119 },
123 settingsViewModel.setReloadListAndNotifyDataset(true) 120 negativeAction = {},
124 }, 121 negativeButtonTitleId = android.R.string.cancel
125 negativeAction = {}, 122 ).show(parentFragmentManager, MessageDialogFragment.TAG)
126 negativeButtonTitleId = android.R.string.cancel 123 settingsViewModel.setShouldShowDeleteProfileDialog("")
127 ).show(parentFragmentManager, MessageDialogFragment.TAG) 124 dismiss()
128 settingsViewModel.setShouldShowDeleteProfileDialog("")
129 dismiss()
130 }
131 }
132 } 125 }
133 } 126 }
134 } 127 }
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 681a18b3b..455b3b5ff 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,14 +13,9 @@ 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
19import androidx.navigation.fragment.NavHostFragment 16import androidx.navigation.fragment.NavHostFragment
20import androidx.navigation.navArgs 17import androidx.navigation.navArgs
21import com.google.android.material.color.MaterialColors 18import com.google.android.material.color.MaterialColors
22import kotlinx.coroutines.flow.collectLatest
23import kotlinx.coroutines.launch
24import org.yuzu.yuzu_emu.NativeLibrary 19import org.yuzu.yuzu_emu.NativeLibrary
25import java.io.IOException 20import java.io.IOException
26import org.yuzu.yuzu_emu.R 21import org.yuzu.yuzu_emu.R
@@ -70,39 +65,23 @@ class SettingsActivity : AppCompatActivity() {
70 ) 65 )
71 } 66 }
72 67
73 lifecycleScope.apply { 68 settingsViewModel.shouldRecreate.collect(
74 launch { 69 this,
75 repeatOnLifecycle(Lifecycle.State.CREATED) { 70 resetState = { settingsViewModel.setShouldRecreate(false) }
76 settingsViewModel.shouldRecreate.collectLatest { 71 ) { if (it) recreate() }
77 if (it) { 72 settingsViewModel.shouldNavigateBack.collect(
78 settingsViewModel.setShouldRecreate(false) 73 this,
79 recreate() 74 resetState = { settingsViewModel.setShouldNavigateBack(false) }
80 } 75 ) { if (it) navigateBack() }
81 } 76 settingsViewModel.shouldShowResetSettingsDialog.collect(
82 } 77 this,
83 } 78 resetState = { settingsViewModel.setShouldShowResetSettingsDialog(false) }
84 launch { 79 ) {
85 repeatOnLifecycle(Lifecycle.State.CREATED) { 80 if (it) {
86 settingsViewModel.shouldNavigateBack.collectLatest { 81 ResetSettingsDialogFragment().show(
87 if (it) { 82 supportFragmentManager,
88 settingsViewModel.setShouldNavigateBack(false) 83 ResetSettingsDialogFragment.TAG
89 navigateBack() 84 )
90 }
91 }
92 }
93 }
94 launch {
95 repeatOnLifecycle(Lifecycle.State.CREATED) {
96 settingsViewModel.shouldShowResetSettingsDialog.collectLatest {
97 if (it) {
98 settingsViewModel.setShouldShowResetSettingsDialog(false)
99 ResetSettingsDialogFragment().show(
100 supportFragmentManager,
101 ResetSettingsDialogFragment.TAG
102 )
103 }
104 }
105 }
106 } 85 }
107 } 86 }
108 87
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt
index 5d1ea5d29..a81ff6b1a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt
@@ -11,12 +11,8 @@ import android.view.View
11import android.view.ViewGroup 11import android.view.ViewGroup
12import androidx.fragment.app.DialogFragment 12import androidx.fragment.app.DialogFragment
13import androidx.fragment.app.activityViewModels 13import androidx.fragment.app.activityViewModels
14import androidx.lifecycle.Lifecycle
15import androidx.lifecycle.lifecycleScope
16import androidx.lifecycle.repeatOnLifecycle
17import com.google.android.material.dialog.MaterialAlertDialogBuilder 14import com.google.android.material.dialog.MaterialAlertDialogBuilder
18import com.google.android.material.slider.Slider 15import com.google.android.material.slider.Slider
19import kotlinx.coroutines.launch
20import org.yuzu.yuzu_emu.R 16import org.yuzu.yuzu_emu.R
21import org.yuzu.yuzu_emu.databinding.DialogSliderBinding 17import org.yuzu.yuzu_emu.databinding.DialogSliderBinding
22import org.yuzu.yuzu_emu.features.input.NativeInput 18import org.yuzu.yuzu_emu.features.input.NativeInput
@@ -29,6 +25,7 @@ import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting
29import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting 25import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting
30import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting 26import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting
31import org.yuzu.yuzu_emu.utils.ParamPackage 27import org.yuzu.yuzu_emu.utils.ParamPackage
28import org.yuzu.yuzu_emu.utils.collect
32 29
33class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener { 30class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener {
34 private var type = 0 31 private var type = 0
@@ -169,17 +166,11 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
169 super.onViewCreated(view, savedInstanceState) 166 super.onViewCreated(view, savedInstanceState)
170 when (type) { 167 when (type) {
171 SettingsItem.TYPE_SLIDER -> { 168 SettingsItem.TYPE_SLIDER -> {
172 viewLifecycleOwner.lifecycleScope.launch { 169 settingsViewModel.sliderTextValue.collect(viewLifecycleOwner) {
173 repeatOnLifecycle(Lifecycle.State.CREATED) { 170 sliderBinding.textValue.text = it
174 settingsViewModel.sliderTextValue.collect { 171 }
175 sliderBinding.textValue.text = it 172 settingsViewModel.sliderProgress.collect(viewLifecycleOwner) {
176 } 173 sliderBinding.slider.value = it.toFloat()
177 }
178 repeatOnLifecycle(Lifecycle.State.CREATED) {
179 settingsViewModel.sliderProgress.collect {
180 sliderBinding.slider.value = it.toFloat()
181 }
182 }
183 } 174 }
184 } 175 }
185 } 176 }
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 0cf944b43..ec16f16c4 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
@@ -13,21 +13,17 @@ import androidx.core.view.WindowInsetsCompat
13import androidx.core.view.updatePadding 13import androidx.core.view.updatePadding
14import androidx.fragment.app.Fragment 14import androidx.fragment.app.Fragment
15import androidx.fragment.app.activityViewModels 15import androidx.fragment.app.activityViewModels
16import androidx.lifecycle.Lifecycle
17import androidx.lifecycle.lifecycleScope
18import androidx.lifecycle.repeatOnLifecycle
19import androidx.navigation.findNavController 16import androidx.navigation.findNavController
20import androidx.navigation.fragment.navArgs 17import androidx.navigation.fragment.navArgs
21import androidx.recyclerview.widget.LinearLayoutManager 18import androidx.recyclerview.widget.LinearLayoutManager
22import com.google.android.material.transition.MaterialSharedAxis 19import com.google.android.material.transition.MaterialSharedAxis
23import kotlinx.coroutines.flow.collectLatest
24import kotlinx.coroutines.launch
25import org.yuzu.yuzu_emu.R 20import org.yuzu.yuzu_emu.R
26import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding 21import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding
27import org.yuzu.yuzu_emu.features.input.NativeInput 22import org.yuzu.yuzu_emu.features.input.NativeInput
28import org.yuzu.yuzu_emu.features.settings.model.Settings 23import org.yuzu.yuzu_emu.features.settings.model.Settings
29import org.yuzu.yuzu_emu.fragments.MessageDialogFragment 24import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
30import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins 25import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
26import org.yuzu.yuzu_emu.utils.collect
31 27
32class SettingsFragment : Fragment() { 28class SettingsFragment : Fragment() {
33 private lateinit var presenter: SettingsFragmentPresenter 29 private lateinit var presenter: SettingsFragmentPresenter
@@ -63,8 +59,7 @@ class SettingsFragment : Fragment() {
63 return binding.root 59 return binding.root
64 } 60 }
65 61
66 // This is using the correct scope, lint is just acting up 62 @SuppressLint("NotifyDataSetChanged")
67 @SuppressLint("UnsafeRepeatOnLifecycleDetector", "NotifyDataSetChanged")
68 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 63 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
69 super.onViewCreated(view, savedInstanceState) 64 super.onViewCreated(view, savedInstanceState)
70 settingsAdapter = SettingsAdapter(this, requireContext()) 65 settingsAdapter = SettingsAdapter(this, requireContext())
@@ -100,65 +95,37 @@ class SettingsFragment : Fragment() {
100 settingsViewModel.setShouldNavigateBack(true) 95 settingsViewModel.setShouldNavigateBack(true)
101 } 96 }
102 97
103 viewLifecycleOwner.lifecycleScope.apply { 98 settingsViewModel.shouldReloadSettingsList.collect(
104 launch { 99 viewLifecycleOwner,
105 repeatOnLifecycle(Lifecycle.State.CREATED) { 100 resetState = { settingsViewModel.setShouldReloadSettingsList(false) }
106 settingsViewModel.shouldReloadSettingsList.collectLatest { 101 ) { if (it) presenter.loadSettingsList() }
107 if (it) { 102 settingsViewModel.adapterItemChanged.collect(
108 settingsViewModel.setShouldReloadSettingsList(false) 103 viewLifecycleOwner,
109 presenter.loadSettingsList() 104 resetState = { settingsViewModel.setAdapterItemChanged(-1) }
110 } 105 ) { if (it != -1) settingsAdapter?.notifyItemChanged(it) }
111 } 106 settingsViewModel.datasetChanged.collect(
112 } 107 viewLifecycleOwner,
113 } 108 resetState = { settingsViewModel.setDatasetChanged(false) }
114 launch { 109 ) { if (it) settingsAdapter?.notifyDataSetChanged() }
115 repeatOnLifecycle(Lifecycle.State.STARTED) { 110 settingsViewModel.reloadListAndNotifyDataset.collect(
116 settingsViewModel.adapterItemChanged.collect { 111 viewLifecycleOwner,
117 if (it != -1) { 112 resetState = { settingsViewModel.setReloadListAndNotifyDataset(false) }
118 settingsAdapter?.notifyItemChanged(it) 113 ) { if (it) presenter.loadSettingsList(true) }
119 settingsViewModel.setAdapterItemChanged(-1) 114 settingsViewModel.shouldShowResetInputDialog.collect(
120 } 115 viewLifecycleOwner,
121 } 116 resetState = { settingsViewModel.setShouldShowResetInputDialog(false) }
122 } 117 ) {
123 } 118 if (it) {
124 launch { 119 MessageDialogFragment.newInstance(
125 repeatOnLifecycle(Lifecycle.State.STARTED) { 120 activity = requireActivity(),
126 settingsViewModel.datasetChanged.collect { 121 titleId = R.string.reset_mapping,
127 if (it) { 122 descriptionId = R.string.reset_mapping_description,
128 settingsAdapter?.notifyDataSetChanged() 123 positiveAction = {
129 settingsViewModel.setDatasetChanged(false) 124 NativeInput.resetControllerMappings(getPlayerIndex())
130 } 125 settingsViewModel.setReloadListAndNotifyDataset(true)
131 } 126 },
132 } 127 negativeAction = {}
133 } 128 ).show(parentFragmentManager, MessageDialogFragment.TAG)
134 launch {
135 repeatOnLifecycle(Lifecycle.State.CREATED) {
136 settingsViewModel.reloadListAndNotifyDataset.collectLatest {
137 if (it) {
138 settingsViewModel.setReloadListAndNotifyDataset(false)
139 presenter.loadSettingsList(true)
140 }
141 }
142 }
143 }
144 launch {
145 repeatOnLifecycle(Lifecycle.State.CREATED) {
146 settingsViewModel.shouldShowResetInputDialog.collectLatest {
147 if (it) {
148 MessageDialogFragment.newInstance(
149 activity = requireActivity(),
150 titleId = R.string.reset_mapping,
151 descriptionId = R.string.reset_mapping_description,
152 positiveAction = {
153 NativeInput.resetControllerMappings(getPlayerIndex())
154 settingsViewModel.setReloadListAndNotifyDataset(true)
155 },
156 negativeAction = {}
157 ).show(parentFragmentManager, MessageDialogFragment.TAG)
158 settingsViewModel.setShouldShowResetInputDialog(false)
159 }
160 }
161 }
162 } 129 }
163 } 130 }
164 131
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsSearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsSearchFragment.kt
index c4c1d563a..ed60cf34f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsSearchFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsSearchFragment.kt
@@ -15,20 +15,17 @@ 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
21import androidx.recyclerview.widget.LinearLayoutManager 18import androidx.recyclerview.widget.LinearLayoutManager
22import com.google.android.material.divider.MaterialDividerItemDecoration 19import com.google.android.material.divider.MaterialDividerItemDecoration
23import com.google.android.material.transition.MaterialSharedAxis 20import com.google.android.material.transition.MaterialSharedAxis
24import info.debatty.java.stringsimilarity.Cosine 21import info.debatty.java.stringsimilarity.Cosine
25import kotlinx.coroutines.launch
26import org.yuzu.yuzu_emu.R 22import org.yuzu.yuzu_emu.R
27import org.yuzu.yuzu_emu.databinding.FragmentSettingsSearchBinding 23import org.yuzu.yuzu_emu.databinding.FragmentSettingsSearchBinding
28import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem 24import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
29import org.yuzu.yuzu_emu.utils.NativeConfig 25import org.yuzu.yuzu_emu.utils.NativeConfig
30import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible 26import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
31import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins 27import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
28import org.yuzu.yuzu_emu.utils.collect
32 29
33class SettingsSearchFragment : Fragment() { 30class SettingsSearchFragment : Fragment() {
34 private var _binding: FragmentSettingsSearchBinding? = null 31 private var _binding: FragmentSettingsSearchBinding? = null
@@ -84,14 +81,10 @@ class SettingsSearchFragment : Fragment() {
84 search() 81 search()
85 binding.settingsList.smoothScrollToPosition(0) 82 binding.settingsList.smoothScrollToPosition(0)
86 } 83 }
87 viewLifecycleOwner.lifecycleScope.launch { 84 settingsViewModel.shouldReloadSettingsList.collect(viewLifecycleOwner) {
88 repeatOnLifecycle(Lifecycle.State.CREATED) { 85 if (it) {
89 settingsViewModel.shouldReloadSettingsList.collect { 86 settingsViewModel.setShouldReloadSettingsList(false)
90 if (it) { 87 search()
91 settingsViewModel.setShouldReloadSettingsList(false)
92 search()
93 }
94 }
95 } 88 }
96 } 89 }
97 90
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
index 872553ac4..110aa2960 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
@@ -3,7 +3,6 @@
3 3
4package org.yuzu.yuzu_emu.fragments 4package org.yuzu.yuzu_emu.fragments
5 5
6import android.annotation.SuppressLint
7import android.content.Intent 6import android.content.Intent
8import android.os.Bundle 7import android.os.Bundle
9import android.view.LayoutInflater 8import android.view.LayoutInflater
@@ -16,9 +15,6 @@ import androidx.core.view.updatePadding
16import androidx.documentfile.provider.DocumentFile 15import androidx.documentfile.provider.DocumentFile
17import androidx.fragment.app.Fragment 16import androidx.fragment.app.Fragment
18import androidx.fragment.app.activityViewModels 17import androidx.fragment.app.activityViewModels
19import androidx.lifecycle.Lifecycle
20import androidx.lifecycle.lifecycleScope
21import androidx.lifecycle.repeatOnLifecycle
22import androidx.navigation.findNavController 18import androidx.navigation.findNavController
23import androidx.navigation.fragment.navArgs 19import androidx.navigation.fragment.navArgs
24import androidx.recyclerview.widget.LinearLayoutManager 20import androidx.recyclerview.widget.LinearLayoutManager
@@ -32,6 +28,7 @@ import org.yuzu.yuzu_emu.model.HomeViewModel
32import org.yuzu.yuzu_emu.utils.AddonUtil 28import org.yuzu.yuzu_emu.utils.AddonUtil
33import org.yuzu.yuzu_emu.utils.FileUtil.copyFilesTo 29import org.yuzu.yuzu_emu.utils.FileUtil.copyFilesTo
34import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins 30import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
31import org.yuzu.yuzu_emu.utils.collect
35import java.io.File 32import java.io.File
36 33
37class AddonsFragment : Fragment() { 34class AddonsFragment : Fragment() {
@@ -60,8 +57,6 @@ class AddonsFragment : Fragment() {
60 return binding.root 57 return binding.root
61 } 58 }
62 59
63 // This is using the correct scope, lint is just acting up
64 @SuppressLint("UnsafeRepeatOnLifecycleDetector")
65 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 60 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
66 super.onViewCreated(view, savedInstanceState) 61 super.onViewCreated(view, savedInstanceState)
67 homeViewModel.setNavigationVisibility(visible = false, animated = false) 62 homeViewModel.setNavigationVisibility(visible = false, animated = false)
@@ -78,57 +73,41 @@ class AddonsFragment : Fragment() {
78 adapter = AddonAdapter(addonViewModel) 73 adapter = AddonAdapter(addonViewModel)
79 } 74 }
80 75
81 viewLifecycleOwner.lifecycleScope.apply { 76 addonViewModel.addonList.collect(viewLifecycleOwner) {
82 launch { 77 (binding.listAddons.adapter as AddonAdapter).submitList(it)
83 repeatOnLifecycle(Lifecycle.State.STARTED) { 78 }
84 addonViewModel.addonList.collect { 79 addonViewModel.showModInstallPicker.collect(
85 (binding.listAddons.adapter as AddonAdapter).submitList(it) 80 viewLifecycleOwner,
86 } 81 resetState = { addonViewModel.showModInstallPicker(false) }
87 } 82 ) { if (it) installAddon.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) }
88 } 83 addonViewModel.showModNoticeDialog.collect(
89 launch { 84 viewLifecycleOwner,
90 repeatOnLifecycle(Lifecycle.State.STARTED) { 85 resetState = { addonViewModel.showModNoticeDialog(false) }
91 addonViewModel.showModInstallPicker.collect { 86 ) {
92 if (it) { 87 if (it) {
93 installAddon.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) 88 MessageDialogFragment.newInstance(
94 addonViewModel.showModInstallPicker(false) 89 requireActivity(),
95 } 90 titleId = R.string.addon_notice,
96 } 91 descriptionId = R.string.addon_notice_description,
97 } 92 dismissible = false,
98 } 93 positiveAction = { addonViewModel.showModInstallPicker(true) },
99 launch { 94 negativeAction = {},
100 repeatOnLifecycle(Lifecycle.State.STARTED) { 95 negativeButtonTitleId = R.string.close
101 addonViewModel.showModNoticeDialog.collect { 96 ).show(parentFragmentManager, MessageDialogFragment.TAG)
102 if (it) {
103 MessageDialogFragment.newInstance(
104 requireActivity(),
105 titleId = R.string.addon_notice,
106 descriptionId = R.string.addon_notice_description,
107 dismissible = false,
108 positiveAction = { addonViewModel.showModInstallPicker(true) },
109 negativeAction = {},
110 negativeButtonTitleId = R.string.close
111 ).show(parentFragmentManager, MessageDialogFragment.TAG)
112 addonViewModel.showModNoticeDialog(false)
113 }
114 }
115 }
116 } 97 }
117 launch { 98 }
118 repeatOnLifecycle(Lifecycle.State.STARTED) { 99 addonViewModel.addonToDelete.collect(
119 addonViewModel.addonToDelete.collect { 100 viewLifecycleOwner,
120 if (it != null) { 101 resetState = { addonViewModel.setAddonToDelete(null) }
121 MessageDialogFragment.newInstance( 102 ) {
122 requireActivity(), 103 if (it != null) {
123 titleId = R.string.confirm_uninstall, 104 MessageDialogFragment.newInstance(
124 descriptionId = R.string.confirm_uninstall_description, 105 requireActivity(),
125 positiveAction = { addonViewModel.onDeleteAddon(it) }, 106 titleId = R.string.confirm_uninstall,
126 negativeAction = {} 107 descriptionId = R.string.confirm_uninstall_description,
127 ).show(parentFragmentManager, MessageDialogFragment.TAG) 108 positiveAction = { addonViewModel.onDeleteAddon(it) },
128 addonViewModel.setAddonToDelete(null) 109 negativeAction = {}
129 } 110 ).show(parentFragmentManager, MessageDialogFragment.TAG)
130 }
131 }
132 } 111 }
133 } 112 }
134 113
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
index 41cff46c1..8b23a1021 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
@@ -3,7 +3,6 @@
3 3
4package org.yuzu.yuzu_emu.fragments 4package org.yuzu.yuzu_emu.fragments
5 5
6import android.annotation.SuppressLint
7import android.os.Bundle 6import android.os.Bundle
8import android.view.LayoutInflater 7import android.view.LayoutInflater
9import android.view.View 8import android.view.View
@@ -14,9 +13,6 @@ import androidx.core.view.WindowInsetsCompat
14import androidx.core.view.updatePadding 13import androidx.core.view.updatePadding
15import androidx.fragment.app.Fragment 14import androidx.fragment.app.Fragment
16import androidx.fragment.app.activityViewModels 15import androidx.fragment.app.activityViewModels
17import androidx.lifecycle.Lifecycle
18import androidx.lifecycle.lifecycleScope
19import androidx.lifecycle.repeatOnLifecycle
20import androidx.navigation.findNavController 16import androidx.navigation.findNavController
21import androidx.navigation.fragment.navArgs 17import androidx.navigation.fragment.navArgs
22import androidx.recyclerview.widget.GridLayoutManager 18import androidx.recyclerview.widget.GridLayoutManager
@@ -35,6 +31,7 @@ import org.yuzu.yuzu_emu.utils.FileUtil
35import org.yuzu.yuzu_emu.utils.GpuDriverHelper 31import org.yuzu.yuzu_emu.utils.GpuDriverHelper
36import org.yuzu.yuzu_emu.utils.NativeConfig 32import org.yuzu.yuzu_emu.utils.NativeConfig
37import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins 33import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
34import org.yuzu.yuzu_emu.utils.collect
38import java.io.File 35import java.io.File
39import java.io.IOException 36import java.io.IOException
40 37
@@ -63,8 +60,6 @@ class DriverManagerFragment : Fragment() {
63 return binding.root 60 return binding.root
64 } 61 }
65 62
66 // This is using the correct scope, lint is just acting up
67 @SuppressLint("UnsafeRepeatOnLifecycleDetector")
68 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 63 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
69 super.onViewCreated(view, savedInstanceState) 64 super.onViewCreated(view, savedInstanceState)
70 homeViewModel.setNavigationVisibility(visible = false, animated = true) 65 homeViewModel.setNavigationVisibility(visible = false, animated = true)
@@ -89,15 +84,8 @@ class DriverManagerFragment : Fragment() {
89 } 84 }
90 } 85 }
91 86
92 viewLifecycleOwner.lifecycleScope.apply { 87 driverViewModel.showClearButton.collect(viewLifecycleOwner) {
93 launch { 88 binding.toolbarDrivers.menu.findItem(R.id.menu_driver_use_global).isVisible = it
94 repeatOnLifecycle(Lifecycle.State.STARTED) {
95 driverViewModel.showClearButton.collect {
96 binding.toolbarDrivers.menu
97 .findItem(R.id.menu_driver_use_global).isVisible = it
98 }
99 }
100 }
101 } 89 }
102 } 90 }
103 91
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriversLoadingDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriversLoadingDialogFragment.kt
index 6a47b29f0..bad56e434 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriversLoadingDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriversLoadingDialogFragment.kt
@@ -10,14 +10,11 @@ import android.view.View
10import android.view.ViewGroup 10import android.view.ViewGroup
11import androidx.fragment.app.DialogFragment 11import androidx.fragment.app.DialogFragment
12import androidx.fragment.app.activityViewModels 12import androidx.fragment.app.activityViewModels
13import androidx.lifecycle.Lifecycle
14import androidx.lifecycle.lifecycleScope
15import androidx.lifecycle.repeatOnLifecycle
16import com.google.android.material.dialog.MaterialAlertDialogBuilder 13import com.google.android.material.dialog.MaterialAlertDialogBuilder
17import kotlinx.coroutines.launch
18import org.yuzu.yuzu_emu.R 14import org.yuzu.yuzu_emu.R
19import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding 15import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
20import org.yuzu.yuzu_emu.model.DriverViewModel 16import org.yuzu.yuzu_emu.model.DriverViewModel
17import org.yuzu.yuzu_emu.utils.collect
21 18
22class DriversLoadingDialogFragment : DialogFragment() { 19class DriversLoadingDialogFragment : DialogFragment() {
23 private val driverViewModel: DriverViewModel by activityViewModels() 20 private val driverViewModel: DriverViewModel by activityViewModels()
@@ -44,13 +41,7 @@ class DriversLoadingDialogFragment : DialogFragment() {
44 41
45 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 42 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
46 super.onViewCreated(view, savedInstanceState) 43 super.onViewCreated(view, savedInstanceState)
47 viewLifecycleOwner.lifecycleScope.apply { 44 driverViewModel.isInteractionAllowed.collect(viewLifecycleOwner) { if (it) dismiss() }
48 launch {
49 repeatOnLifecycle(Lifecycle.State.RESUMED) {
50 driverViewModel.isInteractionAllowed.collect { if (it) dismiss() }
51 }
52 }
53 }
54 } 45 }
55 46
56 companion object { 47 companion object {
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 aedc128d6..c3b2b11f8 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
@@ -32,9 +32,6 @@ import androidx.drawerlayout.widget.DrawerLayout
32import androidx.drawerlayout.widget.DrawerLayout.DrawerListener 32import androidx.drawerlayout.widget.DrawerLayout.DrawerListener
33import androidx.fragment.app.Fragment 33import androidx.fragment.app.Fragment
34import androidx.fragment.app.activityViewModels 34import androidx.fragment.app.activityViewModels
35import androidx.lifecycle.Lifecycle
36import androidx.lifecycle.lifecycleScope
37import androidx.lifecycle.repeatOnLifecycle
38import androidx.navigation.findNavController 35import androidx.navigation.findNavController
39import androidx.navigation.fragment.navArgs 36import androidx.navigation.fragment.navArgs
40import androidx.window.layout.FoldingFeature 37import androidx.window.layout.FoldingFeature
@@ -42,9 +39,6 @@ import androidx.window.layout.WindowInfoTracker
42import androidx.window.layout.WindowLayoutInfo 39import androidx.window.layout.WindowLayoutInfo
43import com.google.android.material.dialog.MaterialAlertDialogBuilder 40import com.google.android.material.dialog.MaterialAlertDialogBuilder
44import com.google.android.material.slider.Slider 41import com.google.android.material.slider.Slider
45import kotlinx.coroutines.Dispatchers
46import kotlinx.coroutines.flow.collectLatest
47import kotlinx.coroutines.launch
48import org.yuzu.yuzu_emu.HomeNavigationDirections 42import org.yuzu.yuzu_emu.HomeNavigationDirections
49import org.yuzu.yuzu_emu.NativeLibrary 43import org.yuzu.yuzu_emu.NativeLibrary
50import org.yuzu.yuzu_emu.R 44import org.yuzu.yuzu_emu.R
@@ -91,14 +85,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
91 if (context is EmulationActivity) { 85 if (context is EmulationActivity) {
92 emulationActivity = context 86 emulationActivity = context
93 NativeLibrary.setEmulationActivity(context) 87 NativeLibrary.setEmulationActivity(context)
94
95 lifecycleScope.launch(Dispatchers.Main) {
96 lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
97 WindowInfoTracker.getOrCreate(context)
98 .windowLayoutInfo(context)
99 .collect { updateFoldableLayout(context, it) }
100 }
101 }
102 } else { 88 } else {
103 throw IllegalStateException("EmulationFragment must have EmulationActivity parent") 89 throw IllegalStateException("EmulationFragment must have EmulationActivity parent")
104 } 90 }
@@ -169,8 +155,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
169 return binding.root 155 return binding.root
170 } 156 }
171 157
172 // This is using the correct scope, lint is just acting up
173 @SuppressLint("UnsafeRepeatOnLifecycleDetector")
174 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 158 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
175 super.onViewCreated(view, savedInstanceState) 159 super.onViewCreated(view, savedInstanceState)
176 if (requireActivity().isFinishing) { 160 if (requireActivity().isFinishing) {
@@ -351,129 +335,86 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
351 binding.loadingTitle.isSelected = true 335 binding.loadingTitle.isSelected = true
352 binding.loadingText.isSelected = true 336 binding.loadingText.isSelected = true
353 337
354 viewLifecycleOwner.lifecycleScope.apply { 338 WindowInfoTracker.getOrCreate(requireContext())
355 launch { 339 .windowLayoutInfo(requireActivity()).collect(viewLifecycleOwner) {
356 repeatOnLifecycle(Lifecycle.State.STARTED) { 340 updateFoldableLayout(requireActivity() as EmulationActivity, it)
357 WindowInfoTracker.getOrCreate(requireContext())
358 .windowLayoutInfo(requireActivity())
359 .collect {
360 updateFoldableLayout(requireActivity() as EmulationActivity, it)
361 }
362 }
363 } 341 }
364 launch { 342 emulationViewModel.shaderProgress.collect(viewLifecycleOwner) {
365 repeatOnLifecycle(Lifecycle.State.CREATED) { 343 if (it > 0 && it != emulationViewModel.totalShaders.value) {
366 emulationViewModel.shaderProgress.collectLatest { 344 binding.loadingProgressIndicator.isIndeterminate = false
367 if (it > 0 && it != emulationViewModel.totalShaders.value) {
368 binding.loadingProgressIndicator.isIndeterminate = false
369
370 if (it < binding.loadingProgressIndicator.max) {
371 binding.loadingProgressIndicator.progress = it
372 }
373 }
374 345
375 if (it == emulationViewModel.totalShaders.value) { 346 if (it < binding.loadingProgressIndicator.max) {
376 binding.loadingText.setText(R.string.loading) 347 binding.loadingProgressIndicator.progress = it
377 binding.loadingProgressIndicator.isIndeterminate = true
378 }
379 }
380 }
381 }
382 launch {
383 repeatOnLifecycle(Lifecycle.State.CREATED) {
384 emulationViewModel.totalShaders.collectLatest {
385 binding.loadingProgressIndicator.max = it
386 }
387 } 348 }
388 } 349 }
389 launch { 350
390 repeatOnLifecycle(Lifecycle.State.CREATED) { 351 if (it == emulationViewModel.totalShaders.value) {
391 emulationViewModel.shaderMessage.collectLatest { 352 binding.loadingText.setText(R.string.loading)
392 if (it.isNotEmpty()) { 353 binding.loadingProgressIndicator.isIndeterminate = true
393 binding.loadingText.text = it
394 }
395 }
396 }
397 } 354 }
398 launch { 355 }
399 repeatOnLifecycle(Lifecycle.State.RESUMED) { 356 emulationViewModel.totalShaders.collect(viewLifecycleOwner) {
400 driverViewModel.isInteractionAllowed.collect { 357 binding.loadingProgressIndicator.max = it
401 if (it) { 358 }
402 startEmulation() 359 emulationViewModel.shaderMessage.collect(viewLifecycleOwner) {
403 } 360 if (it.isNotEmpty()) {
404 } 361 binding.loadingText.text = it
405 }
406 } 362 }
407 launch { 363 }
408 repeatOnLifecycle(Lifecycle.State.CREATED) { 364
409 emulationViewModel.emulationStarted.collectLatest { 365 emulationViewModel.emulationStarted.collect(viewLifecycleOwner) {
410 if (it) { 366 if (it) {
411 binding.drawerLayout.setDrawerLockMode(IntSetting.LOCK_DRAWER.getInt()) 367 binding.drawerLayout.setDrawerLockMode(IntSetting.LOCK_DRAWER.getInt())
412 ViewUtils.showView(binding.surfaceInputOverlay) 368 ViewUtils.showView(binding.surfaceInputOverlay)
413 ViewUtils.hideView(binding.loadingIndicator) 369 ViewUtils.hideView(binding.loadingIndicator)
414 370
415 emulationState.updateSurface() 371 emulationState.updateSurface()
416 372
417 // Setup overlays 373 // Setup overlays
418 updateShowFpsOverlay() 374 updateShowFpsOverlay()
419 updateThermalOverlay() 375 updateThermalOverlay()
420 }
421 }
422 }
423 } 376 }
424 launch { 377 }
425 repeatOnLifecycle(Lifecycle.State.CREATED) { 378 emulationViewModel.isEmulationStopping.collect(viewLifecycleOwner) {
426 emulationViewModel.isEmulationStopping.collectLatest { 379 if (it) {
427 if (it) { 380 binding.loadingText.setText(R.string.shutting_down)
428 binding.loadingText.setText(R.string.shutting_down) 381 ViewUtils.showView(binding.loadingIndicator)
429 ViewUtils.showView(binding.loadingIndicator) 382 ViewUtils.hideView(binding.inputContainer)
430 ViewUtils.hideView(binding.inputContainer) 383 ViewUtils.hideView(binding.showFpsText)
431 ViewUtils.hideView(binding.showFpsText)
432 }
433 }
434 }
435 } 384 }
436 launch { 385 }
437 repeatOnLifecycle(Lifecycle.State.CREATED) { 386 emulationViewModel.drawerOpen.collect(viewLifecycleOwner) {
438 emulationViewModel.drawerOpen.collect { 387 if (it) {
439 if (it) { 388 binding.drawerLayout.open()
440 binding.drawerLayout.open() 389 binding.inGameMenu.requestFocus()
441 binding.inGameMenu.requestFocus() 390 } else {
442 } else { 391 binding.drawerLayout.close()
443 binding.drawerLayout.close()
444 }
445 }
446 }
447 } 392 }
448 launch { 393 }
449 repeatOnLifecycle(Lifecycle.State.CREATED) { 394 emulationViewModel.programChanged.collect(viewLifecycleOwner) {
450 emulationViewModel.programChanged.collect { 395 if (it != 0) {
451 if (it != 0) { 396 emulationViewModel.setEmulationStarted(false)
452 emulationViewModel.setEmulationStarted(false) 397 binding.drawerLayout.close()
453 binding.drawerLayout.close() 398 binding.drawerLayout
454 binding.drawerLayout 399 .setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
455 .setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) 400 ViewUtils.hideView(binding.surfaceInputOverlay)
456 ViewUtils.hideView(binding.surfaceInputOverlay) 401 ViewUtils.showView(binding.loadingIndicator)
457 ViewUtils.showView(binding.loadingIndicator)
458 }
459 }
460 }
461 } 402 }
462 launch { 403 }
463 repeatOnLifecycle(Lifecycle.State.CREATED) { 404 emulationViewModel.emulationStopped.collect(viewLifecycleOwner) {
464 emulationViewModel.emulationStopped.collect { 405 if (it && emulationViewModel.programChanged.value != -1) {
465 if (it && emulationViewModel.programChanged.value != -1) { 406 if (perfStatsUpdater != null) {
466 if (perfStatsUpdater != null) { 407 perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
467 perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
468 }
469 emulationState.changeProgram(emulationViewModel.programChanged.value)
470 emulationViewModel.setProgramChanged(-1)
471 emulationViewModel.setEmulationStopped(false)
472 }
473 }
474 } 408 }
409 emulationState.changeProgram(emulationViewModel.programChanged.value)
410 emulationViewModel.setProgramChanged(-1)
411 emulationViewModel.setEmulationStopped(false)
475 } 412 }
476 } 413 }
414
415 driverViewModel.isInteractionAllowed.collect(viewLifecycleOwner) {
416 if (it) startEmulation()
417 }
477 } 418 }
478 419
479 private fun startEmulation(programIndex: Int = 0) { 420 private fun startEmulation(programIndex: Int = 0) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt
index 5c558b1a5..3a6f7a38c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt
@@ -13,9 +13,6 @@ import androidx.core.view.WindowInsetsCompat
13import androidx.core.view.updatePadding 13import androidx.core.view.updatePadding
14import androidx.fragment.app.Fragment 14import androidx.fragment.app.Fragment
15import androidx.fragment.app.activityViewModels 15import androidx.fragment.app.activityViewModels
16import androidx.lifecycle.Lifecycle
17import androidx.lifecycle.lifecycleScope
18import androidx.lifecycle.repeatOnLifecycle
19import androidx.navigation.findNavController 16import androidx.navigation.findNavController
20import androidx.recyclerview.widget.GridLayoutManager 17import androidx.recyclerview.widget.GridLayoutManager
21import com.google.android.material.transition.MaterialSharedAxis 18import com.google.android.material.transition.MaterialSharedAxis
@@ -27,6 +24,7 @@ import org.yuzu.yuzu_emu.model.GamesViewModel
27import org.yuzu.yuzu_emu.model.HomeViewModel 24import org.yuzu.yuzu_emu.model.HomeViewModel
28import org.yuzu.yuzu_emu.ui.main.MainActivity 25import org.yuzu.yuzu_emu.ui.main.MainActivity
29import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins 26import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
27import org.yuzu.yuzu_emu.utils.collect
30 28
31class GameFoldersFragment : Fragment() { 29class GameFoldersFragment : Fragment() {
32 private var _binding: FragmentFoldersBinding? = null 30 private var _binding: FragmentFoldersBinding? = null
@@ -70,12 +68,8 @@ class GameFoldersFragment : Fragment() {
70 adapter = FolderAdapter(requireActivity(), gamesViewModel) 68 adapter = FolderAdapter(requireActivity(), gamesViewModel)
71 } 69 }
72 70
73 viewLifecycleOwner.lifecycleScope.launch { 71 gamesViewModel.folders.collect(viewLifecycleOwner) {
74 repeatOnLifecycle(Lifecycle.State.CREATED) { 72 (binding.listFolders.adapter as FolderAdapter).submitList(it)
75 gamesViewModel.folders.collect {
76 (binding.listFolders.adapter as FolderAdapter).submitList(it)
77 }
78 }
79 } 73 }
80 74
81 val mainActivity = requireActivity() as MainActivity 75 val mainActivity = requireActivity() as MainActivity
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
index c4da1a65d..c06842c59 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
@@ -3,7 +3,6 @@
3 3
4package org.yuzu.yuzu_emu.fragments 4package org.yuzu.yuzu_emu.fragments
5 5
6import android.annotation.SuppressLint
7import android.content.pm.ShortcutInfo 6import android.content.pm.ShortcutInfo
8import android.content.pm.ShortcutManager 7import android.content.pm.ShortcutManager
9import android.os.Bundle 8import android.os.Bundle
@@ -17,9 +16,7 @@ import androidx.core.view.WindowInsetsCompat
17import androidx.core.view.updatePadding 16import androidx.core.view.updatePadding
18import androidx.fragment.app.Fragment 17import androidx.fragment.app.Fragment
19import androidx.fragment.app.activityViewModels 18import androidx.fragment.app.activityViewModels
20import androidx.lifecycle.Lifecycle
21import androidx.lifecycle.lifecycleScope 19import androidx.lifecycle.lifecycleScope
22import androidx.lifecycle.repeatOnLifecycle
23import androidx.navigation.findNavController 20import androidx.navigation.findNavController
24import androidx.navigation.fragment.navArgs 21import androidx.navigation.fragment.navArgs
25import androidx.recyclerview.widget.GridLayoutManager 22import androidx.recyclerview.widget.GridLayoutManager
@@ -47,6 +44,7 @@ import org.yuzu.yuzu_emu.utils.GpuDriverHelper
47import org.yuzu.yuzu_emu.utils.MemoryUtil 44import org.yuzu.yuzu_emu.utils.MemoryUtil
48import org.yuzu.yuzu_emu.utils.ViewUtils.marquee 45import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
49import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins 46import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
47import org.yuzu.yuzu_emu.utils.collect
50import java.io.BufferedOutputStream 48import java.io.BufferedOutputStream
51import java.io.File 49import java.io.File
52 50
@@ -76,8 +74,6 @@ class GamePropertiesFragment : Fragment() {
76 return binding.root 74 return binding.root
77 } 75 }
78 76
79 // This is using the correct scope, lint is just acting up
80 @SuppressLint("UnsafeRepeatOnLifecycleDetector")
81 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 77 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
82 super.onViewCreated(view, savedInstanceState) 78 super.onViewCreated(view, savedInstanceState)
83 homeViewModel.setNavigationVisibility(visible = false, animated = true) 79 homeViewModel.setNavigationVisibility(visible = false, animated = true)
@@ -116,28 +112,14 @@ class GamePropertiesFragment : Fragment() {
116 112
117 reloadList() 113 reloadList()
118 114
119 viewLifecycleOwner.lifecycleScope.apply { 115 homeViewModel.openImportSaves.collect(
120 launch { 116 viewLifecycleOwner,
121 repeatOnLifecycle(Lifecycle.State.STARTED) { 117 resetState = { homeViewModel.setOpenImportSaves(false) }
122 homeViewModel.openImportSaves.collect { 118 ) { if (it) importSaves.launch(arrayOf("application/zip")) }
123 if (it) { 119 homeViewModel.reloadPropertiesList.collect(
124 importSaves.launch(arrayOf("application/zip")) 120 viewLifecycleOwner,
125 homeViewModel.setOpenImportSaves(false) 121 resetState = { homeViewModel.reloadPropertiesList(false) }
126 } 122 ) { if (it) reloadList() }
127 }
128 }
129 }
130 launch {
131 repeatOnLifecycle(Lifecycle.State.STARTED) {
132 homeViewModel.reloadPropertiesList.collect {
133 if (it) {
134 reloadList()
135 homeViewModel.reloadPropertiesList(false)
136 }
137 }
138 }
139 }
140 }
141 123
142 setInsets() 124 setInsets()
143 } 125 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
index 63112dc6f..d218da1c8 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
@@ -14,9 +14,6 @@ import androidx.core.view.WindowInsetsCompat
14import androidx.core.view.updatePadding 14import androidx.core.view.updatePadding
15import androidx.fragment.app.Fragment 15import androidx.fragment.app.Fragment
16import androidx.fragment.app.activityViewModels 16import androidx.fragment.app.activityViewModels
17import androidx.lifecycle.Lifecycle
18import androidx.lifecycle.lifecycleScope
19import androidx.lifecycle.repeatOnLifecycle
20import androidx.navigation.findNavController 17import androidx.navigation.findNavController
21import androidx.recyclerview.widget.GridLayoutManager 18import androidx.recyclerview.widget.GridLayoutManager
22import com.google.android.material.transition.MaterialSharedAxis 19import com.google.android.material.transition.MaterialSharedAxis
@@ -35,6 +32,7 @@ import org.yuzu.yuzu_emu.ui.main.MainActivity
35import org.yuzu.yuzu_emu.utils.DirectoryInitialization 32import org.yuzu.yuzu_emu.utils.DirectoryInitialization
36import org.yuzu.yuzu_emu.utils.FileUtil 33import org.yuzu.yuzu_emu.utils.FileUtil
37import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins 34import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
35import org.yuzu.yuzu_emu.utils.collect
38import java.io.BufferedOutputStream 36import java.io.BufferedOutputStream
39import java.io.File 37import java.io.File
40import java.math.BigInteger 38import java.math.BigInteger
@@ -75,14 +73,10 @@ class InstallableFragment : Fragment() {
75 binding.root.findNavController().popBackStack() 73 binding.root.findNavController().popBackStack()
76 } 74 }
77 75
78 viewLifecycleOwner.lifecycleScope.launch { 76 homeViewModel.openImportSaves.collect(viewLifecycleOwner) {
79 repeatOnLifecycle(Lifecycle.State.CREATED) { 77 if (it) {
80 homeViewModel.openImportSaves.collect { 78 importSaves.launch(arrayOf("application/zip"))
81 if (it) { 79 homeViewModel.setOpenImportSaves(false)
82 importSaves.launch(arrayOf("application/zip"))
83 homeViewModel.setOpenImportSaves(false)
84 }
85 }
86 } 80 }
87 } 81 }
88 82
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt
index ae29e9cd1..ee3bb0386 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt
@@ -13,16 +13,13 @@ import androidx.appcompat.app.AlertDialog
13import androidx.fragment.app.DialogFragment 13import androidx.fragment.app.DialogFragment
14import androidx.fragment.app.FragmentActivity 14import androidx.fragment.app.FragmentActivity
15import androidx.fragment.app.activityViewModels 15import androidx.fragment.app.activityViewModels
16import androidx.lifecycle.Lifecycle
17import androidx.lifecycle.ViewModelProvider 16import androidx.lifecycle.ViewModelProvider
18import androidx.lifecycle.lifecycleScope
19import androidx.lifecycle.repeatOnLifecycle
20import com.google.android.material.dialog.MaterialAlertDialogBuilder 17import com.google.android.material.dialog.MaterialAlertDialogBuilder
21import kotlinx.coroutines.launch
22import org.yuzu.yuzu_emu.R 18import org.yuzu.yuzu_emu.R
23import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding 19import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
24import org.yuzu.yuzu_emu.model.TaskViewModel 20import org.yuzu.yuzu_emu.model.TaskViewModel
25import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible 21import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
22import org.yuzu.yuzu_emu.utils.collect
26 23
27class ProgressDialogFragment : DialogFragment() { 24class ProgressDialogFragment : DialogFragment() {
28 private val taskViewModel: TaskViewModel by activityViewModels() 25 private val taskViewModel: TaskViewModel by activityViewModels()
@@ -65,70 +62,50 @@ class ProgressDialogFragment : DialogFragment() {
65 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 62 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
66 super.onViewCreated(view, savedInstanceState) 63 super.onViewCreated(view, savedInstanceState)
67 binding.message.isSelected = true 64 binding.message.isSelected = true
68 viewLifecycleOwner.lifecycleScope.apply { 65 taskViewModel.isComplete.collect(viewLifecycleOwner) {
69 launch { 66 if (it) {
70 repeatOnLifecycle(Lifecycle.State.CREATED) { 67 dismiss()
71 taskViewModel.isComplete.collect { 68 when (val result = taskViewModel.result.value) {
72 if (it) { 69 is String -> Toast.makeText(
73 dismiss() 70 requireContext(),
74 when (val result = taskViewModel.result.value) { 71 result,
75 is String -> Toast.makeText( 72 Toast.LENGTH_LONG
76 requireContext(), 73 ).show()
77 result, 74
78 Toast.LENGTH_LONG 75 is MessageDialogFragment -> result.show(
79 ).show() 76 requireActivity().supportFragmentManager,
80 77 MessageDialogFragment.TAG
81 is MessageDialogFragment -> result.show( 78 )
82 requireActivity().supportFragmentManager, 79
83 MessageDialogFragment.TAG 80 else -> {
84 ) 81 // Do nothing
85
86 else -> {
87 // Do nothing
88 }
89 }
90 taskViewModel.clear()
91 }
92 } 82 }
93 } 83 }
84 taskViewModel.clear()
94 } 85 }
95 launch { 86 }
96 repeatOnLifecycle(Lifecycle.State.CREATED) { 87 taskViewModel.cancelled.collect(viewLifecycleOwner) {
97 taskViewModel.cancelled.collect { 88 if (it) {
98 if (it) { 89 dialog?.setTitle(R.string.cancelling)
99 dialog?.setTitle(R.string.cancelling)
100 }
101 }
102 }
103 }
104 launch {
105 repeatOnLifecycle(Lifecycle.State.CREATED) {
106 taskViewModel.progress.collect {
107 if (it != 0.0) {
108 binding.progressBar.apply {
109 isIndeterminate = false
110 progress = (
111 (it / taskViewModel.maxProgress.value) *
112 PROGRESS_BAR_RESOLUTION
113 ).toInt()
114 min = 0
115 max = PROGRESS_BAR_RESOLUTION
116 }
117 }
118 }
119 }
120 } 90 }
121 launch { 91 }
122 repeatOnLifecycle(Lifecycle.State.CREATED) { 92 taskViewModel.progress.collect(viewLifecycleOwner) {
123 taskViewModel.message.collect { 93 if (it != 0.0) {
124 binding.message.setVisible(it.isNotEmpty()) 94 binding.progressBar.apply {
125 if (it.isNotEmpty()) { 95 isIndeterminate = false
126 binding.message.text = it 96 progress = (
127 } 97 (it / taskViewModel.maxProgress.value) *
128 } 98 PROGRESS_BAR_RESOLUTION
99 ).toInt()
100 min = 0
101 max = PROGRESS_BAR_RESOLUTION
129 } 102 }
130 } 103 }
131 } 104 }
105 taskViewModel.message.collect(viewLifecycleOwner) {
106 binding.message.setVisible(it.isNotEmpty())
107 binding.message.text = it
108 }
132 } 109 }
133 110
134 // By default, the ProgressDialog will immediately dismiss itself upon a button being pressed. 111 // By default, the ProgressDialog will immediately dismiss itself upon a button being pressed.
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 9f6509605..662ae9760 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,7 +3,6 @@
3 3
4package org.yuzu.yuzu_emu.fragments 4package org.yuzu.yuzu_emu.fragments
5 5
6import android.annotation.SuppressLint
7import android.content.Context 6import android.content.Context
8import android.content.SharedPreferences 7import android.content.SharedPreferences
9import android.os.Bundle 8import android.os.Bundle
@@ -18,14 +17,9 @@ import androidx.core.view.updatePadding
18import androidx.core.widget.doOnTextChanged 17import androidx.core.widget.doOnTextChanged
19import androidx.fragment.app.Fragment 18import androidx.fragment.app.Fragment
20import androidx.fragment.app.activityViewModels 19import androidx.fragment.app.activityViewModels
21import androidx.lifecycle.Lifecycle
22import androidx.lifecycle.lifecycleScope
23import androidx.lifecycle.repeatOnLifecycle
24import androidx.preference.PreferenceManager 20import androidx.preference.PreferenceManager
25import info.debatty.java.stringsimilarity.Jaccard 21import info.debatty.java.stringsimilarity.Jaccard
26import info.debatty.java.stringsimilarity.JaroWinkler 22import info.debatty.java.stringsimilarity.JaroWinkler
27import kotlinx.coroutines.flow.collectLatest
28import kotlinx.coroutines.launch
29import java.util.Locale 23import java.util.Locale
30import org.yuzu.yuzu_emu.R 24import org.yuzu.yuzu_emu.R
31import org.yuzu.yuzu_emu.YuzuApplication 25import org.yuzu.yuzu_emu.YuzuApplication
@@ -36,6 +30,7 @@ import org.yuzu.yuzu_emu.model.Game
36import org.yuzu.yuzu_emu.model.GamesViewModel 30import org.yuzu.yuzu_emu.model.GamesViewModel
37import org.yuzu.yuzu_emu.model.HomeViewModel 31import org.yuzu.yuzu_emu.model.HomeViewModel
38import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible 32import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
33import org.yuzu.yuzu_emu.utils.collect
39 34
40class SearchFragment : Fragment() { 35class SearchFragment : Fragment() {
41 private var _binding: FragmentSearchBinding? = null 36 private var _binding: FragmentSearchBinding? = null
@@ -59,8 +54,6 @@ class SearchFragment : Fragment() {
59 return binding.root 54 return binding.root
60 } 55 }
61 56
62 // This is using the correct scope, lint is just acting up
63 @SuppressLint("UnsafeRepeatOnLifecycleDetector")
64 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 57 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
65 super.onViewCreated(view, savedInstanceState) 58 super.onViewCreated(view, savedInstanceState)
66 homeViewModel.setNavigationVisibility(visible = true, animated = true) 59 homeViewModel.setNavigationVisibility(visible = true, animated = true)
@@ -86,30 +79,14 @@ class SearchFragment : Fragment() {
86 filterAndSearch() 79 filterAndSearch()
87 } 80 }
88 81
89 viewLifecycleOwner.lifecycleScope.apply { 82 gamesViewModel.searchFocused.collect(
90 launch { 83 viewLifecycleOwner,
91 repeatOnLifecycle(Lifecycle.State.CREATED) { 84 resetState = { gamesViewModel.setSearchFocused(false) }
92 gamesViewModel.searchFocused.collect { 85 ) { if (it) focusSearch() }
93 if (it) { 86 gamesViewModel.games.collect(viewLifecycleOwner) { filterAndSearch() }
94 focusSearch() 87 gamesViewModel.searchedGames.collect(viewLifecycleOwner) {
95 gamesViewModel.setSearchFocused(false) 88 (binding.gridGamesSearch.adapter as GameAdapter).submitList(it)
96 } 89 binding.noResultsView.setVisible(it.isNotEmpty())
97 }
98 }
99 }
100 launch {
101 repeatOnLifecycle(Lifecycle.State.CREATED) {
102 gamesViewModel.games.collectLatest { filterAndSearch() }
103 }
104 }
105 launch {
106 repeatOnLifecycle(Lifecycle.State.CREATED) {
107 gamesViewModel.searchedGames.collect {
108 (binding.gridGamesSearch.adapter as GameAdapter).submitList(it)
109 binding.noResultsView.setVisible(it.isEmpty())
110 }
111 }
112 }
113 } 90 }
114 91
115 binding.clearButton.setOnClickListener { binding.searchText.setText("") } 92 binding.clearButton.setOnClickListener { binding.searchText.setText("") }
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 eb279d309..4f7548e98 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
@@ -4,7 +4,6 @@
4package org.yuzu.yuzu_emu.fragments 4package org.yuzu.yuzu_emu.fragments
5 5
6import android.Manifest 6import android.Manifest
7import android.annotation.SuppressLint
8import android.content.Intent 7import android.content.Intent
9import android.os.Build 8import android.os.Build
10import android.os.Bundle 9import android.os.Bundle
@@ -23,9 +22,6 @@ import androidx.core.view.isVisible
23import androidx.core.view.updatePadding 22import androidx.core.view.updatePadding
24import androidx.fragment.app.Fragment 23import androidx.fragment.app.Fragment
25import androidx.fragment.app.activityViewModels 24import androidx.fragment.app.activityViewModels
26import androidx.lifecycle.Lifecycle
27import androidx.lifecycle.lifecycleScope
28import androidx.lifecycle.repeatOnLifecycle
29import androidx.navigation.findNavController 25import androidx.navigation.findNavController
30import androidx.preference.PreferenceManager 26import androidx.preference.PreferenceManager
31import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback 27import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
@@ -47,6 +43,7 @@ import org.yuzu.yuzu_emu.utils.DirectoryInitialization
47import org.yuzu.yuzu_emu.utils.NativeConfig 43import org.yuzu.yuzu_emu.utils.NativeConfig
48import org.yuzu.yuzu_emu.utils.ViewUtils 44import org.yuzu.yuzu_emu.utils.ViewUtils
49import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible 45import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
46import org.yuzu.yuzu_emu.utils.collect
50 47
51class SetupFragment : Fragment() { 48class SetupFragment : Fragment() {
52 private var _binding: FragmentSetupBinding? = null 49 private var _binding: FragmentSetupBinding? = null
@@ -78,8 +75,6 @@ class SetupFragment : Fragment() {
78 return binding.root 75 return binding.root
79 } 76 }
80 77
81 // This is using the correct scope, lint is just acting up
82 @SuppressLint("UnsafeRepeatOnLifecycleDetector")
83 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 78 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
84 mainActivity = requireActivity() as MainActivity 79 mainActivity = requireActivity() as MainActivity
85 80
@@ -211,28 +206,14 @@ class SetupFragment : Fragment() {
211 ) 206 )
212 } 207 }
213 208
214 viewLifecycleOwner.lifecycleScope.apply { 209 homeViewModel.shouldPageForward.collect(
215 launch { 210 viewLifecycleOwner,
216 repeatOnLifecycle(Lifecycle.State.CREATED) { 211 resetState = { homeViewModel.setShouldPageForward(false) }
217 homeViewModel.shouldPageForward.collect { 212 ) { if (it) pageForward() }
218 if (it) { 213 homeViewModel.gamesDirSelected.collect(
219 pageForward() 214 viewLifecycleOwner,
220 homeViewModel.setShouldPageForward(false) 215 resetState = { homeViewModel.setGamesDirSelected(false) }
221 } 216 ) { if (it) gamesDirCallback.onStepCompleted() }
222 }
223 }
224 }
225 launch {
226 repeatOnLifecycle(Lifecycle.State.CREATED) {
227 homeViewModel.gamesDirSelected.collect {
228 if (it) {
229 gamesDirCallback.onStepCompleted()
230 homeViewModel.setGamesDirSelected(false)
231 }
232 }
233 }
234 }
235 }
236 217
237 binding.viewPager2.apply { 218 binding.viewPager2.apply {
238 adapter = SetupAdapter(requireActivity() as AppCompatActivity, pages) 219 adapter = SetupAdapter(requireActivity() as AppCompatActivity, pages)
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 b3248585e..fadb20e39 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,7 +3,6 @@
3 3
4package org.yuzu.yuzu_emu.ui 4package org.yuzu.yuzu_emu.ui
5 5
6import android.annotation.SuppressLint
7import android.os.Bundle 6import android.os.Bundle
8import android.view.LayoutInflater 7import android.view.LayoutInflater
9import android.view.View 8import android.view.View
@@ -14,12 +13,7 @@ import androidx.core.view.WindowInsetsCompat
14import androidx.core.view.updatePadding 13import androidx.core.view.updatePadding
15import androidx.fragment.app.Fragment 14import androidx.fragment.app.Fragment
16import androidx.fragment.app.activityViewModels 15import androidx.fragment.app.activityViewModels
17import androidx.lifecycle.Lifecycle
18import androidx.lifecycle.lifecycleScope
19import androidx.lifecycle.repeatOnLifecycle
20import com.google.android.material.color.MaterialColors 16import com.google.android.material.color.MaterialColors
21import kotlinx.coroutines.flow.collectLatest
22import kotlinx.coroutines.launch
23import org.yuzu.yuzu_emu.R 17import org.yuzu.yuzu_emu.R
24import org.yuzu.yuzu_emu.adapters.GameAdapter 18import org.yuzu.yuzu_emu.adapters.GameAdapter
25import org.yuzu.yuzu_emu.databinding.FragmentGamesBinding 19import org.yuzu.yuzu_emu.databinding.FragmentGamesBinding
@@ -28,6 +22,7 @@ import org.yuzu.yuzu_emu.model.GamesViewModel
28import org.yuzu.yuzu_emu.model.HomeViewModel 22import org.yuzu.yuzu_emu.model.HomeViewModel
29import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible 23import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
30import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins 24import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
25import org.yuzu.yuzu_emu.utils.collect
31 26
32class GamesFragment : Fragment() { 27class GamesFragment : Fragment() {
33 private var _binding: FragmentGamesBinding? = null 28 private var _binding: FragmentGamesBinding? = null
@@ -45,8 +40,6 @@ class GamesFragment : Fragment() {
45 return binding.root 40 return binding.root
46 } 41 }
47 42
48 // This is using the correct scope, lint is just acting up
49 @SuppressLint("UnsafeRepeatOnLifecycleDetector")
50 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 43 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
51 super.onViewCreated(view, savedInstanceState) 44 super.onViewCreated(view, savedInstanceState)
52 homeViewModel.setNavigationVisibility(visible = true, animated = true) 45 homeViewModel.setNavigationVisibility(visible = true, animated = true)
@@ -89,48 +82,28 @@ class GamesFragment : Fragment() {
89 } 82 }
90 } 83 }
91 84
92 viewLifecycleOwner.lifecycleScope.apply { 85 gamesViewModel.isReloading.collect(viewLifecycleOwner) {
93 launch { 86 binding.swipeRefresh.isRefreshing = it
94 repeatOnLifecycle(Lifecycle.State.RESUMED) { 87 binding.noticeText.setVisible(
95 gamesViewModel.isReloading.collect { 88 visible = gamesViewModel.games.value.isEmpty() && !it,
96 binding.swipeRefresh.isRefreshing = it 89 gone = false
97 binding.noticeText.setVisible( 90 )
98 visible = gamesViewModel.games.value.isEmpty() && !it, 91 }
99 gone = false 92 gamesViewModel.games.collect(viewLifecycleOwner) {
100 ) 93 (binding.gridGames.adapter as GameAdapter).submitList(it)
101 } 94 }
102 } 95 gamesViewModel.shouldSwapData.collect(
103 } 96 viewLifecycleOwner,
104 launch { 97 resetState = { gamesViewModel.setShouldSwapData(false) }
105 repeatOnLifecycle(Lifecycle.State.RESUMED) { 98 ) {
106 gamesViewModel.games.collectLatest { 99 if (it) {
107 (binding.gridGames.adapter as GameAdapter).submitList(it) 100 (binding.gridGames.adapter as GameAdapter).submitList(gamesViewModel.games.value)
108 }
109 }
110 }
111 launch {
112 repeatOnLifecycle(Lifecycle.State.RESUMED) {
113 gamesViewModel.shouldSwapData.collect {
114 if (it) {
115 (binding.gridGames.adapter as GameAdapter).submitList(
116 gamesViewModel.games.value
117 )
118 gamesViewModel.setShouldSwapData(false)
119 }
120 }
121 }
122 }
123 launch {
124 repeatOnLifecycle(Lifecycle.State.RESUMED) {
125 gamesViewModel.shouldScrollToTop.collect {
126 if (it) {
127 scrollToTop()
128 gamesViewModel.setShouldScrollToTop(false)
129 }
130 }
131 }
132 } 101 }
133 } 102 }
103 gamesViewModel.shouldScrollToTop.collect(
104 viewLifecycleOwner,
105 resetState = { gamesViewModel.setShouldScrollToTop(false) }
106 ) { if (it) scrollToTop() }
134 107
135 setInsets() 108 setInsets()
136 } 109 }
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 703fbaf3e..d16f8a931 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,9 +19,6 @@ 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
23import androidx.lifecycle.lifecycleScope
24import androidx.lifecycle.repeatOnLifecycle
25import androidx.navigation.NavController 22import androidx.navigation.NavController
26import androidx.navigation.fragment.NavHostFragment 23import androidx.navigation.fragment.NavHostFragment
27import androidx.navigation.ui.setupWithNavController 24import androidx.navigation.ui.setupWithNavController
@@ -30,7 +27,6 @@ import com.google.android.material.color.MaterialColors
30import com.google.android.material.navigation.NavigationBarView 27import com.google.android.material.navigation.NavigationBarView
31import java.io.File 28import java.io.File
32import java.io.FilenameFilter 29import java.io.FilenameFilter
33import kotlinx.coroutines.launch
34import org.yuzu.yuzu_emu.HomeNavigationDirections 30import org.yuzu.yuzu_emu.HomeNavigationDirections
35import org.yuzu.yuzu_emu.NativeLibrary 31import org.yuzu.yuzu_emu.NativeLibrary
36import org.yuzu.yuzu_emu.R 32import org.yuzu.yuzu_emu.R
@@ -144,38 +140,19 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
144 binding.statusBarShade.setVisible(visible = false, gone = false) 140 binding.statusBarShade.setVisible(visible = false, gone = false)
145 } 141 }
146 142
147 lifecycleScope.apply { 143 homeViewModel.navigationVisible.collect(this) { showNavigation(it.first, it.second) }
148 launch { 144 homeViewModel.statusBarShadeVisible.collect(this) { showStatusBarShade(it) }
149 repeatOnLifecycle(Lifecycle.State.CREATED) { 145 homeViewModel.contentToInstall.collect(
150 homeViewModel.navigationVisible.collect { showNavigation(it.first, it.second) } 146 this,
151 } 147 resetState = { homeViewModel.setContentToInstall(null) }
152 } 148 ) {
153 launch { 149 if (it != null) {
154 repeatOnLifecycle(Lifecycle.State.CREATED) { 150 installContent(it)
155 homeViewModel.statusBarShadeVisible.collect { showStatusBarShade(it) }
156 }
157 }
158 launch {
159 repeatOnLifecycle(Lifecycle.State.CREATED) {
160 homeViewModel.contentToInstall.collect {
161 if (it != null) {
162 installContent(it)
163 homeViewModel.setContentToInstall(null)
164 }
165 }
166 }
167 }
168 launch {
169 repeatOnLifecycle(Lifecycle.State.CREATED) {
170 homeViewModel.checkKeys.collect {
171 if (it) {
172 checkKeys()
173 homeViewModel.setCheckKeys(false)
174 }
175 }
176 }
177 } 151 }
178 } 152 }
153 homeViewModel.checkKeys.collect(this, resetState = { homeViewModel.setCheckKeys(false) }) {
154 if (it) checkKeys()
155 }
179 156
180 setInsets() 157 setInsets()
181 } 158 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/LifecycleUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/LifecycleUtils.kt
new file mode 100644
index 000000000..d5c19c681
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/LifecycleUtils.kt
@@ -0,0 +1,38 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.utils
5
6import androidx.lifecycle.Lifecycle
7import androidx.lifecycle.LifecycleOwner
8import androidx.lifecycle.lifecycleScope
9import androidx.lifecycle.repeatOnLifecycle
10import kotlinx.coroutines.flow.Flow
11import kotlinx.coroutines.flow.MutableStateFlow
12import kotlinx.coroutines.launch
13
14/**
15 * Collects this [Flow] with a given [LifecycleOwner].
16 * @param scope [LifecycleOwner] that this [Flow] will be collected with.
17 * @param repeatState When to repeat collection on this [Flow].
18 * @param resetState Optional lambda to reset state of an underlying [MutableStateFlow] after
19 * [stateCollector] has been run.
20 * @param stateCollector Lambda that receives new state.
21 */
22inline fun <reified T> Flow<T>.collect(
23 scope: LifecycleOwner,
24 repeatState: Lifecycle.State = Lifecycle.State.CREATED,
25 crossinline resetState: () -> Unit = {},
26 crossinline stateCollector: (state: T) -> Unit
27) {
28 scope.apply {
29 lifecycleScope.launch {
30 repeatOnLifecycle(repeatState) {
31 this@collect.collect {
32 stateCollector(it)
33 resetState()
34 }
35 }
36 }
37 }
38}