fix: ordering of search history

This commit is contained in:
rebel onion 2025-01-03 09:54:29 -06:00
parent 7b8af6ea8a
commit 0d365d55c5
4 changed files with 95 additions and 31 deletions

View file

@ -10,52 +10,70 @@ import ani.dantotsu.R
import ani.dantotsu.connections.anilist.AnilistSearch.SearchType import ani.dantotsu.connections.anilist.AnilistSearch.SearchType
import ani.dantotsu.databinding.ItemSearchHistoryBinding import ani.dantotsu.databinding.ItemSearchHistoryBinding
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefManager.asLiveStringSet import ani.dantotsu.settings.saving.PrefManager.asLiveClass
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.settings.saving.SharedPreferenceStringSetLiveData import ani.dantotsu.settings.saving.SharedPreferenceClassLiveData
import java.io.Serializable
data class SearchHistory(val search: String, val time: Long) : Serializable {
companion object {
private const val serialVersionUID = 1L
}
}
class SearchHistoryAdapter(type: SearchType, private val searchClicked: (String) -> Unit) : class SearchHistoryAdapter(type: SearchType, private val searchClicked: (String) -> Unit) :
ListAdapter<String, SearchHistoryAdapter.SearchHistoryViewHolder>( ListAdapter<String, SearchHistoryAdapter.SearchHistoryViewHolder>(
DIFF_CALLBACK_INSTALLED DIFF_CALLBACK_INSTALLED
) { ) {
private var searchHistoryLiveData: SharedPreferenceStringSetLiveData? = null private var searchHistoryLiveData: SharedPreferenceClassLiveData<List<SearchHistory>>? = null
private var searchHistory: MutableSet<String>? = null private var searchHistory: MutableList<SearchHistory>? = null
private var historyType: PrefName = when (type) { private var historyType: PrefName = when (type) {
SearchType.ANIME -> PrefName.AnimeSearchHistory SearchType.ANIME -> PrefName.SortedAnimeSH
SearchType.MANGA -> PrefName.MangaSearchHistory SearchType.MANGA -> PrefName.SortedMangaSH
SearchType.CHARACTER -> PrefName.CharacterSearchHistory SearchType.CHARACTER -> PrefName.SortedCharacterSH
SearchType.STAFF -> PrefName.StaffSearchHistory SearchType.STAFF -> PrefName.SortedStaffSH
SearchType.STUDIO -> PrefName.StudioSearchHistory SearchType.STUDIO -> PrefName.SortedStudioSH
SearchType.USER -> PrefName.UserSearchHistory SearchType.USER -> PrefName.SortedUserSH
} }
private fun MutableList<SearchHistory>?.sorted(): List<String>? =
this?.sortedByDescending { it.time }?.map { it.search }
init { init {
searchHistoryLiveData = searchHistoryLiveData =
PrefManager.getLiveVal(historyType, mutableSetOf<String>()).asLiveStringSet() PrefManager.getLiveVal(historyType, mutableListOf<SearchHistory>()).asLiveClass()
searchHistoryLiveData?.observeForever { searchHistoryLiveData?.observeForever { data ->
searchHistory = it.toMutableSet() searchHistory = data.toMutableList()
submitList(searchHistory?.toList()) submitList(searchHistory?.sorted())
} }
} }
fun remove(item: String) { fun remove(item: String) {
searchHistory?.remove(item) searchHistory?.let { list ->
list.removeAll { it.search == item }
}
PrefManager.setVal(historyType, searchHistory) PrefManager.setVal(historyType, searchHistory)
submitList(searchHistory?.toList()) submitList(searchHistory?.sorted())
} }
fun add(item: String) { fun add(item: String) {
if (searchHistory?.contains(item) == true || item.isBlank()) return val maxSize = 25
if (searchHistory?.any { it.search == item } == true || item.isBlank()) return
if (PrefManager.getVal(PrefName.Incognito)) return if (PrefManager.getVal(PrefName.Incognito)) return
searchHistory?.add(item) searchHistory?.add(SearchHistory(item, System.currentTimeMillis()))
submitList(searchHistory?.toList()) if ((searchHistory?.size ?: 0) > maxSize) {
searchHistory?.removeAt(
searchHistory?.sorted()?.lastIndex ?: 0
)
}
submitList(searchHistory?.sorted())
PrefManager.setVal(historyType, searchHistory) PrefManager.setVal(historyType, searchHistory)
} }
fun clearHistory() { fun clearHistory() {
searchHistory?.clear() searchHistory?.clear()
PrefManager.setVal(historyType, searchHistory) PrefManager.setVal(historyType, searchHistory)
submitList(searchHistory?.toList()) submitList(searchHistory?.sorted())
} }
override fun onCreateViewHolder( override fun onCreateViewHolder(

View file

@ -255,9 +255,6 @@ object PrefManager {
return allEntries return allEntries
} }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun <T> getLiveVal(prefName: PrefName, default: T): SharedPreferenceLiveData<T> { fun <T> getLiveVal(prefName: PrefName, default: T): SharedPreferenceLiveData<T> {
val pref = getPrefLocation(prefName.data.prefLocation) val pref = getPrefLocation(prefName.data.prefLocation)
@ -298,7 +295,11 @@ object PrefManager {
default as Set<String> default as Set<String>
) as SharedPreferenceLiveData<T> ) as SharedPreferenceLiveData<T>
else -> throw IllegalArgumentException("Type not supported") else -> SharedPreferenceClassLiveData(
pref,
prefName.name,
default
)
} }
} }
@ -326,6 +327,11 @@ object PrefManager {
this as? SharedPreferenceStringSetLiveData this as? SharedPreferenceStringSetLiveData
?: throw ClassCastException("Cannot cast to SharedPreferenceLiveData<Set<String>>") ?: throw ClassCastException("Cannot cast to SharedPreferenceLiveData<Set<String>>")
@Suppress("UNCHECKED_CAST")
inline fun <reified T> SharedPreferenceLiveData<*>.asLiveClass(): SharedPreferenceClassLiveData<T> =
this as? SharedPreferenceClassLiveData<T>
?: throw ClassCastException("Cannot cast to SharedPreferenceLiveData<T>")
fun getAnimeDownloadPreferences(): SharedPreferences = fun getAnimeDownloadPreferences(): SharedPreferences =
animeDownloadsPreferences!! //needs to be used externally animeDownloadsPreferences!! //needs to be used externally

View file

@ -3,12 +3,13 @@ package ani.dantotsu.settings.saving
import android.graphics.Color import android.graphics.Color
import ani.dantotsu.connections.comments.AuthResponse import ani.dantotsu.connections.comments.AuthResponse
import ani.dantotsu.connections.mal.MAL import ani.dantotsu.connections.mal.MAL
import ani.dantotsu.media.SearchHistory
import ani.dantotsu.notifications.comment.CommentStore import ani.dantotsu.notifications.comment.CommentStore
import ani.dantotsu.notifications.subscription.SubscriptionStore import ani.dantotsu.notifications.subscription.SubscriptionStore
import ani.dantotsu.settings.saving.internal.Location import ani.dantotsu.settings.saving.internal.Location
import ani.dantotsu.settings.saving.internal.Pref import ani.dantotsu.settings.saving.internal.Pref
enum class PrefName(val data: Pref) { //TODO: Split this into multiple files enum class PrefName(val data: Pref) {
//General //General
SharedUserID(Pref(Location.General, Boolean::class, true)), SharedUserID(Pref(Location.General, Boolean::class, true)),
OfflineView(Pref(Location.General, Int::class, 0)), OfflineView(Pref(Location.General, Int::class, 0)),
@ -34,13 +35,13 @@ enum class PrefName(val data: Pref) { //TODO: Split this into multiple files
MangaExtensionRepos(Pref(Location.General, Set::class, setOf<String>())), MangaExtensionRepos(Pref(Location.General, Set::class, setOf<String>())),
NovelExtensionRepos(Pref(Location.General, Set::class, setOf<String>())), NovelExtensionRepos(Pref(Location.General, Set::class, setOf<String>())),
AnimeSourcesOrder(Pref(Location.General, List::class, listOf<String>())), AnimeSourcesOrder(Pref(Location.General, List::class, listOf<String>())),
AnimeSearchHistory(Pref(Location.General, Set::class, setOf<String>())),
MangaSourcesOrder(Pref(Location.General, List::class, listOf<String>())), MangaSourcesOrder(Pref(Location.General, List::class, listOf<String>())),
MangaSearchHistory(Pref(Location.General, Set::class, setOf<String>())), SortedAnimeSH(Pref(Location.General, List::class, listOf<SearchHistory>())),
CharacterSearchHistory(Pref(Location.General, Set::class, setOf<String>())), SortedMangaSH(Pref(Location.General, List::class, listOf<SearchHistory>())),
StaffSearchHistory(Pref(Location.General, Set::class, setOf<String>())), SortedCharacterSH(Pref(Location.General, List::class, listOf<SearchHistory>())),
StudioSearchHistory(Pref(Location.General, Set::class, setOf<String>())), SortedStaffSH(Pref(Location.General, List::class, listOf<SearchHistory>())),
UserSearchHistory(Pref(Location.General, Set::class, setOf<String>())), SortedStudioSH(Pref(Location.General, List::class, listOf<SearchHistory>())),
SortedUserSH(Pref(Location.General, List::class, listOf<SearchHistory>())),
NovelSourcesOrder(Pref(Location.General, List::class, listOf<String>())), NovelSourcesOrder(Pref(Location.General, List::class, listOf<String>())),
CommentNotificationInterval(Pref(Location.General, Int::class, 0)), CommentNotificationInterval(Pref(Location.General, Int::class, 0)),
AnilistNotificationInterval(Pref(Location.General, Int::class, 3)), AnilistNotificationInterval(Pref(Location.General, Int::class, 3)),

View file

@ -1,7 +1,11 @@
package ani.dantotsu.settings.saving package ani.dantotsu.settings.saving
import android.content.SharedPreferences import android.content.SharedPreferences
import android.util.Base64
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import ani.dantotsu.util.Logger
import java.io.ByteArrayInputStream
import java.io.ObjectInputStream
abstract class SharedPreferenceLiveData<T>( abstract class SharedPreferenceLiveData<T>(
val sharedPrefs: SharedPreferences, val sharedPrefs: SharedPreferences,
@ -78,6 +82,41 @@ class SharedPreferenceStringSetLiveData(
sharedPrefs.getStringSet(key, defValue)?.toSet() ?: defValue sharedPrefs.getStringSet(key, defValue)?.toSet() ?: defValue
} }
@Suppress("UNCHECKED_CAST")
class SharedPreferenceClassLiveData<T>(
sharedPrefs: SharedPreferences,
key: String,
defValue: T
) : SharedPreferenceLiveData<T>(sharedPrefs, key, defValue) {
override fun getValueFromPreferences(key: String, defValue: T): T {
return try {
val serialized = sharedPrefs.getString(key, null)
if (serialized != null) {
val data = Base64.decode(serialized, Base64.DEFAULT)
val bis = ByteArrayInputStream(data)
val ois = ObjectInputStream(bis)
val obj = ois.readObject() as T
obj
} else {
Logger.log("Serialized data is null (key: $key)")
defValue
}
} catch (e: java.io.InvalidClassException) {
Logger.log(e)
try {
sharedPrefs.edit().remove(key).apply()
defValue
} catch (e: Exception) {
Logger.log(e)
defValue
}
} catch (e: Exception) {
Logger.log(e)
defValue
}
}
}
@Suppress("unused") @Suppress("unused")
fun SharedPreferences.intLiveData(key: String, defValue: Int): SharedPreferenceLiveData<Int> { fun SharedPreferences.intLiveData(key: String, defValue: Int): SharedPreferenceLiveData<Int> {
return SharedPreferenceIntLiveData(this, key, defValue) return SharedPreferenceIntLiveData(this, key, defValue)