feat: manual repository entries
Closes Dantotsu#298
This commit is contained in:
parent
ba1725224a
commit
ff3131d988
12 changed files with 369 additions and 109 deletions
|
@ -12,8 +12,8 @@ import ani.dantotsu.R
|
||||||
class Xpandable @JvmOverloads constructor(
|
class Xpandable @JvmOverloads constructor(
|
||||||
context: Context, attrs: AttributeSet? = null
|
context: Context, attrs: AttributeSet? = null
|
||||||
) : LinearLayout(context, attrs) {
|
) : LinearLayout(context, attrs) {
|
||||||
var expanded: Boolean = false
|
private var expanded: Boolean = false
|
||||||
private var listener: OnChangeListener? = null
|
private var listeners: ArrayList<OnChangeListener> = arrayListOf()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
context.withStyledAttributes(attrs, R.styleable.Xpandable) {
|
context.withStyledAttributes(attrs, R.styleable.Xpandable) {
|
||||||
|
@ -50,7 +50,9 @@ class Xpandable @JvmOverloads constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
postDelayed({
|
postDelayed({
|
||||||
listener?.onRetract()
|
listeners.forEach{
|
||||||
|
it.onRetract()
|
||||||
|
}
|
||||||
}, 300)
|
}, 300)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,13 +66,19 @@ class Xpandable @JvmOverloads constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
postDelayed({
|
postDelayed({
|
||||||
listener?.onExpand()
|
listeners.forEach{
|
||||||
|
it.onExpand()
|
||||||
|
}
|
||||||
}, 300)
|
}, 300)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
fun setOnChangeListener(listener: OnChangeListener) {
|
fun addOnChangeListener(listener: OnChangeListener) {
|
||||||
this.listener = listener
|
listeners.add(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeListener(listener: OnChangeListener) {
|
||||||
|
listeners.remove(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OnChangeListener {
|
interface OnChangeListener {
|
||||||
|
|
|
@ -475,7 +475,7 @@ class PlayerSettingsActivity : AppCompatActivity() {
|
||||||
updateSubPreview()
|
updateSubPreview()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
binding.subtitleTest.setOnChangeListener(object: Xpandable.OnChangeListener {
|
binding.subtitleTest.addOnChangeListener(object: Xpandable.OnChangeListener {
|
||||||
override fun onExpand() {
|
override fun onExpand() {
|
||||||
updateSubPreview()
|
updateSubPreview()
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,12 +13,15 @@ import android.os.Build.VERSION.CODENAME
|
||||||
import android.os.Build.VERSION.RELEASE
|
import android.os.Build.VERSION.RELEASE
|
||||||
import android.os.Build.VERSION.SDK_INT
|
import android.os.Build.VERSION.SDK_INT
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.HapticFeedbackConstants
|
||||||
|
import android.view.KeyEvent
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.animation.AnimationUtils
|
import android.view.animation.AnimationUtils
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
|
import android.widget.EditText
|
||||||
import android.widget.RadioButton
|
import android.widget.RadioButton
|
||||||
import android.widget.RadioGroup
|
import android.widget.RadioGroup
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
@ -29,6 +32,7 @@ import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import android.view.HapticFeedbackConstants
|
import android.view.HapticFeedbackConstants
|
||||||
import androidx.core.view.ViewCompat.performHapticFeedback
|
import androidx.core.view.ViewCompat.performHapticFeedback
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.view.updateLayoutParams
|
import androidx.core.view.updateLayoutParams
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
@ -51,6 +55,7 @@ import ani.dantotsu.databinding.ActivitySettingsExtensionsBinding
|
||||||
import ani.dantotsu.databinding.ActivitySettingsMangaBinding
|
import ani.dantotsu.databinding.ActivitySettingsMangaBinding
|
||||||
import ani.dantotsu.databinding.ActivitySettingsNotificationsBinding
|
import ani.dantotsu.databinding.ActivitySettingsNotificationsBinding
|
||||||
import ani.dantotsu.databinding.ActivitySettingsThemeBinding
|
import ani.dantotsu.databinding.ActivitySettingsThemeBinding
|
||||||
|
import ani.dantotsu.databinding.ItemRepositoryBinding
|
||||||
import ani.dantotsu.download.DownloadsManager
|
import ani.dantotsu.download.DownloadsManager
|
||||||
import ani.dantotsu.download.video.ExoplayerDownloadService
|
import ani.dantotsu.download.video.ExoplayerDownloadService
|
||||||
import ani.dantotsu.downloadsPermission
|
import ani.dantotsu.downloadsPermission
|
||||||
|
@ -89,13 +94,17 @@ import eltos.simpledialogfragment.SimpleDialog
|
||||||
import eltos.simpledialogfragment.SimpleDialog.OnDialogResultListener.BUTTON_POSITIVE
|
import eltos.simpledialogfragment.SimpleDialog.OnDialogResultListener.BUTTON_POSITIVE
|
||||||
import eltos.simpledialogfragment.color.SimpleColorDialog
|
import eltos.simpledialogfragment.color.SimpleColorDialog
|
||||||
import eu.kanade.domain.base.BasePreferences
|
import eu.kanade.domain.base.BasePreferences
|
||||||
|
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
|
||||||
|
import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
|
||||||
import io.noties.markwon.Markwon
|
import io.noties.markwon.Markwon
|
||||||
import io.noties.markwon.SoftBreakAddsNewLinePlugin
|
import io.noties.markwon.SoftBreakAddsNewLinePlugin
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
import uy.kohesive.injekt.injectLazy
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
|
||||||
|
@ -115,6 +124,9 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene
|
||||||
private val extensionInstaller = Injekt.get<BasePreferences>().extensionInstaller()
|
private val extensionInstaller = Injekt.get<BasePreferences>().extensionInstaller()
|
||||||
private var cursedCounter = 0
|
private var cursedCounter = 0
|
||||||
|
|
||||||
|
private val animeExtensionManager: AnimeExtensionManager by injectLazy()
|
||||||
|
private val mangaExtensionManager: MangaExtensionManager by injectLazy()
|
||||||
|
|
||||||
@OptIn(UnstableApi::class)
|
@OptIn(UnstableApi::class)
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
@ -583,6 +595,142 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene
|
||||||
}
|
}
|
||||||
|
|
||||||
bindingExtensions = ActivitySettingsExtensionsBinding.bind(binding.root).apply {
|
bindingExtensions = ActivitySettingsExtensionsBinding.bind(binding.root).apply {
|
||||||
|
|
||||||
|
fun setExtensionOutput() {
|
||||||
|
animeRepoInventory.removeAllViews()
|
||||||
|
PrefManager.getVal<Set<String>>(PrefName.AnimeExtensionRepos).forEach { item ->
|
||||||
|
val view = ItemRepositoryBinding.inflate(
|
||||||
|
LayoutInflater.from(animeRepoInventory.context), animeRepoInventory, true
|
||||||
|
)
|
||||||
|
view.repositoryItem.text = item
|
||||||
|
view.repositoryItem.setOnClickListener {
|
||||||
|
snackString(getString(R.string.long_click_delete))
|
||||||
|
}
|
||||||
|
view.repositoryItem.setOnLongClickListener {
|
||||||
|
val anime = PrefManager.getVal<Set<String>>(PrefName.AnimeExtensionRepos)
|
||||||
|
.minus(item)
|
||||||
|
PrefManager.setVal(PrefName.AnimeExtensionRepos, anime)
|
||||||
|
it.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
||||||
|
setExtensionOutput()
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
animeExtensionManager.findAvailableExtensions()
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
animeRepoInventory.isVisible = animeRepoInventory.childCount > 0
|
||||||
|
mangaRepoInventory.removeAllViews()
|
||||||
|
PrefManager.getVal<Set<String>>(PrefName.MangaExtensionRepos).forEach { item ->
|
||||||
|
val view = ItemRepositoryBinding.inflate(
|
||||||
|
LayoutInflater.from(mangaRepoInventory.context), mangaRepoInventory, true
|
||||||
|
)
|
||||||
|
view.repositoryItem.text = item
|
||||||
|
view.repositoryItem.setOnClickListener {
|
||||||
|
snackString(getString(R.string.long_click_delete))
|
||||||
|
}
|
||||||
|
view.repositoryItem.setOnLongClickListener {
|
||||||
|
val anime = PrefManager.getVal<Set<String>>(PrefName.MangaExtensionRepos)
|
||||||
|
.minus(item)
|
||||||
|
PrefManager.setVal(PrefName.MangaExtensionRepos, anime)
|
||||||
|
it.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
||||||
|
setExtensionOutput()
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
mangaExtensionManager.findAvailableExtensions()
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mangaRepoInventory.isVisible = mangaRepoInventory.childCount > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fun processUserInput(input: String, mediaType: MediaType) {
|
||||||
|
val entry = if (input.endsWith("/") || input.endsWith("index.min.json"))
|
||||||
|
input.substring(0, input.lastIndexOf("/")) else input
|
||||||
|
if (mediaType == MediaType.ANIME) {
|
||||||
|
val anime = PrefManager.getVal<Set<String>>(PrefName.AnimeExtensionRepos).plus(entry)
|
||||||
|
PrefManager.setVal(PrefName.AnimeExtensionRepos, anime)
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
animeExtensionManager.findAvailableExtensions()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mediaType == MediaType.MANGA) {
|
||||||
|
val manga = PrefManager.getVal<Set<String>>(PrefName.MangaExtensionRepos).plus(entry)
|
||||||
|
PrefManager.setVal(PrefName.MangaExtensionRepos, manga)
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
mangaExtensionManager.findAvailableExtensions()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setExtensionOutput()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun processEditorAction(dialog: AlertDialog, editText: EditText, mediaType: MediaType) {
|
||||||
|
editText.setOnEditorActionListener { textView, action, keyEvent ->
|
||||||
|
if (action == EditorInfo.IME_ACTION_SEARCH || action == EditorInfo.IME_ACTION_DONE ||
|
||||||
|
(keyEvent?.action == KeyEvent.ACTION_UP
|
||||||
|
&& keyEvent.keyCode == KeyEvent.KEYCODE_ENTER)) {
|
||||||
|
processUserInput(textView.text.toString(), mediaType)
|
||||||
|
dialog.dismiss()
|
||||||
|
return@setOnEditorActionListener true
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setExtensionOutput()
|
||||||
|
animeAddRepository.setOnClickListener {
|
||||||
|
val dialogView = layoutInflater.inflate(R.layout.dialog_user_agent, null)
|
||||||
|
val editText = dialogView.findViewById<TextInputEditText>(R.id.userAgentTextBox).apply {
|
||||||
|
hint = getString(R.string.anime_add_repository)
|
||||||
|
}
|
||||||
|
val alertDialog = AlertDialog.Builder(this@SettingsActivity, R.style.MyPopup)
|
||||||
|
.setTitle(R.string.anime_add_repository)
|
||||||
|
.setView(dialogView)
|
||||||
|
.setPositiveButton(getString(R.string.ok)) { dialog, _ ->
|
||||||
|
processUserInput(editText.text.toString(), MediaType.ANIME)
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
.setNeutralButton(getString(R.string.reset)) { dialog, _ ->
|
||||||
|
PrefManager.removeVal(PrefName.DefaultUserAgent)
|
||||||
|
editText.setText("")
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
.setNegativeButton(getString(R.string.cancel)) { dialog, _ ->
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
.create()
|
||||||
|
|
||||||
|
processEditorAction(alertDialog, editText, MediaType.ANIME)
|
||||||
|
alertDialog.show()
|
||||||
|
alertDialog.window?.setDimAmount(0.8f)
|
||||||
|
}
|
||||||
|
|
||||||
|
mangaAddRepository.setOnClickListener {
|
||||||
|
val dialogView = layoutInflater.inflate(R.layout.dialog_user_agent, null)
|
||||||
|
val editText = dialogView.findViewById<TextInputEditText>(R.id.userAgentTextBox).apply {
|
||||||
|
hint = getString(R.string.manga_add_repository)
|
||||||
|
}
|
||||||
|
val alertDialog = AlertDialog.Builder(this@SettingsActivity, R.style.MyPopup)
|
||||||
|
.setTitle(R.string.manga_add_repository)
|
||||||
|
.setView(dialogView)
|
||||||
|
.setPositiveButton(getString(R.string.ok)) { dialog, _ ->
|
||||||
|
processUserInput(editText.text.toString(), MediaType.MANGA)
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
.setNeutralButton(getString(R.string.reset)) { dialog, _ ->
|
||||||
|
PrefManager.removeVal(PrefName.DefaultUserAgent)
|
||||||
|
editText.setText("")
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
.setNegativeButton(getString(R.string.cancel)) { dialog, _ ->
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
.create()
|
||||||
|
|
||||||
|
processEditorAction(alertDialog, editText, MediaType.MANGA)
|
||||||
|
alertDialog.show()
|
||||||
|
alertDialog.window?.setDimAmount(0.8f)
|
||||||
|
}
|
||||||
|
|
||||||
settingsForceLegacyInstall.isChecked =
|
settingsForceLegacyInstall.isChecked =
|
||||||
extensionInstaller.get() == BasePreferences.ExtensionInstaller.LEGACY
|
extensionInstaller.get() == BasePreferences.ExtensionInstaller.LEGACY
|
||||||
settingsForceLegacyInstall.setOnCheckedChangeListener { _, isChecked ->
|
settingsForceLegacyInstall.setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
@ -1169,4 +1317,4 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene
|
||||||
?: "Unknown Architecture"
|
?: "Unknown Architecture"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,9 @@ enum class PrefName(val data: Pref) { //TODO: Split this into multiple files
|
||||||
"Mozilla/5.0 (Linux; Android 13; Pixel 6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36"
|
"Mozilla/5.0 (Linux; Android 13; Pixel 6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36"
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
AnimeExtensionRepos(Pref(Location.General, Set::class, setOf<String>())),
|
||||||
|
MangaExtensionRepos(Pref(Location.General, Set::class, setOf<String>())),
|
||||||
|
SharedRepositories(Pref(Location.General, Boolean::class, false)),
|
||||||
AnimeSourcesOrder(Pref(Location.General, List::class, listOf<String>())),
|
AnimeSourcesOrder(Pref(Location.General, List::class, listOf<String>())),
|
||||||
AnimeSearchHistory(Pref(Location.General, Set::class, setOf<String>())),
|
AnimeSearchHistory(Pref(Location.General, Set::class, setOf<String>())),
|
||||||
MangaSourcesOrder(Pref(Location.General, List::class, listOf<String>())),
|
MangaSourcesOrder(Pref(Location.General, List::class, listOf<String>())),
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package eu.kanade.tachiyomi.extension.anime.api
|
package eu.kanade.tachiyomi.extension.anime.api
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
import ani.dantotsu.util.Logger
|
import ani.dantotsu.util.Logger
|
||||||
import eu.kanade.tachiyomi.extension.ExtensionUpdateNotifier
|
import eu.kanade.tachiyomi.extension.ExtensionUpdateNotifier
|
||||||
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
|
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
|
||||||
|
@ -32,42 +34,36 @@ internal class AnimeExtensionGithubApi {
|
||||||
preferenceStore.getLong("last_ext_check", 0)
|
preferenceStore.getLong("last_ext_check", 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var requiresFallbackSource = false
|
|
||||||
|
|
||||||
suspend fun findExtensions(): List<AnimeExtension.Available> {
|
suspend fun findExtensions(): List<AnimeExtension.Available> {
|
||||||
return withIOContext {
|
return withIOContext {
|
||||||
val githubResponse = if (requiresFallbackSource) {
|
|
||||||
null
|
val extensions: ArrayList<AnimeExtension.Available> = arrayListOf()
|
||||||
} else {
|
|
||||||
|
PrefManager.getVal<Set<String>>(PrefName.AnimeExtensionRepos).forEach {
|
||||||
try {
|
try {
|
||||||
networkService.client
|
val githubResponse =
|
||||||
.newCall(GET("${REPO_URL_PREFIX}index.min.json"))
|
networkService.client
|
||||||
.awaitSuccess()
|
.newCall(GET("${it}/index.min.json"))
|
||||||
|
.awaitSuccess()
|
||||||
|
|
||||||
|
val repoExtensions = with(json) {
|
||||||
|
githubResponse
|
||||||
|
.parseAs<List<AnimeExtensionJsonObject>>()
|
||||||
|
.toExtensions(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check - a small number of extensions probably means something broke
|
||||||
|
// with the repo generator
|
||||||
|
if (repoExtensions.size < 10) {
|
||||||
|
throw Exception()
|
||||||
|
}
|
||||||
|
|
||||||
|
extensions.addAll(repoExtensions)
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
Logger.log("Failed to get extensions from GitHub")
|
Logger.log("Failed to get extensions from GitHub")
|
||||||
requiresFallbackSource = true
|
|
||||||
null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val response = githubResponse ?: run {
|
|
||||||
networkService.client
|
|
||||||
.newCall(GET("${FALLBACK_REPO_URL_PREFIX}index.min.json"))
|
|
||||||
.awaitSuccess()
|
|
||||||
}
|
|
||||||
|
|
||||||
val extensions = with(json) {
|
|
||||||
response
|
|
||||||
.parseAs<List<AnimeExtensionJsonObject>>()
|
|
||||||
.toExtensions()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sanity check - a small number of extensions probably means something broke
|
|
||||||
// with the repo generator
|
|
||||||
if (extensions.size < 10) {
|
|
||||||
throw Exception()
|
|
||||||
}
|
|
||||||
|
|
||||||
extensions
|
extensions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,7 +107,7 @@ internal class AnimeExtensionGithubApi {
|
||||||
return extensionsWithUpdate
|
return extensionsWithUpdate
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun List<AnimeExtensionJsonObject>.toExtensions(): List<AnimeExtension.Available> {
|
private fun List<AnimeExtensionJsonObject>.toExtensions(repository: String): List<AnimeExtension.Available> {
|
||||||
return this
|
return this
|
||||||
.filter {
|
.filter {
|
||||||
val libVersion = it.extractLibVersion()
|
val libVersion = it.extractLibVersion()
|
||||||
|
@ -130,7 +126,8 @@ internal class AnimeExtensionGithubApi {
|
||||||
hasChangelog = it.hasChangelog == 1,
|
hasChangelog = it.hasChangelog == 1,
|
||||||
sources = it.sources?.toAnimeExtensionSources().orEmpty(),
|
sources = it.sources?.toAnimeExtensionSources().orEmpty(),
|
||||||
apkName = it.apk,
|
apkName = it.apk,
|
||||||
iconUrl = "${getUrlPrefix()}icon/${it.pkg}.png",
|
repository = repository,
|
||||||
|
iconUrl = "${repository}/icon/${it.pkg}.png",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,15 +144,7 @@ internal class AnimeExtensionGithubApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getApkUrl(extension: AnimeExtension.Available): String {
|
fun getApkUrl(extension: AnimeExtension.Available): String {
|
||||||
return "${getUrlPrefix()}apk/${extension.apkName}"
|
return "${extension.repository}/apk/${extension.apkName}"
|
||||||
}
|
|
||||||
|
|
||||||
private fun getUrlPrefix(): String {
|
|
||||||
return if (requiresFallbackSource) {
|
|
||||||
FALLBACK_REPO_URL_PREFIX
|
|
||||||
} else {
|
|
||||||
REPO_URL_PREFIX
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,11 +152,6 @@ private fun AnimeExtensionJsonObject.extractLibVersion(): Double {
|
||||||
return version.substringBeforeLast('.').toDouble()
|
return version.substringBeforeLast('.').toDouble()
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val REPO_URL_PREFIX =
|
|
||||||
"https://raw.githubusercontent.com/aniyomiorg/aniyomi-extensions/repo/"
|
|
||||||
private const val FALLBACK_REPO_URL_PREFIX =
|
|
||||||
"https://gcore.jsdelivr.net/gh/aniyomiorg/aniyomi-extensions@repo/"
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
private data class AnimeExtensionJsonObject(
|
private data class AnimeExtensionJsonObject(
|
||||||
val name: String,
|
val name: String,
|
||||||
|
|
|
@ -47,6 +47,7 @@ sealed class AnimeExtension {
|
||||||
val sources: List<AvailableAnimeSources>,
|
val sources: List<AvailableAnimeSources>,
|
||||||
val apkName: String,
|
val apkName: String,
|
||||||
val iconUrl: String,
|
val iconUrl: String,
|
||||||
|
val repository: String
|
||||||
) : AnimeExtension()
|
) : AnimeExtension()
|
||||||
|
|
||||||
data class Untrusted(
|
data class Untrusted(
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package eu.kanade.tachiyomi.extension.manga.api
|
package eu.kanade.tachiyomi.extension.manga.api
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
import ani.dantotsu.util.Logger
|
import ani.dantotsu.util.Logger
|
||||||
import eu.kanade.tachiyomi.extension.ExtensionUpdateNotifier
|
import eu.kanade.tachiyomi.extension.ExtensionUpdateNotifier
|
||||||
import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
|
import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
|
||||||
|
@ -32,42 +34,37 @@ internal class MangaExtensionGithubApi {
|
||||||
preferenceStore.getLong("last_ext_check", 0)
|
preferenceStore.getLong("last_ext_check", 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var requiresFallbackSource = false
|
|
||||||
|
|
||||||
suspend fun findExtensions(): List<MangaExtension.Available> {
|
suspend fun findExtensions(): List<MangaExtension.Available> {
|
||||||
return withIOContext {
|
return withIOContext {
|
||||||
val githubResponse = if (requiresFallbackSource) {
|
|
||||||
null
|
val extensions: ArrayList<MangaExtension.Available> = arrayListOf()
|
||||||
} else {
|
|
||||||
|
|
||||||
|
PrefManager.getVal<Set<String>>(PrefName.MangaExtensionRepos).forEach {
|
||||||
try {
|
try {
|
||||||
networkService.client
|
val githubResponse =
|
||||||
.newCall(GET("${REPO_URL_PREFIX}index.min.json"))
|
networkService.client
|
||||||
.awaitSuccess()
|
.newCall(GET("${it}/index.min.json"))
|
||||||
|
.awaitSuccess()
|
||||||
|
|
||||||
|
val repoExtensions = with(json) {
|
||||||
|
githubResponse
|
||||||
|
.parseAs<List<ExtensionJsonObject>>()
|
||||||
|
.toExtensions(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check - a small number of extensions probably means something broke
|
||||||
|
// with the repo generator
|
||||||
|
if (repoExtensions.size < 10) {
|
||||||
|
throw Exception()
|
||||||
|
}
|
||||||
|
|
||||||
|
extensions.addAll(repoExtensions)
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
Logger.log("Failed to get extensions from GitHub")
|
Logger.log("Failed to get extensions from GitHub")
|
||||||
requiresFallbackSource = true
|
|
||||||
null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val response = githubResponse ?: run {
|
|
||||||
networkService.client
|
|
||||||
.newCall(GET("${FALLBACK_REPO_URL_PREFIX}index.min.json"))
|
|
||||||
.awaitSuccess()
|
|
||||||
}
|
|
||||||
|
|
||||||
val extensions = with(json) {
|
|
||||||
response
|
|
||||||
.parseAs<List<ExtensionJsonObject>>()
|
|
||||||
.toExtensions()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sanity check - a small number of extensions probably means something broke
|
|
||||||
// with the repo generator
|
|
||||||
if (extensions.size < 100) {
|
|
||||||
throw Exception()
|
|
||||||
}
|
|
||||||
|
|
||||||
extensions
|
extensions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,7 +107,7 @@ internal class MangaExtensionGithubApi {
|
||||||
return extensionsWithUpdate
|
return extensionsWithUpdate
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun List<ExtensionJsonObject>.toExtensions(): List<MangaExtension.Available> {
|
private fun List<ExtensionJsonObject>.toExtensions(repository: String): List<MangaExtension.Available> {
|
||||||
return this
|
return this
|
||||||
.filter {
|
.filter {
|
||||||
val libVersion = it.extractLibVersion()
|
val libVersion = it.extractLibVersion()
|
||||||
|
@ -129,7 +126,8 @@ internal class MangaExtensionGithubApi {
|
||||||
hasChangelog = it.hasChangelog == 1,
|
hasChangelog = it.hasChangelog == 1,
|
||||||
sources = it.sources?.toExtensionSources().orEmpty(),
|
sources = it.sources?.toExtensionSources().orEmpty(),
|
||||||
apkName = it.apk,
|
apkName = it.apk,
|
||||||
iconUrl = "${getUrlPrefix()}icon/${it.pkg}.png",
|
repository = repository,
|
||||||
|
iconUrl = "${repository}/icon/${it.pkg}.png",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,15 +144,7 @@ internal class MangaExtensionGithubApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getApkUrl(extension: MangaExtension.Available): String {
|
fun getApkUrl(extension: MangaExtension.Available): String {
|
||||||
return "${getUrlPrefix()}apk/${extension.apkName}"
|
return "${extension.repository}/apk/${extension.apkName}"
|
||||||
}
|
|
||||||
|
|
||||||
private fun getUrlPrefix(): String {
|
|
||||||
return if (requiresFallbackSource) {
|
|
||||||
FALLBACK_REPO_URL_PREFIX
|
|
||||||
} else {
|
|
||||||
REPO_URL_PREFIX
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ExtensionJsonObject.extractLibVersion(): Double {
|
private fun ExtensionJsonObject.extractLibVersion(): Double {
|
||||||
|
@ -162,10 +152,6 @@ internal class MangaExtensionGithubApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val REPO_URL_PREFIX = "https://raw.githubusercontent.com/keiyoushi/extensions/main/"
|
|
||||||
private const val FALLBACK_REPO_URL_PREFIX =
|
|
||||||
"https://gcore.jsdelivr.net/gh/keiyoushi/extensions@main/"
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
private data class ExtensionJsonObject(
|
private data class ExtensionJsonObject(
|
||||||
val name: String,
|
val name: String,
|
||||||
|
|
|
@ -47,6 +47,7 @@ sealed class MangaExtension {
|
||||||
val sources: List<AvailableMangaSources>,
|
val sources: List<AvailableMangaSources>,
|
||||||
val apkName: String,
|
val apkName: String,
|
||||||
val iconUrl: String,
|
val iconUrl: String,
|
||||||
|
val repository: String
|
||||||
) : MangaExtension()
|
) : MangaExtension()
|
||||||
|
|
||||||
data class Untrusted(
|
data class Untrusted(
|
||||||
|
|
|
@ -177,8 +177,7 @@
|
||||||
android:layout_height="64dp"
|
android:layout_height="64dp"
|
||||||
android:fontFamily="@font/poppins_bold"
|
android:fontFamily="@font/poppins_bold"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:paddingStart="48dp"
|
android:paddingHorizontal="32dp"
|
||||||
android:paddingEnd="32dp"
|
|
||||||
android:text="@string/sub_text_example"
|
android:text="@string/sub_text_example"
|
||||||
android:textColor="?attr/colorSecondary"
|
android:textColor="?attr/colorSecondary"
|
||||||
app:drawableEndCompat="@drawable/ic_round_arrow_drop_down_24"
|
app:drawableEndCompat="@drawable/ic_round_arrow_drop_down_24"
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<ani.dantotsu.others.Xpandable
|
<ani.dantotsu.others.Xpandable
|
||||||
|
android:id="@+id/extensionSettings"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
|
@ -18,14 +20,6 @@
|
||||||
app:drawableEndCompat="@drawable/ic_round_arrow_drop_down_24"
|
app:drawableEndCompat="@drawable/ic_round_arrow_drop_down_24"
|
||||||
tools:ignore="TextContrastCheck" />
|
tools:ignore="TextContrastCheck" />
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="1dp"
|
|
||||||
android:layout_marginStart="-16dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:layout_marginEnd="-16dp"
|
|
||||||
android:background="?android:attr/listDivider" />
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/userAgent"
|
android:id="@+id/userAgent"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -38,8 +32,7 @@
|
||||||
android:fontFamily="@font/poppins_bold"
|
android:fontFamily="@font/poppins_bold"
|
||||||
android:insetTop="0dp"
|
android:insetTop="0dp"
|
||||||
android:insetBottom="0dp"
|
android:insetBottom="0dp"
|
||||||
android:paddingStart="31dp"
|
android:paddingHorizontal="31dp"
|
||||||
android:paddingEnd="31dp"
|
|
||||||
android:text="@string/user_agent"
|
android:text="@string/user_agent"
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
android:textAllCaps="false"
|
android:textAllCaps="false"
|
||||||
|
@ -55,7 +48,118 @@
|
||||||
android:layout_height="1dp"
|
android:layout_height="1dp"
|
||||||
android:layout_marginStart="-16dp"
|
android:layout_marginStart="-16dp"
|
||||||
android:layout_marginEnd="-16dp"
|
android:layout_marginEnd="-16dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:background="?android:attr/listDivider" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/animeRepoHeading"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
|
android:fontFamily="@font/poppins_bold"
|
||||||
|
android:text="@string/anime_repo_listing"
|
||||||
|
android:textSize="14sp"
|
||||||
|
tools:ignore="RtlSymmetry" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/animeRepoInventory"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:paddingStart="36dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:ignore="RtlSymmetry"
|
||||||
|
android:orientation="vertical" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/animeAddRepository"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
android:layout_marginStart="-31dp"
|
||||||
|
android:layout_marginEnd="-31dp"
|
||||||
|
android:background="@drawable/ui_bg"
|
||||||
|
android:backgroundTint="?attr/colorSecondary"
|
||||||
|
android:backgroundTintMode="src_atop"
|
||||||
|
android:fontFamily="@font/poppins_bold"
|
||||||
|
android:insetTop="0dp"
|
||||||
|
android:insetBottom="0dp"
|
||||||
|
android:paddingHorizontal="31dp"
|
||||||
|
android:text="@string/anime_add_repository"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="?attr/colorOnBackground"
|
||||||
|
app:cornerRadius="0dp"
|
||||||
|
app:icon="@drawable/ic_github"
|
||||||
|
app:iconPadding="16dp"
|
||||||
|
app:iconSize="24dp"
|
||||||
|
app:iconTint="?attr/colorPrimary" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginStart="-16dp"
|
||||||
|
android:layout_marginEnd="-16dp"
|
||||||
|
android:background="?android:attr/listDivider" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/mangaRepoHeadingDivider"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginStart="-16dp"
|
||||||
|
android:layout_marginEnd="-16dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:background="?android:attr/listDivider" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/mangaRepoHeading"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:fontFamily="@font/poppins_bold"
|
||||||
|
android:text="@string/manga_repo_listing"
|
||||||
|
android:textSize="14sp"
|
||||||
|
tools:ignore="RtlSymmetry" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/mangaRepoInventory"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:paddingStart="36dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:ignore="RtlSymmetry"
|
||||||
|
android:orientation="vertical" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/mangaAddRepository"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
android:layout_marginStart="-31dp"
|
||||||
|
android:layout_marginEnd="-31dp"
|
||||||
|
android:background="@drawable/ui_bg"
|
||||||
|
android:backgroundTint="?attr/colorSecondary"
|
||||||
|
android:backgroundTintMode="src_atop"
|
||||||
|
android:fontFamily="@font/poppins_bold"
|
||||||
|
android:insetTop="0dp"
|
||||||
|
android:insetBottom="0dp"
|
||||||
|
android:paddingHorizontal="31dp"
|
||||||
|
android:text="@string/manga_add_repository"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="?attr/colorOnBackground"
|
||||||
|
app:cornerRadius="0dp"
|
||||||
|
app:icon="@drawable/ic_github"
|
||||||
|
app:iconPadding="16dp"
|
||||||
|
app:iconSize="24dp"
|
||||||
|
app:iconTint="?attr/colorPrimary" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/mangaRepoDivider"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginStart="-16dp"
|
||||||
|
android:layout_marginEnd="-16dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
android:background="?android:attr/listDivider" />
|
android:background="?android:attr/listDivider" />
|
||||||
|
|
||||||
<com.google.android.material.materialswitch.MaterialSwitch
|
<com.google.android.material.materialswitch.MaterialSwitch
|
||||||
|
@ -113,4 +217,4 @@
|
||||||
app:thumbTint="@color/button_switch_track" />
|
app:thumbTint="@color/button_switch_track" />
|
||||||
|
|
||||||
</ani.dantotsu.others.Xpandable>
|
</ani.dantotsu.others.Xpandable>
|
||||||
</merge>
|
</merge>
|
||||||
|
|
18
app/src/main/res/layout/item_repository.xml
Normal file
18
app/src/main/res/layout/item_repository.xml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/repositoryItem"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fontFamily="@font/poppins_semi_bold"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:textColor="?attr/colorOnBackground" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -252,7 +252,7 @@
|
||||||
<string name="sub_window_color_info">"The subtitle window is the part left and right from them. (where the background isn\'t)"</string>
|
<string name="sub_window_color_info">"The subtitle window is the part left and right from them. (where the background isn\'t)"</string>
|
||||||
<string name="sub_color_info"><b>Note:</b> Changing above settings only affects Soft-Subtitles!</string>
|
<string name="sub_color_info"><b>Note:</b> Changing above settings only affects Soft-Subtitles!</string>
|
||||||
<string name="sub_alpha">Subtitle Transparency</string>
|
<string name="sub_alpha">Subtitle Transparency</string>
|
||||||
<string name="sub_text_example">Example Subtitle</string>
|
<string name="sub_text_example">Example Sub</string>
|
||||||
<string name="sub_font_select">Subtitle Font</string>
|
<string name="sub_font_select">Subtitle Font</string>
|
||||||
<string name="subtitle_font_size">Subtitle Size</string>
|
<string name="subtitle_font_size">Subtitle Size</string>
|
||||||
|
|
||||||
|
@ -417,6 +417,7 @@
|
||||||
<string name="crop_borders">Crop Borders</string>
|
<string name="crop_borders">Crop Borders</string>
|
||||||
<string name="note">NOTE</string>
|
<string name="note">NOTE</string>
|
||||||
|
|
||||||
|
<string name="manage_extension_repos">Manage Extension Repos</string>
|
||||||
<string name="installing_extension">Installing extension</string>
|
<string name="installing_extension">Installing extension</string>
|
||||||
<string name="installation_failed">Installation failed: %1$s</string>
|
<string name="installation_failed">Installation failed: %1$s</string>
|
||||||
<string name="installation_complete">Installation complete</string>
|
<string name="installation_complete">Installation complete</string>
|
||||||
|
@ -859,6 +860,13 @@ Non quae tempore quo provident laudantium qui illo dolor vel quia dolor et exerc
|
||||||
<string name="title_color">Title Color</string>
|
<string name="title_color">Title Color</string>
|
||||||
<string name="stat_text_color">Stats Text Color</string>
|
<string name="stat_text_color">Stats Text Color</string>
|
||||||
<string name="placeholder">Placeholder</string>
|
<string name="placeholder">Placeholder</string>
|
||||||
|
|
||||||
|
<string name="anime_repo_listing">Anime Extension Repos</string>
|
||||||
|
<string name="anime_add_repository">Add Anime Repo</string>
|
||||||
|
<string name="manga_repo_listing">Manga Extension Repos</string>
|
||||||
|
<string name="manga_add_repository">Add Manga Repo</string>
|
||||||
|
<string name="long_click_delete">Long click to delete</string>
|
||||||
|
|
||||||
<string name="trending_movies">Trending Movies</string>
|
<string name="trending_movies">Trending Movies</string>
|
||||||
<string name="include_list">Include list</string>
|
<string name="include_list">Include list</string>
|
||||||
<string name="top_rated">Top rated</string>
|
<string name="top_rated">Top rated</string>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue