fix: sorting extensions order

This commit is contained in:
rebelonion 2024-02-04 01:19:34 -06:00
parent 15abcd77d0
commit c2f108bf44
12 changed files with 146 additions and 82 deletions

View file

@ -49,8 +49,8 @@ import ani.dantotsu.settings.CurrentReaderSettings.Companion.applyWebtoon
import ani.dantotsu.settings.CurrentReaderSettings.Directions.* import ani.dantotsu.settings.CurrentReaderSettings.Directions.*
import ani.dantotsu.settings.CurrentReaderSettings.DualPageModes.* import ani.dantotsu.settings.CurrentReaderSettings.DualPageModes.*
import ani.dantotsu.settings.CurrentReaderSettings.Layouts.* import ani.dantotsu.settings.CurrentReaderSettings.Layouts.*
import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.themes.ThemeManager import ani.dantotsu.themes.ThemeManager
import com.alexvasilkov.gestures.views.GestureFrameLayout import com.alexvasilkov.gestures.views.GestureFrameLayout
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
@ -207,27 +207,19 @@ class MangaReaderActivity : AppCompatActivity() {
chapter = chapters[media.manga!!.selectedChapter] ?: return chapter = chapters[media.manga!!.selectedChapter] ?: return
model.mangaReadSources = if (media.isAdult) HMangaSources else MangaSources model.mangaReadSources = if (media.isAdult) HMangaSources else MangaSources
binding.mangaReaderSource.visibility = if (PrefManager.getVal(PrefName.ShowSource)) View.VISIBLE else View.GONE binding.mangaReaderSource.visibility =
if (PrefManager.getVal(PrefName.ShowSource)) View.VISIBLE else View.GONE
if (model.mangaReadSources!!.names.isEmpty()) { if (model.mangaReadSources!!.names.isEmpty()) {
//try to reload sources //try to reload sources
try { try {
if (media.isAdult) { val mangaSources = MangaSources
val mangaSources = MangaSources val scope = lifecycleScope
val scope = lifecycleScope scope.launch(Dispatchers.IO) {
scope.launch(Dispatchers.IO) { mangaSources.init(
mangaSources.init( Injekt.get<MangaExtensionManager>().installedExtensionsFlow
Injekt.get<MangaExtensionManager>().installedExtensionsFlow )
)
}
model.mangaReadSources = mangaSources
} else {
val mangaSources = HMangaSources
val scope = lifecycleScope
scope.launch(Dispatchers.IO) {
mangaSources.init(Injekt.get<MangaExtensionManager>().installedExtensionsFlow)
}
model.mangaReadSources = mangaSources
} }
model.mangaReadSources = mangaSources
} catch (e: Exception) { } catch (e: Exception) {
Firebase.crashlytics.recordException(e) Firebase.crashlytics.recordException(e)
logError(e) logError(e)
@ -260,7 +252,10 @@ class MangaReaderActivity : AppCompatActivity() {
//Chapter Change //Chapter Change
fun change(index: Int) { fun change(index: Int) {
mangaCache.clear() mangaCache.clear()
PrefManager.setCustomVal("${media.id}_${chaptersArr[currentChapterIndex]}", currentChapterPage) PrefManager.setCustomVal(
"${media.id}_${chaptersArr[currentChapterIndex]}",
currentChapterPage
)
ChapterLoaderDialog.newInstance(chapters[chaptersArr[index]]!!) ChapterLoaderDialog.newInstance(chapters[chaptersArr[index]]!!)
.show(supportFragmentManager, "dialog") .show(supportFragmentManager, "dialog")
} }
@ -850,8 +845,11 @@ class MangaReaderActivity : AppCompatActivity() {
private fun progress(runnable: Runnable) { private fun progress(runnable: Runnable) {
if (maxChapterPage - currentChapterPage <= 1 && Anilist.userid != null) { if (maxChapterPage - currentChapterPage <= 1 && Anilist.userid != null) {
showProgressDialog = showProgressDialog =
if (PrefManager.getVal(PrefName.AskIndividualReader)) PrefManager.getCustomVal("${media.id}_progressDialog", true) if (PrefManager.getVal(PrefName.AskIndividualReader)) PrefManager.getCustomVal(
else false "${media.id}_progressDialog",
true
)
else false
val incognito: Boolean = PrefManager.getVal(PrefName.Incognito) val incognito: Boolean = PrefManager.getVal(PrefName.Incognito)
if (showProgressDialog && !incognito) { if (showProgressDialog && !incognito) {
@ -885,9 +883,13 @@ class MangaReaderActivity : AppCompatActivity() {
.create() .create()
.show() .show()
} else { } else {
if (!incognito && PrefManager.getCustomVal("${media.id}_save_progress", true) && if (media.isAdult) PrefManager.getVal<Boolean>(PrefName.UpdateForHReader) else true) if (!incognito && PrefManager.getCustomVal(
updateProgress( "${media.id}_save_progress",
media, true
) && if (media.isAdult) PrefManager.getVal<Boolean>(PrefName.UpdateForHReader) else true
)
updateProgress(
media,
MangaNameAdapter.findChapterNumber(media.manga!!.selectedChapter!!) MangaNameAdapter.findChapterNumber(media.manga!!.selectedChapter!!)
.toString() .toString()
) )
@ -900,7 +902,11 @@ class MangaReaderActivity : AppCompatActivity() {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
private fun <T> loadReaderSettings(fileName: String, context: Context? = null, toast: Boolean = true): T? { private fun <T> loadReaderSettings(
fileName: String,
context: Context? = null,
toast: Boolean = true
): T? {
val a = context ?: currContext() val a = context ?: currContext()
try { try {
if (a?.fileList() != null) if (a?.fileList() != null)

View file

@ -4,21 +4,16 @@ import android.content.Context
import ani.dantotsu.Lazier import ani.dantotsu.Lazier
import ani.dantotsu.lazyList import ani.dantotsu.lazyList
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefManager.asLiveString
import ani.dantotsu.settings.saving.PrefManager.asLiveStringSet
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
object AnimeSources : WatchSources() { object AnimeSources : WatchSources() {
override var list: List<Lazier<BaseParser>> = emptyList() override var list: List<Lazier<BaseParser>> = emptyList()
var pinnedAnimeSources: List<String> = emptyList() var pinnedAnimeSources: List<String> = emptyList()
suspend fun init(fromExtensions: StateFlow<List<AnimeExtension.Installed>>, context: Context) { suspend fun init(fromExtensions: StateFlow<List<AnimeExtension.Installed>>) {
pinnedAnimeSources = PrefManager.getNullableVal<List<String>>(PrefName.AnimeSourcesOrder, null) pinnedAnimeSources = PrefManager.getNullableVal<List<String>>(PrefName.AnimeSourcesOrder, null)
?: emptyList() ?: emptyList()
@ -58,17 +53,15 @@ object AnimeSources : WatchSources() {
} }
private fun sortPinnedAnimeSources( private fun sortPinnedAnimeSources(
Sources: List<Lazier<BaseParser>>, sources: List<Lazier<BaseParser>>,
pinnedAnimeSources: List<String> pinnedAnimeSources: List<String>
): List<Lazier<BaseParser>> { ): List<Lazier<BaseParser>> {
val pinnedSourcesMap = Sources.filter { pinnedAnimeSources.contains(it.name) } val pinnedSourcesMap = sources.filter { pinnedAnimeSources.contains(it.name) }
.associateBy { it.name } .associateBy { it.name }
val orderedPinnedSources = pinnedAnimeSources.mapNotNull { name -> val orderedPinnedSources = pinnedAnimeSources.mapNotNull { name ->
pinnedSourcesMap[name] pinnedSourcesMap[name]
} }
//find the unpinned sources val unpinnedSources = sources.filterNot { pinnedAnimeSources.contains(it.name) }
val unpinnedSources = Sources.filter { !pinnedAnimeSources.contains(it.name) }
//put the pinned sources at the top of the list
return orderedPinnedSources + unpinnedSources return orderedPinnedSources + unpinnedSources
} }
} }

View file

@ -52,26 +52,20 @@ object MangaSources : MangaReadSources() {
} }
private fun sortPinnedMangaSources( private fun sortPinnedMangaSources(
Sources: List<Lazier<BaseParser>>, sources: List<Lazier<BaseParser>>,
pinnedMangaSources: List<String> pinnedMangaSources: List<String>
): List<Lazier<BaseParser>> { ): List<Lazier<BaseParser>> {
val pinnedSourcesMap = Sources.filter { pinnedMangaSources.contains(it.name) } val pinnedSourcesMap = sources.filter { pinnedMangaSources.contains(it.name) }
.associateBy { it.name } .associateBy { it.name }
val orderedPinnedSources = pinnedMangaSources.mapNotNull { name -> val orderedPinnedSources = pinnedMangaSources.mapNotNull { name ->
pinnedSourcesMap[name] pinnedSourcesMap[name]
} }
//find the unpinned sources val unpinnedSources = sources.filterNot { pinnedMangaSources.contains(it.name) }
val unpinnedSources = Sources.filter { !pinnedMangaSources.contains(it.name) }
//put the pinned sources at the top of the list
return orderedPinnedSources + unpinnedSources return orderedPinnedSources + unpinnedSources
} }
} }
object HMangaSources : MangaReadSources() { object HMangaSources : MangaReadSources() {
val aList: List<Lazier<BaseParser>> = lazyList() private val aList: List<Lazier<BaseParser>> = lazyList()
suspend fun init(fromExtensions: StateFlow<List<MangaExtension.Installed>>) {
//todo
}
override val list = listOf(aList, MangaSources.list).flatten() override val list = listOf(aList, MangaSources.list).flatten()
} }

View file

@ -4,13 +4,19 @@ import android.util.Log
import ani.dantotsu.Lazier import ani.dantotsu.Lazier
import ani.dantotsu.parsers.novel.DynamicNovelParser import ani.dantotsu.parsers.novel.DynamicNovelParser
import ani.dantotsu.parsers.novel.NovelExtension import ani.dantotsu.parsers.novel.NovelExtension
import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
object NovelSources : NovelReadSources() { object NovelSources : NovelReadSources() {
override var list: List<Lazier<BaseParser>> = emptyList() override var list: List<Lazier<BaseParser>> = emptyList()
var pinnedNovelSources: List<String> = emptyList()
suspend fun init(fromExtensions: StateFlow<List<NovelExtension.Installed>>) { suspend fun init(fromExtensions: StateFlow<List<NovelExtension.Installed>>) {
pinnedNovelSources = PrefManager.getNullableVal<List<String>>(PrefName.NovelSourcesOrder, null)
?: emptyList()
// Initialize with the first value from StateFlow // Initialize with the first value from StateFlow
val initialExtensions = fromExtensions.first() val initialExtensions = fromExtensions.first()
list = createParsersFromExtensions(initialExtensions) + Lazier( list = createParsersFromExtensions(initialExtensions) + Lazier(
@ -20,13 +26,25 @@ object NovelSources : NovelReadSources() {
// Update as StateFlow emits new values // Update as StateFlow emits new values
fromExtensions.collect { extensions -> fromExtensions.collect { extensions ->
list = createParsersFromExtensions(extensions) + Lazier( list = sortPinnedNovelSources(
createParsersFromExtensions(extensions),
pinnedNovelSources
) + Lazier(
{ OfflineNovelParser() }, { OfflineNovelParser() },
"Downloaded" "Downloaded"
) )
} }
} }
fun performReorderNovelSources() {
//remove the downloaded source from the list to avoid duplicates
list = list.filter { it.name != "Downloaded" }
list = sortPinnedNovelSources(list, pinnedNovelSources) + Lazier(
{ OfflineNovelParser() },
"Downloaded"
)
}
private fun createParsersFromExtensions(extensions: List<NovelExtension.Installed>): List<Lazier<BaseParser>> { private fun createParsersFromExtensions(extensions: List<NovelExtension.Installed>): List<Lazier<BaseParser>> {
Log.d("NovelSources", "createParsersFromExtensions") Log.d("NovelSources", "createParsersFromExtensions")
Log.d("NovelSources", extensions.toString()) Log.d("NovelSources", extensions.toString())
@ -35,4 +53,17 @@ object NovelSources : NovelReadSources() {
Lazier({ DynamicNovelParser(extension) }, name) Lazier({ DynamicNovelParser(extension) }, name)
} }
} }
private fun sortPinnedNovelSources(
parsers: List<Lazier<BaseParser>>,
pinnedSources: List<String>
): List<Lazier<BaseParser>> {
val pinnedSourcesMap = parsers.filter { pinnedSources.contains(it.name) }
.associateBy { it.name }
val orderedPinnedSources = pinnedSources.mapNotNull { name ->
pinnedSourcesMap[name]
}
val unpinnedSources = parsers.filterNot { pinnedSources.contains(it.name) }
return orderedPinnedSources + unpinnedSources
}
} }

View file

@ -6,9 +6,7 @@ import android.app.NotificationManager
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.GestureDetector
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.FrameLayout import android.widget.FrameLayout
@ -200,7 +198,7 @@ class InstalledAnimeExtensionsFragment : Fragment(), SearchQueryHandler {
viewHolder: RecyclerView.ViewHolder, viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder target: RecyclerView.ViewHolder
): Boolean { ): Boolean {
extensionsAdapter.onMove(viewHolder.adapterPosition, target.adapterPosition) extensionsAdapter.onMove(viewHolder.absoluteAdapterPosition, target.absoluteAdapterPosition)
return true return true
} }
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {} override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {}
@ -224,10 +222,10 @@ class InstalledAnimeExtensionsFragment : Fragment(), SearchQueryHandler {
lifecycleScope.launch { lifecycleScope.launch {
animeExtensionManager.installedExtensionsFlow.collect { extensions -> animeExtensionManager.installedExtensionsFlow.collect { extensions ->
logger("asdfg: Extensions updated")
extensionsAdapter.updateData(sortToAnimeSourcesList(extensions)) extensionsAdapter.updateData(sortToAnimeSourcesList(extensions))
} }
} }
val extensionsRecyclerView: RecyclerView = binding.allAnimeExtensionsRecyclerView
return binding.root return binding.root
} }
@ -245,7 +243,7 @@ class InstalledAnimeExtensionsFragment : Fragment(), SearchQueryHandler {
} }
override fun updateContentBasedOnQuery(query: String?) { override fun updateContentBasedOnQuery(query: String?) {
extensionsAdapter.filter(query ?: "", animeExtensionManager.installedExtensionsFlow.value) extensionsAdapter.filter(query ?: "", sortToAnimeSourcesList(animeExtensionManager.installedExtensionsFlow.value))
} }
override fun notifyDataChanged() { // Do nothing override fun notifyDataChanged() { // Do nothing
@ -258,7 +256,6 @@ class InstalledAnimeExtensionsFragment : Fragment(), SearchQueryHandler {
) : ListAdapter<AnimeExtension.Installed, AnimeExtensionsAdapter.ViewHolder>( ) : ListAdapter<AnimeExtension.Installed, AnimeExtensionsAdapter.ViewHolder>(
DIFF_CALLBACK_INSTALLED DIFF_CALLBACK_INSTALLED
) { ) {
private val data: MutableList<AnimeExtension.Installed> = mutableListOf() private val data: MutableList<AnimeExtension.Installed> = mutableListOf()
fun updateData(newExtensions: List<AnimeExtension.Installed>) { fun updateData(newExtensions: List<AnimeExtension.Installed>) {
@ -282,7 +279,7 @@ class InstalledAnimeExtensionsFragment : Fragment(), SearchQueryHandler {
return ViewHolder(view) return ViewHolder(view)
} }
@SuppressLint("SetTextI18n", "ClickableViewAccessibility") @SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val extension = getItem(position) // Use getItem() from ListAdapter val extension = getItem(position) // Use getItem() from ListAdapter
val nsfw = if (extension.isNsfw) "(18+)" else "" val nsfw = if (extension.isNsfw) "(18+)" else ""
@ -326,7 +323,6 @@ class InstalledAnimeExtensionsFragment : Fragment(), SearchQueryHandler {
val settingsImageView: ImageView = view.findViewById(R.id.settingsImageView) val settingsImageView: ImageView = view.findViewById(R.id.settingsImageView)
val extensionIconImageView: ImageView = view.findViewById(R.id.extensionIconImageView) val extensionIconImageView: ImageView = view.findViewById(R.id.extensionIconImageView)
val closeTextView: ImageView = view.findViewById(R.id.closeTextView) val closeTextView: ImageView = view.findViewById(R.id.closeTextView)
val card = view.findViewById<View>(R.id.extensionCardView)
} }
companion object { companion object {

View file

@ -8,7 +8,6 @@ import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.FrameLayout import android.widget.FrameLayout
@ -36,7 +35,6 @@ import com.google.android.material.tabs.TabLayout
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import com.google.firebase.crashlytics.FirebaseCrashlytics import com.google.firebase.crashlytics.FirebaseCrashlytics
import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension
import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
import eu.kanade.tachiyomi.extension.manga.model.MangaExtension import eu.kanade.tachiyomi.extension.manga.model.MangaExtension
import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.ConfigurableSource
@ -162,7 +160,7 @@ class InstalledMangaExtensionsFragment : Fragment(), SearchQueryHandler {
context, context,
Notifications.CHANNEL_DOWNLOADER_PROGRESS Notifications.CHANNEL_DOWNLOADER_PROGRESS
) )
.setSmallIcon(androidx.media3.ui.R.drawable.exo_ic_check) .setSmallIcon(R.drawable.ic_check)
.setContentTitle("Update complete") .setContentTitle("Update complete")
.setContentText("The extension has been successfully updated.") .setContentText("The extension has been successfully updated.")
.setPriority(NotificationCompat.PRIORITY_LOW) .setPriority(NotificationCompat.PRIORITY_LOW)
@ -196,7 +194,7 @@ class InstalledMangaExtensionsFragment : Fragment(), SearchQueryHandler {
viewHolder: RecyclerView.ViewHolder, viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder target: RecyclerView.ViewHolder
): Boolean { ): Boolean {
extensionsAdapter.onMove(viewHolder.adapterPosition, target.adapterPosition) extensionsAdapter.onMove(viewHolder.absoluteAdapterPosition, target.absoluteAdapterPosition)
return true return true
} }
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {} override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {}
@ -223,7 +221,6 @@ class InstalledMangaExtensionsFragment : Fragment(), SearchQueryHandler {
extensionsAdapter.updateData(sortToMangaSourcesList(extensions)) extensionsAdapter.updateData(sortToMangaSourcesList(extensions))
} }
} }
val extensionsRecyclerView: RecyclerView = binding.allMangaExtensionsRecyclerView
return binding.root return binding.root
} }
@ -240,7 +237,7 @@ class InstalledMangaExtensionsFragment : Fragment(), SearchQueryHandler {
} }
override fun updateContentBasedOnQuery(query: String?) { override fun updateContentBasedOnQuery(query: String?) {
extensionsAdapter.filter(query ?: "", mangaExtensionManager.installedExtensionsFlow.value) extensionsAdapter.filter(query ?: "", sortToMangaSourcesList(mangaExtensionManager.installedExtensionsFlow.value))
} }
override fun notifyDataChanged() { // Do nothing override fun notifyDataChanged() { // Do nothing
@ -317,7 +314,6 @@ class InstalledMangaExtensionsFragment : Fragment(), SearchQueryHandler {
val settingsImageView: ImageView = view.findViewById(R.id.settingsImageView) val settingsImageView: ImageView = view.findViewById(R.id.settingsImageView)
val extensionIconImageView: ImageView = view.findViewById(R.id.extensionIconImageView) val extensionIconImageView: ImageView = view.findViewById(R.id.extensionIconImageView)
val closeTextView: ImageView = view.findViewById(R.id.closeTextView) val closeTextView: ImageView = view.findViewById(R.id.closeTextView)
val card: View = view.findViewById(R.id.extensionCardView)
} }
companion object { companion object {

View file

@ -1,5 +1,6 @@
package ani.dantotsu.settings package ani.dantotsu.settings
import android.annotation.SuppressLint
import android.app.NotificationManager import android.app.NotificationManager
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
@ -14,6 +15,7 @@ import androidx.core.app.NotificationCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -21,6 +23,7 @@ import ani.dantotsu.R
import ani.dantotsu.currContext import ani.dantotsu.currContext
import ani.dantotsu.databinding.FragmentNovelExtensionsBinding import ani.dantotsu.databinding.FragmentNovelExtensionsBinding
import ani.dantotsu.others.LanguageMapper import ani.dantotsu.others.LanguageMapper
import ani.dantotsu.parsers.NovelSources
import ani.dantotsu.parsers.novel.NovelExtension import ani.dantotsu.parsers.novel.NovelExtension
import ani.dantotsu.parsers.novel.NovelExtensionManager import ani.dantotsu.parsers.novel.NovelExtensionManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
@ -32,6 +35,7 @@ import kotlinx.coroutines.launch
import rx.android.schedulers.AndroidSchedulers import rx.android.schedulers.AndroidSchedulers
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.util.Collections
import java.util.Locale import java.util.Locale
class InstalledNovelExtensionsFragment : Fragment(), SearchQueryHandler { class InstalledNovelExtensionsFragment : Fragment(), SearchQueryHandler {
@ -41,7 +45,7 @@ class InstalledNovelExtensionsFragment : Fragment(), SearchQueryHandler {
private val skipIcons: Boolean = PrefManager.getVal(PrefName.SkipExtensionIcons) private val skipIcons: Boolean = PrefManager.getVal(PrefName.SkipExtensionIcons)
private val novelExtensionManager: NovelExtensionManager = Injekt.get() private val novelExtensionManager: NovelExtensionManager = Injekt.get()
private val extensionsAdapter = NovelExtensionsAdapter( private val extensionsAdapter = NovelExtensionsAdapter(
{ pkg -> { _ ->
Toast.makeText(requireContext(), "Source is not configurable", Toast.LENGTH_SHORT) Toast.makeText(requireContext(), "Source is not configurable", Toast.LENGTH_SHORT)
.show() .show()
}, },
@ -85,7 +89,7 @@ class InstalledNovelExtensionsFragment : Fragment(), SearchQueryHandler {
context, context,
Notifications.CHANNEL_DOWNLOADER_PROGRESS Notifications.CHANNEL_DOWNLOADER_PROGRESS
) )
.setSmallIcon(androidx.media3.ui.R.drawable.exo_ic_check) .setSmallIcon(R.drawable.ic_check)
.setContentTitle("Update complete") .setContentTitle("Update complete")
.setContentText("The extension has been successfully updated.") .setContentText("The extension has been successfully updated.")
.setPriority(NotificationCompat.PRIORITY_LOW) .setPriority(NotificationCompat.PRIORITY_LOW)
@ -112,22 +116,58 @@ class InstalledNovelExtensionsFragment : Fragment(), SearchQueryHandler {
extensionsRecyclerView.layoutManager = LinearLayoutManager(requireContext()) extensionsRecyclerView.layoutManager = LinearLayoutManager(requireContext())
extensionsRecyclerView.adapter = extensionsAdapter extensionsRecyclerView.adapter = extensionsAdapter
val itemTouchHelperCallback = object : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP or ItemTouchHelper.DOWN, 0) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
extensionsAdapter.onMove(viewHolder.absoluteAdapterPosition, target.absoluteAdapterPosition)
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {}
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
super.onSelectedChanged(viewHolder, actionState)
if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
viewHolder?.itemView?.elevation = 8f
viewHolder?.itemView?.translationZ = 8f
}
}
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
super.clearView(recyclerView, viewHolder)
viewHolder.itemView.elevation = 0f
viewHolder.itemView.translationZ = 0f
}
}
ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(extensionsRecyclerView)
lifecycleScope.launch { lifecycleScope.launch {
novelExtensionManager.installedExtensionsFlow.collect { extensions -> novelExtensionManager.installedExtensionsFlow.collect { extensions ->
extensionsAdapter.updateData(extensions) extensionsAdapter.updateData(sortToNovelSourcesList(extensions))
} }
} }
val extensionsRecyclerView: RecyclerView = binding.allNovelExtensionsRecyclerView
return binding.root return binding.root
} }
private fun sortToNovelSourcesList(inpt: List<NovelExtension.Installed>): List<NovelExtension.Installed> {
val sourcesMap = inpt.associateBy { it.name }
val orderedSources = NovelSources.pinnedNovelSources.mapNotNull { name ->
sourcesMap[name]
}
return orderedSources + inpt.filter { !NovelSources.pinnedNovelSources.contains(it.name) }
}
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView();_binding = null super.onDestroyView();_binding = null
} }
override fun updateContentBasedOnQuery(query: String?) { override fun updateContentBasedOnQuery(query: String?) {
extensionsAdapter.filter(query ?: "", novelExtensionManager.installedExtensionsFlow.value) extensionsAdapter.filter(query ?: "", sortToNovelSourcesList(novelExtensionManager.installedExtensionsFlow.value))
} }
override fun notifyDataChanged() { // do nothing override fun notifyDataChanged() { // do nothing
@ -136,16 +176,25 @@ class InstalledNovelExtensionsFragment : Fragment(), SearchQueryHandler {
private class NovelExtensionsAdapter( private class NovelExtensionsAdapter(
private val onSettingsClicked: (NovelExtension.Installed) -> Unit, private val onSettingsClicked: (NovelExtension.Installed) -> Unit,
private val onUninstallClicked: (NovelExtension.Installed, Boolean) -> Unit, private val onUninstallClicked: (NovelExtension.Installed, Boolean) -> Unit,
skipIcons: Boolean val skipIcons: Boolean
) : ListAdapter<NovelExtension.Installed, NovelExtensionsAdapter.ViewHolder>( ) : ListAdapter<NovelExtension.Installed, NovelExtensionsAdapter.ViewHolder>(
DIFF_CALLBACK_INSTALLED DIFF_CALLBACK_INSTALLED
) { ) {
private val data: MutableList<NovelExtension.Installed> = mutableListOf()
val skipIcons = skipIcons
fun updateData(newExtensions: List<NovelExtension.Installed>) { fun updateData(newExtensions: List<NovelExtension.Installed>) {
Log.d("NovelExtensionsAdapter", "updateData: $newExtensions") submitList(newExtensions)
submitList(newExtensions) // Use submitList instead of manual list handling data.clear()
data.addAll(newExtensions)
}
fun onMove(fromPosition: Int, toPosition: Int) {
Collections.swap(data, fromPosition, toPosition)
val map = data.map { it.name }.toList()
PrefManager.setVal(PrefName.NovelSourcesOrder, map)
NovelSources.pinnedNovelSources = map
NovelSources.performReorderNovelSources()
notifyItemMoved(fromPosition, toPosition)
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
@ -155,6 +204,7 @@ class InstalledNovelExtensionsFragment : Fragment(), SearchQueryHandler {
return ViewHolder(view) return ViewHolder(view)
} }
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val extension = getItem(position) // Use getItem() from ListAdapter val extension = getItem(position) // Use getItem() from ListAdapter
val nsfw = "" val nsfw = ""
@ -175,7 +225,7 @@ class InstalledNovelExtensionsFragment : Fragment(), SearchQueryHandler {
holder.settingsImageView.setOnClickListener { holder.settingsImageView.setOnClickListener {
onSettingsClicked(extension) onSettingsClicked(extension)
} }
holder.card.setOnLongClickListener { holder.closeTextView.setOnLongClickListener {
onUninstallClicked(extension, true) onUninstallClicked(extension, true)
true true
} }
@ -198,7 +248,6 @@ class InstalledNovelExtensionsFragment : Fragment(), SearchQueryHandler {
val settingsImageView: ImageView = view.findViewById(R.id.settingsImageView) val settingsImageView: ImageView = view.findViewById(R.id.settingsImageView)
val extensionIconImageView: ImageView = view.findViewById(R.id.extensionIconImageView) val extensionIconImageView: ImageView = view.findViewById(R.id.extensionIconImageView)
val closeTextView: ImageView = view.findViewById(R.id.closeTextView) val closeTextView: ImageView = view.findViewById(R.id.closeTextView)
val card = view.findViewById<View>(R.id.extensionCardView)
} }
companion object { companion object {

View file

@ -15,8 +15,6 @@ import java.io.ObjectOutputStream
object PrefManager { object PrefManager {
private var generalPreferences: SharedPreferences? = null private var generalPreferences: SharedPreferences? = null
private var animePreferences: SharedPreferences? = null
private var mangaPreferences: SharedPreferences? = null
private var playerPreferences: SharedPreferences? = null private var playerPreferences: SharedPreferences? = null
private var readerPreferences: SharedPreferences? = null private var readerPreferences: SharedPreferences? = null
private var irrelevantPreferences: SharedPreferences? = null private var irrelevantPreferences: SharedPreferences? = null

View file

@ -25,6 +25,7 @@ enum class PrefName(val data: Pref) { //TODO: Split this into multiple files
AnimeSearchHistory(Pref(Location.General, Set::class, setOf<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>())), MangaSearchHistory(Pref(Location.General, Set::class, setOf<String>())),
NovelSourcesOrder(Pref(Location.General, List::class, listOf<String>())),
//User Interface //User Interface
UseOLED(Pref(Location.UI, Boolean::class, false)), UseOLED(Pref(Location.UI, Boolean::class, false)),

View file

@ -3,8 +3,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
android:paddingStart="32dp" android:paddingStart="16dp"
android:paddingEnd="32dp"> android:paddingEnd="16dp">
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/allAnimeExtensionsRecyclerView" android:id="@+id/allAnimeExtensionsRecyclerView"

View file

@ -3,8 +3,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
android:paddingStart="32dp" android:paddingStart="16dp"
android:paddingEnd="32dp"> android:paddingEnd="16dp">
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/allMangaExtensionsRecyclerView" android:id="@+id/allMangaExtensionsRecyclerView"

View file

@ -3,8 +3,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
android:paddingStart="32dp" android:paddingStart="16dp"
android:paddingEnd="32dp"> android:paddingEnd="16dp">
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/allNovelExtensionsRecyclerView" android:id="@+id/allNovelExtensionsRecyclerView"