mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-04-10 03:18:55 +02:00
[android,ui] fix addons list not refresh upon rotation (#3687)
Fixes a bug reported by Pavel, in which an RC as returning empty patchList after phone rotated. Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3687 Reviewed-by: DraVee <chimera@dravee.dev> Reviewed-by: MaranBr <maranbr@eden-emu.dev> Co-authored-by: xbzk <xbzk@eden-emu.dev> Co-committed-by: xbzk <xbzk@eden-emu.dev>
This commit is contained in:
parent
afec66f598
commit
9423a33fc2
3 changed files with 67 additions and 24 deletions
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.fragments
|
||||
|
|
@ -19,7 +19,6 @@ import androidx.navigation.findNavController
|
|||
import androidx.navigation.fragment.navArgs
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import kotlinx.coroutines.launch
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.adapters.AddonAdapter
|
||||
import org.yuzu.yuzu_emu.databinding.FragmentAddonsBinding
|
||||
|
|
@ -42,7 +41,7 @@ class AddonsFragment : Fragment() {
|
|||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
addonViewModel.onOpenAddons(args.game)
|
||||
addonViewModel.onAddonsViewCreated(args.game)
|
||||
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
||||
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
||||
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
||||
|
|
@ -122,13 +121,15 @@ class AddonsFragment : Fragment() {
|
|||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
addonViewModel.refreshAddons()
|
||||
addonViewModel.onAddonsViewStarted(args.game)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
if (activity?.isChangingConfigurations != true) {
|
||||
addonViewModel.onCloseAddons()
|
||||
}
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
val installAddon =
|
||||
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
|
||||
|
|
@ -167,7 +168,7 @@ class AddonsFragment : Fragment() {
|
|||
} catch (_: Exception) {
|
||||
return@newInstance errorMessage
|
||||
}
|
||||
addonViewModel.refreshAddons()
|
||||
addonViewModel.refreshAddons(force = true)
|
||||
return@newInstance getString(R.string.addon_installed_successfully)
|
||||
}.show(parentFragmentManager, ProgressDialogFragment.TAG)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import org.yuzu.yuzu_emu.utils.NativeConfig
|
|||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
class AddonViewModel : ViewModel() {
|
||||
private val _patchList = MutableStateFlow(mutableListOf<Patch>())
|
||||
private val _patchList = MutableStateFlow<List<Patch>>(emptyList())
|
||||
val addonList get() = _patchList.asStateFlow()
|
||||
|
||||
private val _showModInstallPicker = MutableStateFlow(false)
|
||||
|
|
@ -31,34 +31,62 @@ class AddonViewModel : ViewModel() {
|
|||
val addonToDelete = _addonToDelete.asStateFlow()
|
||||
|
||||
var game: Game? = null
|
||||
private var loadedGameKey: String? = null
|
||||
|
||||
private val isRefreshing = AtomicBoolean(false)
|
||||
private val pendingRefresh = AtomicBoolean(false)
|
||||
|
||||
fun onOpenAddons(game: Game) {
|
||||
fun onAddonsViewCreated(game: Game) {
|
||||
this.game = game
|
||||
refreshAddons()
|
||||
refreshAddons(commitEmpty = false)
|
||||
}
|
||||
|
||||
fun refreshAddons() {
|
||||
if (isRefreshing.get() || game == null) {
|
||||
fun onAddonsViewStarted(game: Game) {
|
||||
this.game = game
|
||||
val hasLoadedCurrentGame = loadedGameKey == gameKey(game)
|
||||
refreshAddons(force = !hasLoadedCurrentGame)
|
||||
}
|
||||
|
||||
fun refreshAddons(force: Boolean = false, commitEmpty: Boolean = true) {
|
||||
val currentGame = game ?: return
|
||||
val currentGameKey = gameKey(currentGame)
|
||||
if (!force && loadedGameKey == currentGameKey) {
|
||||
return
|
||||
}
|
||||
isRefreshing.set(true)
|
||||
if (!isRefreshing.compareAndSet(false, true)) {
|
||||
if (force) {
|
||||
pendingRefresh.set(true)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
val patchList = (
|
||||
NativeLibrary.getPatchesForFile(game!!.path, game!!.programId)
|
||||
?: emptyArray()
|
||||
).toMutableList()
|
||||
try {
|
||||
val patches = withContext(Dispatchers.IO) {
|
||||
NativeLibrary.getPatchesForFile(currentGame.path, currentGame.programId)
|
||||
} ?: return@launch
|
||||
|
||||
val patchList = patches.toMutableList()
|
||||
patchList.sortBy { it.name }
|
||||
|
||||
// Ensure only one update is enabled
|
||||
ensureSingleUpdateEnabled(patchList)
|
||||
|
||||
removeDuplicates(patchList)
|
||||
if (patchList.isEmpty() && !commitEmpty) {
|
||||
return@launch
|
||||
}
|
||||
if (gameKey(game ?: return@launch) != currentGameKey) {
|
||||
return@launch
|
||||
}
|
||||
|
||||
_patchList.value = patchList
|
||||
_patchList.value = patchList.toList()
|
||||
loadedGameKey = currentGameKey
|
||||
} finally {
|
||||
isRefreshing.set(false)
|
||||
if (pendingRefresh.compareAndSet(true, false)) {
|
||||
refreshAddons(force = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -119,17 +147,26 @@ class AddonViewModel : ViewModel() {
|
|||
PatchType.DLC -> NativeLibrary.removeDLC(patch.programId)
|
||||
PatchType.Mod -> NativeLibrary.removeMod(patch.programId, patch.name)
|
||||
}
|
||||
refreshAddons()
|
||||
refreshAddons(force = true)
|
||||
}
|
||||
|
||||
fun onCloseAddons() {
|
||||
if (_patchList.value.isEmpty()) {
|
||||
val currentGame = game ?: run {
|
||||
_patchList.value = emptyList()
|
||||
loadedGameKey = null
|
||||
return
|
||||
}
|
||||
val currentList = _patchList.value
|
||||
if (currentList.isEmpty()) {
|
||||
_patchList.value = emptyList()
|
||||
loadedGameKey = null
|
||||
game = null
|
||||
return
|
||||
}
|
||||
|
||||
NativeConfig.setDisabledAddons(
|
||||
game!!.programId,
|
||||
_patchList.value.mapNotNull {
|
||||
currentGame.programId,
|
||||
currentList.mapNotNull {
|
||||
if (it.enabled) {
|
||||
null
|
||||
} else {
|
||||
|
|
@ -148,7 +185,8 @@ class AddonViewModel : ViewModel() {
|
|||
}.toTypedArray()
|
||||
)
|
||||
NativeConfig.saveGlobalConfig()
|
||||
_patchList.value.clear()
|
||||
_patchList.value = emptyList()
|
||||
loadedGameKey = null
|
||||
game = null
|
||||
}
|
||||
|
||||
|
|
@ -159,4 +197,8 @@ class AddonViewModel : ViewModel() {
|
|||
fun showModNoticeDialog(show: Boolean) {
|
||||
_showModNoticeDialog.value = show
|
||||
}
|
||||
|
||||
private fun gameKey(game: Game): String {
|
||||
return "${game.programId}|${game.path}"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -642,7 +642,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||
}
|
||||
}
|
||||
|
||||
addonViewModel.refreshAddons()
|
||||
addonViewModel.refreshAddons(force = true)
|
||||
|
||||
val separator = System.lineSeparator() ?: "\n"
|
||||
val installResult = StringBuilder()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue