various fixes and updates

This commit is contained in:
Finnley Somdahl 2023-10-20 21:38:40 -05:00
parent da81646297
commit d1270c7c83
21 changed files with 487 additions and 162 deletions

View file

@ -97,6 +97,9 @@ dependencies {
implementation 'com.alexvasilkov:gesture-views:2.8.3'
implementation 'com.github.VipulOG:ebook-reader:0.1.6'
// string matching
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
// Aniyomi
implementation 'io.reactivex:rxjava:1.3.8'
implementation 'io.reactivex:rxandroid:1.2.1'

View file

@ -11,6 +11,7 @@ import ani.dantotsu.aniyomi.anime.custom.PreferenceModule
import eu.kanade.tachiyomi.data.notification.Notifications
import tachiyomi.core.util.system.logcat
import ani.dantotsu.others.DisabledReports
import com.google.android.material.color.DynamicColors
import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase
import logcat.AndroidLogcatLogger
@ -33,6 +34,11 @@ class App : MultiDexApplication() {
override fun onCreate() {
super.onCreate()
val sharedPreferences = getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
val useMaterialYou = sharedPreferences.getBoolean("use_material_you", false)
if(useMaterialYou) {
DynamicColors.applyToActivitiesIfAvailable(this)
}
registerActivityLifecycleCallbacks(mFTActivityLifecycleCallbacks)
Firebase.crashlytics.setCrashlyticsCollectionEnabled(!DisabledReports)

View file

@ -10,6 +10,7 @@ import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.core.preference.AndroidPreferenceStore
import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.network.NetworkPreferences
import kotlinx.serialization.json.Json
import uy.kohesive.injekt.api.InjektModule
import uy.kohesive.injekt.api.InjektRegistrar
@ -21,7 +22,7 @@ class AppModule(val app: Application) : InjektModule {
override fun InjektRegistrar.registerInjectables() {
addSingleton(app)
addSingletonFactory { NetworkHelper(app) }
addSingletonFactory { NetworkHelper(app, get()) }
addSingletonFactory { AnimeExtensionManager(app) }
@ -44,6 +45,13 @@ class PreferenceModule(val application: Application) : InjektModule {
AndroidPreferenceStore(application)
}
addSingletonFactory {
NetworkPreferences(
preferenceStore = get(),
verboseLogging = false,
)
}
addSingletonFactory {
SourcePreferences(get())
}

View file

@ -50,25 +50,14 @@ class MediaDetailsViewModel : ViewModel() {
fun loadSelected(media: Media): Selected {
val data = loadData<Selected>("${media.id}-select") ?: Selected().let {
it.source = if (media.isAdult) "" else when (media.anime != null) {
true -> loadData("settings_def_anime_source") ?: ""
else -> loadData("settings_def_manga_source") ?: ""
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
}
it.preferDub = loadData("settings_prefer_dub") ?: false
it.sourceIndex = loadSelectedStringLocation(it.source)
saveSelected(media.id, it)
it
}
if (media.anime != null) {
val sources = if (media.isAdult) HAnimeSources else AnimeSources
data.sourceIndex = sources.list.indexOfFirst { it.name == data.source }
} else {
val sources = if (media.isAdult) HMangaSources else MangaSources
data.sourceIndex = sources.list.indexOfFirst { it.name == data.source }
}
if (data.sourceIndex == -1) {
data.sourceIndex = 0
}
return data
}
@ -194,8 +183,8 @@ class MediaDetailsViewModel : ViewModel() {
val server = selected.server ?: return false
val link = ep.link ?: return false
ep.extractors = mutableListOf(watchSources?.get(loadSelectedStringLocation(selected.source))?.let {
selected.sourceIndex = loadSelectedStringLocation(selected.source)
ep.extractors = mutableListOf(watchSources?.get(selected.sourceIndex)?.let {
selected.sourceIndex = selected.sourceIndex
if (!post && !it.allowsPreloading) null
else ep.sEpisode?.let { it1 ->
it.loadSingleVideoServer(server, link, ep.extra,
@ -266,7 +255,7 @@ class MediaDetailsViewModel : ViewModel() {
suspend fun loadMangaChapterImages(chapter: MangaChapter, selected: Selected, post: Boolean = true): Boolean {
return tryWithSuspend(true) {
chapter.addImages(
mangaReadSources?.get(loadSelectedStringLocation(selected.source))?.loadImages(chapter.link, chapter.sChapter) ?: return@tryWithSuspend false
mangaReadSources?.get(selected.sourceIndex)?.loadImages(chapter.link, chapter.sChapter) ?: return@tryWithSuspend false
)
if (post) mangaChapter.postValue(chapter)
true
@ -289,7 +278,7 @@ class MediaDetailsViewModel : ViewModel() {
}
suspend fun autoSearchNovels(media: Media) {
val source = novelSources[loadSelectedStringLocation(media.selected?.source?:"")]
val source = novelSources[media.selected?.sourceIndex?:0]
tryWithSuspend(post = true) {
if (source != null) {
novelResponses.postValue(source.sortedSearch(media))

View file

@ -7,7 +7,7 @@ data class Selected(
var recyclerStyle: Int? = null,
var recyclerReversed: Boolean = false,
var chip: Int = 0,
var source: String = "",
//var source: String = "",
var sourceIndex: Int = 0,
var preferDub: Boolean = false,
var server: String? = null,

View file

@ -1,22 +1,33 @@
package ani.dantotsu.media.manga
import android.content.ContentResolver
import android.content.ContentValues
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import android.util.LruCache
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.online.HttpSource
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File
import java.io.FileOutputStream
data class ImageData(
val page: Page,
val source: HttpSource,
val source: HttpSource
){
suspend fun fetchAndProcessImage(page: Page, httpSource: HttpSource): Bitmap? {
suspend fun fetchAndProcessImage(page: Page, httpSource: HttpSource, context: Context): Bitmap? {
return withContext(Dispatchers.IO) {
try {
// Fetch the image
val response = httpSource.getImage(page)
println("Response: ${response.code}")
println("Response: ${response.message}")
// Convert the Response to an InputStream
val inputStream = response.body?.byteStream()
@ -25,6 +36,7 @@ data class ImageData(
val bitmap = BitmapFactory.decodeStream(inputStream)
inputStream?.close()
saveImage(bitmap, context.contentResolver, page.imageUrl!!, Bitmap.CompressFormat.JPEG, 100)
return@withContext bitmap
} catch (e: Exception) {
@ -36,6 +48,39 @@ data class ImageData(
}
}
fun saveImage(bitmap: Bitmap, contentResolver: ContentResolver, filename: String, format: Bitmap.CompressFormat, quality: Int) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, filename)
put(MediaStore.MediaColumns.MIME_TYPE, "image/${format.name.lowercase()}")
put(MediaStore.MediaColumns.RELATIVE_PATH, "${Environment.DIRECTORY_DOWNLOADS}/Dantotsu/Manga")
}
val uri: Uri? = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
uri?.let {
contentResolver.openOutputStream(it)?.use { os ->
bitmap.compress(format, quality, os)
}
}
} else {
val directory = File("${Environment.getExternalStorageDirectory()}${File.separator}Dantotsu${File.separator}Anime")
if (!directory.exists()) {
directory.mkdirs()
}
val file = File(directory, filename)
FileOutputStream(file).use { outputStream ->
bitmap.compress(format, quality, outputStream)
}
}
} catch (e: Exception) {
// Handle exception here
println("Exception while saving image: ${e.message}")
}
}
class MangaCache() {
private val maxMemory = (Runtime.getRuntime().maxMemory() / 1024 / 2).toInt()
private val cache = LruCache<String, ImageData>(maxMemory)

View file

@ -161,7 +161,7 @@ abstract class BaseImageAdapter(
println(mangaCache.get(link.url))
println("cache size: ${mangaCache.size()}")
mangaCache.get(link.url)?.let { imageData ->
val bitmap = imageData.fetchAndProcessImage(imageData.page, imageData.source)
val bitmap = imageData.fetchAndProcessImage(imageData.page, imageData.source, context = this@loadBitmap)
it.load(bitmap)
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)

View file

@ -2,6 +2,7 @@ package ani.dantotsu.parsers
import android.content.ContentResolver
import android.content.ContentValues
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
@ -199,12 +200,14 @@ class DynamicMangaParser(extension: MangaExtension.Installed) : MangaParser() {
return coroutineScope {
try {
println("source.name " + source.name)
val res = source.getPageList(sChapter)
val reIndexedPages = res.mapIndexed { index, page -> Page(index, page.url, page.imageUrl, page.uri) }
val deferreds = reIndexedPages.map { page ->
async(Dispatchers.IO) {
mangaCache.put(page.imageUrl ?: "", ImageData(page, source))
logger("put page: ${page.imageUrl}")
pageToMangaImage(page)
}
}
@ -212,11 +215,44 @@ class DynamicMangaParser(extension: MangaExtension.Installed) : MangaParser() {
deferreds.awaitAll()
} catch (e: Exception) {
logger("loadImages Exception: $e")
Toast.makeText(currContext(), "Failed to load images: $e", Toast.LENGTH_SHORT).show()
emptyList()
}
}
}
suspend fun fetchAndProcessImage(page: Page, httpSource: HttpSource, context: Context): Bitmap? {
return withContext(Dispatchers.IO) {
try {
// Fetch the image
val response = httpSource.getImage(page)
println("Response: ${response.code}")
println("Response: ${response.message}")
// Convert the Response to an InputStream
val inputStream = response.body?.byteStream()
// Convert InputStream to Bitmap
val bitmap = BitmapFactory.decodeStream(inputStream)
inputStream?.close()
ani.dantotsu.media.manga.saveImage(
bitmap,
context.contentResolver,
page.imageUrl!!,
Bitmap.CompressFormat.JPEG,
100
)
return@withContext bitmap
} catch (e: Exception) {
// Handle any exceptions
println("An error occurred: ${e.message}")
return@withContext null
}
}
}
fun fetchAndSaveImage(page: Page, httpSource: HttpSource, contentResolver: ContentResolver) {

View file

@ -7,6 +7,8 @@ import eu.kanade.tachiyomi.source.model.SManga
import java.io.Serializable
import java.net.URLDecoder
import java.net.URLEncoder
import me.xdrop.fuzzywuzzy.FuzzySearch
abstract class BaseParser {
@ -55,21 +57,41 @@ abstract class BaseParser {
setUserText("Searching : ${mediaObj.mainName()}")
val results = search(mediaObj.mainName())
val sortedResults = if (results.isNotEmpty()) {
StringMatcher.closestShowMovedToTop(mediaObj.mainName(), results)
results.sortedByDescending { FuzzySearch.ratio(it.name, mediaObj.mainName()) }
} else {
emptyList()
}
response = sortedResults.firstOrNull()
if (response == null) {
if (response == null || FuzzySearch.ratio(response.name, mediaObj.mainName()) < 100) {
setUserText("Searching : ${mediaObj.nameRomaji}")
val romajiResults = search(mediaObj.nameRomaji)
val sortedRomajiResults = if (romajiResults.isNotEmpty()) {
StringMatcher.closestShowMovedToTop(mediaObj.nameRomaji, romajiResults)
romajiResults.sortedByDescending { FuzzySearch.ratio(it.name, mediaObj.nameRomaji) }
} else {
emptyList()
}
response = sortedRomajiResults.firstOrNull()
val closestRomaji = sortedRomajiResults.firstOrNull()
logger("Closest match from RomajiResults: ${closestRomaji?.name ?: "None"}")
response = if (response == null) {
logger("No exact match found in results. Using closest match from RomajiResults.")
closestRomaji
} 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")
if (romajiRatio > mainNameRatio) {
logger("RomajiResults has a closer match. Replacing response.")
closestRomaji
} else {
logger("Results has a closer or equal match. Keeping existing response.")
response
}
}
}
saveShowResponse(mediaObj.id, response)
}

View file

@ -1,5 +1,7 @@
package ani.dantotsu.parsers
import ani.dantotsu.logger
class StringMatcher {
companion object {
private fun levenshteinDistance(s1: String, s2: String): Int {
@ -52,8 +54,10 @@ class StringMatcher {
val closestShowAndIndex = closestShow(target, shows)
val closestIndex = closestShowAndIndex.second
if (closestIndex == -1) {
logger("No closest show found for $target")
return shows // Return original list if no closest show found
}
logger("Closest show found for $target is ${closestShowAndIndex.first.name}")
return listOf(shows[closestIndex]) + shows.subList(0, closestIndex) + shows.subList(closestIndex + 1, shows.size)
}

View file

@ -11,6 +11,7 @@ import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.getSystemService
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
@ -18,6 +19,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.R
import ani.dantotsu.databinding.FragmentAnimeExtensionsBinding
import ani.dantotsu.loadData
import com.bumptech.glide.Glide
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
@ -35,13 +37,57 @@ class AnimeExtensionsFragment : Fragment(), SearchQueryHandler {
private var _binding: FragmentAnimeExtensionsBinding? = null
private val binding get() = _binding!!
val skipIcons = loadData("skip_extension_icons") ?: false
private lateinit var extensionsRecyclerView: RecyclerView
private lateinit var allextenstionsRecyclerView: RecyclerView
private val animeExtensionManager: AnimeExtensionManager = Injekt.get<AnimeExtensionManager>()
private val extensionsAdapter = AnimeExtensionsAdapter { pkgName ->
animeExtensionManager.uninstallExtension(pkgName)
private val extensionsAdapter = AnimeExtensionsAdapter ({ pkg ->
if(pkg.hasUpdate){
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())
}
private val allExtensionsAdapter = AllAnimeExtensionsAdapter(lifecycleScope) { pkgName ->
)
}else {
animeExtensionManager.uninstallExtension(pkg.pkgName)
}
}, skipIcons)
private val allExtensionsAdapter = AllAnimeExtensionsAdapter(lifecycleScope, { pkgName ->
val notificationManager =
requireContext().getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
@ -84,7 +130,7 @@ class AnimeExtensionsFragment : Fragment(), SearchQueryHandler {
notificationManager.notify(1, builder.build())
}
)
}
}, skipIcons)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = FragmentAnimeExtensionsBinding.inflate(inflater, container, false)
@ -136,9 +182,10 @@ class AnimeExtensionsFragment : Fragment(), SearchQueryHandler {
}
private class AnimeExtensionsAdapter(private val onUninstallClicked: (String) -> Unit) : RecyclerView.Adapter<AnimeExtensionsAdapter.ViewHolder>() {
private class AnimeExtensionsAdapter(private val onUninstallClicked: (AnimeExtension.Installed) -> Unit, skipIcons: Boolean) : RecyclerView.Adapter<AnimeExtensionsAdapter.ViewHolder>() {
private var extensions: List<AnimeExtension.Installed> = emptyList()
val skipIcons = skipIcons
fun updateData(newExtensions: List<AnimeExtension.Installed>) {
extensions = newExtensions
@ -154,10 +201,17 @@ class AnimeExtensionsFragment : Fragment(), SearchQueryHandler {
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val extension = extensions[position]
holder.extensionNameTextView.text = extension.name
if (!skipIcons) {
holder.extensionIconImageView.setImageDrawable(extension.icon)
}
if(extension.hasUpdate){
holder.closeTextView.text = "Update"
holder.closeTextView.setTextColor(ContextCompat.getColor(holder.itemView.context, R.color.warning))
}else{
holder.closeTextView.text = "Uninstall"
}
holder.closeTextView.setOnClickListener {
onUninstallClicked(extension.pkgName)
onUninstallClicked(extension)
}
}
@ -171,8 +225,9 @@ class AnimeExtensionsFragment : Fragment(), SearchQueryHandler {
}
private class AllAnimeExtensionsAdapter(private val coroutineScope: CoroutineScope,
private val onButtonClicked: (AnimeExtension.Available) -> Unit) : RecyclerView.Adapter<AllAnimeExtensionsAdapter.ViewHolder>() {
private val onButtonClicked: (AnimeExtension.Available) -> Unit, skipIcons: Boolean) : RecyclerView.Adapter<AllAnimeExtensionsAdapter.ViewHolder>() {
private var extensions: List<AnimeExtension.Available> = emptyList()
val skipIcons = skipIcons
fun updateData(newExtensions: List<AnimeExtension.Available>, installedExtensions: List<AnimeExtension.Installed> = emptyList()) {
val installedPkgNames = installedExtensions.map { it.pkgName }.toSet()
@ -190,10 +245,12 @@ class AnimeExtensionsFragment : Fragment(), SearchQueryHandler {
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val extension = filteredExtensions[position]
holder.extensionNameTextView.text = extension.name
if (!skipIcons) {
coroutineScope.launch {
val drawable = urlToDrawable(holder.itemView.context, extension.iconUrl)
holder.extensionIconImageView.setImageDrawable(drawable)
}
}
holder.closeTextView.text = "Install"
holder.closeTextView.setOnClickListener {
onButtonClicked(extension)

View file

@ -10,6 +10,7 @@ import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
@ -17,6 +18,7 @@ import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.R
import ani.dantotsu.databinding.FragmentMangaBinding
import ani.dantotsu.databinding.FragmentMangaExtensionsBinding
import ani.dantotsu.loadData
import com.bumptech.glide.Glide
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
@ -34,14 +36,58 @@ class MangaExtensionsFragment : Fragment(), SearchQueryHandler {
private var _binding: FragmentMangaExtensionsBinding? = null
private val binding get() = _binding!!
val skipIcons = loadData("skip_extension_icons") ?: false
private lateinit var extensionsRecyclerView: RecyclerView
private lateinit var allextenstionsRecyclerView: RecyclerView
private val mangaExtensionManager:MangaExtensionManager = Injekt.get<MangaExtensionManager>()
private val extensionsAdapter = MangaExtensionsAdapter { pkgName ->
mangaExtensionManager.uninstallExtension(pkgName)
private val extensionsAdapter = MangaExtensionsAdapter ({ pkg ->
if(pkg.hasUpdate){
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)
}
}, skipIcons)
private val allExtensionsAdapter =
AllMangaExtensionsAdapter(lifecycleScope) { pkgName ->
AllMangaExtensionsAdapter(lifecycleScope, { pkgName ->
val notificationManager =
requireContext().getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
@ -84,7 +130,8 @@ class MangaExtensionsFragment : Fragment(), SearchQueryHandler {
notificationManager.notify(1, builder.build())
}
)
}
}, skipIcons)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = FragmentMangaExtensionsBinding.inflate(inflater, container, false)
@ -134,9 +181,10 @@ class MangaExtensionsFragment : Fragment(), SearchQueryHandler {
super.onDestroyView();_binding = null
}
private class MangaExtensionsAdapter(private val onUninstallClicked: (String) -> Unit) : RecyclerView.Adapter<MangaExtensionsAdapter.ViewHolder>() {
private class MangaExtensionsAdapter(private val onUninstallClicked: (MangaExtension.Installed) -> Unit, skipIcons: Boolean) : RecyclerView.Adapter<MangaExtensionsAdapter.ViewHolder>() {
private var extensions: List<MangaExtension.Installed> = emptyList()
val skipIcons = skipIcons
fun updateData(newExtensions: List<MangaExtension.Installed>) {
extensions = newExtensions
@ -152,10 +200,17 @@ class MangaExtensionsFragment : Fragment(), SearchQueryHandler {
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val extension = extensions[position]
holder.extensionNameTextView.text = extension.name
if(!skipIcons) {
holder.extensionIconImageView.setImageDrawable(extension.icon)
}
if(extension.hasUpdate){
holder.closeTextView.text = "Update"
holder.closeTextView.setTextColor(ContextCompat.getColor(holder.itemView.context, R.color.warning))
}else{
holder.closeTextView.text = "Uninstall"
}
holder.closeTextView.setOnClickListener {
onUninstallClicked(extension.pkgName)
onUninstallClicked(extension)
}
}
@ -169,8 +224,9 @@ class MangaExtensionsFragment : Fragment(), SearchQueryHandler {
}
private class AllMangaExtensionsAdapter(private val coroutineScope: CoroutineScope,
private val onButtonClicked: (MangaExtension.Available) -> Unit) : RecyclerView.Adapter<AllMangaExtensionsAdapter.ViewHolder>() {
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()
@ -188,10 +244,12 @@ class MangaExtensionsFragment : Fragment(), SearchQueryHandler {
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val extension = filteredExtensions[position]
holder.extensionNameTextView.text = extension.name
if (!skipIcons) {
coroutineScope.launch {
val drawable = urlToDrawable(holder.itemView.context, extension.iconUrl)
holder.extensionIconImageView.setImageDrawable(drawable)
}
}
holder.closeTextView.text = "Install"
holder.closeTextView.setOnClickListener {
onButtonClicked(extension)

View file

@ -2,6 +2,7 @@ package ani.dantotsu.settings
import android.annotation.SuppressLint
import android.app.AlertDialog
import android.content.Context
import android.content.Intent
import android.graphics.drawable.Animatable
import android.os.Build.*
@ -31,6 +32,7 @@ import ani.dantotsu.subcriptions.Subscription.Companion.defaultTime
import ani.dantotsu.subcriptions.Subscription.Companion.startSubscription
import ani.dantotsu.subcriptions.Subscription.Companion.timeMinutes
import eu.kanade.domain.base.BasePreferences
import eu.kanade.tachiyomi.network.NetworkPreferences
import io.noties.markwon.Markwon
import io.noties.markwon.SoftBreakAddsNewLinePlugin
import kotlinx.coroutines.Dispatchers
@ -47,6 +49,7 @@ class SettingsActivity : AppCompatActivity() {
}
lateinit var binding: ActivitySettingsBinding
private val extensionInstaller = Injekt.get<BasePreferences>().extensionInstaller()
private val networkPreferences = Injekt.get<NetworkPreferences>()
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
@ -92,18 +95,21 @@ OS Version: $CODENAME $RELEASE ($SDK_INT)
onBackPressedDispatcher.onBackPressed()
}
val animeSourceName = loadData<String>("settings_def_anime_source") ?: AnimeSources.names[0]
// Set the dropdown item in the UI if the name exists in the list.
if (AnimeSources.names.contains(animeSourceName)) {
binding.animeSource.setText(animeSourceName, false)
binding.settingsUseMaterialYou.isChecked = getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getBoolean("use_material_you", false)
binding.settingsUseMaterialYou.setOnCheckedChangeListener { _, isChecked ->
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)
}
binding.animeSource.setAdapter(ArrayAdapter(this, R.layout.item_dropdown, AnimeSources.names))
// Set up the item click listener for the dropdown.
binding.animeSource.setOnItemClickListener { _, _, i, _ ->
val selectedName = AnimeSources.names[i]
// Save the string name of the selected item.
saveData("settings_def_anime_source", selectedName)
saveData("settings_def_anime_source_s", i)
binding.animeSource.clearFocus()
}
@ -131,6 +137,17 @@ OS Version: $CODENAME $RELEASE ($SDK_INT)
}
}
binding.skipExtensionIcons.isChecked = loadData("skip_extension_icons") ?: false
binding.skipExtensionIcons.setOnCheckedChangeListener { _, isChecked ->
saveData("skip_extension_icons", isChecked)
}
binding.userAgent.setText(networkPreferences.defaultUserAgent().get())
binding.userAgent.setOnEditorActionListener { _, _, _ ->
networkPreferences.defaultUserAgent().set(binding.userAgent.text.toString())
true
}
binding.settingsDownloadInSd.isChecked = loadData("sd_dl") ?: false
binding.settingsDownloadInSd.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
@ -169,12 +186,9 @@ OS Version: $CODENAME $RELEASE ($SDK_INT)
saveData("settings_prefer_dub", isChecked)
}
// Load the saved manga source name from data storage.
val mangaSourceName = loadData<String>("settings_def_manga_source") ?: MangaSources.names[0]
// Set the dropdown item in the UI if the name exists in the list.
if (MangaSources.names.contains(mangaSourceName)) {
binding.mangaSource.setText(mangaSourceName, false)
val mangaSource = loadData<Int>("settings_def_manga_source_s")?.let { if (it >= MangaSources.names.size) 0 else it } ?: 0
if (MangaSources.names.isNotEmpty() && mangaSource in 0 until MangaSources.names.size) {
binding.mangaSource.setText(MangaSources.names[mangaSource], false)
}
// Set up the dropdown adapter.
@ -182,9 +196,7 @@ OS Version: $CODENAME $RELEASE ($SDK_INT)
// Set up the item click listener for the dropdown.
binding.mangaSource.setOnItemClickListener { _, _, i, _ ->
val selectedName = MangaSources.names[i]
// Save the string name of the selected item.
saveData("settings_def_manga_source", selectedName)
saveData("settings_def_manga_source_s", i)
binding.mangaSource.clearFocus()
}

View file

@ -15,21 +15,13 @@ class SubscriptionHelper {
companion object {
private fun loadSelected(context: Context, mediaId: Int, isAdult: Boolean, isAnime: Boolean): Selected {
val data = loadData<Selected>("${mediaId}-select", context) ?: Selected().let {
it.source =
if (isAdult) ""
else if (isAnime) {loadData("settings_def_anime_source", context) ?: ""}
else loadData("settings_def_manga_source", context) ?: ""
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
it.preferDub = loadData("settings_prefer_dub", context) ?: false
it
}
if (isAnime){
val sources = if (isAdult) HAnimeSources else AnimeSources
data.sourceIndex = sources.list.indexOfFirst { it.name == data.source }
}else{
val sources = if (isAdult) HMangaSources else MangaSources
data.sourceIndex = sources.list.indexOfFirst { it.name == data.source }
}
if (data.sourceIndex == -1) {data.sourceIndex = 0}
return data
}
@ -40,9 +32,7 @@ class SubscriptionHelper {
fun getAnimeParser(context: Context, isAdult: Boolean, id: Int): AnimeParser {
val sources = if (isAdult) HAnimeSources else AnimeSources
val selected = loadSelected(context, id, isAdult, true)
var location = sources.list.indexOfFirst { it.name == selected.source }
if (location == -1) {location = 0}
val parser = sources[location]
val parser = sources[selected.sourceIndex]
parser.selectDub = selected.preferDub
return parser
}
@ -69,9 +59,7 @@ class SubscriptionHelper {
fun getMangaParser(context: Context, isAdult: Boolean, id: Int): MangaParser {
val sources = if (isAdult) HMangaSources else MangaSources
val selected = loadSelected(context, id, isAdult, false)
var location = sources.list.indexOfFirst { it.name == selected.source }
if (location == -1) {location = 0}
return sources[location]
return sources[selected.sourceIndex]
}
suspend fun getChapter(context: Context, parser: MangaParser, id: Int, isAdult: Boolean): MangaChapter? {

View file

@ -11,11 +11,13 @@ import eu.kanade.tachiyomi.network.interceptor.UncaughtExceptionInterceptor
import eu.kanade.tachiyomi.network.interceptor.UserAgentInterceptor
import okhttp3.Cache
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import java.io.File
import java.util.concurrent.TimeUnit
class NetworkHelper(
context: Context,
private val preferences: NetworkPreferences,
) {
private val cacheDir = File(context.cacheDir, "network_cache")
@ -40,18 +42,17 @@ class NetworkHelper(
.addInterceptor(UncaughtExceptionInterceptor())
.addInterceptor(userAgentInterceptor)
/*if (preferences.verboseLogging().get()) {
if (preferences.verboseLogging().get()) {
val httpLoggingInterceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.HEADERS
}
builder.addNetworkInterceptor(httpLoggingInterceptor)
}*/
}
//when (preferences.dohProvider().get()) {
when (PREF_DOH_CLOUDFLARE) {
when (preferences.dohProvider().get()) {
PREF_DOH_CLOUDFLARE -> builder.dohCloudflare()
PREF_DOH_GOOGLE -> builder.dohGoogle()
/*PREF_DOH_ADGUARD -> builder.dohAdGuard()
PREF_DOH_ADGUARD -> builder.dohAdGuard()
PREF_DOH_QUAD9 -> builder.dohQuad9()
PREF_DOH_ALIDNS -> builder.dohAliDNS()
PREF_DOH_DNSPOD -> builder.dohDNSPod()
@ -60,7 +61,7 @@ class NetworkHelper(
PREF_DOH_MULLVAD -> builder.dohMullvad()
PREF_DOH_CONTROLD -> builder.dohControlD()
PREF_DOH_NJALLA -> builder.dohNajalla()
PREF_DOH_SHECAN -> builder.dohShecan()*/
PREF_DOH_SHECAN -> builder.dohShecan()
}
return builder
@ -75,5 +76,5 @@ class NetworkHelper(
.build()
}
fun defaultUserAgentProvider() = "Mozilla/5.0 (Linux; Android %s; %s) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Mobile Safari/537.36"//preferences.defaultUserAgent().get().trim()
fun defaultUserAgentProvider() = preferences.defaultUserAgent().get().trim()
}

View file

@ -0,0 +1,22 @@
package eu.kanade.tachiyomi.network
import tachiyomi.core.preference.Preference
import tachiyomi.core.preference.PreferenceStore
class NetworkPreferences(
private val preferenceStore: PreferenceStore,
private val verboseLogging: Boolean = false,
) {
fun verboseLogging(): Preference<Boolean> {
return preferenceStore.getBoolean("verbose_logging", verboseLogging)
}
fun dohProvider(): Preference<Int> {
return preferenceStore.getInt("doh_provider", 1)
}
fun defaultUserAgent(): Preference<String> {
return preferenceStore.getString("default_user_agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:110.0) Gecko/20100101 Firefox/110.0")
}
}

View file

@ -74,6 +74,27 @@
android:paddingStart="32dp"
android:paddingEnd="32dp">
<ani.dantotsu.others.Xpandable
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="64dp"
android:fontFamily="@font/poppins_bold"
android:gravity="center_vertical"
android:text="@string/theme"
android:textColor="?attr/colorSecondary"
app:drawableEndCompat="@drawable/ic_round_arrow_drop_down_24"
tools:ignore="TextContrastCheck" />
<LinearLayout
android:id="@+id/settingsThemeContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
@ -81,13 +102,6 @@
android:gravity="center"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:fontFamily="@font/poppins_bold"
android:text="@string/theme"
android:textColor="?attr/colorSecondary" />
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
@ -143,8 +157,27 @@
app:tint="@color/bg_opp"
tools:ignore="ContentDescription,SpeakableTextPresentCheck,ImageContrastCheck" />
</androidx.cardview.widget.CardView>
</LinearLayout>
</LinearLayout>
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/settingsUseMaterialYou"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="false"
android:drawableStart="@drawable/ic_round_new_releases_24"
android:drawablePadding="16dp"
android:elegantTextHeight="true"
android:fontFamily="@font/poppins_bold"
android:minHeight="64dp"
android:text="@string/use_material_you"
android:textAlignment="viewStart"
android:textColor="@color/bg_opp"
app:cornerRadius="0dp"
app:drawableTint="?attr/colorPrimary"
app:showText="false"
app:thumbTint="@color/button_switch_track" />
</ani.dantotsu.others.Xpandable>
<ani.dantotsu.others.Xpandable
android:layout_width="match_parent"
@ -429,6 +462,44 @@
app:showText="false"
app:thumbTint="@color/button_switch_track" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/skipExtensionIcons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="false"
android:drawableStart="@drawable/ic_round_new_releases_24"
android:drawablePadding="16dp"
android:elegantTextHeight="true"
android:fontFamily="@font/poppins_bold"
android:minHeight="64dp"
android:text="@string/skip_loading_extension_icons"
android:textAlignment="viewStart"
android:textColor="@color/bg_opp"
app:cornerRadius="0dp"
app:drawableTint="?attr/colorPrimary"
app:showText="false"
app:thumbTint="@color/button_switch_track" />
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/userAgent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:hint="user agent"
app:boxCornerRadiusBottomEnd="8dp"
app:boxCornerRadiusBottomStart="8dp"
app:boxCornerRadiusTopEnd="8dp"
app:boxCornerRadiusTopStart="8dp"
app:hintAnimationEnabled="true" />
</com.google.android.material.textfield.TextInputLayout>
</ani.dantotsu.others.Xpandable>
<ani.dantotsu.others.Xpandable
@ -698,8 +769,8 @@
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="match_parent"
android:layout_height="64dp"
android:layout_marginTop="8dp"
android:layout_marginStart="-12dp"
android:layout_marginTop="8dp"
android:fontFamily="@font/poppins_bold"
android:insetTop="0dp"
android:insetBottom="0dp"
@ -1222,11 +1293,11 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:clipToPadding="false"
android:gravity="center"
android:orientation="horizontal">
android:orientation="horizontal"
android:paddingTop="16dp"
android:paddingBottom="16dp">
<ImageView
android:id="@+id/settingBuyMeCoffee"

View file

@ -28,6 +28,6 @@
android:layout_gravity="center_vertical"
android:textStyle="bold"
android:text="Uninstall"
android:textColor="@color/fav"
android:textColor="@color/pink_500"
android:textSize="14sp"/>
</LinearLayout>

View file

@ -28,6 +28,6 @@
android:layout_gravity="center_vertical"
android:textStyle="bold"
android:text="Install"
android:textColor="@color/fav"
android:textColor="@color/pink_500"
android:textSize="14sp"/>
</LinearLayout>

View file

@ -21,4 +21,5 @@
<color name="nav_status">#80FFFFFF</color>
<color name="fav">#ad5edd</color>
<color name="filler">#54FF8400</color>
<color name="warning">#FF0000</color>
</resources>

View file

@ -624,5 +624,7 @@
<string name="view_manga">View Manga</string>
<string name="force_legacy_installer">Force Legacy Installer</string>
<string name="extensions_settings">Extensions</string>
<string name="skip_loading_extension_icons">skip loading extension icons</string>
<string name="use_material_you">Use Material You</string>
</resources>