From c728eae2ba5f7f06b73330683d4387d22214b72d Mon Sep 17 00:00:00 2001 From: Finnley Somdahl <87634197+rebelonion@users.noreply.github.com> Date: Mon, 4 Dec 2023 00:29:33 -0600 Subject: [PATCH] more searching --- .../java/ani/dantotsu/parsers/BaseParser.kt | 2 +- .../InstalledAnimeExtensionsFragment.kt | 16 ++++- .../InstalledMangaExtensionsFragment.kt | 16 ++++- .../InstalledNovelExtensionsFragment.kt | 17 ++++- .../settings/paging/AnimePagingSource.kt | 64 +++++++++++++++---- .../settings/paging/MangaPagingSource.kt | 64 +++++++++++++++---- .../settings/paging/NovelPagingSource.kt | 37 ++++++++++- .../main/res/layout/activity_extensions.xml | 9 +++ 8 files changed, 190 insertions(+), 35 deletions(-) diff --git a/app/src/main/java/ani/dantotsu/parsers/BaseParser.kt b/app/src/main/java/ani/dantotsu/parsers/BaseParser.kt index a28ef295..03562f68 100644 --- a/app/src/main/java/ani/dantotsu/parsers/BaseParser.kt +++ b/app/src/main/java/ani/dantotsu/parsers/BaseParser.kt @@ -55,7 +55,7 @@ abstract class BaseParser { * Isn't necessary to override, but recommended, if you want to improve auto search results * **/ open suspend fun autoSearch(mediaObj: Media): ShowResponse? { - var response: ShowResponse? = null//loadSavedShowResponse(mediaObj.id) + var response: ShowResponse? = loadSavedShowResponse(mediaObj.id) if (response != null && this !is OfflineMangaParser) { saveShowResponse(mediaObj.id, response, true) } else { diff --git a/app/src/main/java/ani/dantotsu/settings/InstalledAnimeExtensionsFragment.kt b/app/src/main/java/ani/dantotsu/settings/InstalledAnimeExtensionsFragment.kt index 696fb614..b91f649d 100644 --- a/app/src/main/java/ani/dantotsu/settings/InstalledAnimeExtensionsFragment.kt +++ b/app/src/main/java/ani/dantotsu/settings/InstalledAnimeExtensionsFragment.kt @@ -37,8 +37,9 @@ import kotlinx.coroutines.launch import rx.android.schedulers.AndroidSchedulers import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get +import java.util.Locale -class InstalledAnimeExtensionsFragment : Fragment() { +class InstalledAnimeExtensionsFragment : Fragment(), SearchQueryHandler { private var _binding: FragmentAnimeExtensionsBinding? = null @@ -207,6 +208,9 @@ class InstalledAnimeExtensionsFragment : Fragment() { super.onDestroyView();_binding = null } + override fun updateContentBasedOnQuery(query: String?) { + extensionsAdapter.filter(query ?: "", animeExtensionManager.installedExtensionsFlow.value) + } private class AnimeExtensionsAdapter( private val onSettingsClicked: (AnimeExtension.Installed) -> Unit, @@ -248,6 +252,16 @@ class InstalledAnimeExtensionsFragment : Fragment() { } } + fun filter(query: String, currentList: List) { + val filteredList = ArrayList() + for (extension in currentList) { + if (extension.name.lowercase(Locale.ROOT).contains(query.lowercase(Locale.ROOT))) { + filteredList.add(extension) + } + } + submitList(filteredList) + } + inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { val extensionNameTextView: TextView = view.findViewById(R.id.extensionNameTextView) val extensionVersionTextView: TextView = diff --git a/app/src/main/java/ani/dantotsu/settings/InstalledMangaExtensionsFragment.kt b/app/src/main/java/ani/dantotsu/settings/InstalledMangaExtensionsFragment.kt index 73d8192f..8980c569 100644 --- a/app/src/main/java/ani/dantotsu/settings/InstalledMangaExtensionsFragment.kt +++ b/app/src/main/java/ani/dantotsu/settings/InstalledMangaExtensionsFragment.kt @@ -38,8 +38,9 @@ import kotlinx.coroutines.launch import rx.android.schedulers.AndroidSchedulers import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get +import java.util.Locale -class InstalledMangaExtensionsFragment : Fragment() { +class InstalledMangaExtensionsFragment : Fragment(), SearchQueryHandler { private var _binding: FragmentMangaExtensionsBinding? = null private val binding get() = _binding!! private lateinit var extensionsRecyclerView: RecyclerView @@ -187,6 +188,9 @@ class InstalledMangaExtensionsFragment : Fragment() { super.onDestroyView();_binding = null } + override fun updateContentBasedOnQuery(query: String?) { + extensionsAdapter.filter(query ?: "", mangaExtensionManager.installedExtensionsFlow.value) + } private class MangaExtensionsAdapter( private val onSettingsClicked: (MangaExtension.Installed) -> Unit, @@ -230,6 +234,16 @@ class InstalledMangaExtensionsFragment : Fragment() { } } + fun filter(query: String, currentList: List) { + val filteredList = ArrayList() + for (extension in currentList) { + if (extension.name.lowercase(Locale.ROOT).contains(query.lowercase(Locale.ROOT))) { + filteredList.add(extension) + } + } + submitList(filteredList) + } + inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { val extensionNameTextView: TextView = view.findViewById(R.id.extensionNameTextView) val extensionVersionTextView: TextView = diff --git a/app/src/main/java/ani/dantotsu/settings/InstalledNovelExtensionsFragment.kt b/app/src/main/java/ani/dantotsu/settings/InstalledNovelExtensionsFragment.kt index 734bb475..c0a61dd5 100644 --- a/app/src/main/java/ani/dantotsu/settings/InstalledNovelExtensionsFragment.kt +++ b/app/src/main/java/ani/dantotsu/settings/InstalledNovelExtensionsFragment.kt @@ -11,7 +11,6 @@ import android.widget.ImageView import android.widget.TextView import android.widget.Toast import androidx.core.app.NotificationCompat -import androidx.core.view.updateLayoutParams import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.DiffUtil @@ -32,8 +31,9 @@ import kotlinx.coroutines.launch import rx.android.schedulers.AndroidSchedulers import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get +import java.util.Locale -class InstalledNovelExtensionsFragment : Fragment() { +class InstalledNovelExtensionsFragment : Fragment(), SearchQueryHandler { private var _binding: FragmentNovelExtensionsBinding? = null private val binding get() = _binding!! private lateinit var extensionsRecyclerView: RecyclerView @@ -124,6 +124,9 @@ class InstalledNovelExtensionsFragment : Fragment() { super.onDestroyView();_binding = null } + override fun updateContentBasedOnQuery(query: String?) { + extensionsAdapter.filter(query ?: "", novelExtensionManager.installedExtensionsFlow.value) + } private class NovelExtensionsAdapter( private val onSettingsClicked: (NovelExtension.Installed) -> Unit, @@ -169,6 +172,16 @@ class InstalledNovelExtensionsFragment : Fragment() { } } + fun filter(query: String, currentList: List) { + val filteredList = ArrayList() + for (extension in currentList) { + if (extension.name.lowercase(Locale.ROOT).contains(query.lowercase(Locale.ROOT))) { + filteredList.add(extension) + } + } + submitList(filteredList) + } + inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { val extensionNameTextView: TextView = view.findViewById(R.id.extensionNameTextView) val extensionVersionTextView: TextView = diff --git a/app/src/main/java/ani/dantotsu/settings/paging/AnimePagingSource.kt b/app/src/main/java/ani/dantotsu/settings/paging/AnimePagingSource.kt index f535479a..045ca140 100644 --- a/app/src/main/java/ani/dantotsu/settings/paging/AnimePagingSource.kt +++ b/app/src/main/java/ani/dantotsu/settings/paging/AnimePagingSource.kt @@ -2,6 +2,7 @@ package ani.dantotsu.settings.paging import android.view.LayoutInflater import android.view.ViewGroup +import android.view.animation.LinearInterpolator import android.widget.ImageView import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider @@ -15,18 +16,25 @@ import androidx.paging.PagingState import androidx.paging.cachedIn import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView +import ani.dantotsu.R import ani.dantotsu.databinding.ItemExtensionAllBinding import ani.dantotsu.loadData import ani.dantotsu.others.LanguageMapper import com.bumptech.glide.Glide import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext class AnimeExtensionsViewModelFactory( @@ -52,7 +60,13 @@ class AnimeExtensionsViewModel( } @OptIn(ExperimentalCoroutinesApi::class) - val pagerFlow: Flow> = searchQuery.flatMapLatest { query -> + val pagerFlow: Flow> = combine( + animeExtensionManager.availableExtensionsFlow, + animeExtensionManager.installedExtensionsFlow, + searchQuery + ) { available, installed, query -> + Triple(available, installed, query) + }.flatMapLatest { (available, installed, query) -> Pager( PagingConfig( pageSize = 15, @@ -60,27 +74,23 @@ class AnimeExtensionsViewModel( prefetchDistance = 15 ) ) { - AnimeExtensionPagingSource( - animeExtensionManager.availableExtensionsFlow, - animeExtensionManager.installedExtensionsFlow, - searchQuery - ).also { currentPagingSource = it } + AnimeExtensionPagingSource(available, installed, query) }.flow }.cachedIn(viewModelScope) } class AnimeExtensionPagingSource( - private val availableExtensionsFlow: StateFlow>, - private val installedExtensionsFlow: StateFlow>, - private val searchQuery: StateFlow + private val availableExtensionsFlow: List, + private val installedExtensionsFlow: List, + private val searchQuery: String ) : PagingSource() { override suspend fun load(params: LoadParams): LoadResult { val position = params.key ?: 0 - val installedExtensions = installedExtensionsFlow.first().map { it.pkgName }.toSet() + val installedExtensions = installedExtensionsFlow.map { it.pkgName }.toSet() val availableExtensions = - availableExtensionsFlow.first().filterNot { it.pkgName in installedExtensions } - val query = searchQuery.first() + availableExtensionsFlow.filterNot { it.pkgName in installedExtensions } + val query = searchQuery val isNsfwEnabled: Boolean = loadData("NFSWExtension") ?: true val filteredExtensions = if (query.isEmpty()) { @@ -160,11 +170,28 @@ class AnimeExtensionAdapter(private val clickListener: OnAnimeInstallClickListen inner class AnimeExtensionViewHolder(private val binding: ItemExtensionAllBinding) : RecyclerView.ViewHolder(binding.root) { + + private val job = Job() + private val scope = CoroutineScope(Dispatchers.Main + job) + init { binding.closeTextView.setOnClickListener { val extension = getItem(bindingAdapterPosition) if (extension != null) { clickListener.onInstallClick(extension) + binding.closeTextView.setImageResource(R.drawable.ic_sync) + scope.launch { + while (isActive) { + withContext(Dispatchers.Main) { + binding.closeTextView.animate() + .rotationBy(360f) + .setDuration(1000) + .setInterpolator(LinearInterpolator()) + .start() + } + delay(1000) + } + } } } } @@ -177,6 +204,15 @@ class AnimeExtensionAdapter(private val clickListener: OnAnimeInstallClickListen binding.extensionNameTextView.text = extension.name binding.extensionVersionTextView.text = "$lang ${extension.versionName} $nsfw" } + + fun clear() { + job.cancel() // Cancel the coroutine when the view is recycled + } + } + + override fun onViewRecycled(holder: AnimeExtensionViewHolder) { + super.onViewRecycled(holder) + holder.clear() } } diff --git a/app/src/main/java/ani/dantotsu/settings/paging/MangaPagingSource.kt b/app/src/main/java/ani/dantotsu/settings/paging/MangaPagingSource.kt index 9e27f54f..0ae7e01d 100644 --- a/app/src/main/java/ani/dantotsu/settings/paging/MangaPagingSource.kt +++ b/app/src/main/java/ani/dantotsu/settings/paging/MangaPagingSource.kt @@ -2,6 +2,7 @@ package ani.dantotsu.settings.paging import android.view.LayoutInflater import android.view.ViewGroup +import android.view.animation.LinearInterpolator import android.widget.ImageView import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider @@ -15,18 +16,25 @@ import androidx.paging.PagingState import androidx.paging.cachedIn import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView +import ani.dantotsu.R import ani.dantotsu.databinding.ItemExtensionAllBinding import ani.dantotsu.loadData import ani.dantotsu.others.LanguageMapper import com.bumptech.glide.Glide import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager import eu.kanade.tachiyomi.extension.manga.model.MangaExtension +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext class MangaExtensionsViewModelFactory( private val mangaExtensionManager: MangaExtensionManager @@ -51,7 +59,13 @@ class MangaExtensionsViewModel( } @OptIn(ExperimentalCoroutinesApi::class) - val pagerFlow: Flow> = searchQuery.flatMapLatest { query -> + val pagerFlow: Flow> = combine( + mangaExtensionManager.availableExtensionsFlow, + mangaExtensionManager.installedExtensionsFlow, + searchQuery + ) { available, installed, query -> + Triple(available, installed, query) + }.flatMapLatest { (available, installed, query) -> Pager( PagingConfig( pageSize = 15, @@ -59,28 +73,24 @@ class MangaExtensionsViewModel( prefetchDistance = 15 ) ) { - MangaExtensionPagingSource( - mangaExtensionManager.availableExtensionsFlow, - mangaExtensionManager.installedExtensionsFlow, - searchQuery - ).also { currentPagingSource = it } + MangaExtensionPagingSource(available, installed, query) }.flow }.cachedIn(viewModelScope) } class MangaExtensionPagingSource( - private val availableExtensionsFlow: StateFlow>, - private val installedExtensionsFlow: StateFlow>, - private val searchQuery: StateFlow + private val availableExtensionsFlow: List, + private val installedExtensionsFlow: List, + private val searchQuery: String ) : PagingSource() { override suspend fun load(params: LoadParams): LoadResult { val position = params.key ?: 0 - val installedExtensions = installedExtensionsFlow.first().map { it.pkgName }.toSet() + val installedExtensions = installedExtensionsFlow.map { it.pkgName }.toSet() val availableExtensions = - availableExtensionsFlow.first().filterNot { it.pkgName in installedExtensions } - val query = searchQuery.first() + availableExtensionsFlow.filterNot { it.pkgName in installedExtensions } + val query = searchQuery val isNsfwEnabled: Boolean = loadData("NFSWExtension") ?: true val filteredExtensions = if (query.isEmpty()) { availableExtensions @@ -157,11 +167,28 @@ class MangaExtensionAdapter(private val clickListener: OnMangaInstallClickListen inner class MangaExtensionViewHolder(private val binding: ItemExtensionAllBinding) : RecyclerView.ViewHolder(binding.root) { + + private val job = Job() + private val scope = CoroutineScope(Dispatchers.Main + job) + init { binding.closeTextView.setOnClickListener { val extension = getItem(bindingAdapterPosition) if (extension != null) { clickListener.onInstallClick(extension) + binding.closeTextView.setImageResource(R.drawable.ic_sync) + scope.launch { + while (isActive) { + withContext(Dispatchers.Main) { + binding.closeTextView.animate() + .rotationBy(360f) + .setDuration(1000) + .setInterpolator(LinearInterpolator()) + .start() + } + delay(1000) + } + } } } } @@ -173,6 +200,15 @@ class MangaExtensionAdapter(private val clickListener: OnMangaInstallClickListen binding.extensionNameTextView.text = extension.name binding.extensionVersionTextView.text = "$lang ${extension.versionName} $nsfw" } + + fun clear() { + job.cancel() // Cancel the coroutine when the view is recycled + } + } + + override fun onViewRecycled(holder: MangaExtensionViewHolder) { + super.onViewRecycled(holder) + holder.clear() } } diff --git a/app/src/main/java/ani/dantotsu/settings/paging/NovelPagingSource.kt b/app/src/main/java/ani/dantotsu/settings/paging/NovelPagingSource.kt index e8483c62..82585ce0 100644 --- a/app/src/main/java/ani/dantotsu/settings/paging/NovelPagingSource.kt +++ b/app/src/main/java/ani/dantotsu/settings/paging/NovelPagingSource.kt @@ -2,6 +2,7 @@ package ani.dantotsu.settings.paging import android.view.LayoutInflater import android.view.ViewGroup +import android.view.animation.LinearInterpolator import android.widget.ImageView import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider @@ -15,19 +16,25 @@ import androidx.paging.PagingState import androidx.paging.cachedIn import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView +import ani.dantotsu.R import ani.dantotsu.databinding.ItemExtensionAllBinding import ani.dantotsu.loadData import ani.dantotsu.others.LanguageMapper import ani.dantotsu.parsers.novel.NovelExtension import ani.dantotsu.parsers.novel.NovelExtensionManager import com.bumptech.glide.Glide +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext class NovelExtensionsViewModelFactory( @@ -163,11 +170,28 @@ class NovelExtensionAdapter(private val clickListener: OnNovelInstallClickListen inner class NovelExtensionViewHolder(private val binding: ItemExtensionAllBinding) : RecyclerView.ViewHolder(binding.root) { + + private val job = Job() + private val scope = CoroutineScope(Dispatchers.Main + job) + init { binding.closeTextView.setOnClickListener { val extension = getItem(bindingAdapterPosition) if (extension != null) { clickListener.onInstallClick(extension) + binding.closeTextView.setImageResource(R.drawable.ic_sync) + scope.launch { + while (isActive) { + withContext(Dispatchers.Main) { + binding.closeTextView.animate() + .rotationBy(360f) + .setDuration(1000) + .setInterpolator(LinearInterpolator()) + .start() + } + delay(1000) + } + } } } } @@ -179,6 +203,15 @@ class NovelExtensionAdapter(private val clickListener: OnNovelInstallClickListen binding.extensionNameTextView.text = extension.name binding.extensionVersionTextView.text = "$lang ${extension.versionName} $nsfw" } + + fun clear() { + job.cancel() // Cancel the coroutine when the view is recycled + } + } + + override fun onViewRecycled(holder: NovelExtensionViewHolder) { + super.onViewRecycled(holder) + holder.clear() } } diff --git a/app/src/main/res/layout/activity_extensions.xml b/app/src/main/res/layout/activity_extensions.xml index 5197e60b..7d99ddbc 100644 --- a/app/src/main/res/layout/activity_extensions.xml +++ b/app/src/main/res/layout/activity_extensions.xml @@ -139,5 +139,14 @@ android:layout_height="0dp" android:layout_weight="1" /> + + + \ No newline at end of file