various bugfixes

This commit is contained in:
Finnley Somdahl 2023-10-22 02:28:39 -05:00
parent 0b9f2bb019
commit dc959796e6
26 changed files with 685 additions and 289 deletions

View file

@ -2,6 +2,7 @@ package ani.dantotsu.aniyomi.anime.custom
import android.app.Application
import android.content.Context
import ani.dantotsu.media.manga.MangaCache
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
import tachiyomi.core.preference.PreferenceStore
@ -28,6 +29,9 @@ class AppModule(val app: Application) : InjektModule {
addSingletonFactory { MangaExtensionManager(app) }
val sharedPreferences = app.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
addSingleton(sharedPreferences)
addSingletonFactory {
Json {
ignoreUnknownKeys = true

View file

@ -1,21 +1,30 @@
package ani.dantotsu.connections.discord
import android.annotation.SuppressLint
import android.app.Application.getProcessName
import android.os.Build
import android.os.Bundle
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import ani.dantotsu.R
import ani.dantotsu.connections.discord.Discord.saveToken
import ani.dantotsu.startMainActivity
class Login : AppCompatActivity() {
@SuppressLint("SetJavaScriptEnabled")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
val process = getProcessName()
if (packageName != process) WebView.setDataDirectorySuffix(process)
}
setContentView(R.layout.activity_discord)
val webView = findViewById<WebView>(R.id.discordWebview)
webView.apply {
settings.javaScriptEnabled = true
settings.databaseEnabled = true

View file

@ -113,6 +113,10 @@ data class Media(
this.relation = mediaEdge.relationType?.toString()
}
fun mainName() = nameMAL ?: name ?: nameRomaji
fun mainName() = name ?: nameMAL ?: nameRomaji
fun mangaName() = if (countryOfOrigin != "JP") mainName() else nameRomaji
}
}
object MediaSingleton {
var media: Media? = null
}

View file

@ -1,6 +1,8 @@
package ani.dantotsu.media
import android.app.Activity
import android.content.Context
import android.content.SharedPreferences
import android.os.Handler
import android.os.Looper
import androidx.fragment.app.FragmentManager
@ -40,6 +42,8 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class MediaDetailsViewModel : ViewModel() {
val scrolledToTop = MutableLiveData(true)
@ -48,11 +52,13 @@ class MediaDetailsViewModel : ViewModel() {
saveData("$id-select", data, activity)
}
fun loadSelected(media: Media): Selected {
val sharedPreferences = Injekt.get<SharedPreferences>()
val data = loadData<Selected>("${media.id}-select") ?: Selected().let {
it.sourceIndex = if (media.isAdult) 0 else when (media.anime != null) {
true -> loadData("settings_def_anime_source_s_r") ?: 0
else -> loadData("settings_def_manga_source_s_r") ?: 0
true -> sharedPreferences.getInt("settings_def_anime_source_s_r", 0)
else -> sharedPreferences.getInt(("settings_def_manga_source_s_r"), 0)
}
it.preferDub = loadData("settings_prefer_dub") ?: false
saveSelected(media.id, it)

View file

@ -9,6 +9,8 @@ import ani.dantotsu.databinding.ItemEpisodeCompactBinding
import ani.dantotsu.media.Media
import ani.dantotsu.setAnimation
import ani.dantotsu.connections.updateProgress
import java.util.regex.Matcher
import java.util.regex.Pattern
class MangaChapterAdapter(
private var type: Int,
@ -63,12 +65,12 @@ class MangaChapterAdapter(
val ep = arr[position]
binding.itemEpisodeNumber.text = ep.number
if (media.userProgress != null) {
if ((ep.number.toFloatOrNull() ?: 9999f) <= media.userProgress!!.toFloat())
if ((MangaNameAdapter.findChapterNumber(ep.number) ?: 9999f) <= media.userProgress!!.toFloat())
binding.itemEpisodeViewedCover.visibility = View.VISIBLE
else {
binding.itemEpisodeViewedCover.visibility = View.GONE
binding.itemEpisodeCont.setOnLongClickListener {
updateProgress(media, ep.number)
updateProgress(media, MangaNameAdapter.findChapterNumber(ep.number).toString())
true
}
}
@ -91,14 +93,14 @@ class MangaChapterAdapter(
} else binding.itemChapterTitle.visibility = View.GONE
if (media.userProgress != null) {
if ((ep.number.toFloatOrNull() ?: 9999f) <= media.userProgress!!.toFloat()) {
if ((MangaNameAdapter.findChapterNumber(ep.number) ?: 9999f) <= media.userProgress!!.toFloat()) {
binding.itemEpisodeViewedCover.visibility = View.VISIBLE
binding.itemEpisodeViewed.visibility = View.VISIBLE
} else {
binding.itemEpisodeViewedCover.visibility = View.GONE
binding.itemEpisodeViewed.visibility = View.GONE
binding.root.setOnLongClickListener {
updateProgress(media, ep.number)
updateProgress(media, MangaNameAdapter.findChapterNumber(ep.number).toString())
true
}
}
@ -113,4 +115,6 @@ class MangaChapterAdapter(
fun updateType(t: Int) {
type = t
}
}

View file

@ -0,0 +1,20 @@
package ani.dantotsu.media.manga
import java.util.regex.Matcher
import java.util.regex.Pattern
class MangaNameAdapter {
companion object {
fun findChapterNumber(text: String): Float? {
val regex = "(chapter|chap|ch|c)[\\s:.\\-]*([\\d]+\\.?[\\d]*)"
val pattern: Pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE)
val matcher: Matcher = pattern.matcher(text)
return if (matcher.find()) {
matcher.group(2)?.toFloat()
} else {
null
}
}
}
}

View file

@ -14,6 +14,7 @@ import ani.dantotsu.currActivity
import ani.dantotsu.databinding.BottomSheetSelectorBinding
import ani.dantotsu.media.manga.MangaChapter
import ani.dantotsu.media.MediaDetailsViewModel
import ani.dantotsu.media.MediaSingleton
import ani.dantotsu.others.getSerialized
import ani.dantotsu.tryWith
import kotlinx.coroutines.Dispatchers
@ -49,7 +50,8 @@ class ChapterLoaderDialog : BottomSheetDialogFragment() {
activity?.runOnUiThread {
tryWith { dismiss() }
if(launch) {
val intent = Intent(activity, MangaReaderActivity::class.java).apply { putExtra("media", m) }
MediaSingleton.media = m
val intent = Intent(activity, MangaReaderActivity::class.java)//.apply { putExtra("media", m) }
activity.startActivity(intent)
}
}

View file

@ -30,8 +30,10 @@ import ani.dantotsu.connections.updateProgress
import ani.dantotsu.databinding.ActivityMangaReaderBinding
import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsViewModel
import ani.dantotsu.media.MediaSingleton
import ani.dantotsu.media.manga.MangaCache
import ani.dantotsu.media.manga.MangaChapter
import ani.dantotsu.media.manga.MangaNameAdapter
import ani.dantotsu.others.ImageViewDialog
import ani.dantotsu.others.getSerialized
import ani.dantotsu.parsers.HMangaSources
@ -46,7 +48,12 @@ import ani.dantotsu.settings.UserInterfaceSettings
import com.alexvasilkov.gestures.views.GestureFrameLayout
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase
import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@ -164,10 +171,13 @@ class MangaReaderActivity : AppCompatActivity() {
media = if (model.getMedia().value == null)
try {
(intent.getSerialized("media")) ?: return
//(intent.getSerialized("media")) ?: return
MediaSingleton.media ?: return
} catch (e: Exception) {
logError(e)
return
} finally {
MediaSingleton.media = null
}
else model.getMedia().value ?: return
model.setMedia(media)
@ -180,6 +190,29 @@ class MangaReaderActivity : AppCompatActivity() {
model.mangaReadSources = if (media.isAdult) HMangaSources else MangaSources
binding.mangaReaderSource.visibility = if (settings.showSource) View.VISIBLE else View.GONE
if(model.mangaReadSources!!.names.isEmpty()){
//try to reload sources
try {
if (media.isAdult) {
val mangaSources = MangaSources
val scope = lifecycleScope
scope.launch(Dispatchers.IO) {
mangaSources.init(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
}
}catch (e: Exception){
Firebase.crashlytics.recordException(e)
logError(e)
}
}
binding.mangaReaderSource.text = model.mangaReadSources!!.names[media.selected!!.sourceIndex]
binding.mangaReaderTitle.text = media.userPreferredName
@ -677,7 +710,7 @@ class MangaReaderActivity : AppCompatActivity() {
progressDialog?.setCancelable(false)
?.setPositiveButton(getString(R.string.yes)) { dialog, _ ->
saveData("${media.id}_save_progress", true)
updateProgress(media, media.manga!!.selectedChapter!!)
updateProgress(media, MangaNameAdapter.findChapterNumber(media.manga!!.selectedChapter!!).toString())
dialog.dismiss()
runnable.run()
}
@ -689,7 +722,7 @@ class MangaReaderActivity : AppCompatActivity() {
progressDialog?.show()
} else {
if (loadData<Boolean>("${media.id}_save_progress") != false && if (media.isAdult) settings.updateForH else true)
updateProgress(media, media.manga!!.selectedChapter!!)
updateProgress(media, MangaNameAdapter.findChapterNumber(media.manga!!.selectedChapter!!).toString())
runnable.run()
}
} else {

View file

@ -171,6 +171,17 @@ abstract class AnimeParser : BaseParser() {
}
}
class EmptyAnimeParser: AnimeParser() {
override val name: String = "None"
override val saveName: String = "None"
override val isDubAvailableSeparately: Boolean = false
override suspend fun loadEpisodes(animeLink: String, extra: Map<String, String>?, sAnime: SAnime): List<Episode> = emptyList()
override suspend fun loadVideoServers(episodeLink: String, extra: Map<String, String>?, sEpisode: SEpisode): List<VideoServer> = emptyList()
override suspend fun search(query: String): List<ShowResponse> = emptyList()
}
/**
* A class for containing Episode data of a particular parser
* **/

View file

@ -66,6 +66,7 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
override val saveName = extension.name
override val hostUrl = extension.sources.first().name
override val isDubAvailableSeparately = false
override val isNSFW = extension.isNsfw
override suspend fun loadEpisodes(animeLink: String, extra: Map<String, String>?, sAnime: SAnime): List<Episode> {
val source = extension.sources.first()
if (source is AnimeCatalogueSource) {
@ -176,6 +177,7 @@ class DynamicMangaParser(extension: MangaExtension.Installed) : MangaParser() {
override val name = extension.name
override val saveName = extension.name
override val hostUrl = extension.sources.first().name
override val isNSFW = extension.isNsfw
override suspend fun loadChapters(mangaLink: String, extra: Map<String, String>?, sManga: SManga): List<MangaChapter> {
val source = extension.sources.first() as? CatalogueSource ?: return emptyList()
@ -385,22 +387,22 @@ class DynamicMangaParser(extension: MangaExtension.Installed) : MangaParser() {
private fun SChapterToMangaChapter(sChapter: SChapter): MangaChapter {
val parsedChapterTitle = parseChapterTitle(sChapter.name)
/*val parsedChapterTitle = parseChapterTitle(sChapter.name)
val number = if (sChapter.chapter_number.toInt() != -1){
sChapter.chapter_number.toString()
} else if(parsedChapterTitle.first != null || parsedChapterTitle.second != null){
(parsedChapterTitle.first ?: "") + "." + (parsedChapterTitle.second ?: "")
}else{
sChapter.name
}
}*/
return MangaChapter(
number,
sChapter.name,
sChapter.url,
if (parsedChapterTitle.first != null || parsedChapterTitle.second != null) {
parsedChapterTitle.third
} else {
sChapter.name
},
//if (parsedChapterTitle.first != null || parsedChapterTitle.second != null) {
// parsedChapterTitle.third
//} else {
sChapter.name,
//},
null,
sChapter
)

View file

@ -80,8 +80,8 @@ abstract class BaseParser {
} else {
val romajiRatio = FuzzySearch.ratio(closestRomaji?.name ?: "", mediaObj.nameRomaji)
val mainNameRatio = FuzzySearch.ratio(response.name, mediaObj.mainName())
logger("Fuzzy ratio for closest match in results: $mainNameRatio")
logger("Fuzzy ratio for closest match in RomajiResults: $romajiRatio")
logger("Fuzzy ratio for closest match in results: $mainNameRatio for ${response.name}")
logger("Fuzzy ratio for closest match in RomajiResults: $romajiRatio for ${closestRomaji?.name ?: "None"}")
if (romajiRatio > mainNameRatio) {
logger("RomajiResults has a closer match. Replacing response.")

View file

@ -11,9 +11,11 @@ import eu.kanade.tachiyomi.animesource.model.SAnime
abstract class WatchSources : BaseSources() {
override operator fun get(i: Int): AnimeParser {
return (list.getOrNull(i)?:list[0]).get.value as AnimeParser
return (list.getOrNull(i) ?: list.firstOrNull())?.get?.value as? AnimeParser
?: EmptyAnimeParser()
}
suspend fun loadEpisodesFromMedia(i: Int, media: Media): MutableMap<String, Episode> {
return tryWithSuspend(true) {
val res = get(i).autoSearch(media) ?: return@tryWithSuspend mutableMapOf()
@ -40,7 +42,8 @@ abstract class WatchSources : BaseSources() {
abstract class MangaReadSources : BaseSources() {
override operator fun get(i: Int): MangaParser {
return (list.getOrNull(i)?:list[0]).get.value as MangaParser
return (list.getOrNull(i)?:list.firstOrNull())?.get?.value as? MangaParser
?: EmptyMangaParser()
}
suspend fun loadChaptersFromMedia(i: Int, media: Media): MutableMap<String, MangaChapter> {

View file

@ -33,7 +33,7 @@ abstract class MangaParser : BaseParser() {
* **/
abstract suspend fun loadImages(chapterLink: String, sChapter: SChapter): List<MangaImage>
override suspend fun autoSearch(mediaObj: Media): ShowResponse? {
/*override suspend fun autoSearch(mediaObj: Media): ShowResponse? {
var response = loadSavedShowResponse(mediaObj.id)
if (response != null) {
saveShowResponse(mediaObj.id, response, true)
@ -48,11 +48,22 @@ abstract class MangaParser : BaseParser() {
saveShowResponse(mediaObj.id, response)
}
return response
}
}*/
open fun getTransformation(): BitmapTransformation? = null
}
class EmptyMangaParser: MangaParser() {
override val name: String = "None"
override val saveName: String = "None"
override suspend fun loadChapters(mangaLink: String, extra: Map<String, String>?, sManga: SManga): List<MangaChapter> = emptyList()
override suspend fun loadImages(chapterLink: String, sChapter: SChapter): List<MangaImage> = emptyList()
override suspend fun search(query: String): List<ShowResponse> = emptyList()
}
data class MangaChapter(
/**
* Number of the Chapter in "String",

View file

@ -29,7 +29,9 @@ object MangaSources : MangaReadSources() {
}
object HMangaSources : MangaReadSources() {
val aList: List<Lazier<BaseParser>> = lazyList(
)
val aList: List<Lazier<BaseParser>> = lazyList()
suspend fun init(fromExtensions: StateFlow<List<MangaExtension.Installed>>) {
//todo
}
override val list = listOf(aList,MangaSources.list).flatten()
}

View file

@ -15,7 +15,9 @@ import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.getSystemService
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.R
import ani.dantotsu.databinding.FragmentAnimeExtensionsBinding
@ -33,7 +35,8 @@ import rx.android.schedulers.AndroidSchedulers
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class AnimeExtensionsFragment : Fragment(), SearchQueryHandler {
class AnimeExtensionsFragment : Fragment(),
SearchQueryHandler {
private var _binding: FragmentAnimeExtensionsBinding? = null
private val binding get() = _binding!!
@ -42,104 +45,115 @@ class AnimeExtensionsFragment : Fragment(), SearchQueryHandler {
private lateinit var extensionsRecyclerView: RecyclerView
private lateinit var allextenstionsRecyclerView: RecyclerView
private val animeExtensionManager: AnimeExtensionManager = Injekt.get<AnimeExtensionManager>()
private val extensionsAdapter = AnimeExtensionsAdapter ({ pkg ->
if(pkg.hasUpdate){
private val extensionsAdapter = AnimeExtensionsAdapter({ pkg ->
if (isAdded) { // Check if the fragment is currently added to its activity
val context = requireContext() // Store context in a variable
val notificationManager =
requireContext().getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
animeExtensionManager.updateExtension(pkg)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ installStep ->
val builder = NotificationCompat.Builder(
requireContext(),
Notifications.CHANNEL_DOWNLOADER_PROGRESS
)
.setSmallIcon(R.drawable.ic_round_sync_24)
.setContentTitle("Updating extension")
.setContentText("Step: $installStep")
.setPriority(NotificationCompat.PRIORITY_LOW)
notificationManager.notify(1, builder.build())
},
{ error ->
val builder = NotificationCompat.Builder(
requireContext(),
Notifications.CHANNEL_DOWNLOADER_ERROR
)
.setSmallIcon(R.drawable.ic_round_info_24)
.setContentTitle("Update failed")
.setContentText("Error: ${error.message}")
.setPriority(NotificationCompat.PRIORITY_HIGH)
notificationManager.notify(1, builder.build())
},
{
val builder = NotificationCompat.Builder(
requireContext(),
Notifications.CHANNEL_DOWNLOADER_PROGRESS
)
.setSmallIcon(androidx.media3.ui.R.drawable.exo_ic_check)
.setContentTitle("Update complete")
.setContentText("The extension has been successfully updated.")
.setPriority(NotificationCompat.PRIORITY_LOW)
notificationManager.notify(1, builder.build())
}
)
}else {
animeExtensionManager.uninstallExtension(pkg.pkgName)
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager // Initialize NotificationManager once
if (pkg.hasUpdate) {
animeExtensionManager.updateExtension(pkg)
.observeOn(AndroidSchedulers.mainThread()) // Observe on main thread
.subscribe(
{ installStep ->
val builder = NotificationCompat.Builder(
context,
Notifications.CHANNEL_DOWNLOADER_PROGRESS
)
.setSmallIcon(R.drawable.ic_round_sync_24)
.setContentTitle("Updating extension")
.setContentText("Step: $installStep")
.setPriority(NotificationCompat.PRIORITY_LOW)
notificationManager.notify(1, builder.build())
},
{ error ->
Log.e("AnimeExtensionsAdapter", "Error: ", error) // Log the error
val builder = NotificationCompat.Builder(
context,
Notifications.CHANNEL_DOWNLOADER_ERROR
)
.setSmallIcon(R.drawable.ic_round_info_24)
.setContentTitle("Update failed")
.setContentText("Error: ${error.message}")
.setPriority(NotificationCompat.PRIORITY_HIGH)
notificationManager.notify(1, builder.build())
},
{
val builder = NotificationCompat.Builder(
context,
Notifications.CHANNEL_DOWNLOADER_PROGRESS
)
.setSmallIcon(androidx.media3.ui.R.drawable.exo_ic_check)
.setContentTitle("Update complete")
.setContentText("The extension has been successfully updated.")
.setPriority(NotificationCompat.PRIORITY_LOW)
notificationManager.notify(1, builder.build())
}
)
} else {
animeExtensionManager.uninstallExtension(pkg.pkgName)
}
}
}, skipIcons)
private val allExtensionsAdapter = AllAnimeExtensionsAdapter(lifecycleScope, { pkgName ->
val notificationManager =
requireContext().getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val notificationManager =
requireContext().getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// Start the installation process
animeExtensionManager.installExtension(pkgName)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ installStep ->
val builder = NotificationCompat.Builder(
requireContext(),
Notifications.CHANNEL_DOWNLOADER_PROGRESS
)
.setSmallIcon(R.drawable.ic_round_sync_24)
.setContentTitle("Installing extension")
.setContentText("Step: $installStep")
.setPriority(NotificationCompat.PRIORITY_LOW)
notificationManager.notify(1, builder.build())
},
{ error ->
val builder = NotificationCompat.Builder(
requireContext(),
Notifications.CHANNEL_DOWNLOADER_ERROR
)
.setSmallIcon(R.drawable.ic_round_info_24)
.setContentTitle("Installation failed")
.setContentText("Error: ${error.message}")
.setPriority(NotificationCompat.PRIORITY_HIGH)
notificationManager.notify(1, builder.build())
},
{
val builder = NotificationCompat.Builder(
requireContext(),
Notifications.CHANNEL_DOWNLOADER_PROGRESS
)
.setSmallIcon(androidx.media3.ui.R.drawable.exo_ic_check)
.setContentTitle("Installation complete")
.setContentText("The extension has been successfully installed.")
.setPriority(NotificationCompat.PRIORITY_LOW)
notificationManager.notify(1, builder.build())
}
)
}, skipIcons)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
// Start the installation process
animeExtensionManager.installExtension(pkgName)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ installStep ->
val builder = NotificationCompat.Builder(
requireContext(),
Notifications.CHANNEL_DOWNLOADER_PROGRESS
)
.setSmallIcon(R.drawable.ic_round_sync_24)
.setContentTitle("Installing extension")
.setContentText("Step: $installStep")
.setPriority(NotificationCompat.PRIORITY_LOW)
notificationManager.notify(1, builder.build())
},
{ error ->
val builder = NotificationCompat.Builder(
requireContext(),
Notifications.CHANNEL_DOWNLOADER_ERROR
)
.setSmallIcon(R.drawable.ic_round_info_24)
.setContentTitle("Installation failed")
.setContentText("Error: ${error.message}")
.setPriority(NotificationCompat.PRIORITY_HIGH)
notificationManager.notify(1, builder.build())
},
{
val builder = NotificationCompat.Builder(
requireContext(),
Notifications.CHANNEL_DOWNLOADER_PROGRESS
)
.setSmallIcon(androidx.media3.ui.R.drawable.exo_ic_check)
.setContentTitle("Installation complete")
.setContentText("The extension has been successfully installed.")
.setPriority(NotificationCompat.PRIORITY_LOW)
notificationManager.notify(1, builder.build())
}
)
}, skipIcons)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentAnimeExtensionsBinding.inflate(inflater, container, false)
extensionsRecyclerView = binding.animeExtensionsRecyclerView
extensionsRecyclerView.layoutManager = LinearLayoutManager( requireContext())
extensionsRecyclerView.layoutManager = LinearLayoutManager(requireContext())
extensionsRecyclerView.adapter = extensionsAdapter
allextenstionsRecyclerView = binding.allAnimeExtensionsRecyclerView
allextenstionsRecyclerView.layoutManager = LinearLayoutManager( requireContext())
allextenstionsRecyclerView.layoutManager = LinearLayoutManager(requireContext())
allextenstionsRecyclerView.adapter = allExtensionsAdapter
lifecycleScope.launch {
@ -156,11 +170,11 @@ class AnimeExtensionsFragment : Fragment(), SearchQueryHandler {
Pair(availableExtensions, installedExtensions)
}.collect { pair ->
val (availableExtensions, installedExtensions) = pair
allExtensionsAdapter.updateData(availableExtensions, installedExtensions)
}
}
val extensionsRecyclerView: RecyclerView = binding.animeExtensionsRecyclerView
return binding.root
}
@ -169,7 +183,6 @@ class AnimeExtensionsFragment : Fragment(), SearchQueryHandler {
allExtensionsAdapter.filter("") // Reset the filter
allextenstionsRecyclerView.visibility = View.VISIBLE
extensionsRecyclerView.visibility = View.VISIBLE
println("asdf: ${allExtensionsAdapter.getItemCount()}")
} else {
allExtensionsAdapter.filter(query)
allextenstionsRecyclerView.visibility = View.VISIBLE
@ -182,14 +195,17 @@ class AnimeExtensionsFragment : Fragment(), SearchQueryHandler {
}
private class AnimeExtensionsAdapter(private val onUninstallClicked: (AnimeExtension.Installed) -> Unit, skipIcons: Boolean) : RecyclerView.Adapter<AnimeExtensionsAdapter.ViewHolder>() {
private class AnimeExtensionsAdapter(
private val onUninstallClicked: (AnimeExtension.Installed) -> Unit,
skipIcons: Boolean
) : ListAdapter<AnimeExtension.Installed, AnimeExtensionsAdapter.ViewHolder>(
DIFF_CALLBACK_INSTALLED
) {
private var extensions: List<AnimeExtension.Installed> = emptyList()
val skipIcons = skipIcons
fun updateData(newExtensions: List<AnimeExtension.Installed>) {
extensions = newExtensions
notifyDataSetChanged()
submitList(newExtensions) // Use submitList instead of manual list handling
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
@ -199,15 +215,20 @@ class AnimeExtensionsFragment : Fragment(), SearchQueryHandler {
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val extension = extensions[position]
val extension = getItem(position) // Use getItem() from ListAdapter
holder.extensionNameTextView.text = extension.name
if (!skipIcons) {
holder.extensionIconImageView.setImageDrawable(extension.icon)
}
if(extension.hasUpdate){
if (extension.hasUpdate) {
holder.closeTextView.text = "Update"
holder.closeTextView.setTextColor(ContextCompat.getColor(holder.itemView.context, R.color.warning))
}else{
holder.closeTextView.setTextColor(
ContextCompat.getColor(
holder.itemView.context,
R.color.warning
)
)
} else {
holder.closeTextView.text = "Uninstall"
}
holder.closeTextView.setOnClickListener {
@ -215,59 +236,91 @@ class AnimeExtensionsFragment : Fragment(), SearchQueryHandler {
}
}
override fun getItemCount(): Int = extensions.size
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val extensionNameTextView: TextView = view.findViewById(R.id.extensionNameTextView)
val extensionIconImageView: ImageView = view.findViewById(R.id.extensionIconImageView)
val closeTextView: TextView = view.findViewById(R.id.closeTextView)
}
companion object {
val DIFF_CALLBACK_INSTALLED =
object : DiffUtil.ItemCallback<AnimeExtension.Installed>() {
override fun areItemsTheSame(
oldItem: AnimeExtension.Installed,
newItem: AnimeExtension.Installed
): Boolean {
return oldItem.pkgName == newItem.pkgName
}
override fun areContentsTheSame(
oldItem: AnimeExtension.Installed,
newItem: AnimeExtension.Installed
): Boolean {
return oldItem == newItem
}
}
}
}
private class AllAnimeExtensionsAdapter(private val coroutineScope: CoroutineScope,
private val onButtonClicked: (AnimeExtension.Available) -> Unit, skipIcons: Boolean) : RecyclerView.Adapter<AllAnimeExtensionsAdapter.ViewHolder>() {
private var extensions: List<AnimeExtension.Available> = emptyList()
private class AllAnimeExtensionsAdapter(
private val coroutineScope: CoroutineScope,
private val onButtonClicked: (AnimeExtension.Available) -> Unit,
skipIcons: Boolean
) : ListAdapter<AnimeExtension.Available, AllAnimeExtensionsAdapter.ViewHolder>(
DIFF_CALLBACK_AVAILABLE
) {
val skipIcons = skipIcons
fun updateData(newExtensions: List<AnimeExtension.Available>, installedExtensions: List<AnimeExtension.Installed> = emptyList()) {
val installedPkgNames = installedExtensions.map { it.pkgName }.toSet()
extensions = newExtensions.filter { it.pkgName !in installedPkgNames }
filteredExtensions = extensions
notifyDataSetChanged()
fun updateData(
newExtensions: List<AnimeExtension.Available>,
installedExtensions: List<AnimeExtension.Installed> = emptyList()
) {
coroutineScope.launch(Dispatchers.Default) {
val installedPkgNames = installedExtensions.map { it.pkgName }.toSet()
val filteredExtensions = newExtensions.filter { it.pkgName !in installedPkgNames }
// Switch back to main thread to update UI
withContext(Dispatchers.Main) {
submitList(filteredExtensions)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AllAnimeExtensionsAdapter.ViewHolder {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): AllAnimeExtensionsAdapter.ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_extension_all, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val extension = filteredExtensions[position]
val extension = getItem(position)
holder.extensionNameTextView.text = extension.name
if (!skipIcons) {
coroutineScope.launch {
val drawable = urlToDrawable(holder.itemView.context, extension.iconUrl)
holder.extensionIconImageView.setImageDrawable(drawable)
}
Glide.with(holder.itemView.context)
.load(extension.iconUrl)
.into(holder.extensionIconImageView)
}
holder.closeTextView.text = "Install"
holder.closeTextView.setOnClickListener {
onButtonClicked(extension)
}
}
override fun getItemCount(): Int = filteredExtensions.size
private var filteredExtensions: List<AnimeExtension.Available> = emptyList()
fun filter(query: String) {
filteredExtensions = if (query.isEmpty()) {
extensions
val filteredExtensions = if (query.isEmpty()) {
currentList
} else {
extensions.filter { it.name.contains(query, ignoreCase = true) }
currentList.filter { it.name.contains(query, ignoreCase = true) }
}
notifyDataSetChanged()
submitList(filteredExtensions)
}
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
@ -276,18 +329,24 @@ class AnimeExtensionsFragment : Fragment(), SearchQueryHandler {
val closeTextView: TextView = view.findViewById(R.id.closeTextView)
}
suspend fun urlToDrawable(context: Context, url: String): Drawable? {
return withContext(Dispatchers.IO) {
try {
return@withContext Glide.with(context)
.load(url)
.submit()
.get()
} catch (e: Exception) {
e.printStackTrace()
return@withContext null
companion object {
val DIFF_CALLBACK_AVAILABLE =
object : DiffUtil.ItemCallback<AnimeExtension.Available>() {
override fun areItemsTheSame(
oldItem: AnimeExtension.Available,
newItem: AnimeExtension.Available
): Boolean {
return oldItem.pkgName == newItem.pkgName
}
override fun areContentsTheSame(
oldItem: AnimeExtension.Available,
newItem: AnimeExtension.Available
): Boolean {
return oldItem == newItem
}
}
}
}
}
}

View file

@ -13,6 +13,7 @@ import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.ProgressBar
import android.widget.SearchView
import android.widget.TextView
import androidx.activity.OnBackPressedCallback
@ -49,17 +50,13 @@ import uy.kohesive.injekt.injectLazy
import javax.inject.Inject
class ExtensionsActivity : AppCompatActivity() {
class ExtensionsActivity : AppCompatActivity() {
private val restartMainActivity = object : OnBackPressedCallback(false) {
override fun handleOnBackPressed() = startMainActivity(this@ExtensionsActivity)
}
lateinit var binding: ActivityExtensionsBinding
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -132,4 +129,4 @@ class ExtensionsActivity : AppCompatActivity() {
interface SearchQueryHandler {
fun updateContentBasedOnQuery(query: String?)
}
}

View file

@ -11,26 +11,96 @@ import ani.dantotsu.initActivity
class FAQActivity : AppCompatActivity() {
private lateinit var binding: ActivityFaqBinding
private val faqs = listOf(
private val faqs by lazy {
listOf(
Triple(R.drawable.ic_round_help_24, currContext()!!.getString(R.string.question_1), currContext()!!.getString(R.string.answer_1)),
Triple(R.drawable.ic_round_auto_awesome_24, currContext()!!.getString(R.string.question_2), currContext()!!.getString(R.string.answer_2)),
Triple(R.drawable.ic_round_auto_awesome_24, currContext()!!.getString(R.string.question_17), currContext()!!.getString(R.string.answer_17)),
Triple(R.drawable.ic_round_download_24, currContext()!!.getString(R.string.question_3), currContext()!!.getString(R.string.answer_3)),
Triple(R.drawable.ic_round_help_24, currContext()!!.getString(R.string.question_16), currContext()!!.getString(R.string.answer_16)),
Triple(R.drawable.ic_round_dns_24, currContext()!!.getString(R.string.question_4), currContext()!!.getString(R.string.answer_4)),
Triple(R.drawable.ic_baseline_screen_lock_portrait_24, currContext()!!.getString(R.string.question_5), currContext()!!.getString(R.string.answer_5)),
Triple(R.drawable.ic_anilist, currContext()!!.getString(R.string.question_6), currContext()!!.getString(R.string.answer_6)),
Triple(R.drawable.ic_round_movie_filter_24, currContext()!!.getString(R.string.question_7), currContext()!!.getString(R.string.answer_7)),
Triple(R.drawable.ic_round_menu_book_24, currContext()!!.getString(R.string.question_8), currContext()!!.getString(R.string.answer_8)),
Triple(R.drawable.ic_round_lock_open_24, currContext()!!.getString(R.string.question_9), currContext()!!.getString(R.string.answer_9)),
Triple(R.drawable.ic_round_smart_button_24, currContext()!!.getString(R.string.question_10), currContext()!!.getString(R.string.answer_10)),
Triple(R.drawable.ic_round_smart_button_24, currContext()!!.getString(R.string.question_11), currContext()!!.getString(R.string.answer_11)),
Triple(R.drawable.ic_round_info_24, currContext()!!.getString(R.string.question_12), currContext()!!.getString(R.string.answer_12)),
Triple(R.drawable.ic_round_help_24, currContext()!!.getString(R.string.question_13), currContext()!!.getString(R.string.answer_13)),
Triple(R.drawable.ic_round_art_track_24, currContext()!!.getString(R.string.question_14), currContext()!!.getString(R.string.answer_14)),
Triple(R.drawable.ic_round_video_settings_24, currContext()!!.getString(R.string.question_15), currContext()!!.getString(R.string.answer_15))
Triple(
R.drawable.ic_round_help_24,
currContext()!!.getString(R.string.question_1),
currContext()!!.getString(R.string.answer_1)
),
Triple(
R.drawable.ic_round_auto_awesome_24,
currContext()!!.getString(R.string.question_2),
currContext()!!.getString(R.string.answer_2)
),
Triple(
R.drawable.ic_round_auto_awesome_24,
currContext()!!.getString(R.string.question_17),
currContext()!!.getString(R.string.answer_17)
),
Triple(
R.drawable.ic_round_download_24,
currContext()!!.getString(R.string.question_3),
currContext()!!.getString(R.string.answer_3)
),
Triple(
R.drawable.ic_round_help_24,
currContext()!!.getString(R.string.question_16),
currContext()!!.getString(R.string.answer_16)
),
Triple(
R.drawable.ic_round_dns_24,
currContext()!!.getString(R.string.question_4),
currContext()!!.getString(R.string.answer_4)
),
Triple(
R.drawable.ic_baseline_screen_lock_portrait_24,
currContext()!!.getString(R.string.question_5),
currContext()!!.getString(R.string.answer_5)
),
Triple(
R.drawable.ic_anilist,
currContext()!!.getString(R.string.question_6),
currContext()!!.getString(R.string.answer_6)
),
Triple(
R.drawable.ic_round_movie_filter_24,
currContext()!!.getString(R.string.question_7),
currContext()!!.getString(R.string.answer_7)
),
Triple(
R.drawable.ic_round_menu_book_24,
currContext()!!.getString(R.string.question_8),
currContext()!!.getString(R.string.answer_8)
),
Triple(
R.drawable.ic_round_lock_open_24,
currContext()!!.getString(R.string.question_9),
currContext()!!.getString(R.string.answer_9)
),
Triple(
R.drawable.ic_round_smart_button_24,
currContext()!!.getString(R.string.question_10),
currContext()!!.getString(R.string.answer_10)
),
Triple(
R.drawable.ic_round_smart_button_24,
currContext()!!.getString(R.string.question_11),
currContext()!!.getString(R.string.answer_11)
),
Triple(
R.drawable.ic_round_info_24,
currContext()!!.getString(R.string.question_12),
currContext()!!.getString(R.string.answer_12)
),
Triple(
R.drawable.ic_round_help_24,
currContext()!!.getString(R.string.question_13),
currContext()!!.getString(R.string.answer_13)
),
Triple(
R.drawable.ic_round_art_track_24,
currContext()!!.getString(R.string.question_14),
currContext()!!.getString(R.string.answer_14)
),
Triple(
R.drawable.ic_round_video_settings_24,
currContext()!!.getString(R.string.question_15),
currContext()!!.getString(R.string.answer_15)
)
)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

View file

@ -4,6 +4,7 @@ import android.app.NotificationManager
import android.content.Context
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -13,7 +14,9 @@ import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.R
import ani.dantotsu.databinding.FragmentMangaBinding
@ -32,7 +35,8 @@ import rx.android.schedulers.AndroidSchedulers
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class MangaExtensionsFragment : Fragment(), SearchQueryHandler {
class MangaExtensionsFragment : Fragment(),
SearchQueryHandler {
private var _binding: FragmentMangaExtensionsBinding? = null
private val binding get() = _binding!!
@ -40,52 +44,58 @@ class MangaExtensionsFragment : Fragment(), SearchQueryHandler {
private lateinit var extensionsRecyclerView: RecyclerView
private lateinit var allextenstionsRecyclerView: RecyclerView
private val mangaExtensionManager:MangaExtensionManager = Injekt.get<MangaExtensionManager>()
private val extensionsAdapter = MangaExtensionsAdapter ({ pkg ->
if(pkg.hasUpdate){
private val mangaExtensionManager: MangaExtensionManager = Injekt.get<MangaExtensionManager>()
private val extensionsAdapter = MangaExtensionsAdapter({ pkg ->
if (isAdded) { // Check if the fragment is currently added to its activity
val context = requireContext() // Store context in a variable
val notificationManager =
requireContext().getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
mangaExtensionManager.updateExtension(pkg)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ installStep ->
val builder = NotificationCompat.Builder(
requireContext(),
Notifications.CHANNEL_DOWNLOADER_PROGRESS
)
.setSmallIcon(R.drawable.ic_round_sync_24)
.setContentTitle("Updating extension")
.setContentText("Step: $installStep")
.setPriority(NotificationCompat.PRIORITY_LOW)
notificationManager.notify(1, builder.build())
},
{ error ->
val builder = NotificationCompat.Builder(
requireContext(),
Notifications.CHANNEL_DOWNLOADER_ERROR
)
.setSmallIcon(R.drawable.ic_round_info_24)
.setContentTitle("Update failed")
.setContentText("Error: ${error.message}")
.setPriority(NotificationCompat.PRIORITY_HIGH)
notificationManager.notify(1, builder.build())
},
{
val builder = NotificationCompat.Builder(
requireContext(),
Notifications.CHANNEL_DOWNLOADER_PROGRESS
)
.setSmallIcon(androidx.media3.ui.R.drawable.exo_ic_check)
.setContentTitle("Update complete")
.setContentText("The extension has been successfully updated.")
.setPriority(NotificationCompat.PRIORITY_LOW)
notificationManager.notify(1, builder.build())
}
)
}else {
mangaExtensionManager.uninstallExtension(pkg.pkgName)
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager // Initialize NotificationManager once
if (pkg.hasUpdate) {
mangaExtensionManager.updateExtension(pkg)
.observeOn(AndroidSchedulers.mainThread()) // Observe on main thread
.subscribe(
{ installStep ->
val builder = NotificationCompat.Builder(
context,
Notifications.CHANNEL_DOWNLOADER_PROGRESS
)
.setSmallIcon(R.drawable.ic_round_sync_24)
.setContentTitle("Updating extension")
.setContentText("Step: $installStep")
.setPriority(NotificationCompat.PRIORITY_LOW)
notificationManager.notify(1, builder.build())
},
{ error ->
Log.e("MangaExtensionsAdapter", "Error: ", error) // Log the error
val builder = NotificationCompat.Builder(
context,
Notifications.CHANNEL_DOWNLOADER_ERROR
)
.setSmallIcon(R.drawable.ic_round_info_24)
.setContentTitle("Update failed")
.setContentText("Error: ${error.message}")
.setPriority(NotificationCompat.PRIORITY_HIGH)
notificationManager.notify(1, builder.build())
},
{
val builder = NotificationCompat.Builder(
context,
Notifications.CHANNEL_DOWNLOADER_PROGRESS
)
.setSmallIcon(androidx.media3.ui.R.drawable.exo_ic_check)
.setContentTitle("Update complete")
.setContentText("The extension has been successfully updated.")
.setPriority(NotificationCompat.PRIORITY_LOW)
notificationManager.notify(1, builder.build())
}
)
} else {
mangaExtensionManager.uninstallExtension(pkg.pkgName)
}
}
}, skipIcons)
private val allExtensionsAdapter =
AllMangaExtensionsAdapter(lifecycleScope, { pkgName ->
@ -132,15 +142,19 @@ class MangaExtensionsFragment : Fragment(), SearchQueryHandler {
)
}, skipIcons)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentMangaExtensionsBinding.inflate(inflater, container, false)
extensionsRecyclerView = binding.mangaExtensionsRecyclerView
extensionsRecyclerView.layoutManager = LinearLayoutManager( requireContext())
extensionsRecyclerView.layoutManager = LinearLayoutManager(requireContext())
extensionsRecyclerView.adapter = extensionsAdapter
allextenstionsRecyclerView = binding.allMangaExtensionsRecyclerView
allextenstionsRecyclerView.layoutManager = LinearLayoutManager( requireContext())
allextenstionsRecyclerView.layoutManager = LinearLayoutManager(requireContext())
allextenstionsRecyclerView.adapter = allExtensionsAdapter
lifecycleScope.launch {
@ -161,7 +175,6 @@ class MangaExtensionsFragment : Fragment(), SearchQueryHandler {
}
}
val extensionsRecyclerView: RecyclerView = binding.mangaExtensionsRecyclerView
return binding.root
}
@ -181,14 +194,18 @@ class MangaExtensionsFragment : Fragment(), SearchQueryHandler {
super.onDestroyView();_binding = null
}
private class MangaExtensionsAdapter(private val onUninstallClicked: (MangaExtension.Installed) -> Unit, skipIcons: Boolean) : RecyclerView.Adapter<MangaExtensionsAdapter.ViewHolder>() {
private class MangaExtensionsAdapter(
private val onUninstallClicked: (MangaExtension.Installed) -> Unit,
skipIcons: Boolean
) : ListAdapter<MangaExtension.Installed, MangaExtensionsAdapter.ViewHolder>(
DIFF_CALLBACK_INSTALLED
) {
private var extensions: List<MangaExtension.Installed> = emptyList()
val skipIcons = skipIcons
// Use submitList to update data
fun updateData(newExtensions: List<MangaExtension.Installed>) {
extensions = newExtensions
notifyDataSetChanged()
submitList(newExtensions)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
@ -198,75 +215,118 @@ class MangaExtensionsFragment : Fragment(), SearchQueryHandler {
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val extension = extensions[position]
val extension = getItem(position) // Use getItem from ListAdapter
holder.extensionNameTextView.text = extension.name
if(!skipIcons) {
if (!skipIcons) {
holder.extensionIconImageView.setImageDrawable(extension.icon)
}
if(extension.hasUpdate){
if (extension.hasUpdate) {
holder.closeTextView.text = "Update"
holder.closeTextView.setTextColor(ContextCompat.getColor(holder.itemView.context, R.color.warning))
}else{
holder.closeTextView.setTextColor(
ContextCompat.getColor(
holder.itemView.context,
R.color.warning
)
)
} else {
holder.closeTextView.text = "Uninstall"
}
holder.closeTextView.setOnClickListener {
onUninstallClicked(extension)
}
}
override fun getItemCount(): Int = extensions.size
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val extensionNameTextView: TextView = view.findViewById(R.id.extensionNameTextView)
val extensionIconImageView: ImageView = view.findViewById(R.id.extensionIconImageView)
val closeTextView: TextView = view.findViewById(R.id.closeTextView)
}
companion object {
val DIFF_CALLBACK_INSTALLED =
object : DiffUtil.ItemCallback<MangaExtension.Installed>() {
override fun areItemsTheSame(
oldItem: MangaExtension.Installed,
newItem: MangaExtension.Installed
): Boolean {
return oldItem.pkgName == newItem.pkgName
}
override fun areContentsTheSame(
oldItem: MangaExtension.Installed,
newItem: MangaExtension.Installed
): Boolean {
return oldItem == newItem
}
}
}
}
private class AllMangaExtensionsAdapter(private val coroutineScope: CoroutineScope,
private val onButtonClicked: (MangaExtension.Available) -> Unit, skipIcons: Boolean) : RecyclerView.Adapter<AllMangaExtensionsAdapter.ViewHolder>() {
private var extensions: List<MangaExtension.Available> = emptyList()
val skipIcons = skipIcons
fun updateData(newExtensions: List<MangaExtension.Available>, installedExtensions: List<MangaExtension.Installed> = emptyList()) {
val installedPkgNames = installedExtensions.map { it.pkgName }.toSet()
extensions = newExtensions.filter { it.pkgName !in installedPkgNames }
filteredExtensions = extensions
notifyDataSetChanged()
private class AllMangaExtensionsAdapter(
private val coroutineScope: CoroutineScope,
private val onButtonClicked: (MangaExtension.Available) -> Unit,
skipIcons: Boolean
) : ListAdapter<MangaExtension.Available, AllMangaExtensionsAdapter.ViewHolder>(
DIFF_CALLBACK_AVAILABLE
) {
init {
setHasStableIds(true)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AllMangaExtensionsAdapter.ViewHolder {
val skipIcons = skipIcons
// Use submitList to update the data
fun updateData(
newExtensions: List<MangaExtension.Available>,
installedExtensions: List<MangaExtension.Installed> = emptyList()
) {
coroutineScope.launch(Dispatchers.Default) {
val installedPkgNames = installedExtensions.map { it.pkgName }.toSet()
val filteredExtensions = newExtensions.filter { it.pkgName !in installedPkgNames }
// Switch back to main thread to update UI
withContext(Dispatchers.Main) {
submitList(filteredExtensions)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_extension_all, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val extension = filteredExtensions[position]
val extension = getItem(position) // Use getItem from ListAdapter
holder.extensionNameTextView.text = extension.name
if (!skipIcons) {
coroutineScope.launch {
val drawable = urlToDrawable(holder.itemView.context, extension.iconUrl)
holder.extensionIconImageView.setImageDrawable(drawable)
}
Glide.with(holder.itemView.context)
.load(extension.iconUrl)
.into(holder.extensionIconImageView)
}
holder.closeTextView.text = "Install"
holder.closeTextView.setOnClickListener {
onButtonClicked(extension)
}
}
override fun getItemCount(): Int = filteredExtensions.size
private var filteredExtensions: List<MangaExtension.Available> = emptyList()
// Filtering function
fun filter(query: String) {
filteredExtensions = if (query.isEmpty()) {
extensions
val filteredExtensions = if (query.isEmpty()) {
currentList
} else {
extensions.filter { it.name.contains(query, ignoreCase = true) }
currentList.filter { it.name.contains(query, ignoreCase = true) }
}
notifyDataSetChanged()
submitList(filteredExtensions)
}
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
@ -275,18 +335,24 @@ class MangaExtensionsFragment : Fragment(), SearchQueryHandler {
val closeTextView: TextView = view.findViewById(R.id.closeTextView)
}
suspend fun urlToDrawable(context: Context, url: String): Drawable? {
return withContext(Dispatchers.IO) {
try {
return@withContext Glide.with(context)
.load(url)
.submit()
.get()
} catch (e: Exception) {
e.printStackTrace()
return@withContext null
companion object {
val DIFF_CALLBACK_AVAILABLE =
object : DiffUtil.ItemCallback<MangaExtension.Available>() {
override fun areItemsTheSame(
oldItem: MangaExtension.Available,
newItem: MangaExtension.Available
): Boolean {
return oldItem.pkgName == newItem.pkgName
}
override fun areContentsTheSame(
oldItem: MangaExtension.Available,
newItem: MangaExtension.Available
): Boolean {
return oldItem == newItem
}
}
}
}
}
}

View file

@ -100,16 +100,18 @@ OS Version: $CODENAME $RELEASE ($SDK_INT)
getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit().putBoolean("use_material_you", isChecked).apply()
}
val animeSource = loadData<Int>("settings_def_anime_source_s")?.let { if (it >= AnimeSources.names.size) 0 else it } ?: 0
if (MangaSources.names.isNotEmpty() && animeSource in 0 until MangaSources.names.size) {
binding.mangaSource.setText(MangaSources.names[animeSource], false)
//val animeSource = loadData<Int>("settings_def_anime_source_s")?.let { if (it >= AnimeSources.names.size) 0 else it } ?: 0
val animeSource = getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getInt("settings_def_anime_source_s_r", 0)
if (AnimeSources.names.isNotEmpty() && animeSource in 0 until AnimeSources.names.size) {
binding.animeSource.setText(AnimeSources.names[animeSource], false)
}
binding.animeSource.setAdapter(ArrayAdapter(this, R.layout.item_dropdown, AnimeSources.names))
binding.animeSource.setOnItemClickListener { _, _, i, _ ->
saveData("settings_def_anime_source_s", i)
//saveData("settings_def_anime_source_s", i)
getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit().putInt("settings_def_anime_source_s_r", i).apply()
binding.animeSource.clearFocus()
}
@ -186,7 +188,8 @@ OS Version: $CODENAME $RELEASE ($SDK_INT)
saveData("settings_prefer_dub", isChecked)
}
val mangaSource = loadData<Int>("settings_def_manga_source_s")?.let { if (it >= MangaSources.names.size) 0 else it } ?: 0
//val mangaSource = loadData<Int>("settings_def_manga_source_s")?.let { if (it >= MangaSources.names.size) 0 else it } ?: 0
val mangaSource = getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getInt("settings_def_manga_source_s_r", 0)
if (MangaSources.names.isNotEmpty() && mangaSource in 0 until MangaSources.names.size) {
binding.mangaSource.setText(MangaSources.names[mangaSource], false)
}
@ -196,7 +199,8 @@ OS Version: $CODENAME $RELEASE ($SDK_INT)
// Set up the item click listener for the dropdown.
binding.mangaSource.setOnItemClickListener { _, _, i, _ ->
saveData("settings_def_manga_source_s", i)
//saveData("settings_def_manga_source_s", i)
getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit().putInt("settings_def_manga_source_s_r", i).apply()
binding.mangaSource.clearFocus()
}

View file

@ -14,11 +14,12 @@ import kotlinx.coroutines.withTimeoutOrNull
class SubscriptionHelper {
companion object {
private fun loadSelected(context: Context, mediaId: Int, isAdult: Boolean, isAnime: Boolean): Selected {
val sharedPreferences = context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
val data = loadData<Selected>("${mediaId}-select", context) ?: Selected().let {
it.sourceIndex =
if (isAdult) 0
else if (isAnime) {loadData("settings_def_anime_source_s_r", context) ?: 0}
else loadData("settings_def_manga_source_s_r", context) ?: 0
else if (isAnime) {sharedPreferences.getInt("settings_def_anime_source_s_r",0)}
else {sharedPreferences.getInt("settings_def_manga_source_s_r",0)}
it.preferDub = loadData("settings_prefer_dub", context) ?: false
it
}