aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/android
diff options
context:
space:
mode:
authort895 <[email protected]>2024-02-17 21:23:38 -0500
committert895 <[email protected]>2024-02-17 23:09:09 -0500
commit35a3c7226a796ae7205753c13924fe0becd8fe60 (patch)
tree9b705b44d686b2ca2feac0057ac92404cab4b3e6 /src/android
parent5d3c7433b828dbb136ea8f6a9add625eec10af71 (diff)
downloadyuzu-android-35a3c7226a796ae7205753c13924fe0becd8fe60.tar.gz
yuzu-android-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
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.content.res.ResourcesCompat
-import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
-import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.databinding.CardInstallableIconBinding
import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding
import org.yuzu.yuzu_emu.model.GameProperty
@@ -18,6 +14,7 @@ import org.yuzu.yuzu_emu.model.InstallableProperty
import org.yuzu.yuzu_emu.model.SubmenuProperty
import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
+import org.yuzu.yuzu_emu.utils.collect
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class GamePropertiesAdapter(
@@ -82,11 +79,7 @@ class GamePropertiesAdapter(
binding.details.text = submenuProperty.details.invoke()
} else if (submenuProperty.detailsFlow != null) {
binding.details.setVisible(true)
- viewLifecycle.lifecycleScope.launch {
- viewLifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
- submenuProperty.detailsFlow.collect { binding.details.text = it }
- }
- }
+ submenuProperty.detailsFlow.collect(viewLifecycle) { binding.details.text = it }
} else {
binding.details.setVisible(false)
}
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
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
-import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
-import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
import org.yuzu.yuzu_emu.model.HomeSetting
import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
+import org.yuzu.yuzu_emu.utils.collect
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class HomeSettingAdapter(
@@ -59,11 +56,7 @@ class HomeSettingAdapter(
binding.optionIcon.alpha = 0.5f
}
- viewLifecycle.lifecycleScope.launch {
- viewLifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) {
- model.details.collect { updateOptionDetails(it) }
- }
- }
+ model.details.collect(viewLifecycle) { updateOptionDetails(it) }
binding.optionDetail.marquee()
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
import android.widget.Toast
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.DialogInputProfilesBinding
import org.yuzu.yuzu_emu.features.settings.model.view.InputProfileSetting
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
+import org.yuzu.yuzu_emu.utils.collect
class InputProfileDialogFragment : DialogFragment() {
private var position = 0
@@ -110,25 +107,21 @@ class InputProfileDialogFragment : DialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- viewLifecycleOwner.lifecycleScope.launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- settingsViewModel.shouldShowDeleteProfileDialog.collect {
- if (it.isNotEmpty()) {
- MessageDialogFragment.newInstance(
- activity = requireActivity(),
- titleId = R.string.delete_input_profile,
- descriptionId = R.string.delete_input_profile_description,
- positiveAction = {
- setting.deleteProfile(it)
- settingsViewModel.setReloadListAndNotifyDataset(true)
- },
- negativeAction = {},
- negativeButtonTitleId = android.R.string.cancel
- ).show(parentFragmentManager, MessageDialogFragment.TAG)
- settingsViewModel.setShouldShowDeleteProfileDialog("")
- dismiss()
- }
- }
+ settingsViewModel.shouldShowDeleteProfileDialog.collect(viewLifecycleOwner) {
+ if (it.isNotEmpty()) {
+ MessageDialogFragment.newInstance(
+ activity = requireActivity(),
+ titleId = R.string.delete_input_profile,
+ descriptionId = R.string.delete_input_profile_description,
+ positiveAction = {
+ setting.deleteProfile(it)
+ settingsViewModel.setReloadListAndNotifyDataset(true)
+ },
+ negativeAction = {},
+ negativeButtonTitleId = android.R.string.cancel
+ ).show(parentFragmentManager, MessageDialogFragment.TAG)
+ settingsViewModel.setShouldShowDeleteProfileDialog("")
+ dismiss()
}
}
}
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
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.navArgs
import com.google.android.material.color.MaterialColors
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.NativeLibrary
import java.io.IOException
import org.yuzu.yuzu_emu.R
@@ -70,39 +65,23 @@ class SettingsActivity : AppCompatActivity() {
)
}
- lifecycleScope.apply {
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- settingsViewModel.shouldRecreate.collectLatest {
- if (it) {
- settingsViewModel.setShouldRecreate(false)
- recreate()
- }
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- settingsViewModel.shouldNavigateBack.collectLatest {
- if (it) {
- settingsViewModel.setShouldNavigateBack(false)
- navigateBack()
- }
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- settingsViewModel.shouldShowResetSettingsDialog.collectLatest {
- if (it) {
- settingsViewModel.setShouldShowResetSettingsDialog(false)
- ResetSettingsDialogFragment().show(
- supportFragmentManager,
- ResetSettingsDialogFragment.TAG
- )
- }
- }
- }
+ settingsViewModel.shouldRecreate.collect(
+ this,
+ resetState = { settingsViewModel.setShouldRecreate(false) }
+ ) { if (it) recreate() }
+ settingsViewModel.shouldNavigateBack.collect(
+ this,
+ resetState = { settingsViewModel.setShouldNavigateBack(false) }
+ ) { if (it) navigateBack() }
+ settingsViewModel.shouldShowResetSettingsDialog.collect(
+ this,
+ resetState = { settingsViewModel.setShouldShowResetSettingsDialog(false) }
+ ) {
+ if (it) {
+ ResetSettingsDialogFragment().show(
+ supportFragmentManager,
+ ResetSettingsDialogFragment.TAG
+ )
}
}
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
import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.slider.Slider
-import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.DialogSliderBinding
import org.yuzu.yuzu_emu.features.input.NativeInput
@@ -29,6 +25,7 @@ import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting
import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting
import org.yuzu.yuzu_emu.utils.ParamPackage
+import org.yuzu.yuzu_emu.utils.collect
class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener {
private var type = 0
@@ -169,17 +166,11 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
super.onViewCreated(view, savedInstanceState)
when (type) {
SettingsItem.TYPE_SLIDER -> {
- viewLifecycleOwner.lifecycleScope.launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- settingsViewModel.sliderTextValue.collect {
- sliderBinding.textValue.text = it
- }
- }
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- settingsViewModel.sliderProgress.collect {
- sliderBinding.slider.value = it.toFloat()
- }
- }
+ settingsViewModel.sliderTextValue.collect(viewLifecycleOwner) {
+ sliderBinding.textValue.text = it
+ }
+ settingsViewModel.sliderProgress.collect(viewLifecycleOwner) {
+ sliderBinding.slider.value = it.toFloat()
}
}
}
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
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.transition.MaterialSharedAxis
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding
import org.yuzu.yuzu_emu.features.input.NativeInput
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
+import org.yuzu.yuzu_emu.utils.collect
class SettingsFragment : Fragment() {
private lateinit var presenter: SettingsFragmentPresenter
@@ -63,8 +59,7 @@ class SettingsFragment : Fragment() {
return binding.root
}
- // This is using the correct scope, lint is just acting up
- @SuppressLint("UnsafeRepeatOnLifecycleDetector", "NotifyDataSetChanged")
+ @SuppressLint("NotifyDataSetChanged")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
settingsAdapter = SettingsAdapter(this, requireContext())
@@ -100,65 +95,37 @@ class SettingsFragment : Fragment() {
settingsViewModel.setShouldNavigateBack(true)
}
- viewLifecycleOwner.lifecycleScope.apply {
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- settingsViewModel.shouldReloadSettingsList.collectLatest {
- if (it) {
- settingsViewModel.setShouldReloadSettingsList(false)
- presenter.loadSettingsList()
- }
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- settingsViewModel.adapterItemChanged.collect {
- if (it != -1) {
- settingsAdapter?.notifyItemChanged(it)
- settingsViewModel.setAdapterItemChanged(-1)
- }
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- settingsViewModel.datasetChanged.collect {
- if (it) {
- settingsAdapter?.notifyDataSetChanged()
- settingsViewModel.setDatasetChanged(false)
- }
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- settingsViewModel.reloadListAndNotifyDataset.collectLatest {
- if (it) {
- settingsViewModel.setReloadListAndNotifyDataset(false)
- presenter.loadSettingsList(true)
- }
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- settingsViewModel.shouldShowResetInputDialog.collectLatest {
- if (it) {
- MessageDialogFragment.newInstance(
- activity = requireActivity(),
- titleId = R.string.reset_mapping,
- descriptionId = R.string.reset_mapping_description,
- positiveAction = {
- NativeInput.resetControllerMappings(getPlayerIndex())
- settingsViewModel.setReloadListAndNotifyDataset(true)
- },
- negativeAction = {}
- ).show(parentFragmentManager, MessageDialogFragment.TAG)
- settingsViewModel.setShouldShowResetInputDialog(false)
- }
- }
- }
+ settingsViewModel.shouldReloadSettingsList.collect(
+ viewLifecycleOwner,
+ resetState = { settingsViewModel.setShouldReloadSettingsList(false) }
+ ) { if (it) presenter.loadSettingsList() }
+ settingsViewModel.adapterItemChanged.collect(
+ viewLifecycleOwner,
+ resetState = { settingsViewModel.setAdapterItemChanged(-1) }
+ ) { if (it != -1) settingsAdapter?.notifyItemChanged(it) }
+ settingsViewModel.datasetChanged.collect(
+ viewLifecycleOwner,
+ resetState = { settingsViewModel.setDatasetChanged(false) }
+ ) { if (it) settingsAdapter?.notifyDataSetChanged() }
+ settingsViewModel.reloadListAndNotifyDataset.collect(
+ viewLifecycleOwner,
+ resetState = { settingsViewModel.setReloadListAndNotifyDataset(false) }
+ ) { if (it) presenter.loadSettingsList(true) }
+ settingsViewModel.shouldShowResetInputDialog.collect(
+ viewLifecycleOwner,
+ resetState = { settingsViewModel.setShouldShowResetInputDialog(false) }
+ ) {
+ if (it) {
+ MessageDialogFragment.newInstance(
+ activity = requireActivity(),
+ titleId = R.string.reset_mapping,
+ descriptionId = R.string.reset_mapping_description,
+ positiveAction = {
+ NativeInput.resetControllerMappings(getPlayerIndex())
+ settingsViewModel.setReloadListAndNotifyDataset(true)
+ },
+ negativeAction = {}
+ ).show(parentFragmentManager, MessageDialogFragment.TAG)
}
}
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
import androidx.core.widget.doOnTextChanged
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.divider.MaterialDividerItemDecoration
import com.google.android.material.transition.MaterialSharedAxis
import info.debatty.java.stringsimilarity.Cosine
-import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.FragmentSettingsSearchBinding
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
+import org.yuzu.yuzu_emu.utils.collect
class SettingsSearchFragment : Fragment() {
private var _binding: FragmentSettingsSearchBinding? = null
@@ -84,14 +81,10 @@ class SettingsSearchFragment : Fragment() {
search()
binding.settingsList.smoothScrollToPosition(0)
}
- viewLifecycleOwner.lifecycleScope.launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- settingsViewModel.shouldReloadSettingsList.collect {
- if (it) {
- settingsViewModel.setShouldReloadSettingsList(false)
- search()
- }
- }
+ settingsViewModel.shouldReloadSettingsList.collect(viewLifecycleOwner) {
+ if (it) {
+ settingsViewModel.setShouldReloadSettingsList(false)
+ search()
}
}
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 @@
package org.yuzu.yuzu_emu.fragments
-import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
@@ -16,9 +15,6 @@ import androidx.core.view.updatePadding
import androidx.documentfile.provider.DocumentFile
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearLayoutManager
@@ -32,6 +28,7 @@ import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.AddonUtil
import org.yuzu.yuzu_emu.utils.FileUtil.copyFilesTo
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
+import org.yuzu.yuzu_emu.utils.collect
import java.io.File
class AddonsFragment : Fragment() {
@@ -60,8 +57,6 @@ class AddonsFragment : Fragment() {
return binding.root
}
- // This is using the correct scope, lint is just acting up
- @SuppressLint("UnsafeRepeatOnLifecycleDetector")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setNavigationVisibility(visible = false, animated = false)
@@ -78,57 +73,41 @@ class AddonsFragment : Fragment() {
adapter = AddonAdapter(addonViewModel)
}
- viewLifecycleOwner.lifecycleScope.apply {
- launch {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- addonViewModel.addonList.collect {
- (binding.listAddons.adapter as AddonAdapter).submitList(it)
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- addonViewModel.showModInstallPicker.collect {
- if (it) {
- installAddon.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
- addonViewModel.showModInstallPicker(false)
- }
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- addonViewModel.showModNoticeDialog.collect {
- if (it) {
- MessageDialogFragment.newInstance(
- requireActivity(),
- titleId = R.string.addon_notice,
- descriptionId = R.string.addon_notice_description,
- dismissible = false,
- positiveAction = { addonViewModel.showModInstallPicker(true) },
- negativeAction = {},
- negativeButtonTitleId = R.string.close
- ).show(parentFragmentManager, MessageDialogFragment.TAG)
- addonViewModel.showModNoticeDialog(false)
- }
- }
- }
+ addonViewModel.addonList.collect(viewLifecycleOwner) {
+ (binding.listAddons.adapter as AddonAdapter).submitList(it)
+ }
+ addonViewModel.showModInstallPicker.collect(
+ viewLifecycleOwner,
+ resetState = { addonViewModel.showModInstallPicker(false) }
+ ) { if (it) installAddon.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) }
+ addonViewModel.showModNoticeDialog.collect(
+ viewLifecycleOwner,
+ resetState = { addonViewModel.showModNoticeDialog(false) }
+ ) {
+ if (it) {
+ MessageDialogFragment.newInstance(
+ requireActivity(),
+ titleId = R.string.addon_notice,
+ descriptionId = R.string.addon_notice_description,
+ dismissible = false,
+ positiveAction = { addonViewModel.showModInstallPicker(true) },
+ negativeAction = {},
+ negativeButtonTitleId = R.string.close
+ ).show(parentFragmentManager, MessageDialogFragment.TAG)
}
- launch {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- addonViewModel.addonToDelete.collect {
- if (it != null) {
- MessageDialogFragment.newInstance(
- requireActivity(),
- titleId = R.string.confirm_uninstall,
- descriptionId = R.string.confirm_uninstall_description,
- positiveAction = { addonViewModel.onDeleteAddon(it) },
- negativeAction = {}
- ).show(parentFragmentManager, MessageDialogFragment.TAG)
- addonViewModel.setAddonToDelete(null)
- }
- }
- }
+ }
+ addonViewModel.addonToDelete.collect(
+ viewLifecycleOwner,
+ resetState = { addonViewModel.setAddonToDelete(null) }
+ ) {
+ if (it != null) {
+ MessageDialogFragment.newInstance(
+ requireActivity(),
+ titleId = R.string.confirm_uninstall,
+ descriptionId = R.string.confirm_uninstall_description,
+ positiveAction = { addonViewModel.onDeleteAddon(it) },
+ negativeAction = {}
+ ).show(parentFragmentManager, MessageDialogFragment.TAG)
}
}
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 @@
package org.yuzu.yuzu_emu.fragments
-import android.annotation.SuppressLint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -14,9 +13,6 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.GridLayoutManager
@@ -35,6 +31,7 @@ import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
+import org.yuzu.yuzu_emu.utils.collect
import java.io.File
import java.io.IOException
@@ -63,8 +60,6 @@ class DriverManagerFragment : Fragment() {
return binding.root
}
- // This is using the correct scope, lint is just acting up
- @SuppressLint("UnsafeRepeatOnLifecycleDetector")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setNavigationVisibility(visible = false, animated = true)
@@ -89,15 +84,8 @@ class DriverManagerFragment : Fragment() {
}
}
- viewLifecycleOwner.lifecycleScope.apply {
- launch {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- driverViewModel.showClearButton.collect {
- binding.toolbarDrivers.menu
- .findItem(R.id.menu_driver_use_global).isVisible = it
- }
- }
- }
+ driverViewModel.showClearButton.collect(viewLifecycleOwner) {
+ binding.toolbarDrivers.menu.findItem(R.id.menu_driver_use_global).isVisible = it
}
}
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
import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
import org.yuzu.yuzu_emu.model.DriverViewModel
+import org.yuzu.yuzu_emu.utils.collect
class DriversLoadingDialogFragment : DialogFragment() {
private val driverViewModel: DriverViewModel by activityViewModels()
@@ -44,13 +41,7 @@ class DriversLoadingDialogFragment : DialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- viewLifecycleOwner.lifecycleScope.apply {
- launch {
- repeatOnLifecycle(Lifecycle.State.RESUMED) {
- driverViewModel.isInteractionAllowed.collect { if (it) dismiss() }
- }
- }
- }
+ driverViewModel.isInteractionAllowed.collect(viewLifecycleOwner) { if (it) dismiss() }
}
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
import androidx.drawerlayout.widget.DrawerLayout.DrawerListener
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController
import androidx.navigation.fragment.navArgs
import androidx.window.layout.FoldingFeature
@@ -42,9 +39,6 @@ import androidx.window.layout.WindowInfoTracker
import androidx.window.layout.WindowLayoutInfo
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.slider.Slider
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
@@ -91,14 +85,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
if (context is EmulationActivity) {
emulationActivity = context
NativeLibrary.setEmulationActivity(context)
-
- lifecycleScope.launch(Dispatchers.Main) {
- lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
- WindowInfoTracker.getOrCreate(context)
- .windowLayoutInfo(context)
- .collect { updateFoldableLayout(context, it) }
- }
- }
} else {
throw IllegalStateException("EmulationFragment must have EmulationActivity parent")
}
@@ -169,8 +155,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
return binding.root
}
- // This is using the correct scope, lint is just acting up
- @SuppressLint("UnsafeRepeatOnLifecycleDetector")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (requireActivity().isFinishing) {
@@ -351,129 +335,86 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
binding.loadingTitle.isSelected = true
binding.loadingText.isSelected = true
- viewLifecycleOwner.lifecycleScope.apply {
- launch {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- WindowInfoTracker.getOrCreate(requireContext())
- .windowLayoutInfo(requireActivity())
- .collect {
- updateFoldableLayout(requireActivity() as EmulationActivity, it)
- }
- }
+ WindowInfoTracker.getOrCreate(requireContext())
+ .windowLayoutInfo(requireActivity()).collect(viewLifecycleOwner) {
+ updateFoldableLayout(requireActivity() as EmulationActivity, it)
}
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- emulationViewModel.shaderProgress.collectLatest {
- if (it > 0 && it != emulationViewModel.totalShaders.value) {
- binding.loadingProgressIndicator.isIndeterminate = false
-
- if (it < binding.loadingProgressIndicator.max) {
- binding.loadingProgressIndicator.progress = it
- }
- }
+ emulationViewModel.shaderProgress.collect(viewLifecycleOwner) {
+ if (it > 0 && it != emulationViewModel.totalShaders.value) {
+ binding.loadingProgressIndicator.isIndeterminate = false
- if (it == emulationViewModel.totalShaders.value) {
- binding.loadingText.setText(R.string.loading)
- binding.loadingProgressIndicator.isIndeterminate = true
- }
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- emulationViewModel.totalShaders.collectLatest {
- binding.loadingProgressIndicator.max = it
- }
+ if (it < binding.loadingProgressIndicator.max) {
+ binding.loadingProgressIndicator.progress = it
}
}
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- emulationViewModel.shaderMessage.collectLatest {
- if (it.isNotEmpty()) {
- binding.loadingText.text = it
- }
- }
- }
+
+ if (it == emulationViewModel.totalShaders.value) {
+ binding.loadingText.setText(R.string.loading)
+ binding.loadingProgressIndicator.isIndeterminate = true
}
- launch {
- repeatOnLifecycle(Lifecycle.State.RESUMED) {
- driverViewModel.isInteractionAllowed.collect {
- if (it) {
- startEmulation()
- }
- }
- }
+ }
+ emulationViewModel.totalShaders.collect(viewLifecycleOwner) {
+ binding.loadingProgressIndicator.max = it
+ }
+ emulationViewModel.shaderMessage.collect(viewLifecycleOwner) {
+ if (it.isNotEmpty()) {
+ binding.loadingText.text = it
}
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- emulationViewModel.emulationStarted.collectLatest {
- if (it) {
- binding.drawerLayout.setDrawerLockMode(IntSetting.LOCK_DRAWER.getInt())
- ViewUtils.showView(binding.surfaceInputOverlay)
- ViewUtils.hideView(binding.loadingIndicator)
-
- emulationState.updateSurface()
-
- // Setup overlays
- updateShowFpsOverlay()
- updateThermalOverlay()
- }
- }
- }
+ }
+
+ emulationViewModel.emulationStarted.collect(viewLifecycleOwner) {
+ if (it) {
+ binding.drawerLayout.setDrawerLockMode(IntSetting.LOCK_DRAWER.getInt())
+ ViewUtils.showView(binding.surfaceInputOverlay)
+ ViewUtils.hideView(binding.loadingIndicator)
+
+ emulationState.updateSurface()
+
+ // Setup overlays
+ updateShowFpsOverlay()
+ updateThermalOverlay()
}
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- emulationViewModel.isEmulationStopping.collectLatest {
- if (it) {
- binding.loadingText.setText(R.string.shutting_down)
- ViewUtils.showView(binding.loadingIndicator)
- ViewUtils.hideView(binding.inputContainer)
- ViewUtils.hideView(binding.showFpsText)
- }
- }
- }
+ }
+ emulationViewModel.isEmulationStopping.collect(viewLifecycleOwner) {
+ if (it) {
+ binding.loadingText.setText(R.string.shutting_down)
+ ViewUtils.showView(binding.loadingIndicator)
+ ViewUtils.hideView(binding.inputContainer)
+ ViewUtils.hideView(binding.showFpsText)
}
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- emulationViewModel.drawerOpen.collect {
- if (it) {
- binding.drawerLayout.open()
- binding.inGameMenu.requestFocus()
- } else {
- binding.drawerLayout.close()
- }
- }
- }
+ }
+ emulationViewModel.drawerOpen.collect(viewLifecycleOwner) {
+ if (it) {
+ binding.drawerLayout.open()
+ binding.inGameMenu.requestFocus()
+ } else {
+ binding.drawerLayout.close()
}
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- emulationViewModel.programChanged.collect {
- if (it != 0) {
- emulationViewModel.setEmulationStarted(false)
- binding.drawerLayout.close()
- binding.drawerLayout
- .setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
- ViewUtils.hideView(binding.surfaceInputOverlay)
- ViewUtils.showView(binding.loadingIndicator)
- }
- }
- }
+ }
+ emulationViewModel.programChanged.collect(viewLifecycleOwner) {
+ if (it != 0) {
+ emulationViewModel.setEmulationStarted(false)
+ binding.drawerLayout.close()
+ binding.drawerLayout
+ .setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
+ ViewUtils.hideView(binding.surfaceInputOverlay)
+ ViewUtils.showView(binding.loadingIndicator)
}
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- emulationViewModel.emulationStopped.collect {
- if (it && emulationViewModel.programChanged.value != -1) {
- if (perfStatsUpdater != null) {
- perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
- }
- emulationState.changeProgram(emulationViewModel.programChanged.value)
- emulationViewModel.setProgramChanged(-1)
- emulationViewModel.setEmulationStopped(false)
- }
- }
+ }
+ emulationViewModel.emulationStopped.collect(viewLifecycleOwner) {
+ if (it && emulationViewModel.programChanged.value != -1) {
+ if (perfStatsUpdater != null) {
+ perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
}
+ emulationState.changeProgram(emulationViewModel.programChanged.value)
+ emulationViewModel.setProgramChanged(-1)
+ emulationViewModel.setEmulationStopped(false)
}
}
+
+ driverViewModel.isInteractionAllowed.collect(viewLifecycleOwner) {
+ if (it) startEmulation()
+ }
}
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
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.transition.MaterialSharedAxis
@@ -27,6 +24,7 @@ import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
+import org.yuzu.yuzu_emu.utils.collect
class GameFoldersFragment : Fragment() {
private var _binding: FragmentFoldersBinding? = null
@@ -70,12 +68,8 @@ class GameFoldersFragment : Fragment() {
adapter = FolderAdapter(requireActivity(), gamesViewModel)
}
- viewLifecycleOwner.lifecycleScope.launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- gamesViewModel.folders.collect {
- (binding.listFolders.adapter as FolderAdapter).submitList(it)
- }
- }
+ gamesViewModel.folders.collect(viewLifecycleOwner) {
+ (binding.listFolders.adapter as FolderAdapter).submitList(it)
}
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 @@
package org.yuzu.yuzu_emu.fragments
-import android.annotation.SuppressLint
import android.content.pm.ShortcutInfo
import android.content.pm.ShortcutManager
import android.os.Bundle
@@ -17,9 +16,7 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.GridLayoutManager
@@ -47,6 +44,7 @@ import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.MemoryUtil
import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
+import org.yuzu.yuzu_emu.utils.collect
import java.io.BufferedOutputStream
import java.io.File
@@ -76,8 +74,6 @@ class GamePropertiesFragment : Fragment() {
return binding.root
}
- // This is using the correct scope, lint is just acting up
- @SuppressLint("UnsafeRepeatOnLifecycleDetector")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setNavigationVisibility(visible = false, animated = true)
@@ -116,28 +112,14 @@ class GamePropertiesFragment : Fragment() {
reloadList()
- viewLifecycleOwner.lifecycleScope.apply {
- launch {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- homeViewModel.openImportSaves.collect {
- if (it) {
- importSaves.launch(arrayOf("application/zip"))
- homeViewModel.setOpenImportSaves(false)
- }
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- homeViewModel.reloadPropertiesList.collect {
- if (it) {
- reloadList()
- homeViewModel.reloadPropertiesList(false)
- }
- }
- }
- }
- }
+ homeViewModel.openImportSaves.collect(
+ viewLifecycleOwner,
+ resetState = { homeViewModel.setOpenImportSaves(false) }
+ ) { if (it) importSaves.launch(arrayOf("application/zip")) }
+ homeViewModel.reloadPropertiesList.collect(
+ viewLifecycleOwner,
+ resetState = { homeViewModel.reloadPropertiesList(false) }
+ ) { if (it) reloadList() }
setInsets()
}
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
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.transition.MaterialSharedAxis
@@ -35,6 +32,7 @@ import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
+import org.yuzu.yuzu_emu.utils.collect
import java.io.BufferedOutputStream
import java.io.File
import java.math.BigInteger
@@ -75,14 +73,10 @@ class InstallableFragment : Fragment() {
binding.root.findNavController().popBackStack()
}
- viewLifecycleOwner.lifecycleScope.launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- homeViewModel.openImportSaves.collect {
- if (it) {
- importSaves.launch(arrayOf("application/zip"))
- homeViewModel.setOpenImportSaves(false)
- }
- }
+ homeViewModel.openImportSaves.collect(viewLifecycleOwner) {
+ if (it) {
+ importSaves.launch(arrayOf("application/zip"))
+ homeViewModel.setOpenImportSaves(false)
}
}
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
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
import org.yuzu.yuzu_emu.model.TaskViewModel
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
+import org.yuzu.yuzu_emu.utils.collect
class ProgressDialogFragment : DialogFragment() {
private val taskViewModel: TaskViewModel by activityViewModels()
@@ -65,70 +62,50 @@ class ProgressDialogFragment : DialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.message.isSelected = true
- viewLifecycleOwner.lifecycleScope.apply {
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- taskViewModel.isComplete.collect {
- if (it) {
- dismiss()
- when (val result = taskViewModel.result.value) {
- is String -> Toast.makeText(
- requireContext(),
- result,
- Toast.LENGTH_LONG
- ).show()
-
- is MessageDialogFragment -> result.show(
- requireActivity().supportFragmentManager,
- MessageDialogFragment.TAG
- )
-
- else -> {
- // Do nothing
- }
- }
- taskViewModel.clear()
- }
+ taskViewModel.isComplete.collect(viewLifecycleOwner) {
+ if (it) {
+ dismiss()
+ when (val result = taskViewModel.result.value) {
+ is String -> Toast.makeText(
+ requireContext(),
+ result,
+ Toast.LENGTH_LONG
+ ).show()
+
+ is MessageDialogFragment -> result.show(
+ requireActivity().supportFragmentManager,
+ MessageDialogFragment.TAG
+ )
+
+ else -> {
+ // Do nothing
}
}
+ taskViewModel.clear()
}
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- taskViewModel.cancelled.collect {
- if (it) {
- dialog?.setTitle(R.string.cancelling)
- }
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- taskViewModel.progress.collect {
- if (it != 0.0) {
- binding.progressBar.apply {
- isIndeterminate = false
- progress = (
- (it / taskViewModel.maxProgress.value) *
- PROGRESS_BAR_RESOLUTION
- ).toInt()
- min = 0
- max = PROGRESS_BAR_RESOLUTION
- }
- }
- }
- }
+ }
+ taskViewModel.cancelled.collect(viewLifecycleOwner) {
+ if (it) {
+ dialog?.setTitle(R.string.cancelling)
}
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- taskViewModel.message.collect {
- binding.message.setVisible(it.isNotEmpty())
- if (it.isNotEmpty()) {
- binding.message.text = it
- }
- }
+ }
+ taskViewModel.progress.collect(viewLifecycleOwner) {
+ if (it != 0.0) {
+ binding.progressBar.apply {
+ isIndeterminate = false
+ progress = (
+ (it / taskViewModel.maxProgress.value) *
+ PROGRESS_BAR_RESOLUTION
+ ).toInt()
+ min = 0
+ max = PROGRESS_BAR_RESOLUTION
}
}
}
+ taskViewModel.message.collect(viewLifecycleOwner) {
+ binding.message.setVisible(it.isNotEmpty())
+ binding.message.text = it
+ }
}
// 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 @@
package org.yuzu.yuzu_emu.fragments
-import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
@@ -18,14 +17,9 @@ import androidx.core.view.updatePadding
import androidx.core.widget.doOnTextChanged
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import androidx.preference.PreferenceManager
import info.debatty.java.stringsimilarity.Jaccard
import info.debatty.java.stringsimilarity.JaroWinkler
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.launch
import java.util.Locale
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
@@ -36,6 +30,7 @@ import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
+import org.yuzu.yuzu_emu.utils.collect
class SearchFragment : Fragment() {
private var _binding: FragmentSearchBinding? = null
@@ -59,8 +54,6 @@ class SearchFragment : Fragment() {
return binding.root
}
- // This is using the correct scope, lint is just acting up
- @SuppressLint("UnsafeRepeatOnLifecycleDetector")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setNavigationVisibility(visible = true, animated = true)
@@ -86,30 +79,14 @@ class SearchFragment : Fragment() {
filterAndSearch()
}
- viewLifecycleOwner.lifecycleScope.apply {
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- gamesViewModel.searchFocused.collect {
- if (it) {
- focusSearch()
- gamesViewModel.setSearchFocused(false)
- }
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- gamesViewModel.games.collectLatest { filterAndSearch() }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- gamesViewModel.searchedGames.collect {
- (binding.gridGamesSearch.adapter as GameAdapter).submitList(it)
- binding.noResultsView.setVisible(it.isEmpty())
- }
- }
- }
+ gamesViewModel.searchFocused.collect(
+ viewLifecycleOwner,
+ resetState = { gamesViewModel.setSearchFocused(false) }
+ ) { if (it) focusSearch() }
+ gamesViewModel.games.collect(viewLifecycleOwner) { filterAndSearch() }
+ gamesViewModel.searchedGames.collect(viewLifecycleOwner) {
+ (binding.gridGamesSearch.adapter as GameAdapter).submitList(it)
+ binding.noResultsView.setVisible(it.isNotEmpty())
}
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 @@
package org.yuzu.yuzu_emu.fragments
import android.Manifest
-import android.annotation.SuppressLint
import android.content.Intent
import android.os.Build
import android.os.Bundle
@@ -23,9 +22,6 @@ import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController
import androidx.preference.PreferenceManager
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
@@ -47,6 +43,7 @@ import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.ViewUtils
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
+import org.yuzu.yuzu_emu.utils.collect
class SetupFragment : Fragment() {
private var _binding: FragmentSetupBinding? = null
@@ -78,8 +75,6 @@ class SetupFragment : Fragment() {
return binding.root
}
- // This is using the correct scope, lint is just acting up
- @SuppressLint("UnsafeRepeatOnLifecycleDetector")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
mainActivity = requireActivity() as MainActivity
@@ -211,28 +206,14 @@ class SetupFragment : Fragment() {
)
}
- viewLifecycleOwner.lifecycleScope.apply {
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- homeViewModel.shouldPageForward.collect {
- if (it) {
- pageForward()
- homeViewModel.setShouldPageForward(false)
- }
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- homeViewModel.gamesDirSelected.collect {
- if (it) {
- gamesDirCallback.onStepCompleted()
- homeViewModel.setGamesDirSelected(false)
- }
- }
- }
- }
- }
+ homeViewModel.shouldPageForward.collect(
+ viewLifecycleOwner,
+ resetState = { homeViewModel.setShouldPageForward(false) }
+ ) { if (it) pageForward() }
+ homeViewModel.gamesDirSelected.collect(
+ viewLifecycleOwner,
+ resetState = { homeViewModel.setGamesDirSelected(false) }
+ ) { if (it) gamesDirCallback.onStepCompleted() }
binding.viewPager2.apply {
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 @@
package org.yuzu.yuzu_emu.ui
-import android.annotation.SuppressLint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -14,12 +13,7 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import com.google.android.material.color.MaterialColors
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.adapters.GameAdapter
import org.yuzu.yuzu_emu.databinding.FragmentGamesBinding
@@ -28,6 +22,7 @@ import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
+import org.yuzu.yuzu_emu.utils.collect
class GamesFragment : Fragment() {
private var _binding: FragmentGamesBinding? = null
@@ -45,8 +40,6 @@ class GamesFragment : Fragment() {
return binding.root
}
- // This is using the correct scope, lint is just acting up
- @SuppressLint("UnsafeRepeatOnLifecycleDetector")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setNavigationVisibility(visible = true, animated = true)
@@ -89,48 +82,28 @@ class GamesFragment : Fragment() {
}
}
- viewLifecycleOwner.lifecycleScope.apply {
- launch {
- repeatOnLifecycle(Lifecycle.State.RESUMED) {
- gamesViewModel.isReloading.collect {
- binding.swipeRefresh.isRefreshing = it
- binding.noticeText.setVisible(
- visible = gamesViewModel.games.value.isEmpty() && !it,
- gone = false
- )
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.RESUMED) {
- gamesViewModel.games.collectLatest {
- (binding.gridGames.adapter as GameAdapter).submitList(it)
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.RESUMED) {
- gamesViewModel.shouldSwapData.collect {
- if (it) {
- (binding.gridGames.adapter as GameAdapter).submitList(
- gamesViewModel.games.value
- )
- gamesViewModel.setShouldSwapData(false)
- }
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.RESUMED) {
- gamesViewModel.shouldScrollToTop.collect {
- if (it) {
- scrollToTop()
- gamesViewModel.setShouldScrollToTop(false)
- }
- }
- }
+ gamesViewModel.isReloading.collect(viewLifecycleOwner) {
+ binding.swipeRefresh.isRefreshing = it
+ binding.noticeText.setVisible(
+ visible = gamesViewModel.games.value.isEmpty() && !it,
+ gone = false
+ )
+ }
+ gamesViewModel.games.collect(viewLifecycleOwner) {
+ (binding.gridGames.adapter as GameAdapter).submitList(it)
+ }
+ gamesViewModel.shouldSwapData.collect(
+ viewLifecycleOwner,
+ resetState = { gamesViewModel.setShouldSwapData(false) }
+ ) {
+ if (it) {
+ (binding.gridGames.adapter as GameAdapter).submitList(gamesViewModel.games.value)
}
}
+ gamesViewModel.shouldScrollToTop.collect(
+ viewLifecycleOwner,
+ resetState = { gamesViewModel.setShouldScrollToTop(false) }
+ ) { if (it) scrollToTop() }
setInsets()
}
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
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.NavController
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.setupWithNavController
@@ -30,7 +27,6 @@ import com.google.android.material.color.MaterialColors
import com.google.android.material.navigation.NavigationBarView
import java.io.File
import java.io.FilenameFilter
-import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
@@ -144,38 +140,19 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
binding.statusBarShade.setVisible(visible = false, gone = false)
}
- lifecycleScope.apply {
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- homeViewModel.navigationVisible.collect { showNavigation(it.first, it.second) }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- homeViewModel.statusBarShadeVisible.collect { showStatusBarShade(it) }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- homeViewModel.contentToInstall.collect {
- if (it != null) {
- installContent(it)
- homeViewModel.setContentToInstall(null)
- }
- }
- }
- }
- launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- homeViewModel.checkKeys.collect {
- if (it) {
- checkKeys()
- homeViewModel.setCheckKeys(false)
- }
- }
- }
+ homeViewModel.navigationVisible.collect(this) { showNavigation(it.first, it.second) }
+ homeViewModel.statusBarShadeVisible.collect(this) { showStatusBarShade(it) }
+ homeViewModel.contentToInstall.collect(
+ this,
+ resetState = { homeViewModel.setContentToInstall(null) }
+ ) {
+ if (it != null) {
+ installContent(it)
}
}
+ homeViewModel.checkKeys.collect(this, resetState = { homeViewModel.setCheckKeys(false) }) {
+ if (it) checkKeys()
+ }
setInsets()
}
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 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.utils
+
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.launch
+
+/**
+ * Collects this [Flow] with a given [LifecycleOwner].
+ * @param scope [LifecycleOwner] that this [Flow] will be collected with.
+ * @param repeatState When to repeat collection on this [Flow].
+ * @param resetState Optional lambda to reset state of an underlying [MutableStateFlow] after
+ * [stateCollector] has been run.
+ * @param stateCollector Lambda that receives new state.
+ */
+inline fun <reified T> Flow<T>.collect(
+ scope: LifecycleOwner,
+ repeatState: Lifecycle.State = Lifecycle.State.CREATED,
+ crossinline resetState: () -> Unit = {},
+ crossinline stateCollector: (state: T) -> Unit
+) {
+ scope.apply {
+ lifecycleScope.launch {
+ repeatOnLifecycle(repeatState) {
+ stateCollector(it)
+ resetState()
+ }
+ }
+ }
+ }
+}