mirror of
				https://git.suyu.dev/suyu/suyu
				synced 2025-11-04 08:59:03 -06:00 
			
		
		
		
	android: Game data cache
This commit is contained in:
		@@ -2,6 +2,7 @@ plugins {
 | 
			
		||||
    id("com.android.application")
 | 
			
		||||
    id("org.jetbrains.kotlin.android")
 | 
			
		||||
    id("kotlin-parcelize")
 | 
			
		||||
    kotlin("plugin.serialization") version "1.8.21"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -164,6 +165,7 @@ dependencies {
 | 
			
		||||
    implementation("androidx.navigation:navigation-fragment-ktx:2.5.3")
 | 
			
		||||
    implementation("androidx.navigation:navigation-ui-ktx:2.5.3")
 | 
			
		||||
    implementation("info.debatty:java-string-similarity:2.0.0")
 | 
			
		||||
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun getVersion(): String {
 | 
			
		||||
 
 | 
			
		||||
@@ -100,7 +100,6 @@ class GameAdapter(private val activity: AppCompatActivity) :
 | 
			
		||||
            return oldItem.gameId == newItem.gameId
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @SuppressLint("DiffUtilEquals")
 | 
			
		||||
        override fun areContentsTheSame(oldItem: Game, newItem: Game): Boolean {
 | 
			
		||||
            return oldItem == newItem
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -5,9 +5,11 @@ package org.yuzu.yuzu_emu.model
 | 
			
		||||
 | 
			
		||||
import android.os.Parcelable
 | 
			
		||||
import kotlinx.parcelize.Parcelize
 | 
			
		||||
import kotlinx.serialization.Serializable
 | 
			
		||||
import java.util.HashSet
 | 
			
		||||
 | 
			
		||||
@Parcelize
 | 
			
		||||
@Serializable
 | 
			
		||||
class Game(
 | 
			
		||||
    val title: String,
 | 
			
		||||
    val description: String,
 | 
			
		||||
@@ -19,6 +21,18 @@ class Game(
 | 
			
		||||
    val keyAddedToLibraryTime get() = "${gameId}_AddedToLibraryTime"
 | 
			
		||||
    val keyLastPlayedTime get() = "${gameId}_LastPlayed"
 | 
			
		||||
 | 
			
		||||
    override fun equals(other: Any?): Boolean {
 | 
			
		||||
        if (other !is Game)
 | 
			
		||||
            return false
 | 
			
		||||
 | 
			
		||||
        return title == other.title
 | 
			
		||||
                && description == other.description
 | 
			
		||||
                && regions == other.regions
 | 
			
		||||
                && path == other.path
 | 
			
		||||
                && gameId == other.gameId
 | 
			
		||||
                && company == other.company
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        val extensions: Set<String> = HashSet(
 | 
			
		||||
            listOf(".xci", ".nsp", ".nca", ".nro")
 | 
			
		||||
 
 | 
			
		||||
@@ -7,11 +7,16 @@ import androidx.lifecycle.LiveData
 | 
			
		||||
import androidx.lifecycle.MutableLiveData
 | 
			
		||||
import androidx.lifecycle.ViewModel
 | 
			
		||||
import androidx.lifecycle.viewModelScope
 | 
			
		||||
import androidx.preference.PreferenceManager
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
import kotlinx.coroutines.withContext
 | 
			
		||||
import kotlinx.serialization.decodeFromString
 | 
			
		||||
import kotlinx.serialization.json.Json
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary
 | 
			
		||||
import org.yuzu.yuzu_emu.YuzuApplication
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.GameHelper
 | 
			
		||||
import java.util.Locale
 | 
			
		||||
 | 
			
		||||
class GamesViewModel : ViewModel() {
 | 
			
		||||
    private val _games = MutableLiveData<List<Game>>(emptyList())
 | 
			
		||||
@@ -33,9 +38,30 @@ class GamesViewModel : ViewModel() {
 | 
			
		||||
    val searchFocused: LiveData<Boolean> get() = _searchFocused
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        // Retrieve list of cached games
 | 
			
		||||
        val storedGames = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
 | 
			
		||||
            .getStringSet(GameHelper.KEY_GAMES, emptySet())
 | 
			
		||||
        if (storedGames!!.isNotEmpty()) {
 | 
			
		||||
            val deserializedGames = mutableSetOf<Game>()
 | 
			
		||||
            storedGames.forEach {
 | 
			
		||||
                deserializedGames.add(Json.decodeFromString(it))
 | 
			
		||||
            }
 | 
			
		||||
            setGames(deserializedGames.toList())
 | 
			
		||||
        }
 | 
			
		||||
        reloadGames(false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun setGames(games: List<Game>) {
 | 
			
		||||
        val sortedList = games.sortedWith(
 | 
			
		||||
            compareBy(
 | 
			
		||||
                { it.title.lowercase(Locale.getDefault()) },
 | 
			
		||||
                { it.path }
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        _games.postValue(sortedList)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun setSearchedGames(games: List<Game>) {
 | 
			
		||||
        _searchedGames.postValue(games)
 | 
			
		||||
    }
 | 
			
		||||
@@ -60,7 +86,7 @@ class GamesViewModel : ViewModel() {
 | 
			
		||||
        viewModelScope.launch {
 | 
			
		||||
            withContext(Dispatchers.IO) {
 | 
			
		||||
                NativeLibrary.resetRomMetadata()
 | 
			
		||||
                _games.postValue(GameHelper.getGames())
 | 
			
		||||
                setGames(GameHelper.getGames())
 | 
			
		||||
                _isReloading.postValue(false)
 | 
			
		||||
 | 
			
		||||
                if (directoryChanged) {
 | 
			
		||||
 
 | 
			
		||||
@@ -20,10 +20,8 @@ import org.yuzu.yuzu_emu.R
 | 
			
		||||
import org.yuzu.yuzu_emu.adapters.GameAdapter
 | 
			
		||||
import org.yuzu.yuzu_emu.databinding.FragmentGamesBinding
 | 
			
		||||
import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager
 | 
			
		||||
import org.yuzu.yuzu_emu.model.Game
 | 
			
		||||
import org.yuzu.yuzu_emu.model.GamesViewModel
 | 
			
		||||
import org.yuzu.yuzu_emu.model.HomeViewModel
 | 
			
		||||
import java.util.Locale
 | 
			
		||||
 | 
			
		||||
class GamesFragment : Fragment() {
 | 
			
		||||
    private var _binding: FragmentGamesBinding? = null
 | 
			
		||||
@@ -81,7 +79,7 @@ class GamesFragment : Fragment() {
 | 
			
		||||
            binding.swipeRefresh.isRefreshing = isReloading
 | 
			
		||||
        }
 | 
			
		||||
        gamesViewModel.games.observe(viewLifecycleOwner) {
 | 
			
		||||
            submitGamesList(it)
 | 
			
		||||
            (binding.gridGames.adapter as GameAdapter).submitList(it)
 | 
			
		||||
            if (it.isEmpty()) {
 | 
			
		||||
                binding.noticeText.visibility = View.VISIBLE
 | 
			
		||||
            } else {
 | 
			
		||||
@@ -91,7 +89,7 @@ class GamesFragment : Fragment() {
 | 
			
		||||
 | 
			
		||||
        gamesViewModel.shouldSwapData.observe(viewLifecycleOwner) { shouldSwapData ->
 | 
			
		||||
            if (shouldSwapData) {
 | 
			
		||||
                submitGamesList(gamesViewModel.games.value!!)
 | 
			
		||||
                (binding.gridGames.adapter as GameAdapter).submitList(gamesViewModel.games.value!!)
 | 
			
		||||
                gamesViewModel.setShouldSwapData(false)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -115,11 +113,6 @@ class GamesFragment : Fragment() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun submitGamesList(gameList: List<Game>) {
 | 
			
		||||
        val sortedList = gameList.sortedBy { it.title.lowercase(Locale.getDefault()) }
 | 
			
		||||
        (binding.gridGames.adapter as GameAdapter).submitList(sortedList)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onDestroyView() {
 | 
			
		||||
        super.onDestroyView()
 | 
			
		||||
        _binding = null
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,6 @@ import androidx.navigation.ui.setupWithNavController
 | 
			
		||||
import androidx.preference.PreferenceManager
 | 
			
		||||
import com.google.android.material.color.MaterialColors
 | 
			
		||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
 | 
			
		||||
import com.google.android.material.elevation.ElevationOverlayProvider
 | 
			
		||||
import com.google.android.material.navigation.NavigationBarView
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
 
 | 
			
		||||
@@ -6,19 +6,22 @@ package org.yuzu.yuzu_emu.utils
 | 
			
		||||
import android.content.SharedPreferences
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import androidx.preference.PreferenceManager
 | 
			
		||||
import kotlinx.serialization.decodeFromString
 | 
			
		||||
import kotlinx.serialization.encodeToString
 | 
			
		||||
import kotlinx.serialization.json.Json
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary
 | 
			
		||||
import org.yuzu.yuzu_emu.YuzuApplication
 | 
			
		||||
import org.yuzu.yuzu_emu.model.Game
 | 
			
		||||
import java.util.*
 | 
			
		||||
import kotlin.collections.ArrayList
 | 
			
		||||
 | 
			
		||||
object GameHelper {
 | 
			
		||||
    const val KEY_GAME_PATH = "game_path"
 | 
			
		||||
    const val KEY_GAMES = "Games"
 | 
			
		||||
 | 
			
		||||
    private lateinit var preferences: SharedPreferences
 | 
			
		||||
 | 
			
		||||
    fun getGames(): ArrayList<Game> {
 | 
			
		||||
        val games = ArrayList<Game>()
 | 
			
		||||
    fun getGames(): List<Game> {
 | 
			
		||||
        val games = mutableListOf<Game>()
 | 
			
		||||
        val context = YuzuApplication.appContext
 | 
			
		||||
        val gamesDir =
 | 
			
		||||
            PreferenceManager.getDefaultSharedPreferences(context).getString(KEY_GAME_PATH, "")
 | 
			
		||||
@@ -44,7 +47,17 @@ object GameHelper {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return games
 | 
			
		||||
        // Cache list of games found on disk
 | 
			
		||||
        val serializedGames = mutableSetOf<String>()
 | 
			
		||||
        games.forEach {
 | 
			
		||||
            serializedGames.add(Json.encodeToString(it))
 | 
			
		||||
        }
 | 
			
		||||
        preferences.edit()
 | 
			
		||||
            .remove(KEY_GAMES)
 | 
			
		||||
            .putStringSet(KEY_GAMES, serializedGames)
 | 
			
		||||
            .apply()
 | 
			
		||||
 | 
			
		||||
        return games.toList()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getGame(filePath: String): Game {
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
            android:gravity="center"
 | 
			
		||||
            android:padding="@dimen/spacing_large"
 | 
			
		||||
            android:text="@string/empty_gamelist"
 | 
			
		||||
            tools:visibility="gone" />
 | 
			
		||||
            android:visibility="gone" />
 | 
			
		||||
 | 
			
		||||
        <androidx.recyclerview.widget.RecyclerView
 | 
			
		||||
            android:id="@+id/grid_games"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user