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