more searching

This commit is contained in:
Finnley Somdahl 2023-12-04 00:29:33 -06:00
parent 3ded6ba87a
commit c728eae2ba
8 changed files with 190 additions and 35 deletions

View file

@ -55,7 +55,7 @@ abstract class BaseParser {
* Isn't necessary to override, but recommended, if you want to improve auto search results * Isn't necessary to override, but recommended, if you want to improve auto search results
* **/ * **/
open suspend fun autoSearch(mediaObj: Media): ShowResponse? { 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) { if (response != null && this !is OfflineMangaParser) {
saveShowResponse(mediaObj.id, response, true) saveShowResponse(mediaObj.id, response, true)
} else { } else {

View file

@ -37,8 +37,9 @@ 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.Locale
class InstalledAnimeExtensionsFragment : Fragment() { class InstalledAnimeExtensionsFragment : Fragment(), SearchQueryHandler {
private var _binding: FragmentAnimeExtensionsBinding? = null private var _binding: FragmentAnimeExtensionsBinding? = null
@ -207,6 +208,9 @@ class InstalledAnimeExtensionsFragment : Fragment() {
super.onDestroyView();_binding = null super.onDestroyView();_binding = null
} }
override fun updateContentBasedOnQuery(query: String?) {
extensionsAdapter.filter(query ?: "", animeExtensionManager.installedExtensionsFlow.value)
}
private class AnimeExtensionsAdapter( private class AnimeExtensionsAdapter(
private val onSettingsClicked: (AnimeExtension.Installed) -> Unit, private val onSettingsClicked: (AnimeExtension.Installed) -> Unit,
@ -248,6 +252,16 @@ class InstalledAnimeExtensionsFragment : Fragment() {
} }
} }
fun filter(query: String, currentList: List<AnimeExtension.Installed>) {
val filteredList = ArrayList<AnimeExtension.Installed>()
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) { inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val extensionNameTextView: TextView = view.findViewById(R.id.extensionNameTextView) val extensionNameTextView: TextView = view.findViewById(R.id.extensionNameTextView)
val extensionVersionTextView: TextView = val extensionVersionTextView: TextView =

View file

@ -38,8 +38,9 @@ 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.Locale
class InstalledMangaExtensionsFragment : Fragment() { class InstalledMangaExtensionsFragment : Fragment(), SearchQueryHandler {
private var _binding: FragmentMangaExtensionsBinding? = null private var _binding: FragmentMangaExtensionsBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
private lateinit var extensionsRecyclerView: RecyclerView private lateinit var extensionsRecyclerView: RecyclerView
@ -187,6 +188,9 @@ class InstalledMangaExtensionsFragment : Fragment() {
super.onDestroyView();_binding = null super.onDestroyView();_binding = null
} }
override fun updateContentBasedOnQuery(query: String?) {
extensionsAdapter.filter(query ?: "", mangaExtensionManager.installedExtensionsFlow.value)
}
private class MangaExtensionsAdapter( private class MangaExtensionsAdapter(
private val onSettingsClicked: (MangaExtension.Installed) -> Unit, private val onSettingsClicked: (MangaExtension.Installed) -> Unit,
@ -230,6 +234,16 @@ class InstalledMangaExtensionsFragment : Fragment() {
} }
} }
fun filter(query: String, currentList: List<MangaExtension.Installed>) {
val filteredList = ArrayList<MangaExtension.Installed>()
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) { inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val extensionNameTextView: TextView = view.findViewById(R.id.extensionNameTextView) val extensionNameTextView: TextView = view.findViewById(R.id.extensionNameTextView)
val extensionVersionTextView: TextView = val extensionVersionTextView: TextView =

View file

@ -11,7 +11,6 @@ import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.view.updateLayoutParams
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
@ -32,8 +31,9 @@ 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.Locale
class InstalledNovelExtensionsFragment : Fragment() { class InstalledNovelExtensionsFragment : Fragment(), SearchQueryHandler {
private var _binding: FragmentNovelExtensionsBinding? = null private var _binding: FragmentNovelExtensionsBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
private lateinit var extensionsRecyclerView: RecyclerView private lateinit var extensionsRecyclerView: RecyclerView
@ -124,6 +124,9 @@ class InstalledNovelExtensionsFragment : Fragment() {
super.onDestroyView();_binding = null super.onDestroyView();_binding = null
} }
override fun updateContentBasedOnQuery(query: String?) {
extensionsAdapter.filter(query ?: "", novelExtensionManager.installedExtensionsFlow.value)
}
private class NovelExtensionsAdapter( private class NovelExtensionsAdapter(
private val onSettingsClicked: (NovelExtension.Installed) -> Unit, private val onSettingsClicked: (NovelExtension.Installed) -> Unit,
@ -169,6 +172,16 @@ class InstalledNovelExtensionsFragment : Fragment() {
} }
} }
fun filter(query: String, currentList: List<NovelExtension.Installed>) {
val filteredList = ArrayList<NovelExtension.Installed>()
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) { inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val extensionNameTextView: TextView = view.findViewById(R.id.extensionNameTextView) val extensionNameTextView: TextView = view.findViewById(R.id.extensionNameTextView)
val extensionVersionTextView: TextView = val extensionVersionTextView: TextView =

View file

@ -2,6 +2,7 @@ package ani.dantotsu.settings.paging
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import android.view.animation.LinearInterpolator
import android.widget.ImageView import android.widget.ImageView
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
@ -15,18 +16,25 @@ import androidx.paging.PagingState
import androidx.paging.cachedIn import androidx.paging.cachedIn
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.R
import ani.dantotsu.databinding.ItemExtensionAllBinding import ani.dantotsu.databinding.ItemExtensionAllBinding
import ani.dantotsu.loadData import ani.dantotsu.loadData
import ani.dantotsu.others.LanguageMapper import ani.dantotsu.others.LanguageMapper
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow 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.flow.flatMapLatest
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class AnimeExtensionsViewModelFactory( class AnimeExtensionsViewModelFactory(
@ -52,7 +60,13 @@ class AnimeExtensionsViewModel(
} }
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
val pagerFlow: Flow<PagingData<AnimeExtension.Available>> = searchQuery.flatMapLatest { query -> val pagerFlow: Flow<PagingData<AnimeExtension.Available>> = combine(
animeExtensionManager.availableExtensionsFlow,
animeExtensionManager.installedExtensionsFlow,
searchQuery
) { available, installed, query ->
Triple(available, installed, query)
}.flatMapLatest { (available, installed, query) ->
Pager( Pager(
PagingConfig( PagingConfig(
pageSize = 15, pageSize = 15,
@ -60,27 +74,23 @@ class AnimeExtensionsViewModel(
prefetchDistance = 15 prefetchDistance = 15
) )
) { ) {
AnimeExtensionPagingSource( AnimeExtensionPagingSource(available, installed, query)
animeExtensionManager.availableExtensionsFlow,
animeExtensionManager.installedExtensionsFlow,
searchQuery
).also { currentPagingSource = it }
}.flow }.flow
}.cachedIn(viewModelScope) }.cachedIn(viewModelScope)
} }
class AnimeExtensionPagingSource( class AnimeExtensionPagingSource(
private val availableExtensionsFlow: StateFlow<List<AnimeExtension.Available>>, private val availableExtensionsFlow: List<AnimeExtension.Available>,
private val installedExtensionsFlow: StateFlow<List<AnimeExtension.Installed>>, private val installedExtensionsFlow: List<AnimeExtension.Installed>,
private val searchQuery: StateFlow<String> private val searchQuery: String
) : PagingSource<Int, AnimeExtension.Available>() { ) : PagingSource<Int, AnimeExtension.Available>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, AnimeExtension.Available> { override suspend fun load(params: LoadParams<Int>): LoadResult<Int, AnimeExtension.Available> {
val position = params.key ?: 0 val position = params.key ?: 0
val installedExtensions = installedExtensionsFlow.first().map { it.pkgName }.toSet() val installedExtensions = installedExtensionsFlow.map { it.pkgName }.toSet()
val availableExtensions = val availableExtensions =
availableExtensionsFlow.first().filterNot { it.pkgName in installedExtensions } availableExtensionsFlow.filterNot { it.pkgName in installedExtensions }
val query = searchQuery.first() val query = searchQuery
val isNsfwEnabled: Boolean = loadData("NFSWExtension") ?: true val isNsfwEnabled: Boolean = loadData("NFSWExtension") ?: true
val filteredExtensions = if (query.isEmpty()) { val filteredExtensions = if (query.isEmpty()) {
@ -160,11 +170,28 @@ class AnimeExtensionAdapter(private val clickListener: OnAnimeInstallClickListen
inner class AnimeExtensionViewHolder(private val binding: ItemExtensionAllBinding) : inner class AnimeExtensionViewHolder(private val binding: ItemExtensionAllBinding) :
RecyclerView.ViewHolder(binding.root) { RecyclerView.ViewHolder(binding.root) {
private val job = Job()
private val scope = CoroutineScope(Dispatchers.Main + job)
init { init {
binding.closeTextView.setOnClickListener { binding.closeTextView.setOnClickListener {
val extension = getItem(bindingAdapterPosition) val extension = getItem(bindingAdapterPosition)
if (extension != null) { if (extension != null) {
clickListener.onInstallClick(extension) 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.extensionNameTextView.text = extension.name
binding.extensionVersionTextView.text = "$lang ${extension.versionName} $nsfw" 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()
} }
} }

View file

@ -2,6 +2,7 @@ package ani.dantotsu.settings.paging
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import android.view.animation.LinearInterpolator
import android.widget.ImageView import android.widget.ImageView
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
@ -15,18 +16,25 @@ import androidx.paging.PagingState
import androidx.paging.cachedIn import androidx.paging.cachedIn
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.R
import ani.dantotsu.databinding.ItemExtensionAllBinding import ani.dantotsu.databinding.ItemExtensionAllBinding
import ani.dantotsu.loadData import ani.dantotsu.loadData
import ani.dantotsu.others.LanguageMapper import ani.dantotsu.others.LanguageMapper
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
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 kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow 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.flow.flatMapLatest
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class MangaExtensionsViewModelFactory( class MangaExtensionsViewModelFactory(
private val mangaExtensionManager: MangaExtensionManager private val mangaExtensionManager: MangaExtensionManager
@ -51,7 +59,13 @@ class MangaExtensionsViewModel(
} }
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
val pagerFlow: Flow<PagingData<MangaExtension.Available>> = searchQuery.flatMapLatest { query -> val pagerFlow: Flow<PagingData<MangaExtension.Available>> = combine(
mangaExtensionManager.availableExtensionsFlow,
mangaExtensionManager.installedExtensionsFlow,
searchQuery
) { available, installed, query ->
Triple(available, installed, query)
}.flatMapLatest { (available, installed, query) ->
Pager( Pager(
PagingConfig( PagingConfig(
pageSize = 15, pageSize = 15,
@ -59,28 +73,24 @@ class MangaExtensionsViewModel(
prefetchDistance = 15 prefetchDistance = 15
) )
) { ) {
MangaExtensionPagingSource( MangaExtensionPagingSource(available, installed, query)
mangaExtensionManager.availableExtensionsFlow,
mangaExtensionManager.installedExtensionsFlow,
searchQuery
).also { currentPagingSource = it }
}.flow }.flow
}.cachedIn(viewModelScope) }.cachedIn(viewModelScope)
} }
class MangaExtensionPagingSource( class MangaExtensionPagingSource(
private val availableExtensionsFlow: StateFlow<List<MangaExtension.Available>>, private val availableExtensionsFlow: List<MangaExtension.Available>,
private val installedExtensionsFlow: StateFlow<List<MangaExtension.Installed>>, private val installedExtensionsFlow: List<MangaExtension.Installed>,
private val searchQuery: StateFlow<String> private val searchQuery: String
) : PagingSource<Int, MangaExtension.Available>() { ) : PagingSource<Int, MangaExtension.Available>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, MangaExtension.Available> { override suspend fun load(params: LoadParams<Int>): LoadResult<Int, MangaExtension.Available> {
val position = params.key ?: 0 val position = params.key ?: 0
val installedExtensions = installedExtensionsFlow.first().map { it.pkgName }.toSet() val installedExtensions = installedExtensionsFlow.map { it.pkgName }.toSet()
val availableExtensions = val availableExtensions =
availableExtensionsFlow.first().filterNot { it.pkgName in installedExtensions } availableExtensionsFlow.filterNot { it.pkgName in installedExtensions }
val query = searchQuery.first() val query = searchQuery
val isNsfwEnabled: Boolean = loadData("NFSWExtension") ?: true val isNsfwEnabled: Boolean = loadData("NFSWExtension") ?: true
val filteredExtensions = if (query.isEmpty()) { val filteredExtensions = if (query.isEmpty()) {
availableExtensions availableExtensions
@ -157,11 +167,28 @@ class MangaExtensionAdapter(private val clickListener: OnMangaInstallClickListen
inner class MangaExtensionViewHolder(private val binding: ItemExtensionAllBinding) : inner class MangaExtensionViewHolder(private val binding: ItemExtensionAllBinding) :
RecyclerView.ViewHolder(binding.root) { RecyclerView.ViewHolder(binding.root) {
private val job = Job()
private val scope = CoroutineScope(Dispatchers.Main + job)
init { init {
binding.closeTextView.setOnClickListener { binding.closeTextView.setOnClickListener {
val extension = getItem(bindingAdapterPosition) val extension = getItem(bindingAdapterPosition)
if (extension != null) { if (extension != null) {
clickListener.onInstallClick(extension) 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.extensionNameTextView.text = extension.name
binding.extensionVersionTextView.text = "$lang ${extension.versionName} $nsfw" 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()
} }
} }

View file

@ -2,6 +2,7 @@ package ani.dantotsu.settings.paging
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import android.view.animation.LinearInterpolator
import android.widget.ImageView import android.widget.ImageView
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
@ -15,19 +16,25 @@ import androidx.paging.PagingState
import androidx.paging.cachedIn import androidx.paging.cachedIn
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.R
import ani.dantotsu.databinding.ItemExtensionAllBinding import ani.dantotsu.databinding.ItemExtensionAllBinding
import ani.dantotsu.loadData import ani.dantotsu.loadData
import ani.dantotsu.others.LanguageMapper import ani.dantotsu.others.LanguageMapper
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 com.bumptech.glide.Glide import com.bumptech.glide.Glide
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class NovelExtensionsViewModelFactory( class NovelExtensionsViewModelFactory(
@ -163,11 +170,28 @@ class NovelExtensionAdapter(private val clickListener: OnNovelInstallClickListen
inner class NovelExtensionViewHolder(private val binding: ItemExtensionAllBinding) : inner class NovelExtensionViewHolder(private val binding: ItemExtensionAllBinding) :
RecyclerView.ViewHolder(binding.root) { RecyclerView.ViewHolder(binding.root) {
private val job = Job()
private val scope = CoroutineScope(Dispatchers.Main + job)
init { init {
binding.closeTextView.setOnClickListener { binding.closeTextView.setOnClickListener {
val extension = getItem(bindingAdapterPosition) val extension = getItem(bindingAdapterPosition)
if (extension != null) { if (extension != null) {
clickListener.onInstallClick(extension) 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.extensionNameTextView.text = extension.name
binding.extensionVersionTextView.text = "$lang ${extension.versionName} $nsfw" 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()
} }
} }

View file

@ -139,5 +139,14 @@
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" /> android:layout_weight="1" />
<FrameLayout
android:id="@+id/fragmentExtensionsContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
android:layout_gravity="bottom"
android:layout_weight="1">
</FrameLayout>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>