From ab360b3a7594e624b25be3cc8a8273807c4c00de Mon Sep 17 00:00:00 2001
From: rebelonion <87634197+rebelonion@users.noreply.github.com>
Date: Sun, 19 May 2024 14:17:58 -0500
Subject: [PATCH] feat: extension testing
---
app/src/main/AndroidManifest.xml | 1 +
.../ani/dantotsu/parsers/AniyomiAdapter.kt | 9 +-
.../java/ani/dantotsu/parsers/BaseParser.kt | 45 ++-
.../dantotsu/parsers/ExtensionSelectItem.kt | 38 ++
.../ani/dantotsu/parsers/ExtensionTestItem.kt | 367 ++++++++++++++++++
.../ExtensionTestSettingsBottomDialog.kt | 117 ++++++
.../dantotsu/parsers/ParserTestActivity.kt | 112 ++++++
.../dantotsu/settings/ExtensionsActivity.kt | 11 +
.../settings/SettingsExtensionsActivity.kt | 17 +
.../main/res/layout/activity_extensions.xml | 9 +
.../main/res/layout/activity_parser_test.xml | 109 ++++++
.../bottom_sheet_extension_test_settings.xml | 100 +++++
.../main/res/layout/item_extension_select.xml | 40 ++
.../main/res/layout/item_extension_test.xml | 111 ++++++
app/src/main/res/values/strings.xml | 21 +
15 files changed, 1103 insertions(+), 4 deletions(-)
create mode 100644 app/src/main/java/ani/dantotsu/parsers/ExtensionSelectItem.kt
create mode 100644 app/src/main/java/ani/dantotsu/parsers/ExtensionTestItem.kt
create mode 100644 app/src/main/java/ani/dantotsu/parsers/ExtensionTestSettingsBottomDialog.kt
create mode 100644 app/src/main/java/ani/dantotsu/parsers/ParserTestActivity.kt
create mode 100644 app/src/main/res/layout/activity_parser_test.xml
create mode 100644 app/src/main/res/layout/bottom_sheet_extension_test_settings.xml
create mode 100644 app/src/main/res/layout/item_extension_select.xml
create mode 100644 app/src/main/res/layout/item_extension_test.xml
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 68841a8d..738d7be6 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -200,6 +200,7 @@
android:parentActivityName=".MainActivity" />
+
diff --git a/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt b/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt
index bacc5e22..df97b2ec 100644
--- a/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt
@@ -53,8 +53,11 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
override val name = extension.name
override val saveName = extension.name
- override val hostUrl = extension.sources.first().name
+ override val hostUrl =
+ (extension.sources.first() as? AnimeHttpSource)?.baseUrl ?: extension.sources.first().name
override val isNSFW = extension.isNsfw
+ override val icon = extension.icon
+
override var selectDub: Boolean
get() = getDub()
set(value) {
@@ -324,8 +327,10 @@ 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 hostUrl =
+ (extension.sources.first() as? HttpSource)?.baseUrl ?: extension.sources.first().name
override val isNSFW = extension.isNsfw
+ override val icon = extension.icon
override suspend fun loadChapters(
mangaLink: String,
diff --git a/app/src/main/java/ani/dantotsu/parsers/BaseParser.kt b/app/src/main/java/ani/dantotsu/parsers/BaseParser.kt
index 2d83ab06..3ddfbb09 100644
--- a/app/src/main/java/ani/dantotsu/parsers/BaseParser.kt
+++ b/app/src/main/java/ani/dantotsu/parsers/BaseParser.kt
@@ -1,17 +1,21 @@
package ani.dantotsu.parsers
+import android.graphics.drawable.Drawable
import ani.dantotsu.FileUrl
import ani.dantotsu.R
import ani.dantotsu.currContext
import ani.dantotsu.media.Media
+import ani.dantotsu.okHttpClient
import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.util.Logger
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.source.model.SManga
import me.xdrop.fuzzywuzzy.FuzzySearch
+import okhttp3.Request
import java.io.Serializable
import java.net.URLDecoder
import java.net.URLEncoder
+import kotlin.system.measureTimeMillis
abstract class BaseParser {
@@ -41,6 +45,11 @@ abstract class BaseParser {
* **/
open val language = "English"
+ /**
+ * Icon of the site, can be null
+ */
+ open val icon: Drawable? = null
+
/**
* Search for Anime/Manga/Novel, returns a List of Responses
*
@@ -133,10 +142,39 @@ abstract class BaseParser {
return response
}
+ /**
+ * ping the site to check if it's working or not.
+ * @return Triple : First Int is the status code, Second Int is the response time in milliseconds, Third String is the response message.
+ */
+ fun ping(): Triple {
+ val client = okHttpClient
+ var statusCode = 0
+ var responseTime: Int? = null
+ var responseMessage = ""
+ try {
+ val request = Request.Builder()
+ .url(hostUrl)
+ .build()
+ responseTime = measureTimeMillis {
+ client.newCall(request).execute().use { response ->
+ statusCode = response.code
+ responseMessage = response.message
+ }
+ }.toInt()
+ } catch (e: Exception) {
+ Logger.log("Failed to ping $name")
+ statusCode = -1
+ responseMessage = if (e.message.isNullOrEmpty()) "None" else e.message!!
+ Logger.log(e)
+ }
+ return Triple(statusCode, responseTime, responseMessage)
+ }
/**
* Used to get an existing Search Response which was selected by the user.
- * **/
+ * @param mediaId : The mediaId of the Media object.
+ * @return ShowResponse? : The ShowResponse object if found, else null.
+ */
open suspend fun loadSavedShowResponse(mediaId: Int): ShowResponse? {
checkIfVariablesAreEmpty()
return PrefManager.getNullableCustomVal(
@@ -148,7 +186,10 @@ abstract class BaseParser {
/**
* Used to save Shows Response using `saveName`.
- * **/
+ * @param mediaId : The mediaId of the Media object.
+ * @param response : The ShowResponse object to save.
+ * @param selected : Boolean : If the ShowResponse was selected by the user or not.
+ */
open fun saveShowResponse(mediaId: Int, response: ShowResponse?, selected: Boolean = false) {
if (response != null) {
checkIfVariablesAreEmpty()
diff --git a/app/src/main/java/ani/dantotsu/parsers/ExtensionSelectItem.kt b/app/src/main/java/ani/dantotsu/parsers/ExtensionSelectItem.kt
new file mode 100644
index 00000000..8c9c20d5
--- /dev/null
+++ b/app/src/main/java/ani/dantotsu/parsers/ExtensionSelectItem.kt
@@ -0,0 +1,38 @@
+package ani.dantotsu.parsers
+
+import android.graphics.drawable.Drawable
+import android.view.View
+import ani.dantotsu.R
+import ani.dantotsu.databinding.ItemExtensionSelectBinding
+import com.xwray.groupie.viewbinding.BindableItem
+
+class ExtensionSelectItem(
+ private val name: String,
+ private val image: Drawable?,
+ private var isSelected: Boolean,
+ val selectCallback: (String, Boolean) -> Unit
+) : BindableItem() {
+ private lateinit var binding: ItemExtensionSelectBinding
+
+ override fun bind(viewBinding: ItemExtensionSelectBinding, position: Int) {
+ binding = viewBinding
+ binding.extensionNameTextView.text = name
+ image?.let {
+ binding.extensionIconImageView.setImageDrawable(it)
+ }
+ binding.extensionCheckBox.setOnCheckedChangeListener(null)
+ binding.extensionCheckBox.isChecked = isSelected
+ binding.extensionCheckBox.setOnCheckedChangeListener { _, isChecked ->
+ isSelected = isChecked
+ selectCallback(name, isChecked)
+ }
+ }
+
+ override fun getLayout(): Int {
+ return R.layout.item_extension_select
+ }
+
+ override fun initializeViewBinding(view: View): ItemExtensionSelectBinding {
+ return ItemExtensionSelectBinding.bind(view)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/parsers/ExtensionTestItem.kt b/app/src/main/java/ani/dantotsu/parsers/ExtensionTestItem.kt
new file mode 100644
index 00000000..c87bf268
--- /dev/null
+++ b/app/src/main/java/ani/dantotsu/parsers/ExtensionTestItem.kt
@@ -0,0 +1,367 @@
+package ani.dantotsu.parsers
+
+import android.content.Context
+import android.view.View
+import androidx.core.view.isVisible
+import ani.dantotsu.R
+import ani.dantotsu.databinding.ItemExtensionTestBinding
+import ani.dantotsu.getThemeColor
+import com.xwray.groupie.viewbinding.BindableItem
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+class ExtensionTestItem(
+ private var extensionType: String,
+ private var testType: String,
+ private var extension: BaseParser,
+ private var searchString: String = "Chainsaw Man"
+) : BindableItem() {
+ private lateinit var binding: ItemExtensionTestBinding
+ private lateinit var context: Context
+ private var job: Job? = null
+ private var isRunning = false
+ private var pingResult: Triple? = null
+ private var searchResultSize: Int? = null
+ private var episodeResultSize: Int? = null
+ private var serverResultSize: Int? = null
+
+ override fun bind(viewBinding: ItemExtensionTestBinding, position: Int) {
+ binding = viewBinding
+ context = binding.root.context
+ binding.extensionIconImageView.setImageDrawable(extension.icon)
+ binding.extensionNameTextView.text = extension.name
+ binding.extensionLoading.isVisible = isRunning
+ hideAllResults()
+
+ pingResult()
+ searchResult()
+ episodeResult()
+ serverResult()
+ }
+
+ override fun getLayout(): Int {
+ return R.layout.item_extension_test
+ }
+
+ override fun initializeViewBinding(view: View): ItemExtensionTestBinding {
+ return ItemExtensionTestBinding.bind(view)
+ }
+
+ private fun hideAllResults() {
+ if (::binding.isInitialized.not()) return
+ binding.searchResultText.isVisible = false
+ binding.episodeResultText.isVisible = false
+ binding.serverResultText.isVisible = false
+ }
+
+ fun cancelJob() {
+ job?.cancel()
+ job = null
+ binding.extensionLoading.isVisible = false
+ }
+
+ fun startTest() {
+ pingResult = null
+ searchResultSize = null
+ episodeResultSize = null
+ serverResultSize = null
+ isRunning = true
+ hideAllResults()
+ job?.cancel()
+ job = Job()
+ CoroutineScope(Dispatchers.IO + job!!).launch {
+ when (extensionType) {
+ "anime" -> {
+ val extension = extension as AnimeParser
+ runAnimeTest(extension)
+ }
+
+ "manga" -> {
+ val extension = extension as MangaParser
+ runMangaTest(extension)
+ }
+
+ "novel" -> {
+ val extension = extension as NovelParser
+ runNovelTest(extension)
+ }
+ }
+ }
+ }
+
+ private suspend fun runAnimeTest(extension: AnimeParser) {
+ pingResult = extension.ping()
+ withContext(Dispatchers.Main) {
+ pingResult()
+ }
+ if (testType == "ping") {
+ done()
+ return
+ }
+ val searchResult = extension.search(searchString)
+ searchResultSize = searchResult.size
+ withContext(Dispatchers.Main) {
+ searchResult()
+ }
+ if (searchResultSize == 0 || testType == "basic") {
+ done()
+ return
+ }
+ val episodeResult = extension.loadEpisodes("", null, searchResult.first().sAnime!!)
+ episodeResultSize = episodeResult.size
+ withContext(Dispatchers.Main) {
+ episodeResult()
+ }
+ if (episodeResultSize == 0) {
+ done()
+ return
+ }
+ val serverResult = extension.loadVideoServers("", null, episodeResult.first().sEpisode!!)
+ serverResultSize = serverResult.size
+ withContext(Dispatchers.Main) {
+ serverResult()
+ }
+
+ done()
+ }
+
+ private suspend fun runMangaTest(extension: MangaParser) {
+ pingResult = extension.ping()
+ withContext(Dispatchers.Main) {
+ pingResult()
+ }
+ if (testType == "ping") {
+ done()
+ return
+ }
+ val searchResult = extension.search(searchString)
+ searchResultSize = searchResult.size
+ withContext(Dispatchers.Main) {
+ searchResult()
+ }
+ if (searchResultSize == 0 || testType == "basic") {
+ done()
+ return
+ }
+ val chapterResult = extension.loadChapters("", null, searchResult.first().sManga!!)
+ episodeResultSize = chapterResult.size
+ withContext(Dispatchers.Main) {
+ episodeResult()
+ }
+ if (episodeResultSize == 0) {
+ done()
+ return
+ }
+ val serverResult = extension.loadImages("", chapterResult.first().sChapter)
+ serverResultSize = serverResult.size
+ withContext(Dispatchers.Main) {
+ serverResult()
+ }
+
+ withContext(Dispatchers.Main) {
+ if (::binding.isInitialized )
+ binding.extensionLoading.isVisible = false
+ isRunning = false
+ }
+ }
+
+ private suspend fun runNovelTest(extension: NovelParser) {
+ withContext(Dispatchers.Main) {
+ pingResult()
+ }
+ if (testType == "ping") {
+ done()
+ return
+ }
+ val searchResult = extension.search(searchString)
+ searchResultSize = searchResult.size
+ withContext(Dispatchers.Main) {
+ searchResult()
+ }
+ if (searchResultSize == 0 || testType == "basic") {
+ done()
+ return
+ }
+ val chapterResult = extension.loadBook(searchResult.first().link, null)
+ episodeResultSize = chapterResult.links.size
+ withContext(Dispatchers.Main) {
+ episodeResult()
+ serverResult()
+ }
+
+ withContext(Dispatchers.Main) {
+ if (::binding.isInitialized )
+ binding.extensionLoading.isVisible = false
+ isRunning = false
+ }
+ }
+
+ private fun done() {
+ if (::binding.isInitialized.not()) return
+ binding.extensionLoading.isVisible = false
+ isRunning = false
+ }
+
+ private fun pingResult() {
+ if (::binding.isInitialized.not()) return
+ if (extensionType == "novel") {
+ binding.pingResultText.isVisible = true
+ binding.pingResultText.text = context.getString(R.string.test_not_supported)
+ binding.pingResultText.setCompoundDrawablesWithIntrinsicBounds(
+ R.drawable.ic_round_info_24, 0, 0, 0
+ )
+ return
+ }
+ if (pingResult == null) {
+ binding.pingResultText.isVisible = false
+ return
+ } else {
+ binding.pingResultText.isVisible = true
+ }
+ binding.pingResultText.setTextColor(
+ context.getThemeColor(com.google.android.material.R.attr.colorPrimary)
+ )
+ val (code, time, message) = pingResult!!
+ if (code == 200) {
+ binding.pingResultText.text = context.getString(R.string.ping_success, time.toString())
+ binding.pingResultText.setCompoundDrawablesWithIntrinsicBounds(
+ R.drawable.ic_circle_check, 0, 0, 0
+ )
+ return
+ }
+ binding.pingResultText.text =
+ context.getString(R.string.ping_error, code.toString(), message)
+ binding.pingResultText.setCompoundDrawablesWithIntrinsicBounds(
+ R.drawable.ic_circle_cancel, 0, 0, 0
+ )
+ binding.pingResultText.setTextColor(
+ context.getThemeColor(com.google.android.material.R.attr.colorError)
+ )
+ }
+
+ private fun searchResult() {
+ if (::binding.isInitialized.not()) return
+ if (searchResultSize == null) {
+ binding.searchResultText.isVisible = false
+ return
+ }
+ binding.searchResultText.setTextColor(
+ context.getThemeColor(com.google.android.material.R.attr.colorPrimary)
+ )
+ binding.searchResultText.isVisible = true
+ if (searchResultSize == 0) {
+ val text = context.getString(R.string.title_search_test,
+ context.getString(R.string.no_results_found))
+ binding.searchResultText.text = text
+ binding.searchResultText.setCompoundDrawablesWithIntrinsicBounds(
+ R.drawable.ic_circle_cancel, 0, 0, 0
+ )
+ binding.searchResultText.setTextColor(
+ context.getThemeColor(com.google.android.material.R.attr.colorError)
+ )
+ return
+ }
+ val text = context.getString(R.string.title_search_test,
+ context.getString(R.string.results_found, searchResultSize.toString()))
+ binding.searchResultText.text = text
+ binding.searchResultText.setCompoundDrawablesWithIntrinsicBounds(
+ R.drawable.ic_circle_check, 0, 0, 0
+ )
+ }
+
+ private fun episodeResult() {
+ if (::binding.isInitialized.not()) return
+ if (episodeResultSize == null) {
+ binding.episodeResultText.isVisible = false
+ return
+ }
+ binding.episodeResultText.setTextColor(
+ context.getThemeColor(com.google.android.material.R.attr.colorPrimary)
+ )
+ binding.episodeResultText.isVisible = true
+ if (episodeResultSize == 0) {
+ val text = when(extensionType) {
+ "anime" -> context.getString(R.string.episode_search_test,
+ context.getString(R.string.no_results_found))
+ "manga" -> context.getString(R.string.chapter_search_test,
+ context.getString(R.string.no_results_found))
+ else -> context.getString(R.string.book_search_test,
+ context.getString(R.string.no_results_found))
+ }
+ binding.episodeResultText.text = text
+ binding.episodeResultText.setCompoundDrawablesWithIntrinsicBounds(
+ R.drawable.ic_circle_cancel, 0, 0, 0
+ )
+ binding.episodeResultText.setTextColor(
+ context.getThemeColor(com.google.android.material.R.attr.colorError)
+ )
+ return
+ }
+ val text = when(extensionType) {
+ "anime" -> context.getString(R.string.episode_search_test,
+ context.getString(R.string.results_found, episodeResultSize.toString()))
+ "manga" -> context.getString(R.string.chapter_search_test,
+ context.getString(R.string.results_found, episodeResultSize.toString()))
+ else -> context.getString(R.string.book_search_test,
+ context.getString(R.string.results_found, episodeResultSize.toString()))
+ }
+ binding.episodeResultText.text = text
+ binding.episodeResultText.setCompoundDrawablesWithIntrinsicBounds(
+ R.drawable.ic_circle_check, 0, 0, 0
+ )
+ }
+
+ private fun serverResult() {
+ if (::binding.isInitialized.not()) return
+ if (extensionType == "novel") {
+ binding.pingResultText.isVisible = true
+ binding.pingResultText.text = context.getString(R.string.test_not_supported)
+ binding.pingResultText.setCompoundDrawablesWithIntrinsicBounds(
+ R.drawable.ic_round_info_24, 0, 0, 0
+ )
+ return
+ }
+ if (serverResultSize == null) {
+ binding.serverResultText.isVisible = false
+ return
+ }
+ binding.serverResultText.setTextColor(
+ context.getThemeColor(com.google.android.material.R.attr.colorPrimary)
+ )
+ binding.serverResultText.isVisible = true
+ if (serverResultSize == 0) {
+ val text = when(extensionType) {
+ "anime" -> context.getString(R.string.video_search_test,
+ context.getString(R.string.no_results_found))
+ "manga" -> context.getString(R.string.image_search_test,
+ context.getString(R.string.no_results_found))
+ else -> context.getString(R.string.book_search_test,
+ context.getString(R.string.no_results_found))
+ }
+ binding.serverResultText.text = text
+ binding.serverResultText.setCompoundDrawablesWithIntrinsicBounds(
+ R.drawable.ic_circle_cancel, 0, 0, 0
+ )
+ binding.serverResultText.setTextColor(
+ context.getThemeColor(com.google.android.material.R.attr.colorError)
+ )
+ return
+ }
+ val text = when(extensionType) {
+ "anime" -> context.getString(R.string.video_search_test,
+ context.getString(R.string.results_found, serverResultSize.toString()))
+ "manga" -> context.getString(R.string.image_search_test,
+ context.getString(R.string.results_found, serverResultSize.toString()))
+ else -> context.getString(R.string.book_search_test,
+ context.getString(R.string.results_found, serverResultSize.toString()))
+ }
+ binding.serverResultText.text = text
+ binding.serverResultText.setCompoundDrawablesWithIntrinsicBounds(
+ R.drawable.ic_circle_check, 0, 0, 0
+ )
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/parsers/ExtensionTestSettingsBottomDialog.kt b/app/src/main/java/ani/dantotsu/parsers/ExtensionTestSettingsBottomDialog.kt
new file mode 100644
index 00000000..fcedad3f
--- /dev/null
+++ b/app/src/main/java/ani/dantotsu/parsers/ExtensionTestSettingsBottomDialog.kt
@@ -0,0 +1,117 @@
+package ani.dantotsu.parsers
+
+import android.graphics.drawable.Drawable
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.LinearLayoutManager
+import ani.dantotsu.BottomSheetDialogFragment
+import ani.dantotsu.databinding.BottomSheetExtensionTestSettingsBinding
+import ani.dantotsu.parsers.novel.NovelExtensionManager
+import com.xwray.groupie.GroupieAdapter
+import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
+import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
+import uy.kohesive.injekt.Injekt
+import uy.kohesive.injekt.api.get
+
+class ExtensionTestSettingsBottomDialog : BottomSheetDialogFragment() {
+ private var _binding: BottomSheetExtensionTestSettingsBinding? = null
+ private val binding get() = _binding!!
+ private val adapter: GroupieAdapter = GroupieAdapter()
+ private val animeExtension: AnimeExtensionManager = Injekt.get()
+ private val mangaExtensions: MangaExtensionManager = Injekt.get()
+ private val novelExtensions: NovelExtensionManager = Injekt.get()
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ _binding = BottomSheetExtensionTestSettingsBinding.inflate(inflater, container, false)
+ return _binding?.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ binding.extensionSelectionRecyclerView.adapter = adapter
+ binding.extensionSelectionRecyclerView.layoutManager = LinearLayoutManager(
+ context,
+ LinearLayoutManager.VERTICAL,
+ false
+ )
+ binding.animeRadioButton.setOnCheckedChangeListener { _, b ->
+ if (b) {
+ extensionType = "anime"
+ extensionsToTest.clear()
+ setupAdapter()
+ }
+ }
+ binding.mangaRadioButton.setOnCheckedChangeListener { _, b ->
+ if (b) {
+ extensionType = "manga"
+ extensionsToTest.clear()
+ setupAdapter()
+ }
+ }
+ binding.novelsRadioButton.setOnCheckedChangeListener { _, b ->
+ if (b) {
+ extensionType = "novel"
+ extensionsToTest.clear()
+ setupAdapter()
+ }
+ }
+ binding.pingRadioButton.setOnCheckedChangeListener { _, b ->
+ if (b) {
+ testType = "ping"
+ }
+ }
+ binding.basicRadioButton.setOnCheckedChangeListener { _, b ->
+ if (b) {
+ testType = "basic"
+ }
+ }
+ binding.fullRadioButton.setOnCheckedChangeListener { _, b ->
+ if (b) {
+ testType = "full"
+ }
+ }
+ setupAdapter()
+ }
+
+ override fun onDestroyView() {
+ _binding = null
+ super.onDestroyView()
+ }
+
+ private fun setupAdapter() {
+ val namesAndUrls: Map = when (extensionType) {
+ "anime" -> animeExtension.installedExtensionsFlow.value.associate { it.name to it.icon }
+ "manga" -> mangaExtensions.installedExtensionsFlow.value.associate { it.name to it.icon }
+ "novel" -> novelExtensions.installedExtensionsFlow.value.associate { it.name to it.icon }
+ else -> emptyMap()
+ }
+ adapter.clear()
+ namesAndUrls.forEach { (name, icon) ->
+ val isSelected = extensionsToTest.contains(name)
+ adapter.add(ExtensionSelectItem(name, icon, isSelected, ::selectedCallback))
+ }
+ }
+
+ private fun selectedCallback(name: String, isSelected: Boolean) {
+ if (isSelected) {
+ extensionsToTest.add(name)
+ } else {
+ extensionsToTest.remove(name)
+ }
+ }
+
+ companion object {
+ fun newInstance(): ExtensionTestSettingsBottomDialog {
+ return ExtensionTestSettingsBottomDialog()
+ }
+
+ var extensionType = "anime"
+ var testType = "ping"
+ var extensionsToTest: MutableList = mutableListOf()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/parsers/ParserTestActivity.kt b/app/src/main/java/ani/dantotsu/parsers/ParserTestActivity.kt
new file mode 100644
index 00000000..7be91fe2
--- /dev/null
+++ b/app/src/main/java/ani/dantotsu/parsers/ParserTestActivity.kt
@@ -0,0 +1,112 @@
+package ani.dantotsu.parsers
+
+import android.os.Bundle
+import android.view.ViewGroup
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.view.updateLayoutParams
+import androidx.recyclerview.widget.LinearLayoutManager
+import ani.dantotsu.R
+import ani.dantotsu.databinding.ActivityParserTestBinding
+import ani.dantotsu.initActivity
+import ani.dantotsu.navBarHeight
+import ani.dantotsu.statusBarHeight
+import ani.dantotsu.themes.ThemeManager
+import ani.dantotsu.toast
+import com.xwray.groupie.GroupieAdapter
+
+class ParserTestActivity : AppCompatActivity() {
+ private lateinit var binding: ActivityParserTestBinding
+ val adapter = GroupieAdapter()
+ val extensionsToTest: MutableList = mutableListOf()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ ThemeManager(this).applyTheme()
+ initActivity(this)
+ binding = ActivityParserTestBinding.inflate(layoutInflater)
+ binding.toolbar.updateLayoutParams {
+ topMargin = statusBarHeight
+ }
+ binding.extensionResultsRecyclerView.updateLayoutParams {
+ bottomMargin = navBarHeight
+ }
+ setContentView(binding.root)
+
+ binding.extensionResultsRecyclerView.adapter = adapter
+ binding.extensionResultsRecyclerView.layoutManager = LinearLayoutManager(
+ this,
+ LinearLayoutManager.VERTICAL,
+ false
+ )
+ binding.backButton.setOnClickListener {
+ onBackPressedDispatcher.onBackPressed()
+ }
+ binding.optionsLayout.setOnClickListener {
+ ExtensionTestSettingsBottomDialog.newInstance()
+ .show(supportFragmentManager, "extension_test_settings")
+ }
+
+ binding.startButton.setOnClickListener {
+ if (ExtensionTestSettingsBottomDialog.extensionsToTest.isEmpty()) {
+ toast(R.string.no_extensions_selected)
+ return@setOnClickListener
+ }
+ extensionsToTest.forEach {
+ it.cancelJob()
+ }
+ extensionsToTest.clear()
+ adapter.clear()
+ when (ExtensionTestSettingsBottomDialog.extensionType) {
+ "anime" -> {
+ ExtensionTestSettingsBottomDialog.extensionsToTest.forEach { name ->
+ val extension =
+ AnimeSources.list.find { source -> source.name == name }?.get?.value
+ extension?.let {
+ extensionsToTest.add(
+ ExtensionTestItem(
+ "anime",
+ ExtensionTestSettingsBottomDialog.testType,
+ it
+ )
+ )
+ }
+ }
+ }
+ "manga" -> {
+ ExtensionTestSettingsBottomDialog.extensionsToTest.forEach { name ->
+ val extension =
+ MangaSources.list.find { source -> source.name == name }?.get?.value
+ extension?.let {
+ extensionsToTest.add(
+ ExtensionTestItem(
+ "manga",
+ ExtensionTestSettingsBottomDialog.testType,
+ it
+ )
+ )
+ }
+ }
+ }
+ "novel" -> {
+ ExtensionTestSettingsBottomDialog.extensionsToTest.forEach { name ->
+ val extension =
+ NovelSources.list.find { source -> source.name == name }?.get?.value
+ extension?.let {
+ extensionsToTest.add(
+ ExtensionTestItem(
+ "novel",
+ ExtensionTestSettingsBottomDialog.testType,
+ it
+ )
+ )
+ }
+ }
+ }
+ }
+ extensionsToTest.forEach {
+ adapter.add(it)
+ it.startTest()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/settings/ExtensionsActivity.kt b/app/src/main/java/ani/dantotsu/settings/ExtensionsActivity.kt
index 0125c3df..0fa65612 100644
--- a/app/src/main/java/ani/dantotsu/settings/ExtensionsActivity.kt
+++ b/app/src/main/java/ani/dantotsu/settings/ExtensionsActivity.kt
@@ -1,6 +1,7 @@
package ani.dantotsu.settings
import android.app.AlertDialog
+import android.content.Intent
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
@@ -13,6 +14,7 @@ import android.view.inputmethod.EditorInfo
import android.widget.AutoCompleteTextView
import android.widget.EditText
import androidx.appcompat.app.AppCompatActivity
+import androidx.core.content.ContextCompat
import androidx.core.view.updateLayoutParams
import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
@@ -28,6 +30,7 @@ import ani.dantotsu.media.MediaType
import ani.dantotsu.navBarHeight
import ani.dantotsu.others.AndroidBug5497Workaround
import ani.dantotsu.others.LanguageMapper
+import ani.dantotsu.parsers.ParserTestActivity
import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.statusBarHeight
@@ -70,6 +73,14 @@ class ExtensionsActivity : AppCompatActivity() {
bottomMargin = statusBarHeight + navBarHeight
}
+ binding.testButton.setOnClickListener {
+ ContextCompat.startActivity(
+ this,
+ Intent(this, ParserTestActivity::class.java),
+ null
+ )
+ }
+
val tabLayout = findViewById(R.id.tabLayout)
val viewPager = findViewById(R.id.viewPager)
viewPager.offscreenPageLimit = 1
diff --git a/app/src/main/java/ani/dantotsu/settings/SettingsExtensionsActivity.kt b/app/src/main/java/ani/dantotsu/settings/SettingsExtensionsActivity.kt
index 078d7b38..abf742ba 100644
--- a/app/src/main/java/ani/dantotsu/settings/SettingsExtensionsActivity.kt
+++ b/app/src/main/java/ani/dantotsu/settings/SettingsExtensionsActivity.kt
@@ -1,6 +1,7 @@
package ani.dantotsu.settings
import android.app.AlertDialog
+import android.content.Intent
import android.os.Bundle
import android.view.HapticFeedbackConstants
import android.view.KeyEvent
@@ -9,6 +10,7 @@ import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.widget.EditText
import androidx.appcompat.app.AppCompatActivity
+import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.recyclerview.widget.LinearLayoutManager
@@ -20,6 +22,7 @@ import ani.dantotsu.databinding.ItemRepositoryBinding
import ani.dantotsu.initActivity
import ani.dantotsu.media.MediaType
import ani.dantotsu.navBarHeight
+import ani.dantotsu.parsers.ParserTestActivity
import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.statusBarHeight
@@ -232,6 +235,20 @@ class SettingsExtensionsActivity : AppCompatActivity() {
setExtensionOutput(it.attachView, MediaType.MANGA)
}
),
+ Settings(
+ type = 1,
+ name = getString(R.string.extension_test),
+ desc = getString(R.string.extension_test_desc),
+ icon = R.drawable.ic_round_search_sources_24,
+ isActivity = true,
+ onClick = {
+ ContextCompat.startActivity(
+ context,
+ Intent(context, ParserTestActivity::class.java),
+ null
+ )
+ }
+ ),
Settings(
type = 1,
name = getString(R.string.user_agent),
diff --git a/app/src/main/res/layout/activity_extensions.xml b/app/src/main/res/layout/activity_extensions.xml
index 246c1785..a95edfed 100644
--- a/app/src/main/res/layout/activity_extensions.xml
+++ b/app/src/main/res/layout/activity_extensions.xml
@@ -41,6 +41,15 @@
android:layout_marginHorizontal="16dp"
android:orientation="horizontal">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/bottom_sheet_extension_test_settings.xml b/app/src/main/res/layout/bottom_sheet_extension_test_settings.xml
new file mode 100644
index 00000000..bacd922a
--- /dev/null
+++ b/app/src/main/res/layout/bottom_sheet_extension_test_settings.xml
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_extension_select.xml b/app/src/main/res/layout/item_extension_select.xml
new file mode 100644
index 00000000..98af9cad
--- /dev/null
+++ b/app/src/main/res/layout/item_extension_select.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_extension_test.xml b/app/src/main/res/layout/item_extension_test.xml
new file mode 100644
index 00000000..e64da5a3
--- /dev/null
+++ b/app/src/main/res/layout/item_extension_test.xml
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 5640e828..9b7b1eda 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -996,4 +996,25 @@ Non quae tempore quo provident laudantium qui illo dolor vel quia dolor et exerc
Subscriptions
Subscription Deleted
Cannot install when app is in background
+ Ping
+ Basic
+ Full
+ Test Type
+ Extension Type
+ View Options
+ Extension Test
+ Test if the extensions are working
+ Start Test
+ No extensions selected
+ Ping successful: %1$s ms
+ Ping failed. Code: %1$s, Message: %2$s
+ Test not supported for this extension
+ No results found. Extension may be broken
+ %1$s results found
+ Title Search Test: %1$s
+ Episode Search Test: %1$s
+ Chapter Search Test: %1$s
+ Video Search Test: %1$s
+ Image Search Test: %1$s
+ Book Search Test: %1$s