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.alexvasilkov:gesture-views:2.8.3'
implementation 'com.github.VipulOG:ebook-reader:0.1.6' implementation 'com.github.VipulOG:ebook-reader:0.1.6'
// string matching
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
// Aniyomi // Aniyomi
implementation 'io.reactivex:rxjava:1.3.8' implementation 'io.reactivex:rxjava:1.3.8'
implementation 'io.reactivex:rxandroid:1.2.1' 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 eu.kanade.tachiyomi.data.notification.Notifications
import tachiyomi.core.util.system.logcat import tachiyomi.core.util.system.logcat
import ani.dantotsu.others.DisabledReports import ani.dantotsu.others.DisabledReports
import com.google.android.material.color.DynamicColors
import com.google.firebase.crashlytics.ktx.crashlytics import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase import com.google.firebase.ktx.Firebase
import logcat.AndroidLogcatLogger import logcat.AndroidLogcatLogger
@ -33,6 +34,11 @@ class App : MultiDexApplication() {
override fun onCreate() { override fun onCreate() {
super.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) registerActivityLifecycleCallbacks(mFTActivityLifecycleCallbacks)
Firebase.crashlytics.setCrashlyticsCollectionEnabled(!DisabledReports) 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.core.preference.AndroidPreferenceStore
import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.network.NetworkPreferences
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import uy.kohesive.injekt.api.InjektModule import uy.kohesive.injekt.api.InjektModule
import uy.kohesive.injekt.api.InjektRegistrar import uy.kohesive.injekt.api.InjektRegistrar
@ -21,7 +22,7 @@ class AppModule(val app: Application) : InjektModule {
override fun InjektRegistrar.registerInjectables() { override fun InjektRegistrar.registerInjectables() {
addSingleton(app) addSingleton(app)
addSingletonFactory { NetworkHelper(app) } addSingletonFactory { NetworkHelper(app, get()) }
addSingletonFactory { AnimeExtensionManager(app) } addSingletonFactory { AnimeExtensionManager(app) }
@ -44,6 +45,13 @@ class PreferenceModule(val application: Application) : InjektModule {
AndroidPreferenceStore(application) AndroidPreferenceStore(application)
} }
addSingletonFactory {
NetworkPreferences(
preferenceStore = get(),
verboseLogging = false,
)
}
addSingletonFactory { addSingletonFactory {
SourcePreferences(get()) SourcePreferences(get())
} }

View file

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

View file

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

View file

@ -1,22 +1,33 @@
package ani.dantotsu.media.manga package ani.dantotsu.media.manga
import android.content.ContentResolver
import android.content.ContentValues
import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory 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 android.util.LruCache
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.File
import java.io.FileOutputStream
data class ImageData( data class ImageData(
val page: Page, 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) { return withContext(Dispatchers.IO) {
try { try {
// Fetch the image // Fetch the image
val response = httpSource.getImage(page) val response = httpSource.getImage(page)
println("Response: ${response.code}")
println("Response: ${response.message}")
// Convert the Response to an InputStream // Convert the Response to an InputStream
val inputStream = response.body?.byteStream() val inputStream = response.body?.byteStream()
@ -25,6 +36,7 @@ data class ImageData(
val bitmap = BitmapFactory.decodeStream(inputStream) val bitmap = BitmapFactory.decodeStream(inputStream)
inputStream?.close() inputStream?.close()
saveImage(bitmap, context.contentResolver, page.imageUrl!!, Bitmap.CompressFormat.JPEG, 100)
return@withContext bitmap return@withContext bitmap
} catch (e: Exception) { } 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() { class MangaCache() {
private val maxMemory = (Runtime.getRuntime().maxMemory() / 1024 / 2).toInt() private val maxMemory = (Runtime.getRuntime().maxMemory() / 1024 / 2).toInt()
private val cache = LruCache<String, ImageData>(maxMemory) private val cache = LruCache<String, ImageData>(maxMemory)

View file

@ -161,7 +161,7 @@ abstract class BaseImageAdapter(
println(mangaCache.get(link.url)) println(mangaCache.get(link.url))
println("cache size: ${mangaCache.size()}") println("cache size: ${mangaCache.size()}")
mangaCache.get(link.url)?.let { imageData -> 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) it.load(bitmap)
.skipMemoryCache(true) .skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE) .diskCacheStrategy(DiskCacheStrategy.NONE)

View file

@ -2,6 +2,7 @@ package ani.dantotsu.parsers
import android.content.ContentResolver import android.content.ContentResolver
import android.content.ContentValues import android.content.ContentValues
import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.net.Uri import android.net.Uri
@ -199,12 +200,14 @@ class DynamicMangaParser(extension: MangaExtension.Installed) : MangaParser() {
return coroutineScope { return coroutineScope {
try { try {
println("source.name " + source.name)
val res = source.getPageList(sChapter) val res = source.getPageList(sChapter)
val reIndexedPages = res.mapIndexed { index, page -> Page(index, page.url, page.imageUrl, page.uri) } val reIndexedPages = res.mapIndexed { index, page -> Page(index, page.url, page.imageUrl, page.uri) }
val deferreds = reIndexedPages.map { page -> val deferreds = reIndexedPages.map { page ->
async(Dispatchers.IO) { async(Dispatchers.IO) {
mangaCache.put(page.imageUrl ?: "", ImageData(page, source)) mangaCache.put(page.imageUrl ?: "", ImageData(page, source))
logger("put page: ${page.imageUrl}")
pageToMangaImage(page) pageToMangaImage(page)
} }
} }
@ -212,11 +215,44 @@ class DynamicMangaParser(extension: MangaExtension.Installed) : MangaParser() {
deferreds.awaitAll() deferreds.awaitAll()
} catch (e: Exception) { } catch (e: Exception) {
logger("loadImages Exception: $e") logger("loadImages Exception: $e")
Toast.makeText(currContext(), "Failed to load images: $e", Toast.LENGTH_SHORT).show()
emptyList() 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) { 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.io.Serializable
import java.net.URLDecoder import java.net.URLDecoder
import java.net.URLEncoder import java.net.URLEncoder
import me.xdrop.fuzzywuzzy.FuzzySearch
abstract class BaseParser { abstract class BaseParser {
@ -55,21 +57,41 @@ abstract class BaseParser {
setUserText("Searching : ${mediaObj.mainName()}") setUserText("Searching : ${mediaObj.mainName()}")
val results = search(mediaObj.mainName()) val results = search(mediaObj.mainName())
val sortedResults = if (results.isNotEmpty()) { val sortedResults = if (results.isNotEmpty()) {
StringMatcher.closestShowMovedToTop(mediaObj.mainName(), results) results.sortedByDescending { FuzzySearch.ratio(it.name, mediaObj.mainName()) }
} else { } else {
emptyList() emptyList()
} }
response = sortedResults.firstOrNull() response = sortedResults.firstOrNull()
if (response == null) { if (response == null || FuzzySearch.ratio(response.name, mediaObj.mainName()) < 100) {
setUserText("Searching : ${mediaObj.nameRomaji}") setUserText("Searching : ${mediaObj.nameRomaji}")
val romajiResults = search(mediaObj.nameRomaji) val romajiResults = search(mediaObj.nameRomaji)
val sortedRomajiResults = if (romajiResults.isNotEmpty()) { val sortedRomajiResults = if (romajiResults.isNotEmpty()) {
StringMatcher.closestShowMovedToTop(mediaObj.nameRomaji, romajiResults) romajiResults.sortedByDescending { FuzzySearch.ratio(it.name, mediaObj.nameRomaji) }
} else { } else {
emptyList() 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) saveShowResponse(mediaObj.id, response)
} }

View file

@ -1,5 +1,7 @@
package ani.dantotsu.parsers package ani.dantotsu.parsers
import ani.dantotsu.logger
class StringMatcher { class StringMatcher {
companion object { companion object {
private fun levenshteinDistance(s1: String, s2: String): Int { private fun levenshteinDistance(s1: String, s2: String): Int {
@ -52,8 +54,10 @@ class StringMatcher {
val closestShowAndIndex = closestShow(target, shows) val closestShowAndIndex = closestShow(target, shows)
val closestIndex = closestShowAndIndex.second val closestIndex = closestShowAndIndex.second
if (closestIndex == -1) { if (closestIndex == -1) {
logger("No closest show found for $target")
return shows // Return original list if no closest show found 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) 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.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.getSystemService import androidx.core.content.ContextCompat.getSystemService
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@ -18,6 +19,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.databinding.FragmentAnimeExtensionsBinding import ani.dantotsu.databinding.FragmentAnimeExtensionsBinding
import ani.dantotsu.loadData
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
@ -35,13 +37,57 @@ class AnimeExtensionsFragment : Fragment(), SearchQueryHandler {
private var _binding: FragmentAnimeExtensionsBinding? = null private var _binding: FragmentAnimeExtensionsBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
val skipIcons = loadData("skip_extension_icons") ?: false
private lateinit var extensionsRecyclerView: RecyclerView private lateinit var extensionsRecyclerView: RecyclerView
private lateinit var allextenstionsRecyclerView: RecyclerView private lateinit var allextenstionsRecyclerView: RecyclerView
private val animeExtensionManager: AnimeExtensionManager = Injekt.get<AnimeExtensionManager>() private val animeExtensionManager: AnimeExtensionManager = Injekt.get<AnimeExtensionManager>()
private val extensionsAdapter = AnimeExtensionsAdapter { pkgName -> private val extensionsAdapter = AnimeExtensionsAdapter ({ pkg ->
animeExtensionManager.uninstallExtension(pkgName) 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 = val notificationManager =
requireContext().getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager requireContext().getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
@ -84,7 +130,7 @@ class AnimeExtensionsFragment : Fragment(), SearchQueryHandler {
notificationManager.notify(1, builder.build()) notificationManager.notify(1, builder.build())
} }
) )
} }, skipIcons)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = FragmentAnimeExtensionsBinding.inflate(inflater, container, false) _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() private var extensions: List<AnimeExtension.Installed> = emptyList()
val skipIcons = skipIcons
fun updateData(newExtensions: List<AnimeExtension.Installed>) { fun updateData(newExtensions: List<AnimeExtension.Installed>) {
extensions = newExtensions extensions = newExtensions
@ -154,10 +201,17 @@ class AnimeExtensionsFragment : Fragment(), SearchQueryHandler {
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val extension = extensions[position] val extension = extensions[position]
holder.extensionNameTextView.text = extension.name holder.extensionNameTextView.text = extension.name
if (!skipIcons) {
holder.extensionIconImageView.setImageDrawable(extension.icon) 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.text = "Uninstall"
}
holder.closeTextView.setOnClickListener { holder.closeTextView.setOnClickListener {
onUninstallClicked(extension.pkgName) onUninstallClicked(extension)
} }
} }
@ -171,8 +225,9 @@ class AnimeExtensionsFragment : Fragment(), SearchQueryHandler {
} }
private class AllAnimeExtensionsAdapter(private val coroutineScope: CoroutineScope, 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() private var extensions: List<AnimeExtension.Available> = emptyList()
val skipIcons = skipIcons
fun updateData(newExtensions: List<AnimeExtension.Available>, installedExtensions: List<AnimeExtension.Installed> = emptyList()) { fun updateData(newExtensions: List<AnimeExtension.Available>, installedExtensions: List<AnimeExtension.Installed> = emptyList()) {
val installedPkgNames = installedExtensions.map { it.pkgName }.toSet() val installedPkgNames = installedExtensions.map { it.pkgName }.toSet()
@ -190,10 +245,12 @@ class AnimeExtensionsFragment : Fragment(), SearchQueryHandler {
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val extension = filteredExtensions[position] val extension = filteredExtensions[position]
holder.extensionNameTextView.text = extension.name holder.extensionNameTextView.text = extension.name
if (!skipIcons) {
coroutineScope.launch { coroutineScope.launch {
val drawable = urlToDrawable(holder.itemView.context, extension.iconUrl) val drawable = urlToDrawable(holder.itemView.context, extension.iconUrl)
holder.extensionIconImageView.setImageDrawable(drawable) holder.extensionIconImageView.setImageDrawable(drawable)
} }
}
holder.closeTextView.text = "Install" holder.closeTextView.text = "Install"
holder.closeTextView.setOnClickListener { holder.closeTextView.setOnClickListener {
onButtonClicked(extension) onButtonClicked(extension)

View file

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

View file

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

View file

@ -15,21 +15,13 @@ class SubscriptionHelper {
companion object { companion object {
private fun loadSelected(context: Context, mediaId: Int, isAdult: Boolean, isAnime: Boolean): Selected { private fun loadSelected(context: Context, mediaId: Int, isAdult: Boolean, isAnime: Boolean): Selected {
val data = loadData<Selected>("${mediaId}-select", context) ?: Selected().let { val data = loadData<Selected>("${mediaId}-select", context) ?: Selected().let {
it.source = it.sourceIndex =
if (isAdult) "" if (isAdult) 0
else if (isAnime) {loadData("settings_def_anime_source", context) ?: ""} else if (isAnime) {loadData("settings_def_anime_source_s_r", context) ?: 0}
else loadData("settings_def_manga_source", context) ?: "" else loadData("settings_def_manga_source_s_r", context) ?: 0
it.preferDub = loadData("settings_prefer_dub", context) ?: false it.preferDub = loadData("settings_prefer_dub", context) ?: false
it 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 return data
} }
@ -40,9 +32,7 @@ class SubscriptionHelper {
fun getAnimeParser(context: Context, isAdult: Boolean, id: Int): AnimeParser { fun getAnimeParser(context: Context, isAdult: Boolean, id: Int): AnimeParser {
val sources = if (isAdult) HAnimeSources else AnimeSources val sources = if (isAdult) HAnimeSources else AnimeSources
val selected = loadSelected(context, id, isAdult, true) val selected = loadSelected(context, id, isAdult, true)
var location = sources.list.indexOfFirst { it.name == selected.source } val parser = sources[selected.sourceIndex]
if (location == -1) {location = 0}
val parser = sources[location]
parser.selectDub = selected.preferDub parser.selectDub = selected.preferDub
return parser return parser
} }
@ -69,9 +59,7 @@ class SubscriptionHelper {
fun getMangaParser(context: Context, isAdult: Boolean, id: Int): MangaParser { fun getMangaParser(context: Context, isAdult: Boolean, id: Int): MangaParser {
val sources = if (isAdult) HMangaSources else MangaSources val sources = if (isAdult) HMangaSources else MangaSources
val selected = loadSelected(context, id, isAdult, false) val selected = loadSelected(context, id, isAdult, false)
var location = sources.list.indexOfFirst { it.name == selected.source } return sources[selected.sourceIndex]
if (location == -1) {location = 0}
return sources[location]
} }
suspend fun getChapter(context: Context, parser: MangaParser, id: Int, isAdult: Boolean): MangaChapter? { 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 eu.kanade.tachiyomi.network.interceptor.UserAgentInterceptor
import okhttp3.Cache import okhttp3.Cache
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import java.io.File import java.io.File
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
class NetworkHelper( class NetworkHelper(
context: Context, context: Context,
private val preferences: NetworkPreferences,
) { ) {
private val cacheDir = File(context.cacheDir, "network_cache") private val cacheDir = File(context.cacheDir, "network_cache")
@ -40,18 +42,17 @@ class NetworkHelper(
.addInterceptor(UncaughtExceptionInterceptor()) .addInterceptor(UncaughtExceptionInterceptor())
.addInterceptor(userAgentInterceptor) .addInterceptor(userAgentInterceptor)
/*if (preferences.verboseLogging().get()) { if (preferences.verboseLogging().get()) {
val httpLoggingInterceptor = HttpLoggingInterceptor().apply { val httpLoggingInterceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.HEADERS level = HttpLoggingInterceptor.Level.HEADERS
} }
builder.addNetworkInterceptor(httpLoggingInterceptor) builder.addNetworkInterceptor(httpLoggingInterceptor)
}*/ }
//when (preferences.dohProvider().get()) { when (preferences.dohProvider().get()) {
when (PREF_DOH_CLOUDFLARE) {
PREF_DOH_CLOUDFLARE -> builder.dohCloudflare() PREF_DOH_CLOUDFLARE -> builder.dohCloudflare()
PREF_DOH_GOOGLE -> builder.dohGoogle() PREF_DOH_GOOGLE -> builder.dohGoogle()
/*PREF_DOH_ADGUARD -> builder.dohAdGuard() PREF_DOH_ADGUARD -> builder.dohAdGuard()
PREF_DOH_QUAD9 -> builder.dohQuad9() PREF_DOH_QUAD9 -> builder.dohQuad9()
PREF_DOH_ALIDNS -> builder.dohAliDNS() PREF_DOH_ALIDNS -> builder.dohAliDNS()
PREF_DOH_DNSPOD -> builder.dohDNSPod() PREF_DOH_DNSPOD -> builder.dohDNSPod()
@ -60,7 +61,7 @@ class NetworkHelper(
PREF_DOH_MULLVAD -> builder.dohMullvad() PREF_DOH_MULLVAD -> builder.dohMullvad()
PREF_DOH_CONTROLD -> builder.dohControlD() PREF_DOH_CONTROLD -> builder.dohControlD()
PREF_DOH_NJALLA -> builder.dohNajalla() PREF_DOH_NJALLA -> builder.dohNajalla()
PREF_DOH_SHECAN -> builder.dohShecan()*/ PREF_DOH_SHECAN -> builder.dohShecan()
} }
return builder return builder
@ -75,5 +76,5 @@ class NetworkHelper(
.build() .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:paddingStart="32dp"
android:paddingEnd="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 <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
@ -81,13 +102,6 @@
android:gravity="center" android:gravity="center"
android:orientation="horizontal"> 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 <androidx.cardview.widget.CardView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -143,8 +157,27 @@
app:tint="@color/bg_opp" app:tint="@color/bg_opp"
tools:ignore="ContentDescription,SpeakableTextPresentCheck,ImageContrastCheck" /> tools:ignore="ContentDescription,SpeakableTextPresentCheck,ImageContrastCheck" />
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
</LinearLayout>
</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 <ani.dantotsu.others.Xpandable
android:layout_width="match_parent" android:layout_width="match_parent"
@ -429,6 +462,44 @@
app:showText="false" app:showText="false"
app:thumbTint="@color/button_switch_track" /> 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>
<ani.dantotsu.others.Xpandable <ani.dantotsu.others.Xpandable
@ -698,8 +769,8 @@
style="@style/Widget.Material3.Button.TextButton" style="@style/Widget.Material3.Button.TextButton"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="64dp" android:layout_height="64dp"
android:layout_marginTop="8dp"
android:layout_marginStart="-12dp" android:layout_marginStart="-12dp"
android:layout_marginTop="8dp"
android:fontFamily="@font/poppins_bold" android:fontFamily="@font/poppins_bold"
android:insetTop="0dp" android:insetTop="0dp"
android:insetBottom="0dp" android:insetBottom="0dp"
@ -1222,11 +1293,11 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:clipToPadding="false" android:clipToPadding="false"
android:gravity="center" android:gravity="center"
android:orientation="horizontal"> android:orientation="horizontal"
android:paddingTop="16dp"
android:paddingBottom="16dp">
<ImageView <ImageView
android:id="@+id/settingBuyMeCoffee" android:id="@+id/settingBuyMeCoffee"

View file

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

View file

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

View file

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

View file

@ -624,5 +624,7 @@
<string name="view_manga">View Manga</string> <string name="view_manga">View Manga</string>
<string name="force_legacy_installer">Force Legacy Installer</string> <string name="force_legacy_installer">Force Legacy Installer</string>
<string name="extensions_settings">Extensions</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> </resources>