Merge pull request #13054 from t895/lifecycle-utils
android: Create lifecycle utility to simplify common StateFlow operations
This commit is contained in:
		| @@ -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) | ||||
|             } | ||||
|   | ||||
| @@ -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) } | ||||
|   | ||||
| @@ -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() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -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 | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -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() | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -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) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -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() | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -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) | ||||
|                     } | ||||
|                 } | ||||
|         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.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) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             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) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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 | ||||
|         emulationViewModel.shaderProgress.collect(viewLifecycleOwner) { | ||||
|             if (it > 0 && it != emulationViewModel.totalShaders.value) { | ||||
|                 binding.loadingProgressIndicator.isIndeterminate = false | ||||
|  | ||||
|                             if (it < binding.loadingProgressIndicator.max) { | ||||
|                                 binding.loadingProgressIndicator.progress = it | ||||
|                             } | ||||
|                         } | ||||
|                 if (it < binding.loadingProgressIndicator.max) { | ||||
|                     binding.loadingProgressIndicator.progress = it | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|                         if (it == emulationViewModel.totalShaders.value) { | ||||
|                             binding.loadingText.setText(R.string.loading) | ||||
|                             binding.loadingProgressIndicator.isIndeterminate = true | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             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 | ||||
|                     } | ||||
|                 } | ||||
|         } | ||||
|         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.shaderMessage.collectLatest { | ||||
|                         if (it.isNotEmpty()) { | ||||
|                             binding.loadingText.text = it | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             launch { | ||||
|                 repeatOnLifecycle(Lifecycle.State.RESUMED) { | ||||
|                     driverViewModel.isInteractionAllowed.collect { | ||||
|                         if (it) { | ||||
|                             startEmulation() | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             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() | ||||
|         emulationViewModel.emulationStarted.collect(viewLifecycleOwner) { | ||||
|             if (it) { | ||||
|                 binding.drawerLayout.setDrawerLockMode(IntSetting.LOCK_DRAWER.getInt()) | ||||
|                 ViewUtils.showView(binding.surfaceInputOverlay) | ||||
|                 ViewUtils.hideView(binding.loadingIndicator) | ||||
|  | ||||
|                             // Setup overlays | ||||
|                             updateShowFpsOverlay() | ||||
|                             updateThermalOverlay() | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 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) { | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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() | ||||
|     } | ||||
|   | ||||
| @@ -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) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -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() | ||||
|         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 | ||||
|                                 ) | ||||
|                     is MessageDialogFragment -> result.show( | ||||
|                         requireActivity().supportFragmentManager, | ||||
|                         MessageDialogFragment.TAG | ||||
|                     ) | ||||
|  | ||||
|                                 else -> { | ||||
|                                     // Do nothing | ||||
|                                 } | ||||
|                             } | ||||
|                             taskViewModel.clear() | ||||
|                         } | ||||
|                     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 | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             launch { | ||||
|                 repeatOnLifecycle(Lifecycle.State.CREATED) { | ||||
|                     taskViewModel.message.collect { | ||||
|                         binding.message.setVisible(it.isNotEmpty()) | ||||
|                         if (it.isNotEmpty()) { | ||||
|                             binding.message.text = it | ||||
|                         } | ||||
|                     } | ||||
|         } | ||||
|         taskViewModel.cancelled.collect(viewLifecycleOwner) { | ||||
|             if (it) { | ||||
|                 dialog?.setTitle(R.string.cancelling) | ||||
|             } | ||||
|         } | ||||
|         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. | ||||
|   | ||||
| @@ -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("") } | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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() | ||||
|     } | ||||
|   | ||||
| @@ -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() | ||||
|     } | ||||
|   | ||||
| @@ -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) { | ||||
|                 this@collect.collect { | ||||
|                     stateCollector(it) | ||||
|                     resetState() | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user