android: Use StateFlow instead of LiveData
This commit is contained in:
		| @@ -10,8 +10,12 @@ 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 androidx.recyclerview.widget.RecyclerView | ||||
| import kotlinx.coroutines.launch | ||||
| import org.yuzu.yuzu_emu.R | ||||
| import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding | ||||
| import org.yuzu.yuzu_emu.fragments.MessageDialogFragment | ||||
| @@ -86,7 +90,11 @@ class HomeSettingAdapter( | ||||
|                 binding.optionIcon.alpha = 0.5f | ||||
|             } | ||||
|  | ||||
|             option.details.observe(viewLifecycle) { updateOptionDetails(it) } | ||||
|             viewLifecycle.lifecycleScope.launch { | ||||
|                 viewLifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) { | ||||
|                     option.details.collect { updateOptionDetails(it) } | ||||
|                 } | ||||
|             } | ||||
|             binding.optionDetail.postDelayed( | ||||
|                 { | ||||
|                     binding.optionDetail.ellipsize = TextUtils.TruncateAt.MARQUEE | ||||
|   | ||||
| @@ -13,9 +13,14 @@ 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 java.io.IOException | ||||
| import org.yuzu.yuzu_emu.R | ||||
| import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding | ||||
| @@ -66,25 +71,39 @@ class SettingsActivity : AppCompatActivity() { | ||||
|             ) | ||||
|         } | ||||
|  | ||||
|         settingsViewModel.shouldRecreate.observe(this) { | ||||
|             if (it) { | ||||
|                 settingsViewModel.setShouldRecreate(false) | ||||
|                 recreate() | ||||
|         lifecycleScope.apply { | ||||
|             launch { | ||||
|                 repeatOnLifecycle(Lifecycle.State.CREATED) { | ||||
|                     settingsViewModel.shouldRecreate.collectLatest { | ||||
|                         if (it) { | ||||
|                             settingsViewModel.setShouldRecreate(false) | ||||
|                             recreate() | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         settingsViewModel.shouldNavigateBack.observe(this) { | ||||
|             if (it) { | ||||
|                 settingsViewModel.setShouldNavigateBack(false) | ||||
|                 navigateBack() | ||||
|             launch { | ||||
|                 repeatOnLifecycle(Lifecycle.State.CREATED) { | ||||
|                     settingsViewModel.shouldNavigateBack.collectLatest { | ||||
|                         if (it) { | ||||
|                             settingsViewModel.setShouldNavigateBack(false) | ||||
|                             navigateBack() | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         settingsViewModel.shouldShowResetSettingsDialog.observe(this) { | ||||
|             if (it) { | ||||
|                 settingsViewModel.setShouldShowResetSettingsDialog(false) | ||||
|                 ResetSettingsDialogFragment().show( | ||||
|                     supportFragmentManager, | ||||
|                     ResetSettingsDialogFragment.TAG | ||||
|                 ) | ||||
|             launch { | ||||
|                 repeatOnLifecycle(Lifecycle.State.CREATED) { | ||||
|                     settingsViewModel.shouldShowResetSettingsDialog.collectLatest { | ||||
|                         if (it) { | ||||
|                             settingsViewModel.setShouldShowResetSettingsDialog(false) | ||||
|                             ResetSettingsDialogFragment().show( | ||||
|                                 supportFragmentManager, | ||||
|                                 ResetSettingsDialogFragment.TAG | ||||
|                             ) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -13,11 +13,15 @@ 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.divider.MaterialDividerItemDecoration | ||||
| import com.google.android.material.transition.MaterialSharedAxis | ||||
| import kotlinx.coroutines.launch | ||||
| import org.yuzu.yuzu_emu.R | ||||
| import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding | ||||
| import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile | ||||
| @@ -51,6 +55,8 @@ class SettingsFragment : Fragment() { | ||||
|         return binding.root | ||||
|     } | ||||
|  | ||||
|     // This is using the correct scope, lint is just acting up | ||||
|     @SuppressLint("UnsafeRepeatOnLifecycleDetector") | ||||
|     override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||||
|         settingsAdapter = SettingsAdapter(this, requireContext()) | ||||
|         presenter = SettingsFragmentPresenter( | ||||
| @@ -75,24 +81,27 @@ class SettingsFragment : Fragment() { | ||||
|             settingsViewModel.setShouldNavigateBack(true) | ||||
|         } | ||||
|  | ||||
|         settingsViewModel.toolbarTitle.observe(viewLifecycleOwner) { | ||||
|             if (it.isNotEmpty()) binding.toolbarSettingsLayout.title = it | ||||
|         } | ||||
|  | ||||
|         settingsViewModel.shouldReloadSettingsList.observe(viewLifecycleOwner) { | ||||
|             if (it) { | ||||
|                 settingsViewModel.setShouldReloadSettingsList(false) | ||||
|                 presenter.loadSettingsList() | ||||
|         viewLifecycleOwner.lifecycleScope.apply { | ||||
|             launch { | ||||
|                 repeatOnLifecycle(Lifecycle.State.CREATED) { | ||||
|                     settingsViewModel.shouldReloadSettingsList.collectLatest { | ||||
|                         if (it) { | ||||
|                             settingsViewModel.setShouldReloadSettingsList(false) | ||||
|                             presenter.loadSettingsList() | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         settingsViewModel.isUsingSearch.observe(viewLifecycleOwner) { | ||||
|             if (it) { | ||||
|                 reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true) | ||||
|                 exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false) | ||||
|             } else { | ||||
|                 reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) | ||||
|                 exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) | ||||
|             launch { | ||||
|                 settingsViewModel.isUsingSearch.collectLatest { | ||||
|                     if (it) { | ||||
|                         reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true) | ||||
|                         exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false) | ||||
|                     } else { | ||||
|                         reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) | ||||
|                         exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -39,6 +39,7 @@ 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 | ||||
| @@ -129,6 +130,8 @@ 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?) { | ||||
|         binding.surfaceEmulation.holder.addCallback(this) | ||||
|         binding.showFpsText.setTextColor(Color.YELLOW) | ||||
| @@ -205,59 +208,80 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | ||||
|             } | ||||
|         ) | ||||
|  | ||||
|         viewLifecycleOwner.lifecycleScope.launch(Dispatchers.Main) { | ||||
|             lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { | ||||
|                 WindowInfoTracker.getOrCreate(requireContext()) | ||||
|                     .windowLayoutInfo(requireActivity()) | ||||
|                     .collect { updateFoldableLayout(requireActivity() as EmulationActivity, it) } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         GameIconUtils.loadGameIcon(game, binding.loadingImage) | ||||
|         binding.loadingTitle.text = game.title | ||||
|         binding.loadingTitle.isSelected = true | ||||
|         binding.loadingText.isSelected = true | ||||
|  | ||||
|         emulationViewModel.shaderProgress.observe(viewLifecycleOwner) { | ||||
|             if (it > 0 && it != emulationViewModel.totalShaders.value!!) { | ||||
|                 binding.loadingProgressIndicator.isIndeterminate = false | ||||
|  | ||||
|                 if (it < binding.loadingProgressIndicator.max) { | ||||
|                     binding.loadingProgressIndicator.progress = it | ||||
|         viewLifecycleOwner.lifecycleScope.apply { | ||||
|             launch { | ||||
|                 repeatOnLifecycle(Lifecycle.State.STARTED) { | ||||
|                     WindowInfoTracker.getOrCreate(requireContext()) | ||||
|                         .windowLayoutInfo(requireActivity()) | ||||
|                         .collect { | ||||
|                             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 == emulationViewModel.totalShaders.value!!) { | ||||
|                 binding.loadingText.setText(R.string.loading) | ||||
|                 binding.loadingProgressIndicator.isIndeterminate = true | ||||
|                             if (it < binding.loadingProgressIndicator.max) { | ||||
|                                 binding.loadingProgressIndicator.progress = it | ||||
|                             } | ||||
|                         } | ||||
|  | ||||
|                         if (it == emulationViewModel.totalShaders.value) { | ||||
|                             binding.loadingText.setText(R.string.loading) | ||||
|                             binding.loadingProgressIndicator.isIndeterminate = true | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         emulationViewModel.totalShaders.observe(viewLifecycleOwner) { | ||||
|             binding.loadingProgressIndicator.max = it | ||||
|         } | ||||
|         emulationViewModel.shaderMessage.observe(viewLifecycleOwner) { | ||||
|             if (it.isNotEmpty()) { | ||||
|                 binding.loadingText.text = it | ||||
|             launch { | ||||
|                 repeatOnLifecycle(Lifecycle.State.CREATED) { | ||||
|                     emulationViewModel.totalShaders.collectLatest { | ||||
|                         binding.loadingProgressIndicator.max = it | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         emulationViewModel.emulationStarted.observe(viewLifecycleOwner) { started -> | ||||
|             if (started) { | ||||
|                 binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED) | ||||
|                 ViewUtils.showView(binding.surfaceInputOverlay) | ||||
|                 ViewUtils.hideView(binding.loadingIndicator) | ||||
|  | ||||
|                 // Setup overlay | ||||
|                 updateShowFpsOverlay() | ||||
|             launch { | ||||
|                 repeatOnLifecycle(Lifecycle.State.CREATED) { | ||||
|                     emulationViewModel.shaderMessage.collectLatest { | ||||
|                         if (it.isNotEmpty()) { | ||||
|                             binding.loadingText.text = it | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|             launch { | ||||
|                 repeatOnLifecycle(Lifecycle.State.CREATED) { | ||||
|                     emulationViewModel.emulationStarted.collectLatest { | ||||
|                         if (it) { | ||||
|                             binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED) | ||||
|                             ViewUtils.showView(binding.surfaceInputOverlay) | ||||
|                             ViewUtils.hideView(binding.loadingIndicator) | ||||
|  | ||||
|         emulationViewModel.isEmulationStopping.observe(viewLifecycleOwner) { | ||||
|             if (it) { | ||||
|                 binding.loadingText.setText(R.string.shutting_down) | ||||
|                 ViewUtils.showView(binding.loadingIndicator) | ||||
|                 ViewUtils.hideView(binding.inputContainer) | ||||
|                 ViewUtils.hideView(binding.showFpsText) | ||||
|                             // Setup overlay | ||||
|                             updateShowFpsOverlay() | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             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) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -274,9 +298,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             if (EmulationMenuSettings.showOverlay && | ||||
|                 emulationViewModel.emulationStarted.value == true | ||||
|             ) { | ||||
|             if (EmulationMenuSettings.showOverlay && emulationViewModel.emulationStarted.value) { | ||||
|                 binding.surfaceInputOverlay.post { | ||||
|                     binding.surfaceInputOverlay.visibility = View.VISIBLE | ||||
|                 } | ||||
|   | ||||
| @@ -9,8 +9,12 @@ import android.widget.Toast | ||||
| import androidx.appcompat.app.AppCompatActivity | ||||
| import androidx.fragment.app.DialogFragment | ||||
| 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.databinding.DialogProgressBarBinding | ||||
| import org.yuzu.yuzu_emu.model.TaskViewModel | ||||
|  | ||||
| @@ -28,21 +32,27 @@ class IndeterminateProgressDialogFragment : DialogFragment() { | ||||
|             .create() | ||||
|         dialog.setCanceledOnTouchOutside(false) | ||||
|  | ||||
|         taskViewModel.isComplete.observe(this) { complete -> | ||||
|             if (complete) { | ||||
|                 dialog.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 | ||||
|                     ) | ||||
|         viewLifecycleOwner.lifecycleScope.launch { | ||||
|             repeatOnLifecycle(Lifecycle.State.CREATED) { | ||||
|                 taskViewModel.isComplete.collect { | ||||
|                     if (it) { | ||||
|                         dialog.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 | ||||
|                             ) | ||||
|                         } | ||||
|                         taskViewModel.clear() | ||||
|                     } | ||||
|                 } | ||||
|                 taskViewModel.clear() | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (taskViewModel.isRunning.value == false) { | ||||
|         if (!taskViewModel.isRunning.value) { | ||||
|             taskViewModel.runTask() | ||||
|         } | ||||
|         return dialog | ||||
|   | ||||
| @@ -3,6 +3,7 @@ | ||||
|  | ||||
| package org.yuzu.yuzu_emu.fragments | ||||
|  | ||||
| import android.annotation.SuppressLint | ||||
| import android.content.Context | ||||
| import android.content.SharedPreferences | ||||
| import android.os.Bundle | ||||
| @@ -17,9 +18,13 @@ 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.launch | ||||
| import java.util.Locale | ||||
| import org.yuzu.yuzu_emu.R | ||||
| import org.yuzu.yuzu_emu.YuzuApplication | ||||
| @@ -52,6 +57,8 @@ 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?) { | ||||
|         homeViewModel.setNavigationVisibility(visible = true, animated = false) | ||||
|         preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) | ||||
| @@ -79,21 +86,32 @@ class SearchFragment : Fragment() { | ||||
|             filterAndSearch() | ||||
|         } | ||||
|  | ||||
|         gamesViewModel.apply { | ||||
|             searchFocused.observe(viewLifecycleOwner) { searchFocused -> | ||||
|                 if (searchFocused) { | ||||
|                     focusSearch() | ||||
|                     gamesViewModel.setSearchFocused(false) | ||||
|         viewLifecycleOwner.lifecycleScope.apply { | ||||
|             launch { | ||||
|                 repeatOnLifecycle(Lifecycle.State.CREATED) { | ||||
|                     gamesViewModel.searchFocused.collect { | ||||
|                         if (it) { | ||||
|                             focusSearch() | ||||
|                             gamesViewModel.setSearchFocused(false) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             games.observe(viewLifecycleOwner) { filterAndSearch() } | ||||
|             searchedGames.observe(viewLifecycleOwner) { | ||||
|                 (binding.gridGamesSearch.adapter as GameAdapter).submitList(it) | ||||
|                 if (it.isEmpty()) { | ||||
|                     binding.noResultsView.visibility = View.VISIBLE | ||||
|                 } else { | ||||
|                     binding.noResultsView.visibility = View.GONE | ||||
|             launch { | ||||
|                 repeatOnLifecycle(Lifecycle.State.CREATED) { | ||||
|                     gamesViewModel.games.collect { filterAndSearch() } | ||||
|                 } | ||||
|             } | ||||
|             launch { | ||||
|                 repeatOnLifecycle(Lifecycle.State.CREATED) { | ||||
|                     gamesViewModel.searchedGames.collect { | ||||
|                         (binding.gridGamesSearch.adapter as GameAdapter).submitList(it) | ||||
|                         if (it.isEmpty()) { | ||||
|                             binding.noResultsView.visibility = View.VISIBLE | ||||
|                         } else { | ||||
|                             binding.noResultsView.visibility = View.GONE | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| @@ -109,7 +127,7 @@ class SearchFragment : Fragment() { | ||||
|     private inner class ScoredGame(val score: Double, val item: Game) | ||||
|  | ||||
|     private fun filterAndSearch() { | ||||
|         val baseList = gamesViewModel.games.value!! | ||||
|         val baseList = gamesViewModel.games.value | ||||
|         val filteredList: List<Game> = when (binding.chipGroup.checkedChipId) { | ||||
|             R.id.chip_recently_played -> { | ||||
|                 baseList.filter { | ||||
|   | ||||
| @@ -15,10 +15,14 @@ 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 | ||||
| @@ -79,10 +83,14 @@ class SettingsSearchFragment : Fragment() { | ||||
|             search() | ||||
|             binding.settingsList.smoothScrollToPosition(0) | ||||
|         } | ||||
|         settingsViewModel.shouldReloadSettingsList.observe(viewLifecycleOwner) { | ||||
|             if (it) { | ||||
|                 settingsViewModel.setShouldReloadSettingsList(false) | ||||
|                 search() | ||||
|         viewLifecycleOwner.lifecycleScope.launch { | ||||
|             repeatOnLifecycle(Lifecycle.State.CREATED) { | ||||
|                 settingsViewModel.shouldReloadSettingsList.collect { | ||||
|                     if (it) { | ||||
|                         settingsViewModel.setShouldReloadSettingsList(false) | ||||
|                         search() | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -22,10 +22,14 @@ 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 | ||||
| import com.google.android.material.transition.MaterialFadeThrough | ||||
| import kotlinx.coroutines.launch | ||||
| import java.io.File | ||||
| import org.yuzu.yuzu_emu.R | ||||
| import org.yuzu.yuzu_emu.YuzuApplication | ||||
| @@ -206,10 +210,14 @@ class SetupFragment : Fragment() { | ||||
|             ) | ||||
|         } | ||||
|  | ||||
|         homeViewModel.shouldPageForward.observe(viewLifecycleOwner) { | ||||
|             if (it) { | ||||
|                 pageForward() | ||||
|                 homeViewModel.setShouldPageForward(false) | ||||
|         viewLifecycleOwner.lifecycleScope.launch { | ||||
|             repeatOnLifecycle(Lifecycle.State.CREATED) { | ||||
|                 homeViewModel.shouldPageForward.collect { | ||||
|                     if (it) { | ||||
|                         pageForward() | ||||
|                         homeViewModel.setShouldPageForward(false) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -3,28 +3,28 @@ | ||||
|  | ||||
| package org.yuzu.yuzu_emu.model | ||||
|  | ||||
| import androidx.lifecycle.LiveData | ||||
| import androidx.lifecycle.MutableLiveData | ||||
| import androidx.lifecycle.ViewModel | ||||
| import kotlinx.coroutines.flow.MutableStateFlow | ||||
| import kotlinx.coroutines.flow.StateFlow | ||||
|  | ||||
| class EmulationViewModel : ViewModel() { | ||||
|     private val _emulationStarted = MutableLiveData(false) | ||||
|     val emulationStarted: LiveData<Boolean> get() = _emulationStarted | ||||
|     val emulationStarted: StateFlow<Boolean> get() = _emulationStarted | ||||
|     private val _emulationStarted = MutableStateFlow(false) | ||||
|  | ||||
|     private val _isEmulationStopping = MutableLiveData(false) | ||||
|     val isEmulationStopping: LiveData<Boolean> get() = _isEmulationStopping | ||||
|     val isEmulationStopping: StateFlow<Boolean> get() = _isEmulationStopping | ||||
|     private val _isEmulationStopping = MutableStateFlow(false) | ||||
|  | ||||
|     private val _shaderProgress = MutableLiveData(0) | ||||
|     val shaderProgress: LiveData<Int> get() = _shaderProgress | ||||
|     val shaderProgress: StateFlow<Int> get() = _shaderProgress | ||||
|     private val _shaderProgress = MutableStateFlow(0) | ||||
|  | ||||
|     private val _totalShaders = MutableLiveData(0) | ||||
|     val totalShaders: LiveData<Int> get() = _totalShaders | ||||
|     val totalShaders: StateFlow<Int> get() = _totalShaders | ||||
|     private val _totalShaders = MutableStateFlow(0) | ||||
|  | ||||
|     private val _shaderMessage = MutableLiveData("") | ||||
|     val shaderMessage: LiveData<String> get() = _shaderMessage | ||||
|     val shaderMessage: StateFlow<String> get() = _shaderMessage | ||||
|     private val _shaderMessage = MutableStateFlow("") | ||||
|  | ||||
|     fun setEmulationStarted(started: Boolean) { | ||||
|         _emulationStarted.postValue(started) | ||||
|         _emulationStarted.value = started | ||||
|     } | ||||
|  | ||||
|     fun setIsEmulationStopping(value: Boolean) { | ||||
| @@ -50,10 +50,18 @@ class EmulationViewModel : ViewModel() { | ||||
|     } | ||||
|  | ||||
|     fun clear() { | ||||
|         _emulationStarted.value = false | ||||
|         _isEmulationStopping.value = false | ||||
|         _shaderProgress.value = 0 | ||||
|         _totalShaders.value = 0 | ||||
|         _shaderMessage.value = "" | ||||
|         setEmulationStarted(false) | ||||
|         setIsEmulationStopping(false) | ||||
|         setShaderProgress(0) | ||||
|         setTotalShaders(0) | ||||
|         setShaderMessage("") | ||||
|     } | ||||
|  | ||||
|     companion object { | ||||
|         const val KEY_EMULATION_STARTED = "EmulationStarted" | ||||
|         const val KEY_IS_EMULATION_STOPPING = "IsEmulationStarting" | ||||
|         const val KEY_SHADER_PROGRESS = "ShaderProgress" | ||||
|         const val KEY_TOTAL_SHADERS = "TotalShaders" | ||||
|         const val KEY_SHADER_MESSAGE = "ShaderMessage" | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -5,13 +5,13 @@ package org.yuzu.yuzu_emu.model | ||||
|  | ||||
| import android.net.Uri | ||||
| import androidx.documentfile.provider.DocumentFile | ||||
| import androidx.lifecycle.LiveData | ||||
| import androidx.lifecycle.MutableLiveData | ||||
| import androidx.lifecycle.ViewModel | ||||
| import androidx.lifecycle.viewModelScope | ||||
| import androidx.preference.PreferenceManager | ||||
| import java.util.Locale | ||||
| import kotlinx.coroutines.Dispatchers | ||||
| import kotlinx.coroutines.flow.MutableStateFlow | ||||
| import kotlinx.coroutines.flow.StateFlow | ||||
| import kotlinx.coroutines.launch | ||||
| import kotlinx.coroutines.withContext | ||||
| import kotlinx.serialization.ExperimentalSerializationApi | ||||
| @@ -24,23 +24,23 @@ import org.yuzu.yuzu_emu.utils.GameHelper | ||||
|  | ||||
| @OptIn(ExperimentalSerializationApi::class) | ||||
| class GamesViewModel : ViewModel() { | ||||
|     private val _games = MutableLiveData<List<Game>>(emptyList()) | ||||
|     val games: LiveData<List<Game>> get() = _games | ||||
|     val games: StateFlow<List<Game>> get() = _games | ||||
|     private val _games = MutableStateFlow(emptyList<Game>()) | ||||
|  | ||||
|     private val _searchedGames = MutableLiveData<List<Game>>(emptyList()) | ||||
|     val searchedGames: LiveData<List<Game>> get() = _searchedGames | ||||
|     val searchedGames: StateFlow<List<Game>> get() = _searchedGames | ||||
|     private val _searchedGames = MutableStateFlow(emptyList<Game>()) | ||||
|  | ||||
|     private val _isReloading = MutableLiveData(false) | ||||
|     val isReloading: LiveData<Boolean> get() = _isReloading | ||||
|     val isReloading: StateFlow<Boolean> get() = _isReloading | ||||
|     private val _isReloading = MutableStateFlow(false) | ||||
|  | ||||
|     private val _shouldSwapData = MutableLiveData(false) | ||||
|     val shouldSwapData: LiveData<Boolean> get() = _shouldSwapData | ||||
|     val shouldSwapData: StateFlow<Boolean> get() = _shouldSwapData | ||||
|     private val _shouldSwapData = MutableStateFlow(false) | ||||
|  | ||||
|     private val _shouldScrollToTop = MutableLiveData(false) | ||||
|     val shouldScrollToTop: LiveData<Boolean> get() = _shouldScrollToTop | ||||
|     val shouldScrollToTop: StateFlow<Boolean> get() = _shouldScrollToTop | ||||
|     private val _shouldScrollToTop = MutableStateFlow(false) | ||||
|  | ||||
|     private val _searchFocused = MutableLiveData(false) | ||||
|     val searchFocused: LiveData<Boolean> get() = _searchFocused | ||||
|     val searchFocused: StateFlow<Boolean> get() = _searchFocused | ||||
|     private val _searchFocused = MutableStateFlow(false) | ||||
|  | ||||
|     init { | ||||
|         // Ensure keys are loaded so that ROM metadata can be decrypted. | ||||
| @@ -79,36 +79,36 @@ class GamesViewModel : ViewModel() { | ||||
|             ) | ||||
|         ) | ||||
|  | ||||
|         _games.postValue(sortedList) | ||||
|         _games.value = sortedList | ||||
|     } | ||||
|  | ||||
|     fun setSearchedGames(games: List<Game>) { | ||||
|         _searchedGames.postValue(games) | ||||
|         _searchedGames.value = games | ||||
|     } | ||||
|  | ||||
|     fun setShouldSwapData(shouldSwap: Boolean) { | ||||
|         _shouldSwapData.postValue(shouldSwap) | ||||
|         _shouldSwapData.value = shouldSwap | ||||
|     } | ||||
|  | ||||
|     fun setShouldScrollToTop(shouldScroll: Boolean) { | ||||
|         _shouldScrollToTop.postValue(shouldScroll) | ||||
|         _shouldScrollToTop.value = shouldScroll | ||||
|     } | ||||
|  | ||||
|     fun setSearchFocused(searchFocused: Boolean) { | ||||
|         _searchFocused.postValue(searchFocused) | ||||
|         _searchFocused.value = searchFocused | ||||
|     } | ||||
|  | ||||
|     fun reloadGames(directoryChanged: Boolean) { | ||||
|         if (isReloading.value == true) { | ||||
|         if (isReloading.value) { | ||||
|             return | ||||
|         } | ||||
|         _isReloading.postValue(true) | ||||
|         _isReloading.value = true | ||||
|  | ||||
|         viewModelScope.launch { | ||||
|             withContext(Dispatchers.IO) { | ||||
|                 NativeLibrary.resetRomMetadata() | ||||
|                 setGames(GameHelper.getGames()) | ||||
|                 _isReloading.postValue(false) | ||||
|                 _isReloading.value = false | ||||
|  | ||||
|                 if (directoryChanged) { | ||||
|                     setShouldSwapData(true) | ||||
|   | ||||
| @@ -3,8 +3,8 @@ | ||||
|  | ||||
| package org.yuzu.yuzu_emu.model | ||||
|  | ||||
| import androidx.lifecycle.LiveData | ||||
| import androidx.lifecycle.MutableLiveData | ||||
| import kotlinx.coroutines.flow.MutableStateFlow | ||||
| import kotlinx.coroutines.flow.StateFlow | ||||
|  | ||||
| data class HomeSetting( | ||||
|     val titleId: Int, | ||||
| @@ -14,5 +14,5 @@ data class HomeSetting( | ||||
|     val isEnabled: () -> Boolean = { true }, | ||||
|     val disabledTitleId: Int = 0, | ||||
|     val disabledMessageId: Int = 0, | ||||
|     val details: LiveData<String> = MutableLiveData("") | ||||
|     val details: StateFlow<String> = MutableStateFlow("") | ||||
| ) | ||||
|   | ||||
| @@ -5,47 +5,43 @@ package org.yuzu.yuzu_emu.model | ||||
|  | ||||
| import android.net.Uri | ||||
| import androidx.fragment.app.FragmentActivity | ||||
| import androidx.lifecycle.LiveData | ||||
| import androidx.lifecycle.MutableLiveData | ||||
| import androidx.lifecycle.ViewModel | ||||
| import androidx.lifecycle.ViewModelProvider | ||||
| import androidx.preference.PreferenceManager | ||||
| import kotlinx.coroutines.flow.MutableStateFlow | ||||
| import kotlinx.coroutines.flow.StateFlow | ||||
| import org.yuzu.yuzu_emu.YuzuApplication | ||||
| import org.yuzu.yuzu_emu.utils.GameHelper | ||||
|  | ||||
| class HomeViewModel : ViewModel() { | ||||
|     private val _navigationVisible = MutableLiveData<Pair<Boolean, Boolean>>() | ||||
|     val navigationVisible: LiveData<Pair<Boolean, Boolean>> get() = _navigationVisible | ||||
|     val navigationVisible: StateFlow<Pair<Boolean, Boolean>> get() = _navigationVisible | ||||
|     private val _navigationVisible = MutableStateFlow(Pair(false, false)) | ||||
|  | ||||
|     private val _statusBarShadeVisible = MutableLiveData(true) | ||||
|     val statusBarShadeVisible: LiveData<Boolean> get() = _statusBarShadeVisible | ||||
|     val statusBarShadeVisible: StateFlow<Boolean> get() = _statusBarShadeVisible | ||||
|     private val _statusBarShadeVisible = MutableStateFlow(true) | ||||
|  | ||||
|     private val _shouldPageForward = MutableLiveData(false) | ||||
|     val shouldPageForward: LiveData<Boolean> get() = _shouldPageForward | ||||
|     val shouldPageForward: StateFlow<Boolean> get() = _shouldPageForward | ||||
|     private val _shouldPageForward = MutableStateFlow(false) | ||||
|  | ||||
|     private val _gamesDir = MutableLiveData( | ||||
|     val gamesDir: StateFlow<String> get() = _gamesDir | ||||
|     private val _gamesDir = MutableStateFlow( | ||||
|         Uri.parse( | ||||
|             PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) | ||||
|                 .getString(GameHelper.KEY_GAME_PATH, "") | ||||
|         ).path ?: "" | ||||
|     ) | ||||
|     val gamesDir: LiveData<String> get() = _gamesDir | ||||
|  | ||||
|     var navigatedToSetup = false | ||||
|  | ||||
|     init { | ||||
|         _navigationVisible.value = Pair(false, false) | ||||
|     } | ||||
|  | ||||
|     fun setNavigationVisibility(visible: Boolean, animated: Boolean) { | ||||
|         if (_navigationVisible.value?.first == visible) { | ||||
|         if (navigationVisible.value.first == visible) { | ||||
|             return | ||||
|         } | ||||
|         _navigationVisible.value = Pair(visible, animated) | ||||
|     } | ||||
|  | ||||
|     fun setStatusBarShadeVisibility(visible: Boolean) { | ||||
|         if (_statusBarShadeVisible.value == visible) { | ||||
|         if (statusBarShadeVisible.value == visible) { | ||||
|             return | ||||
|         } | ||||
|         _statusBarShadeVisible.value = visible | ||||
|   | ||||
| @@ -3,48 +3,43 @@ | ||||
|  | ||||
| package org.yuzu.yuzu_emu.model | ||||
|  | ||||
| import androidx.lifecycle.LiveData | ||||
| import androidx.lifecycle.MutableLiveData | ||||
| import androidx.lifecycle.SavedStateHandle | ||||
| import androidx.lifecycle.ViewModel | ||||
| import kotlinx.coroutines.flow.MutableStateFlow | ||||
| import kotlinx.coroutines.flow.StateFlow | ||||
| import org.yuzu.yuzu_emu.R | ||||
| import org.yuzu.yuzu_emu.YuzuApplication | ||||
| import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem | ||||
|  | ||||
| class SettingsViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() { | ||||
| class SettingsViewModel : ViewModel() { | ||||
|     var game: Game? = null | ||||
|  | ||||
|     var shouldSave = false | ||||
|  | ||||
|     var clickedItem: SettingsItem? = null | ||||
|  | ||||
|     private val _toolbarTitle = MutableLiveData("") | ||||
|     val toolbarTitle: LiveData<String> get() = _toolbarTitle | ||||
|     val shouldRecreate: StateFlow<Boolean> get() = _shouldRecreate | ||||
|     private val _shouldRecreate = MutableStateFlow(false) | ||||
|  | ||||
|     private val _shouldRecreate = MutableLiveData(false) | ||||
|     val shouldRecreate: LiveData<Boolean> get() = _shouldRecreate | ||||
|     val shouldNavigateBack: StateFlow<Boolean> get() = _shouldNavigateBack | ||||
|     private val _shouldNavigateBack = MutableStateFlow(false) | ||||
|  | ||||
|     private val _shouldNavigateBack = MutableLiveData(false) | ||||
|     val shouldNavigateBack: LiveData<Boolean> get() = _shouldNavigateBack | ||||
|     val shouldShowResetSettingsDialog: StateFlow<Boolean> get() = _shouldShowResetSettingsDialog | ||||
|     private val _shouldShowResetSettingsDialog = MutableStateFlow(false) | ||||
|  | ||||
|     private val _shouldShowResetSettingsDialog = MutableLiveData(false) | ||||
|     val shouldShowResetSettingsDialog: LiveData<Boolean> get() = _shouldShowResetSettingsDialog | ||||
|     val shouldReloadSettingsList: StateFlow<Boolean> get() = _shouldReloadSettingsList | ||||
|     private val _shouldReloadSettingsList = MutableStateFlow(false) | ||||
|  | ||||
|     private val _shouldReloadSettingsList = MutableLiveData(false) | ||||
|     val shouldReloadSettingsList: LiveData<Boolean> get() = _shouldReloadSettingsList | ||||
|     val isUsingSearch: StateFlow<Boolean> get() = _isUsingSearch | ||||
|     private val _isUsingSearch = MutableStateFlow(false) | ||||
|  | ||||
|     private val _isUsingSearch = MutableLiveData(false) | ||||
|     val isUsingSearch: LiveData<Boolean> get() = _isUsingSearch | ||||
|     val sliderProgress: StateFlow<Int> get() = _sliderProgress | ||||
|     private val _sliderProgress = MutableStateFlow(-1) | ||||
|  | ||||
|     val sliderProgress = savedStateHandle.getStateFlow(KEY_SLIDER_PROGRESS, -1) | ||||
|     val sliderTextValue: StateFlow<String> get() = _sliderTextValue | ||||
|     private val _sliderTextValue = MutableStateFlow("") | ||||
|  | ||||
|     val sliderTextValue = savedStateHandle.getStateFlow(KEY_SLIDER_TEXT_VALUE, "") | ||||
|  | ||||
|     val adapterItemChanged = savedStateHandle.getStateFlow(KEY_ADAPTER_ITEM_CHANGED, -1) | ||||
|  | ||||
|     fun setToolbarTitle(value: String) { | ||||
|         _toolbarTitle.value = value | ||||
|     } | ||||
|     val adapterItemChanged: StateFlow<Int> get() = _adapterItemChanged | ||||
|     private val _adapterItemChanged = MutableStateFlow(-1) | ||||
|  | ||||
|     fun setShouldRecreate(value: Boolean) { | ||||
|         _shouldRecreate.value = value | ||||
| @@ -67,8 +62,8 @@ class SettingsViewModel(private val savedStateHandle: SavedStateHandle) : ViewMo | ||||
|     } | ||||
|  | ||||
|     fun setSliderTextValue(value: Float, units: String) { | ||||
|         savedStateHandle[KEY_SLIDER_PROGRESS] = value | ||||
|         savedStateHandle[KEY_SLIDER_TEXT_VALUE] = String.format( | ||||
|         _sliderProgress.value = value.toInt() | ||||
|         _sliderTextValue.value = String.format( | ||||
|             YuzuApplication.appContext.getString(R.string.value_with_units), | ||||
|             value.toInt().toString(), | ||||
|             units | ||||
| @@ -76,21 +71,15 @@ class SettingsViewModel(private val savedStateHandle: SavedStateHandle) : ViewMo | ||||
|     } | ||||
|  | ||||
|     fun setSliderProgress(value: Float) { | ||||
|         savedStateHandle[KEY_SLIDER_PROGRESS] = value | ||||
|         _sliderProgress.value = value.toInt() | ||||
|     } | ||||
|  | ||||
|     fun setAdapterItemChanged(value: Int) { | ||||
|         savedStateHandle[KEY_ADAPTER_ITEM_CHANGED] = value | ||||
|         _adapterItemChanged.value = value | ||||
|     } | ||||
|  | ||||
|     fun clear() { | ||||
|         game = null | ||||
|         shouldSave = false | ||||
|     } | ||||
|  | ||||
|     companion object { | ||||
|         const val KEY_SLIDER_TEXT_VALUE = "SliderTextValue" | ||||
|         const val KEY_SLIDER_PROGRESS = "SliderProgress" | ||||
|         const val KEY_ADAPTER_ITEM_CHANGED = "AdapterItemChanged" | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -3,29 +3,25 @@ | ||||
|  | ||||
| package org.yuzu.yuzu_emu.model | ||||
|  | ||||
| import androidx.lifecycle.LiveData | ||||
| import androidx.lifecycle.MutableLiveData | ||||
| import androidx.lifecycle.ViewModel | ||||
| import androidx.lifecycle.viewModelScope | ||||
| import kotlinx.coroutines.Dispatchers | ||||
| import kotlinx.coroutines.flow.MutableStateFlow | ||||
| import kotlinx.coroutines.flow.StateFlow | ||||
| import kotlinx.coroutines.launch | ||||
|  | ||||
| class TaskViewModel : ViewModel() { | ||||
|     private val _result = MutableLiveData<Any>() | ||||
|     val result: LiveData<Any> = _result | ||||
|     val result: StateFlow<Any> get() = _result | ||||
|     private val _result = MutableStateFlow(Any()) | ||||
|  | ||||
|     private val _isComplete = MutableLiveData<Boolean>() | ||||
|     val isComplete: LiveData<Boolean> = _isComplete | ||||
|     val isComplete: StateFlow<Boolean> get() = _isComplete | ||||
|     private val _isComplete = MutableStateFlow(false) | ||||
|  | ||||
|     private val _isRunning = MutableLiveData<Boolean>() | ||||
|     val isRunning: LiveData<Boolean> = _isRunning | ||||
|     val isRunning: StateFlow<Boolean> get() = _isRunning | ||||
|     private val _isRunning = MutableStateFlow(false) | ||||
|  | ||||
|     lateinit var task: () -> Any | ||||
|  | ||||
|     init { | ||||
|         clear() | ||||
|     } | ||||
|  | ||||
|     fun clear() { | ||||
|         _result.value = Any() | ||||
|         _isComplete.value = false | ||||
| @@ -33,15 +29,16 @@ class TaskViewModel : ViewModel() { | ||||
|     } | ||||
|  | ||||
|     fun runTask() { | ||||
|         if (_isRunning.value == true) { | ||||
|         if (isRunning.value) { | ||||
|             return | ||||
|         } | ||||
|         _isRunning.value = true | ||||
|  | ||||
|         viewModelScope.launch(Dispatchers.IO) { | ||||
|             val res = task() | ||||
|             _result.postValue(res) | ||||
|             _isComplete.postValue(true) | ||||
|             _result.value = res | ||||
|             _isComplete.value = true | ||||
|             _isRunning.value = false | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -3,6 +3,7 @@ | ||||
|  | ||||
| package org.yuzu.yuzu_emu.ui | ||||
|  | ||||
| import android.annotation.SuppressLint | ||||
| import android.os.Bundle | ||||
| import android.view.LayoutInflater | ||||
| import android.view.View | ||||
| @@ -14,8 +15,12 @@ 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 com.google.android.material.transition.MaterialFadeThrough | ||||
| import kotlinx.coroutines.launch | ||||
| import org.yuzu.yuzu_emu.R | ||||
| import org.yuzu.yuzu_emu.adapters.GameAdapter | ||||
| import org.yuzu.yuzu_emu.databinding.FragmentGamesBinding | ||||
| @@ -44,6 +49,8 @@ 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?) { | ||||
|         homeViewModel.setNavigationVisibility(visible = true, animated = false) | ||||
|  | ||||
| @@ -80,37 +87,48 @@ class GamesFragment : Fragment() { | ||||
|                 if (_binding == null) { | ||||
|                     return@post | ||||
|                 } | ||||
|                 binding.swipeRefresh.isRefreshing = gamesViewModel.isReloading.value!! | ||||
|                 binding.swipeRefresh.isRefreshing = gamesViewModel.isReloading.value | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         gamesViewModel.apply { | ||||
|             // Watch for when we get updates to any of our games lists | ||||
|             isReloading.observe(viewLifecycleOwner) { isReloading -> | ||||
|                 binding.swipeRefresh.isRefreshing = isReloading | ||||
|             } | ||||
|             games.observe(viewLifecycleOwner) { | ||||
|                 (binding.gridGames.adapter as GameAdapter).submitList(it) | ||||
|                 if (it.isEmpty()) { | ||||
|                     binding.noticeText.visibility = View.VISIBLE | ||||
|                 } else { | ||||
|                     binding.noticeText.visibility = View.GONE | ||||
|         viewLifecycleOwner.lifecycleScope.apply { | ||||
|             launch { | ||||
|                 repeatOnLifecycle(Lifecycle.State.CREATED) { | ||||
|                     gamesViewModel.isReloading.collect { binding.swipeRefresh.isRefreshing = it } | ||||
|                 } | ||||
|             } | ||||
|             shouldSwapData.observe(viewLifecycleOwner) { shouldSwapData -> | ||||
|                 if (shouldSwapData) { | ||||
|                     (binding.gridGames.adapter as GameAdapter).submitList( | ||||
|                         gamesViewModel.games.value!! | ||||
|                     ) | ||||
|                     gamesViewModel.setShouldSwapData(false) | ||||
|             launch { | ||||
|                 repeatOnLifecycle(Lifecycle.State.CREATED) { | ||||
|                     gamesViewModel.games.collect { | ||||
|                         (binding.gridGames.adapter as GameAdapter).submitList(it) | ||||
|                         if (it.isEmpty()) { | ||||
|                             binding.noticeText.visibility = View.VISIBLE | ||||
|                         } else { | ||||
|                             binding.noticeText.visibility = View.GONE | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // Check if the user reselected the games menu item and then scroll to top of the list | ||||
|             shouldScrollToTop.observe(viewLifecycleOwner) { shouldScroll -> | ||||
|                 if (shouldScroll) { | ||||
|                     scrollToTop() | ||||
|                     gamesViewModel.setShouldScrollToTop(false) | ||||
|             launch { | ||||
|                 repeatOnLifecycle(Lifecycle.State.CREATED) { | ||||
|                     gamesViewModel.shouldSwapData.collect { | ||||
|                         if (it) { | ||||
|                             (binding.gridGames.adapter as GameAdapter).submitList( | ||||
|                                 gamesViewModel.games.value | ||||
|                             ) | ||||
|                             gamesViewModel.setShouldSwapData(false) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             launch { | ||||
|                 repeatOnLifecycle(Lifecycle.State.CREATED) { | ||||
|                     gamesViewModel.shouldScrollToTop.collect { | ||||
|                         if (it) { | ||||
|                             scrollToTop() | ||||
|                             gamesViewModel.setShouldScrollToTop(false) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -19,7 +19,9 @@ 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 | ||||
| @@ -115,16 +117,22 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | ||||
|         } | ||||
|  | ||||
|         // Prevents navigation from being drawn for a short time on recreation if set to hidden | ||||
|         if (!homeViewModel.navigationVisible.value?.first!!) { | ||||
|         if (!homeViewModel.navigationVisible.value.first) { | ||||
|             binding.navigationView.visibility = View.INVISIBLE | ||||
|             binding.statusBarShade.visibility = View.INVISIBLE | ||||
|         } | ||||
|  | ||||
|         homeViewModel.navigationVisible.observe(this) { | ||||
|             showNavigation(it.first, it.second) | ||||
|         } | ||||
|         homeViewModel.statusBarShadeVisible.observe(this) { visible -> | ||||
|             showStatusBarShade(visible) | ||||
|         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) } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Dismiss previous notifications (should not happen unless a crash occurred) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Charles Lombardo
					Charles Lombardo