???
This commit is contained in:
parent
ff02280239
commit
664b5a4bdd
46 changed files with 317 additions and 190 deletions
|
@ -180,7 +180,9 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
||||||
|
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
@OptIn(UnstableApi::class) private fun grid(){
|
|
||||||
|
@OptIn(UnstableApi::class)
|
||||||
|
private fun grid() {
|
||||||
gridView.setOnItemClickListener { parent, view, position, id ->
|
gridView.setOnItemClickListener { parent, view, position, id ->
|
||||||
// Get the OfflineAnimeModel that was clicked
|
// Get the OfflineAnimeModel that was clicked
|
||||||
val item = adapter.getItem(position) as OfflineAnimeModel
|
val item = adapter.getItem(position) as OfflineAnimeModel
|
||||||
|
@ -211,7 +213,10 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
||||||
builder.setMessage("Are you sure you want to delete ${item.title}?")
|
builder.setMessage("Are you sure you want to delete ${item.title}?")
|
||||||
builder.setPositiveButton("Yes") { _, _ ->
|
builder.setPositiveButton("Yes") { _, _ ->
|
||||||
downloadManager.removeMedia(item.title, type)
|
downloadManager.removeMedia(item.title, type)
|
||||||
val mediaIds = requireContext().getSharedPreferences(getString(R.string.anime_downloads), Context.MODE_PRIVATE)
|
val mediaIds = requireContext().getSharedPreferences(
|
||||||
|
getString(R.string.anime_downloads),
|
||||||
|
Context.MODE_PRIVATE
|
||||||
|
)
|
||||||
?.all?.filter { it.key.contains(item.title) }?.values ?: emptySet()
|
?.all?.filter { it.key.contains(item.title) }?.values ?: emptySet()
|
||||||
if (mediaIds.isEmpty()) {
|
if (mediaIds.isEmpty()) {
|
||||||
snackString("No media found") // if this happens, terrible things have happened
|
snackString("No media found") // if this happens, terrible things have happened
|
||||||
|
@ -231,6 +236,7 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSearchQuery(query: String) {
|
override fun onSearchQuery(query: String) {
|
||||||
adapter.onSearchQuery(query)
|
adapter.onSearchQuery(query)
|
||||||
}
|
}
|
||||||
|
@ -254,7 +260,8 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val scrollTop = view.findViewById<CardView>(R.id.mangaPageScrollTop)
|
val scrollTop = view.findViewById<CardView>(R.id.mangaPageScrollTop)
|
||||||
scrollTop.translationY = -(navBarHeight + bottomBar.height + bottomBar.marginBottom).toFloat()
|
scrollTop.translationY =
|
||||||
|
-(navBarHeight + bottomBar.height + bottomBar.marginBottom).toFloat()
|
||||||
val visible = false
|
val visible = false
|
||||||
|
|
||||||
fun animate() {
|
fun animate() {
|
||||||
|
@ -401,9 +408,13 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
||||||
mediaModel.status == currActivity()!!.getString(R.string.status_releasing)
|
mediaModel.status == currActivity()!!.getString(R.string.status_releasing)
|
||||||
val isUserScored = mediaModel.userScore != 0
|
val isUserScored = mediaModel.userScore != 0
|
||||||
val watchedEpisodes = (mediaModel.userProgress ?: "~").toString()
|
val watchedEpisodes = (mediaModel.userProgress ?: "~").toString()
|
||||||
val totalEpisode = if (mediaModel.anime?.nextAiringEpisode != null) (mediaModel.anime.nextAiringEpisode.toString() + " | " + (mediaModel.anime.totalEpisodes ?: "~").toString()) else (mediaModel.anime?.totalEpisodes ?: "~").toString()
|
val totalEpisode =
|
||||||
|
if (mediaModel.anime?.nextAiringEpisode != null) (mediaModel.anime.nextAiringEpisode.toString() + " | " + (mediaModel.anime.totalEpisodes
|
||||||
|
?: "~").toString()) else (mediaModel.anime?.totalEpisodes ?: "~").toString()
|
||||||
val chapters = " Chapters"
|
val chapters = " Chapters"
|
||||||
val totalEpisodesList = if (mediaModel.anime?.nextAiringEpisode != null) (mediaModel.anime.nextAiringEpisode.toString()) else (mediaModel.anime?.totalEpisodes ?: "~").toString()
|
val totalEpisodesList =
|
||||||
|
if (mediaModel.anime?.nextAiringEpisode != null) (mediaModel.anime.nextAiringEpisode.toString()) else (mediaModel.anime?.totalEpisodes
|
||||||
|
?: "~").toString()
|
||||||
return OfflineAnimeModel(
|
return OfflineAnimeModel(
|
||||||
title,
|
title,
|
||||||
score,
|
score,
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
package ani.dantotsu.download.anime
|
package ani.dantotsu.download.anime
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
data class OfflineAnimeModel (
|
|
||||||
|
data class OfflineAnimeModel(
|
||||||
val title: String,
|
val title: String,
|
||||||
val score: String,
|
val score: String,
|
||||||
val totalEpisode: String,
|
val totalEpisode: String,
|
||||||
|
|
|
@ -171,11 +171,13 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
||||||
gridView.scheduleLayoutAnimation()
|
gridView.scheduleLayoutAnimation()
|
||||||
grid()
|
grid()
|
||||||
val total = view.findViewById<TextView>(R.id.total)
|
val total = view.findViewById<TextView>(R.id.total)
|
||||||
total.text = if (gridView.count > 0) "Manga and Novels (${gridView.count})" else "Empty List"
|
total.text =
|
||||||
|
if (gridView.count > 0) "Manga and Novels (${gridView.count})" else "Empty List"
|
||||||
|
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
private fun grid(){
|
|
||||||
|
private fun grid() {
|
||||||
gridView.setOnItemClickListener { parent, view, position, id ->
|
gridView.setOnItemClickListener { parent, view, position, id ->
|
||||||
// Get the OfflineMangaModel that was clicked
|
// Get the OfflineMangaModel that was clicked
|
||||||
val item = adapter.getItem(position) as OfflineMangaModel
|
val item = adapter.getItem(position) as OfflineMangaModel
|
||||||
|
@ -220,6 +222,7 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSearchQuery(query: String) {
|
override fun onSearchQuery(query: String) {
|
||||||
adapter.onSearchQuery(query)
|
adapter.onSearchQuery(query)
|
||||||
}
|
}
|
||||||
|
@ -243,7 +246,8 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val scrollTop = view.findViewById<CardView>(R.id.mangaPageScrollTop)
|
val scrollTop = view.findViewById<CardView>(R.id.mangaPageScrollTop)
|
||||||
scrollTop.translationY = -(navBarHeight + bottomBar.height + bottomBar.marginBottom).toFloat()
|
scrollTop.translationY =
|
||||||
|
-(navBarHeight + bottomBar.height + bottomBar.marginBottom).toFloat()
|
||||||
val visible = false
|
val visible = false
|
||||||
|
|
||||||
fun animate() {
|
fun animate() {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package ani.dantotsu.home
|
package ani.dantotsu.home
|
||||||
|
|
||||||
import android.animation.ObjectAnimator
|
import android.animation.ObjectAnimator
|
||||||
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
|
||||||
|
|
|
@ -140,6 +140,7 @@ class MangaPageAdapter : RecyclerView.Adapter<MangaPageAdapter.MangaPageViewHold
|
||||||
fun updateHeight() {
|
fun updateHeight() {
|
||||||
trendingViewPager!!.updateLayoutParams { height += statusBarHeight }
|
trendingViewPager!!.updateLayoutParams { height += statusBarHeight }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateTrending(adaptor: MediaAdaptor) {
|
fun updateTrending(adaptor: MediaAdaptor) {
|
||||||
binding.mangaTrendingProgressBar.visibility = View.GONE
|
binding.mangaTrendingProgressBar.visibility = View.GONE
|
||||||
binding.mangaTrendingViewPager.adapter = adaptor
|
binding.mangaTrendingViewPager.adapter = adaptor
|
||||||
|
|
|
@ -1,24 +1,19 @@
|
||||||
package ani.dantotsu.media
|
package ani.dantotsu.media
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Intent
|
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import androidx.annotation.OptIn
|
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.media3.common.util.UnstableApi
|
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.connections.anilist.Anilist
|
import ani.dantotsu.connections.anilist.Anilist
|
||||||
import ani.dantotsu.currActivity
|
|
||||||
import ani.dantotsu.currContext
|
import ani.dantotsu.currContext
|
||||||
import ani.dantotsu.loadData
|
import ani.dantotsu.loadData
|
||||||
import ani.dantotsu.logger
|
import ani.dantotsu.logger
|
||||||
import ani.dantotsu.media.anime.Episode
|
import ani.dantotsu.media.anime.Episode
|
||||||
import ani.dantotsu.media.anime.ExoplayerView
|
|
||||||
import ani.dantotsu.media.anime.SelectorDialogFragment
|
import ani.dantotsu.media.anime.SelectorDialogFragment
|
||||||
import ani.dantotsu.media.manga.MangaChapter
|
import ani.dantotsu.media.manga.MangaChapter
|
||||||
import ani.dantotsu.others.AniSkip
|
import ani.dantotsu.others.AniSkip
|
||||||
|
@ -260,7 +255,12 @@ class MediaDetailsViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
media.selected = this.loadSelected(media)
|
media.selected = this.loadSelected(media)
|
||||||
val selector =
|
val selector =
|
||||||
SelectorDialogFragment.newInstance(media.selected!!.server, launch, prevEp, isDownload)
|
SelectorDialogFragment.newInstance(
|
||||||
|
media.selected!!.server,
|
||||||
|
launch,
|
||||||
|
prevEp,
|
||||||
|
isDownload
|
||||||
|
)
|
||||||
selector.show(manager, "dialog")
|
selector.show(manager, "dialog")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,11 +166,17 @@ class AnimeWatchFragment : Fragment() {
|
||||||
if (!loaded) {
|
if (!loaded) {
|
||||||
model.watchSources = if (media.isAdult) HAnimeSources else AnimeSources
|
model.watchSources = if (media.isAdult) HAnimeSources else AnimeSources
|
||||||
|
|
||||||
val offlineMode = model.watchSources!!.isDownloadedSource(media.selected!!.sourceIndex)
|
val offlineMode =
|
||||||
|
model.watchSources!!.isDownloadedSource(media.selected!!.sourceIndex)
|
||||||
|
|
||||||
headerAdapter = AnimeWatchAdapter(it, this, model.watchSources!!)
|
headerAdapter = AnimeWatchAdapter(it, this, model.watchSources!!)
|
||||||
episodeAdapter =
|
episodeAdapter =
|
||||||
EpisodeAdapter(style ?: uiSettings.animeDefaultView, media, this, offlineMode = offlineMode)
|
EpisodeAdapter(
|
||||||
|
style ?: uiSettings.animeDefaultView,
|
||||||
|
media,
|
||||||
|
this,
|
||||||
|
offlineMode = offlineMode
|
||||||
|
)
|
||||||
|
|
||||||
binding.animeSourceRecycler.adapter =
|
binding.animeSourceRecycler.adapter =
|
||||||
ConcatAdapter(headerAdapter, episodeAdapter)
|
ConcatAdapter(headerAdapter, episodeAdapter)
|
||||||
|
@ -421,7 +427,10 @@ class AnimeWatchFragment : Fragment() {
|
||||||
fun onAnimeEpisodeStopDownloadClick(i: String) {
|
fun onAnimeEpisodeStopDownloadClick(i: String) {
|
||||||
val cancelIntent = Intent().apply {
|
val cancelIntent = Intent().apply {
|
||||||
action = AnimeDownloaderService.ACTION_CANCEL_DOWNLOAD
|
action = AnimeDownloaderService.ACTION_CANCEL_DOWNLOAD
|
||||||
putExtra(AnimeDownloaderService.EXTRA_TASK_NAME, AnimeDownloaderService.AnimeDownloadTask.getTaskName(media.mainName(), i))
|
putExtra(
|
||||||
|
AnimeDownloaderService.EXTRA_TASK_NAME,
|
||||||
|
AnimeDownloaderService.AnimeDownloadTask.getTaskName(media.mainName(), i)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
requireContext().sendBroadcast(cancelIntent)
|
requireContext().sendBroadcast(cancelIntent)
|
||||||
|
|
||||||
|
@ -447,12 +456,12 @@ class AnimeWatchFragment : Fragment() {
|
||||||
)
|
)
|
||||||
val taskName = AnimeDownloaderService.AnimeDownloadTask.getTaskName(media.mainName(), i)
|
val taskName = AnimeDownloaderService.AnimeDownloadTask.getTaskName(media.mainName(), i)
|
||||||
val id = requireContext().getSharedPreferences(
|
val id = requireContext().getSharedPreferences(
|
||||||
ContextCompat.getString(requireContext(), R.string.anime_downloads),
|
ContextCompat.getString(requireContext(), R.string.anime_downloads),
|
||||||
Context.MODE_PRIVATE
|
Context.MODE_PRIVATE
|
||||||
).getString(
|
).getString(
|
||||||
taskName,
|
taskName,
|
||||||
""
|
""
|
||||||
) ?: ""
|
) ?: ""
|
||||||
requireContext().getSharedPreferences(
|
requireContext().getSharedPreferences(
|
||||||
ContextCompat.getString(requireContext(), R.string.anime_downloads),
|
ContextCompat.getString(requireContext(), R.string.anime_downloads),
|
||||||
Context.MODE_PRIVATE
|
Context.MODE_PRIVATE
|
||||||
|
|
|
@ -75,7 +75,6 @@ import ani.dantotsu.media.MediaDetailsViewModel
|
||||||
import ani.dantotsu.media.SubtitleDownloader
|
import ani.dantotsu.media.SubtitleDownloader
|
||||||
import ani.dantotsu.others.AniSkip
|
import ani.dantotsu.others.AniSkip
|
||||||
import ani.dantotsu.others.AniSkip.getType
|
import ani.dantotsu.others.AniSkip.getType
|
||||||
import ani.dantotsu.others.Download.download
|
|
||||||
import ani.dantotsu.others.LangSet
|
import ani.dantotsu.others.LangSet
|
||||||
import ani.dantotsu.others.ResettableTimer
|
import ani.dantotsu.others.ResettableTimer
|
||||||
import ani.dantotsu.others.getSerialized
|
import ani.dantotsu.others.getSerialized
|
||||||
|
|
|
@ -6,7 +6,6 @@ import android.content.Intent
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.HapticFeedbackConstants
|
import android.view.HapticFeedbackConstants
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
|
|
@ -7,9 +7,7 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.core.view.updateLayoutParams
|
import androidx.core.view.updateLayoutParams
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import ani.dantotsu.App
|
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.currContext
|
|
||||||
import ani.dantotsu.databinding.FragmentOfflineBinding
|
import ani.dantotsu.databinding.FragmentOfflineBinding
|
||||||
import ani.dantotsu.isOnline
|
import ani.dantotsu.isOnline
|
||||||
import ani.dantotsu.navBarHeight
|
import ani.dantotsu.navBarHeight
|
||||||
|
|
|
@ -169,7 +169,8 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
sourceLanguage = 0
|
sourceLanguage = 0
|
||||||
extension.sources[sourceLanguage]
|
extension.sources[sourceLanguage]
|
||||||
} as? AnimeHttpSource ?: (extension.sources[sourceLanguage] as? AnimeCatalogueSource ?: return emptyList())
|
} as? AnimeHttpSource ?: (extension.sources[sourceLanguage] as? AnimeCatalogueSource
|
||||||
|
?: return emptyList())
|
||||||
return try {
|
return try {
|
||||||
val res = source.fetchSearchAnime(1, query, source.getFilterList()).awaitSingle()
|
val res = source.fetchSearchAnime(1, query, source.getFilterList()).awaitSingle()
|
||||||
logger("query: $query")
|
logger("query: $query")
|
||||||
|
|
|
@ -11,9 +11,10 @@ class NotificationClickReceiver : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context, intent: Intent?) {
|
override fun onReceive(context: Context, intent: Intent?) {
|
||||||
|
|
||||||
context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit()
|
context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit()
|
||||||
.putBoolean("incognito", false)
|
.putBoolean("incognito", false)
|
||||||
.apply()
|
.apply()
|
||||||
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
val notificationManager =
|
||||||
|
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
notificationManager.cancel(INCOGNITO_CHANNEL_ID)
|
notificationManager.cancel(INCOGNITO_CHANNEL_ID)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.os.Build
|
|
||||||
import android.view.Window
|
import android.view.Window
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
|
@ -72,7 +71,7 @@ class ThemeManager(private val context: Activity) {
|
||||||
} else {
|
} else {
|
||||||
winParams.flags = winParams.flags and bits.inv()
|
winParams.flags = winParams.flags and bits.inv()
|
||||||
}
|
}
|
||||||
win.setAttributes(winParams)
|
win.attributes = winParams
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun applyDynamicColors(
|
private fun applyDynamicColors(
|
||||||
|
|
|
@ -20,7 +20,10 @@ class BasePreferences(
|
||||||
|
|
||||||
fun acraEnabled() = preferenceStore.getBoolean("acra.enable", true)
|
fun acraEnabled() = preferenceStore.getBoolean("acra.enable", true)
|
||||||
|
|
||||||
fun deviceHasPip() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && context.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
|
fun deviceHasPip() =
|
||||||
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && context.packageManager.hasSystemFeature(
|
||||||
|
PackageManager.FEATURE_PICTURE_IN_PICTURE
|
||||||
|
)
|
||||||
|
|
||||||
enum class ExtensionInstaller(val titleResId: String) {
|
enum class ExtensionInstaller(val titleResId: String) {
|
||||||
LEGACY("Legacy"),
|
LEGACY("Legacy"),
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package eu.kanade.domain.base
|
package eu.kanade.domain.base
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import eu.kanade.tachiyomi.util.system.hasMiuiPackageInstaller
|
|
||||||
import eu.kanade.domain.base.BasePreferences.ExtensionInstaller
|
import eu.kanade.domain.base.BasePreferences.ExtensionInstaller
|
||||||
|
import eu.kanade.tachiyomi.util.system.hasMiuiPackageInstaller
|
||||||
import eu.kanade.tachiyomi.util.system.isShizukuInstalled
|
import eu.kanade.tachiyomi.util.system.isShizukuInstalled
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import tachiyomi.core.preference.Preference
|
import tachiyomi.core.preference.Preference
|
||||||
|
@ -19,14 +19,14 @@ class ExtensionInstallerPreference(
|
||||||
override fun key() = "extension_installer"
|
override fun key() = "extension_installer"
|
||||||
|
|
||||||
|
|
||||||
|
val entries
|
||||||
val entries get() = BasePreferences.ExtensionInstaller.values().run {
|
get() = ExtensionInstaller.values().run {
|
||||||
if (context.hasMiuiPackageInstaller) {
|
if (context.hasMiuiPackageInstaller) {
|
||||||
filter { it != BasePreferences.ExtensionInstaller.PACKAGEINSTALLER }
|
filter { it != ExtensionInstaller.PACKAGEINSTALLER }
|
||||||
} else {
|
} else {
|
||||||
toList()
|
toList()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override fun defaultValue() = if (context.hasMiuiPackageInstaller) {
|
override fun defaultValue() = if (context.hasMiuiPackageInstaller) {
|
||||||
ExtensionInstaller.LEGACY
|
ExtensionInstaller.LEGACY
|
||||||
|
@ -39,9 +39,11 @@ class ExtensionInstallerPreference(
|
||||||
ExtensionInstaller.PACKAGEINSTALLER -> {
|
ExtensionInstaller.PACKAGEINSTALLER -> {
|
||||||
if (context.hasMiuiPackageInstaller) return ExtensionInstaller.LEGACY
|
if (context.hasMiuiPackageInstaller) return ExtensionInstaller.LEGACY
|
||||||
}
|
}
|
||||||
|
|
||||||
ExtensionInstaller.SHIZUKU -> {
|
ExtensionInstaller.SHIZUKU -> {
|
||||||
if (!context.isShizukuInstalled) return defaultValue()
|
if (!context.isShizukuInstalled) return defaultValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
NOTICE
|
NOTICE
|
||||||
|
|
||||||
This software includes code modified from Aniyomi, available at https://github.com/aniyomiorg/aniyomi/.
|
This software includes code modified from Aniyomi, available
|
||||||
|
at https://github.com/aniyomiorg/aniyomi/.
|
|
@ -38,9 +38,6 @@ open class Video(
|
||||||
@Transient
|
@Transient
|
||||||
@Volatile
|
@Volatile
|
||||||
var status: State = State.QUEUE
|
var status: State = State.QUEUE
|
||||||
set(value) {
|
|
||||||
field = value
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
private val _progressFlow = MutableStateFlow(0)
|
private val _progressFlow = MutableStateFlow(0)
|
||||||
|
|
|
@ -69,7 +69,11 @@ sealed class AndroidPreference<T>(
|
||||||
key: String,
|
key: String,
|
||||||
defaultValue: String,
|
defaultValue: String,
|
||||||
) : AndroidPreference<String>(preferences, keyFlow, key, defaultValue) {
|
) : AndroidPreference<String>(preferences, keyFlow, key, defaultValue) {
|
||||||
override fun read(preferences: SharedPreferences, key: String, defaultValue: String): String {
|
override fun read(
|
||||||
|
preferences: SharedPreferences,
|
||||||
|
key: String,
|
||||||
|
defaultValue: String
|
||||||
|
): String {
|
||||||
return try {
|
return try {
|
||||||
preferences.getString(key, defaultValue) ?: defaultValue
|
preferences.getString(key, defaultValue) ?: defaultValue
|
||||||
} catch (e: ClassCastException) {
|
} catch (e: ClassCastException) {
|
||||||
|
@ -145,7 +149,11 @@ sealed class AndroidPreference<T>(
|
||||||
key: String,
|
key: String,
|
||||||
defaultValue: Boolean,
|
defaultValue: Boolean,
|
||||||
) : AndroidPreference<Boolean>(preferences, keyFlow, key, defaultValue) {
|
) : AndroidPreference<Boolean>(preferences, keyFlow, key, defaultValue) {
|
||||||
override fun read(preferences: SharedPreferences, key: String, defaultValue: Boolean): Boolean {
|
override fun read(
|
||||||
|
preferences: SharedPreferences,
|
||||||
|
key: String,
|
||||||
|
defaultValue: Boolean
|
||||||
|
): Boolean {
|
||||||
return try {
|
return try {
|
||||||
preferences.getBoolean(key, defaultValue)
|
preferences.getBoolean(key, defaultValue)
|
||||||
} catch (e: ClassCastException) {
|
} catch (e: ClassCastException) {
|
||||||
|
@ -164,7 +172,11 @@ sealed class AndroidPreference<T>(
|
||||||
key: String,
|
key: String,
|
||||||
defaultValue: Set<String>,
|
defaultValue: Set<String>,
|
||||||
) : AndroidPreference<Set<String>>(preferences, keyFlow, key, defaultValue) {
|
) : AndroidPreference<Set<String>>(preferences, keyFlow, key, defaultValue) {
|
||||||
override fun read(preferences: SharedPreferences, key: String, defaultValue: Set<String>): Set<String> {
|
override fun read(
|
||||||
|
preferences: SharedPreferences,
|
||||||
|
key: String,
|
||||||
|
defaultValue: Set<String>
|
||||||
|
): Set<String> {
|
||||||
return try {
|
return try {
|
||||||
preferences.getStringSet(key, defaultValue) ?: defaultValue
|
preferences.getStringSet(key, defaultValue) ?: defaultValue
|
||||||
} catch (e: ClassCastException) {
|
} catch (e: ClassCastException) {
|
||||||
|
|
|
@ -68,7 +68,8 @@ class AndroidPreferenceStore(
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
private val SharedPreferences.keyFlow
|
private val SharedPreferences.keyFlow
|
||||||
get() = callbackFlow {
|
get() = callbackFlow {
|
||||||
val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key: String? -> trySend(key) }
|
val listener =
|
||||||
|
SharedPreferences.OnSharedPreferenceChangeListener { _, key: String? -> trySend(key) }
|
||||||
registerOnSharedPreferenceChangeListener(listener)
|
registerOnSharedPreferenceChangeListener(listener)
|
||||||
awaitClose {
|
awaitClose {
|
||||||
unregisterOnSharedPreferenceChangeListener(listener)
|
unregisterOnSharedPreferenceChangeListener(listener)
|
||||||
|
|
|
@ -1,37 +1,16 @@
|
||||||
package tachiyomi.core.util.system
|
package tachiyomi.core.util.system
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.res.Configuration
|
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.graphics.BitmapRegionDecoder
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.graphics.Matrix
|
import android.graphics.Matrix
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
import android.graphics.drawable.ColorDrawable
|
|
||||||
import android.graphics.drawable.Drawable
|
|
||||||
import android.graphics.drawable.GradientDrawable
|
|
||||||
import android.os.Build
|
|
||||||
import android.webkit.MimeTypeMap
|
|
||||||
import androidx.annotation.ColorInt
|
|
||||||
import androidx.core.graphics.alpha
|
|
||||||
import androidx.core.graphics.applyCanvas
|
import androidx.core.graphics.applyCanvas
|
||||||
import androidx.core.graphics.blue
|
|
||||||
import androidx.core.graphics.createBitmap
|
import androidx.core.graphics.createBitmap
|
||||||
import androidx.core.graphics.get
|
|
||||||
import androidx.core.graphics.green
|
|
||||||
import androidx.core.graphics.red
|
|
||||||
import com.hippo.unifile.UniFile
|
|
||||||
import logcat.LogPriority
|
|
||||||
import java.io.BufferedInputStream
|
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.net.URLConnection
|
|
||||||
import kotlin.math.abs
|
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
|
||||||
|
|
||||||
object ImageUtil {
|
object ImageUtil {
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import ani.dantotsu.MainActivity
|
import ani.dantotsu.MainActivity
|
||||||
import eu.kanade.tachiyomi.core.Constants
|
import eu.kanade.tachiyomi.core.Constants
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Global [BroadcastReceiver] that runs on UI thread
|
* Global [BroadcastReceiver] that runs on UI thread
|
||||||
* Pending Broadcasts should be made from here.
|
* Pending Broadcasts should be made from here.
|
||||||
|
@ -28,7 +29,12 @@ class NotificationReceiver {
|
||||||
action = Constants.SHORTCUT_EXTENSIONS
|
action = Constants.SHORTCUT_EXTENSIONS
|
||||||
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||||
}
|
}
|
||||||
return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getActivity(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ import eu.kanade.tachiyomi.extension.anime.util.AnimeExtensionInstallReceiver
|
||||||
import eu.kanade.tachiyomi.extension.anime.util.AnimeExtensionInstaller
|
import eu.kanade.tachiyomi.extension.anime.util.AnimeExtensionInstaller
|
||||||
import eu.kanade.tachiyomi.extension.anime.util.AnimeExtensionLoader
|
import eu.kanade.tachiyomi.extension.anime.util.AnimeExtensionLoader
|
||||||
import eu.kanade.tachiyomi.util.preference.plusAssign
|
import eu.kanade.tachiyomi.util.preference.plusAssign
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
@ -57,20 +56,24 @@ class AnimeExtensionManager(
|
||||||
|
|
||||||
private val iconMap = mutableMapOf<String, Drawable>()
|
private val iconMap = mutableMapOf<String, Drawable>()
|
||||||
|
|
||||||
private val _installedAnimeExtensionsFlow = MutableStateFlow(emptyList<AnimeExtension.Installed>())
|
private val _installedAnimeExtensionsFlow =
|
||||||
|
MutableStateFlow(emptyList<AnimeExtension.Installed>())
|
||||||
val installedExtensionsFlow = _installedAnimeExtensionsFlow.asStateFlow()
|
val installedExtensionsFlow = _installedAnimeExtensionsFlow.asStateFlow()
|
||||||
|
|
||||||
private var subLanguagesEnabledOnFirstRun = preferences.enabledLanguages().isSet()
|
private var subLanguagesEnabledOnFirstRun = preferences.enabledLanguages().isSet()
|
||||||
|
|
||||||
fun getAppIconForSource(sourceId: Long): Drawable? {
|
fun getAppIconForSource(sourceId: Long): Drawable? {
|
||||||
val pkgName = _installedAnimeExtensionsFlow.value.find { ext -> ext.sources.any { it.id == sourceId } }?.pkgName
|
val pkgName =
|
||||||
|
_installedAnimeExtensionsFlow.value.find { ext -> ext.sources.any { it.id == sourceId } }?.pkgName
|
||||||
if (pkgName != null) {
|
if (pkgName != null) {
|
||||||
return iconMap[pkgName] ?: iconMap.getOrPut(pkgName) { context.packageManager.getApplicationIcon(pkgName) }
|
return iconMap[pkgName]
|
||||||
|
?: iconMap.getOrPut(pkgName) { context.packageManager.getApplicationIcon(pkgName) }
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private val _availableAnimeExtensionsFlow = MutableStateFlow(emptyList<AnimeExtension.Available>())
|
private val _availableAnimeExtensionsFlow =
|
||||||
|
MutableStateFlow(emptyList<AnimeExtension.Available>())
|
||||||
val availableExtensionsFlow = _availableAnimeExtensionsFlow.asStateFlow()
|
val availableExtensionsFlow = _availableAnimeExtensionsFlow.asStateFlow()
|
||||||
|
|
||||||
private var availableAnimeExtensionsSourcesData: Map<Long, AnimeSourceData> = emptyMap()
|
private var availableAnimeExtensionsSourcesData: Map<Long, AnimeSourceData> = emptyMap()
|
||||||
|
@ -84,7 +87,8 @@ class AnimeExtensionManager(
|
||||||
|
|
||||||
fun getSourceData(id: Long) = availableAnimeExtensionsSourcesData[id]
|
fun getSourceData(id: Long) = availableAnimeExtensionsSourcesData[id]
|
||||||
|
|
||||||
private val _untrustedAnimeExtensionsFlow = MutableStateFlow(emptyList<AnimeExtension.Untrusted>())
|
private val _untrustedAnimeExtensionsFlow =
|
||||||
|
MutableStateFlow(emptyList<AnimeExtension.Untrusted>())
|
||||||
val untrustedExtensionsFlow = _untrustedAnimeExtensionsFlow.asStateFlow()
|
val untrustedExtensionsFlow = _untrustedAnimeExtensionsFlow.asStateFlow()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
@ -213,8 +217,9 @@ class AnimeExtensionManager(
|
||||||
* @param extension The anime extension to be updated.
|
* @param extension The anime extension to be updated.
|
||||||
*/
|
*/
|
||||||
fun updateExtension(extension: AnimeExtension.Installed): Observable<InstallStep> {
|
fun updateExtension(extension: AnimeExtension.Installed): Observable<InstallStep> {
|
||||||
val availableExt = _availableAnimeExtensionsFlow.value.find { it.pkgName == extension.pkgName }
|
val availableExt =
|
||||||
?: return Observable.empty()
|
_availableAnimeExtensionsFlow.value.find { it.pkgName == extension.pkgName }
|
||||||
|
?: return Observable.empty()
|
||||||
return installExtension(availableExt)
|
return installExtension(availableExt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,20 +256,27 @@ class AnimeExtensionManager(
|
||||||
* @param signature The signature to whitelist.
|
* @param signature The signature to whitelist.
|
||||||
*/
|
*/
|
||||||
fun trustSignature(signature: String) {
|
fun trustSignature(signature: String) {
|
||||||
val untrustedSignatures = _untrustedAnimeExtensionsFlow.value.map { it.signatureHash }.toSet()
|
val untrustedSignatures =
|
||||||
|
_untrustedAnimeExtensionsFlow.value.map { it.signatureHash }.toSet()
|
||||||
if (signature !in untrustedSignatures) return
|
if (signature !in untrustedSignatures) return
|
||||||
|
|
||||||
AnimeExtensionLoader.trustedSignatures += signature
|
AnimeExtensionLoader.trustedSignatures += signature
|
||||||
preferences.trustedSignatures() += signature
|
preferences.trustedSignatures() += signature
|
||||||
|
|
||||||
val nowTrustedAnimeExtensions = _untrustedAnimeExtensionsFlow.value.filter { it.signatureHash == signature }
|
val nowTrustedAnimeExtensions =
|
||||||
|
_untrustedAnimeExtensionsFlow.value.filter { it.signatureHash == signature }
|
||||||
_untrustedAnimeExtensionsFlow.value -= nowTrustedAnimeExtensions
|
_untrustedAnimeExtensionsFlow.value -= nowTrustedAnimeExtensions
|
||||||
|
|
||||||
val ctx = context
|
val ctx = context
|
||||||
launchNow {
|
launchNow {
|
||||||
nowTrustedAnimeExtensions
|
nowTrustedAnimeExtensions
|
||||||
.map { animeextension ->
|
.map { animeextension ->
|
||||||
async { AnimeExtensionLoader.loadExtensionFromPkgName(ctx, animeextension.pkgName) }
|
async {
|
||||||
|
AnimeExtensionLoader.loadExtensionFromPkgName(
|
||||||
|
ctx,
|
||||||
|
animeextension.pkgName
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.map { it.await() }
|
.map { it.await() }
|
||||||
.forEach { result ->
|
.forEach { result ->
|
||||||
|
@ -307,11 +319,13 @@ class AnimeExtensionManager(
|
||||||
* @param pkgName The package name of the uninstalled application.
|
* @param pkgName The package name of the uninstalled application.
|
||||||
*/
|
*/
|
||||||
private fun unregisterAnimeExtension(pkgName: String) {
|
private fun unregisterAnimeExtension(pkgName: String) {
|
||||||
val installedAnimeExtension = _installedAnimeExtensionsFlow.value.find { it.pkgName == pkgName }
|
val installedAnimeExtension =
|
||||||
|
_installedAnimeExtensionsFlow.value.find { it.pkgName == pkgName }
|
||||||
if (installedAnimeExtension != null) {
|
if (installedAnimeExtension != null) {
|
||||||
_installedAnimeExtensionsFlow.value -= installedAnimeExtension
|
_installedAnimeExtensionsFlow.value -= installedAnimeExtension
|
||||||
}
|
}
|
||||||
val untrustedAnimeExtension = _untrustedAnimeExtensionsFlow.value.find { it.pkgName == pkgName }
|
val untrustedAnimeExtension =
|
||||||
|
_untrustedAnimeExtensionsFlow.value.find { it.pkgName == pkgName }
|
||||||
if (untrustedAnimeExtension != null) {
|
if (untrustedAnimeExtension != null) {
|
||||||
_untrustedAnimeExtensionsFlow.value -= untrustedAnimeExtension
|
_untrustedAnimeExtensionsFlow.value -= untrustedAnimeExtension
|
||||||
}
|
}
|
||||||
|
@ -354,13 +368,15 @@ class AnimeExtensionManager(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun AnimeExtension.Installed.updateExists(availableAnimeExtension: AnimeExtension.Available? = null): Boolean {
|
private fun AnimeExtension.Installed.updateExists(availableAnimeExtension: AnimeExtension.Available? = null): Boolean {
|
||||||
val availableExt = availableAnimeExtension ?: _availableAnimeExtensionsFlow.value.find { it.pkgName == pkgName }
|
val availableExt = availableAnimeExtension
|
||||||
|
?: _availableAnimeExtensionsFlow.value.find { it.pkgName == pkgName }
|
||||||
if (isUnofficial || availableExt == null) return false
|
if (isUnofficial || availableExt == null) return false
|
||||||
|
|
||||||
return (availableExt.versionCode > versionCode || availableExt.libVersion > libVersion)
|
return (availableExt.versionCode > versionCode || availableExt.libVersion > libVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updatePendingUpdatesCount() {
|
private fun updatePendingUpdatesCount() {
|
||||||
preferences.animeExtensionUpdatesCount().set(_installedAnimeExtensionsFlow.value.count { it.hasUpdate })
|
preferences.animeExtensionUpdatesCount()
|
||||||
|
.set(_installedAnimeExtensionsFlow.value.count { it.hasUpdate })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,10 @@ internal class AnimeExtensionGithubApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun checkForUpdates(context: Context, fromAvailableExtensionList: Boolean = false): List<AnimeExtension.Installed>? {
|
suspend fun checkForUpdates(
|
||||||
|
context: Context,
|
||||||
|
fromAvailableExtensionList: Boolean = false
|
||||||
|
): List<AnimeExtension.Installed>? {
|
||||||
// Limit checks to once a day at most
|
// Limit checks to once a day at most
|
||||||
if (fromAvailableExtensionList && Date().time < lastExtCheck.get() + 1.days.inWholeMilliseconds) {
|
if (fromAvailableExtensionList && Date().time < lastExtCheck.get() + 1.days.inWholeMilliseconds) {
|
||||||
return null
|
return null
|
||||||
|
@ -161,8 +164,10 @@ 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 REPO_URL_PREFIX =
|
||||||
private const val FALLBACK_REPO_URL_PREFIX = "https://gcore.jsdelivr.net/gh/aniyomiorg/aniyomi-extensions@repo/"
|
"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(
|
||||||
|
|
|
@ -3,8 +3,8 @@ package eu.kanade.tachiyomi.extension.anime.util
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import ani.dantotsu.themes.ThemeManager
|
|
||||||
import ani.dantotsu.others.LangSet
|
import ani.dantotsu.others.LangSet
|
||||||
|
import ani.dantotsu.themes.ThemeManager
|
||||||
import eu.kanade.tachiyomi.extension.InstallStep
|
import eu.kanade.tachiyomi.extension.InstallStep
|
||||||
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
|
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
|
||||||
import eu.kanade.tachiyomi.util.system.hasMiuiPackageInstaller
|
import eu.kanade.tachiyomi.util.system.hasMiuiPackageInstaller
|
||||||
|
@ -27,7 +27,7 @@ class AnimeExtensionInstallActivity : Activity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
LangSet.setLocale(this)
|
LangSet.setLocale(this)
|
||||||
ThemeManager(this).applyTheme()
|
ThemeManager(this).applyTheme()
|
||||||
|
|
||||||
val installIntent = Intent(Intent.ACTION_INSTALL_PACKAGE)
|
val installIntent = Intent(Intent.ACTION_INSTALL_PACKAGE)
|
||||||
.setDataAndType(intent.data, intent.type)
|
.setDataAndType(intent.data, intent.type)
|
||||||
|
|
|
@ -62,6 +62,7 @@ internal class AnimeExtensionInstallReceiver(private val listener: Listener) :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Intent.ACTION_PACKAGE_REPLACED -> {
|
Intent.ACTION_PACKAGE_REPLACED -> {
|
||||||
launchNow {
|
launchNow {
|
||||||
when (val result = getExtensionFromIntent(context, intent)) {
|
when (val result = getExtensionFromIntent(context, intent)) {
|
||||||
|
@ -72,6 +73,7 @@ internal class AnimeExtensionInstallReceiver(private val listener: Listener) :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Intent.ACTION_PACKAGE_REMOVED -> {
|
Intent.ACTION_PACKAGE_REMOVED -> {
|
||||||
if (isReplacing(intent)) return
|
if (isReplacing(intent)) return
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,11 @@ internal class AnimeExtensionInstaller(private val context: Context) {
|
||||||
val request = DownloadManager.Request(downloadUri)
|
val request = DownloadManager.Request(downloadUri)
|
||||||
.setTitle(extension.name)
|
.setTitle(extension.name)
|
||||||
.setMimeType(APK_MIME)
|
.setMimeType(APK_MIME)
|
||||||
.setDestinationInExternalFilesDir(context, Environment.DIRECTORY_DOWNLOADS, downloadUri.lastPathSegment)
|
.setDestinationInExternalFilesDir(
|
||||||
|
context,
|
||||||
|
Environment.DIRECTORY_DOWNLOADS,
|
||||||
|
downloadUri.lastPathSegment
|
||||||
|
)
|
||||||
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
|
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
|
||||||
|
|
||||||
val id = downloadManager.enqueue(request)
|
val id = downloadManager.enqueue(request)
|
||||||
|
@ -144,6 +148,7 @@ internal class AnimeExtensionInstaller(private val context: Context) {
|
||||||
|
|
||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
val intent =
|
val intent =
|
||||||
AnimeExtensionInstallService.getIntent(context, downloadId, uri, installer)
|
AnimeExtensionInstallService.getIntent(context, downloadId, uri, installer)
|
||||||
|
|
|
@ -73,7 +73,10 @@ internal class MangaExtensionGithubApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun checkForUpdates(context: Context, fromAvailableExtensionList: Boolean = false): List<MangaExtension.Installed>? {
|
suspend fun checkForUpdates(
|
||||||
|
context: Context,
|
||||||
|
fromAvailableExtensionList: Boolean = false
|
||||||
|
): List<MangaExtension.Installed>? {
|
||||||
// Limit checks to once a day at most
|
// Limit checks to once a day at most
|
||||||
if (fromAvailableExtensionList && Date().time < lastExtCheck.get() + 1.days.inWholeMilliseconds) {
|
if (fromAvailableExtensionList && Date().time < lastExtCheck.get() + 1.days.inWholeMilliseconds) {
|
||||||
return null
|
return null
|
||||||
|
@ -161,7 +164,8 @@ internal class MangaExtensionGithubApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val REPO_URL_PREFIX = "https://raw.githubusercontent.com/keiyoushi/extensions/main/"
|
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/"
|
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(
|
||||||
|
|
|
@ -23,7 +23,10 @@ class PackageInstallerInstallerManga(private val service: Service) : InstallerMa
|
||||||
|
|
||||||
private val packageActionReceiver = object : BroadcastReceiver() {
|
private val packageActionReceiver = object : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
when (intent.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE)) {
|
when (intent.getIntExtra(
|
||||||
|
PackageInstaller.EXTRA_STATUS,
|
||||||
|
PackageInstaller.STATUS_FAILURE
|
||||||
|
)) {
|
||||||
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
|
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
|
||||||
val userAction = intent.getParcelableExtraCompat<Intent>(Intent.EXTRA_INTENT)
|
val userAction = intent.getParcelableExtraCompat<Intent>(Intent.EXTRA_INTENT)
|
||||||
if (userAction == null) {
|
if (userAction == null) {
|
||||||
|
@ -34,9 +37,11 @@ class PackageInstallerInstallerManga(private val service: Service) : InstallerMa
|
||||||
userAction.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
userAction.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
service.startActivity(userAction)
|
service.startActivity(userAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
PackageInstaller.STATUS_FAILURE_ABORTED -> {
|
PackageInstaller.STATUS_FAILURE_ABORTED -> {
|
||||||
continueQueue(InstallStep.Idle)
|
continueQueue(InstallStep.Idle)
|
||||||
}
|
}
|
||||||
|
|
||||||
PackageInstaller.STATUS_SUCCESS -> continueQueue(InstallStep.Installed)
|
PackageInstaller.STATUS_SUCCESS -> continueQueue(InstallStep.Installed)
|
||||||
else -> continueQueue(InstallStep.Error)
|
else -> continueQueue(InstallStep.Error)
|
||||||
}
|
}
|
||||||
|
@ -52,7 +57,8 @@ class PackageInstallerInstallerManga(private val service: Service) : InstallerMa
|
||||||
super.processEntry(entry)
|
super.processEntry(entry)
|
||||||
activeSession = null
|
activeSession = null
|
||||||
try {
|
try {
|
||||||
val installParams = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
|
val installParams =
|
||||||
|
PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
installParams.setRequireUserAction(PackageInstaller.SessionParams.USER_ACTION_NOT_REQUIRED)
|
installParams.setRequireUserAction(PackageInstaller.SessionParams.USER_ACTION_NOT_REQUIRED)
|
||||||
}
|
}
|
||||||
|
@ -60,7 +66,8 @@ class PackageInstallerInstallerManga(private val service: Service) : InstallerMa
|
||||||
val fileSize = service.getUriSize(entry.uri) ?: throw IllegalStateException()
|
val fileSize = service.getUriSize(entry.uri) ?: throw IllegalStateException()
|
||||||
installParams.setSize(fileSize)
|
installParams.setSize(fileSize)
|
||||||
|
|
||||||
val inputStream = service.contentResolver.openInputStream(entry.uri) ?: throw IllegalStateException()
|
val inputStream =
|
||||||
|
service.contentResolver.openInputStream(entry.uri) ?: throw IllegalStateException()
|
||||||
val session = packageInstaller.openSession(activeSession!!.second)
|
val session = packageInstaller.openSession(activeSession!!.second)
|
||||||
val outputStream = session.openWrite(entry.downloadId.toString(), 0, fileSize)
|
val outputStream = session.openWrite(entry.downloadId.toString(), 0, fileSize)
|
||||||
session.use {
|
session.use {
|
||||||
|
@ -108,7 +115,12 @@ class PackageInstallerInstallerManga(private val service: Service) : InstallerMa
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
ContextCompat.registerReceiver(service, packageActionReceiver, IntentFilter(INSTALL_ACTION), ContextCompat.RECEIVER_EXPORTED)
|
ContextCompat.registerReceiver(
|
||||||
|
service,
|
||||||
|
packageActionReceiver,
|
||||||
|
IntentFilter(INSTALL_ACTION),
|
||||||
|
ContextCompat.RECEIVER_EXPORTED
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,8 @@ package eu.kanade.tachiyomi.extension.manga.util
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import ani.dantotsu.themes.ThemeManager
|
|
||||||
import ani.dantotsu.others.LangSet
|
import ani.dantotsu.others.LangSet
|
||||||
|
import ani.dantotsu.themes.ThemeManager
|
||||||
import eu.kanade.tachiyomi.extension.InstallStep
|
import eu.kanade.tachiyomi.extension.InstallStep
|
||||||
import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
|
import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
|
||||||
import eu.kanade.tachiyomi.util.system.hasMiuiPackageInstaller
|
import eu.kanade.tachiyomi.util.system.hasMiuiPackageInstaller
|
||||||
|
@ -27,7 +27,7 @@ class MangaExtensionInstallActivity : Activity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
LangSet.setLocale(this)
|
LangSet.setLocale(this)
|
||||||
ThemeManager(this).applyTheme()
|
ThemeManager(this).applyTheme()
|
||||||
|
|
||||||
val installIntent = Intent(Intent.ACTION_INSTALL_PACKAGE)
|
val installIntent = Intent(Intent.ACTION_INSTALL_PACKAGE)
|
||||||
.setDataAndType(intent.data, intent.type)
|
.setDataAndType(intent.data, intent.type)
|
||||||
|
|
|
@ -62,6 +62,7 @@ internal class MangaExtensionInstallReceiver(private val listener: Listener) :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Intent.ACTION_PACKAGE_REPLACED -> {
|
Intent.ACTION_PACKAGE_REPLACED -> {
|
||||||
launchNow {
|
launchNow {
|
||||||
when (val result = getExtensionFromIntent(context, intent)) {
|
when (val result = getExtensionFromIntent(context, intent)) {
|
||||||
|
@ -72,6 +73,7 @@ internal class MangaExtensionInstallReceiver(private val listener: Listener) :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Intent.ACTION_PACKAGE_REMOVED -> {
|
Intent.ACTION_PACKAGE_REMOVED -> {
|
||||||
if (isReplacing(intent)) return
|
if (isReplacing(intent)) return
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,7 @@ package eu.kanade.tachiyomi.extension.manga.util
|
||||||
import android.app.Service
|
import android.app.Service
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.content.pm.ServiceInfo
|
import android.content.pm.ServiceInfo
|
||||||
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
|
@ -34,8 +32,12 @@ class MangaExtensionInstallService : Service() {
|
||||||
setProgress(100, 100, true)
|
setProgress(100, 100, true)
|
||||||
}.build()
|
}.build()
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
startForeground(Notifications.ID_EXTENSION_INSTALLER, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC)
|
startForeground(
|
||||||
}else{
|
Notifications.ID_EXTENSION_INSTALLER,
|
||||||
|
notification,
|
||||||
|
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
|
||||||
|
)
|
||||||
|
} else {
|
||||||
startForeground(Notifications.ID_EXTENSION_INSTALLER, notification)
|
startForeground(Notifications.ID_EXTENSION_INSTALLER, notification)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,7 +55,10 @@ class MangaExtensionInstallService : Service() {
|
||||||
|
|
||||||
if (installer == null) {
|
if (installer == null) {
|
||||||
installer = when (installerUsed) {
|
installer = when (installerUsed) {
|
||||||
BasePreferences.ExtensionInstaller.PACKAGEINSTALLER -> PackageInstallerInstallerManga(this)
|
BasePreferences.ExtensionInstaller.PACKAGEINSTALLER -> PackageInstallerInstallerManga(
|
||||||
|
this
|
||||||
|
)
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
logcat(LogPriority.ERROR) { "Not implemented for installer $installerUsed" }
|
logcat(LogPriority.ERROR) { "Not implemented for installer $installerUsed" }
|
||||||
stopSelf()
|
stopSelf()
|
||||||
|
|
|
@ -41,15 +41,18 @@ internal object MangaExtensionLoader {
|
||||||
const val LIB_VERSION_MIN = 1.2
|
const val LIB_VERSION_MIN = 1.2
|
||||||
const val LIB_VERSION_MAX = 1.5
|
const val LIB_VERSION_MAX = 1.5
|
||||||
|
|
||||||
private const val PACKAGE_FLAGS = PackageManager.GET_CONFIGURATIONS or PackageManager.GET_SIGNATURES
|
private const val PACKAGE_FLAGS =
|
||||||
|
PackageManager.GET_CONFIGURATIONS or PackageManager.GET_SIGNATURES
|
||||||
|
|
||||||
// inorichi's key
|
// inorichi's key
|
||||||
private const val officialSignature = "7ce04da7773d41b489f4693a366c36bcd0a11fc39b547168553c285bd7348e23"
|
private const val officialSignature =
|
||||||
|
"7ce04da7773d41b489f4693a366c36bcd0a11fc39b547168553c285bd7348e23"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of the trusted signatures.
|
* List of the trusted signatures.
|
||||||
*/
|
*/
|
||||||
var trustedSignatures = mutableSetOf<String>() + preferences.trustedSignatures().get() + officialSignature
|
var trustedSignatures =
|
||||||
|
mutableSetOf<String>() + preferences.trustedSignatures().get() + officialSignature
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a list of all the installed extensions initialized concurrently.
|
* Return a list of all the installed extensions initialized concurrently.
|
||||||
|
@ -105,7 +108,11 @@ internal object MangaExtensionLoader {
|
||||||
* @param pkgName The package name of the extension to load.
|
* @param pkgName The package name of the extension to load.
|
||||||
* @param pkgInfo The package info of the extension.
|
* @param pkgInfo The package info of the extension.
|
||||||
*/
|
*/
|
||||||
private fun loadMangaExtension(context: Context, pkgName: String, pkgInfo: PackageInfo): MangaLoadResult {
|
private fun loadMangaExtension(
|
||||||
|
context: Context,
|
||||||
|
pkgName: String,
|
||||||
|
pkgInfo: PackageInfo
|
||||||
|
): MangaLoadResult {
|
||||||
val pkgManager = context.packageManager
|
val pkgManager = context.packageManager
|
||||||
|
|
||||||
val appInfo = try {
|
val appInfo = try {
|
||||||
|
@ -116,7 +123,8 @@ internal object MangaExtensionLoader {
|
||||||
return MangaLoadResult.Error
|
return MangaLoadResult.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
val extName = pkgManager.getApplicationLabel(appInfo).toString().substringAfter("Tachiyomi: ")
|
val extName =
|
||||||
|
pkgManager.getApplicationLabel(appInfo).toString().substringAfter("Tachiyomi: ")
|
||||||
val versionName = pkgInfo.versionName
|
val versionName = pkgInfo.versionName
|
||||||
val versionCode = PackageInfoCompat.getLongVersionCode(pkgInfo)
|
val versionCode = PackageInfoCompat.getLongVersionCode(pkgInfo)
|
||||||
|
|
||||||
|
@ -130,7 +138,7 @@ internal object MangaExtensionLoader {
|
||||||
if (libVersion == null || libVersion < LIB_VERSION_MIN || libVersion > LIB_VERSION_MAX) {
|
if (libVersion == null || libVersion < LIB_VERSION_MIN || libVersion > LIB_VERSION_MAX) {
|
||||||
logcat(LogPriority.WARN) {
|
logcat(LogPriority.WARN) {
|
||||||
"Lib version is $libVersion, while only versions " +
|
"Lib version is $libVersion, while only versions " +
|
||||||
"$LIB_VERSION_MIN to $LIB_VERSION_MAX are allowed"
|
"$LIB_VERSION_MIN to $LIB_VERSION_MAX are allowed"
|
||||||
}
|
}
|
||||||
return MangaLoadResult.Error
|
return MangaLoadResult.Error
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.network
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import ani.dantotsu.Mapper
|
import ani.dantotsu.Mapper
|
||||||
import ani.dantotsu.defaultHeaders
|
|
||||||
import com.lagradost.nicehttp.Requests
|
import com.lagradost.nicehttp.Requests
|
||||||
import eu.kanade.tachiyomi.network.interceptor.CloudflareInterceptor
|
import eu.kanade.tachiyomi.network.interceptor.CloudflareInterceptor
|
||||||
import eu.kanade.tachiyomi.network.interceptor.UncaughtExceptionInterceptor
|
import eu.kanade.tachiyomi.network.interceptor.UncaughtExceptionInterceptor
|
||||||
|
@ -31,42 +30,40 @@ class NetworkHelper(
|
||||||
CloudflareInterceptor(context, cookieJar, ::defaultUserAgentProvider)
|
CloudflareInterceptor(context, cookieJar, ::defaultUserAgentProvider)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun baseClientBuilder(callTimout: Int = 2): OkHttpClient.Builder
|
private fun baseClientBuilder(callTimout: Int = 2): OkHttpClient.Builder {
|
||||||
{
|
val builder = OkHttpClient.Builder()
|
||||||
val builder = OkHttpClient.Builder()
|
.cookieJar(cookieJar)
|
||||||
.cookieJar(cookieJar)
|
.connectTimeout(30, TimeUnit.SECONDS)
|
||||||
.connectTimeout(30, TimeUnit.SECONDS)
|
.readTimeout(30, TimeUnit.SECONDS)
|
||||||
.readTimeout(30, TimeUnit.SECONDS)
|
.callTimeout(callTimout.toLong(), TimeUnit.MINUTES)
|
||||||
.callTimeout(callTimout.toLong(), TimeUnit.MINUTES)
|
.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()) {
|
|
||||||
PREF_DOH_CLOUDFLARE -> builder.dohCloudflare()
|
|
||||||
PREF_DOH_GOOGLE -> builder.dohGoogle()
|
|
||||||
PREF_DOH_ADGUARD -> builder.dohAdGuard()
|
|
||||||
PREF_DOH_QUAD9 -> builder.dohQuad9()
|
|
||||||
PREF_DOH_ALIDNS -> builder.dohAliDNS()
|
|
||||||
PREF_DOH_DNSPOD -> builder.dohDNSPod()
|
|
||||||
PREF_DOH_360 -> builder.doh360()
|
|
||||||
PREF_DOH_QUAD101 -> builder.dohQuad101()
|
|
||||||
PREF_DOH_MULLVAD -> builder.dohMullvad()
|
|
||||||
PREF_DOH_CONTROLD -> builder.dohControlD()
|
|
||||||
PREF_DOH_NJALLA -> builder.dohNajalla()
|
|
||||||
PREF_DOH_SHECAN -> builder.dohShecan()
|
|
||||||
PREF_DOH_LIBREDNS -> builder.dohLibreDNS()
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
when (preferences.dohProvider().get()) {
|
||||||
|
PREF_DOH_CLOUDFLARE -> builder.dohCloudflare()
|
||||||
|
PREF_DOH_GOOGLE -> builder.dohGoogle()
|
||||||
|
PREF_DOH_ADGUARD -> builder.dohAdGuard()
|
||||||
|
PREF_DOH_QUAD9 -> builder.dohQuad9()
|
||||||
|
PREF_DOH_ALIDNS -> builder.dohAliDNS()
|
||||||
|
PREF_DOH_DNSPOD -> builder.dohDNSPod()
|
||||||
|
PREF_DOH_360 -> builder.doh360()
|
||||||
|
PREF_DOH_QUAD101 -> builder.dohQuad101()
|
||||||
|
PREF_DOH_MULLVAD -> builder.dohMullvad()
|
||||||
|
PREF_DOH_CONTROLD -> builder.dohControlD()
|
||||||
|
PREF_DOH_NJALLA -> builder.dohNajalla()
|
||||||
|
PREF_DOH_SHECAN -> builder.dohShecan()
|
||||||
|
PREF_DOH_LIBREDNS -> builder.dohLibreDNS()
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
val client by lazy { baseClientBuilder().cache(Cache(cacheDir, cacheSize)).build() }
|
val client by lazy { baseClientBuilder().cache(Cache(cacheDir, cacheSize)).build() }
|
||||||
|
@ -80,15 +77,15 @@ class NetworkHelper(
|
||||||
}
|
}
|
||||||
|
|
||||||
val requestClient = Requests(
|
val requestClient = Requests(
|
||||||
client,
|
client,
|
||||||
mapOf(
|
mapOf(
|
||||||
"User-Agent" to
|
"User-Agent" to
|
||||||
defaultUserAgentProvider()
|
defaultUserAgentProvider()
|
||||||
.format(Build.VERSION.RELEASE, Build.MODEL)
|
.format(Build.VERSION.RELEASE, Build.MODEL)
|
||||||
),
|
),
|
||||||
defaultCacheTime = 6,
|
defaultCacheTime = 6,
|
||||||
defaultCacheTimeUnit = TimeUnit.HOURS,
|
defaultCacheTimeUnit = TimeUnit.HOURS,
|
||||||
responseParser = Mapper
|
responseParser = Mapper
|
||||||
)
|
)
|
||||||
|
|
||||||
fun defaultUserAgentProvider() = preferences.defaultUserAgent().get().trim()
|
fun defaultUserAgentProvider() = preferences.defaultUserAgent().get().trim()
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
package eu.kanade.tachiyomi.network
|
package eu.kanade.tachiyomi.network
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.network.ProgressListener
|
|
||||||
import eu.kanade.tachiyomi.network.ProgressResponseBody
|
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import kotlinx.serialization.DeserializationStrategy
|
import kotlinx.serialization.DeserializationStrategy
|
||||||
|
|
|
@ -9,7 +9,10 @@ import okio.Source
|
||||||
import okio.buffer
|
import okio.buffer
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class ProgressResponseBody(private val responseBody: ResponseBody, private val progressListener: ProgressListener) : ResponseBody() {
|
class ProgressResponseBody(
|
||||||
|
private val responseBody: ResponseBody,
|
||||||
|
private val progressListener: ProgressListener
|
||||||
|
) : ResponseBody() {
|
||||||
|
|
||||||
private val bufferedSource: BufferedSource by lazy {
|
private val bufferedSource: BufferedSource by lazy {
|
||||||
source(responseBody.source()).buffer()
|
source(responseBody.source()).buffer()
|
||||||
|
@ -36,7 +39,11 @@ class ProgressResponseBody(private val responseBody: ResponseBody, private val p
|
||||||
val bytesRead = super.read(sink, byteCount)
|
val bytesRead = super.read(sink, byteCount)
|
||||||
// read() returns the number of bytes read, or -1 if this source is exhausted.
|
// read() returns the number of bytes read, or -1 if this source is exhausted.
|
||||||
totalBytesRead += if (bytesRead != -1L) bytesRead else 0
|
totalBytesRead += if (bytesRead != -1L) bytesRead else 0
|
||||||
progressListener.update(totalBytesRead, responseBody.contentLength(), bytesRead == -1L)
|
progressListener.update(
|
||||||
|
totalBytesRead,
|
||||||
|
responseBody.contentLength(),
|
||||||
|
bytesRead == -1L
|
||||||
|
)
|
||||||
return bytesRead
|
return bytesRead
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,7 +95,17 @@ private fun isRequestHeaderSafe(_name: String, _value: String): Boolean {
|
||||||
val name = _name.lowercase(Locale.ENGLISH)
|
val name = _name.lowercase(Locale.ENGLISH)
|
||||||
val value = _value.lowercase(Locale.ENGLISH)
|
val value = _value.lowercase(Locale.ENGLISH)
|
||||||
if (name in unsafeHeaderNames || name.startsWith("proxy-")) return false
|
if (name in unsafeHeaderNames || name.startsWith("proxy-")) return false
|
||||||
if (name == "connection" && value == "upgrade") return false
|
return !(name == "connection" && value == "upgrade")
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
private val unsafeHeaderNames = listOf("content-length", "host", "trailer", "te", "upgrade", "cookie2", "keep-alive", "transfer-encoding", "set-cookie")
|
|
||||||
|
private val unsafeHeaderNames = listOf(
|
||||||
|
"content-length",
|
||||||
|
"host",
|
||||||
|
"trailer",
|
||||||
|
"te",
|
||||||
|
"upgrade",
|
||||||
|
"cookie2",
|
||||||
|
"keep-alive",
|
||||||
|
"transfer-encoding",
|
||||||
|
"set-cookie"
|
||||||
|
)
|
||||||
|
|
|
@ -33,7 +33,8 @@ interface MangaSource {
|
||||||
"Use the 1.x API instead",
|
"Use the 1.x API instead",
|
||||||
ReplaceWith("getMangaDetails"),
|
ReplaceWith("getMangaDetails"),
|
||||||
)
|
)
|
||||||
fun fetchMangaDetails(manga: SManga): Observable<SManga> = throw IllegalStateException("Not used")
|
fun fetchMangaDetails(manga: SManga): Observable<SManga> =
|
||||||
|
throw IllegalStateException("Not used")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an observable with all the available chapters for a manga.
|
* Returns an observable with all the available chapters for a manga.
|
||||||
|
@ -44,7 +45,8 @@ interface MangaSource {
|
||||||
"Use the 1.x API instead",
|
"Use the 1.x API instead",
|
||||||
ReplaceWith("getChapterList"),
|
ReplaceWith("getChapterList"),
|
||||||
)
|
)
|
||||||
fun fetchChapterList(manga: SManga): Observable<List<SChapter>> = throw IllegalStateException("Not used")
|
fun fetchChapterList(manga: SManga): Observable<List<SChapter>> =
|
||||||
|
throw IllegalStateException("Not used")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an observable with the list of pages a chapter has. Pages should be returned
|
* Returns an observable with the list of pages a chapter has. Pages should be returned
|
||||||
|
|
|
@ -31,7 +31,8 @@ class AndroidAnimeSourceManager(
|
||||||
|
|
||||||
private val stubSourcesMap = ConcurrentHashMap<Long, StubAnimeSource>()
|
private val stubSourcesMap = ConcurrentHashMap<Long, StubAnimeSource>()
|
||||||
|
|
||||||
override val catalogueSources: Flow<List<AnimeCatalogueSource>> = sourcesMapFlow.map { it.values.filterIsInstance<AnimeCatalogueSource>() }
|
override val catalogueSources: Flow<List<AnimeCatalogueSource>> =
|
||||||
|
sourcesMapFlow.map { it.values.filterIsInstance<AnimeCatalogueSource>() }
|
||||||
|
|
||||||
init {
|
init {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
|
@ -66,9 +67,11 @@ class AndroidAnimeSourceManager(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getOnlineSources() = sourcesMapFlow.value.values.filterIsInstance<AnimeHttpSource>()
|
override fun getOnlineSources() =
|
||||||
|
sourcesMapFlow.value.values.filterIsInstance<AnimeHttpSource>()
|
||||||
|
|
||||||
override fun getCatalogueSources() = sourcesMapFlow.value.values.filterIsInstance<AnimeCatalogueSource>()
|
override fun getCatalogueSources() =
|
||||||
|
sourcesMapFlow.value.values.filterIsInstance<AnimeCatalogueSource>()
|
||||||
|
|
||||||
override fun getStubSources(): List<StubAnimeSource> {
|
override fun getStubSources(): List<StubAnimeSource> {
|
||||||
val onlineSourceIds = getOnlineSources().map { it.id }
|
val onlineSourceIds = getOnlineSources().map { it.id }
|
||||||
|
|
|
@ -31,7 +31,8 @@ class AndroidMangaSourceManager(
|
||||||
|
|
||||||
private val stubSourcesMap = ConcurrentHashMap<Long, StubMangaSource>()
|
private val stubSourcesMap = ConcurrentHashMap<Long, StubMangaSource>()
|
||||||
|
|
||||||
override val catalogueSources: Flow<List<CatalogueSource>> = sourcesMapFlow.map { it.values.filterIsInstance<CatalogueSource>() }
|
override val catalogueSources: Flow<List<CatalogueSource>> =
|
||||||
|
sourcesMapFlow.map { it.values.filterIsInstance<CatalogueSource>() }
|
||||||
|
|
||||||
init {
|
init {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
|
@ -67,7 +68,8 @@ class AndroidMangaSourceManager(
|
||||||
|
|
||||||
override fun getOnlineSources() = sourcesMapFlow.value.values.filterIsInstance<HttpSource>()
|
override fun getOnlineSources() = sourcesMapFlow.value.values.filterIsInstance<HttpSource>()
|
||||||
|
|
||||||
override fun getCatalogueSources() = sourcesMapFlow.value.values.filterIsInstance<CatalogueSource>()
|
override fun getCatalogueSources() =
|
||||||
|
sourcesMapFlow.value.values.filterIsInstance<CatalogueSource>()
|
||||||
|
|
||||||
override fun getStubSources(): List<StubMangaSource> {
|
override fun getStubSources(): List<StubMangaSource> {
|
||||||
val onlineSourceIds = getOnlineSources().map { it.id }
|
val onlineSourceIds = getOnlineSources().map { it.id }
|
||||||
|
|
|
@ -3,7 +3,9 @@ package eu.kanade.tachiyomi.source.model
|
||||||
sealed class Filter<T>(val name: String, var state: T) {
|
sealed class Filter<T>(val name: String, var state: T) {
|
||||||
open class Header(name: String) : Filter<Any>(name, 0)
|
open class Header(name: String) : Filter<Any>(name, 0)
|
||||||
open class Separator(name: String = "") : Filter<Any>(name, 0)
|
open class Separator(name: String = "") : Filter<Any>(name, 0)
|
||||||
abstract class Select<V>(name: String, val values: Array<V>, state: Int = 0) : Filter<Int>(name, state)
|
abstract class Select<V>(name: String, val values: Array<V>, state: Int = 0) :
|
||||||
|
Filter<Int>(name, state)
|
||||||
|
|
||||||
abstract class Text(name: String, state: String = "") : Filter<String>(name, state)
|
abstract class Text(name: String, state: String = "") : Filter<String>(name, state)
|
||||||
abstract class CheckBox(name: String, state: Boolean = false) : Filter<Boolean>(name, state)
|
abstract class CheckBox(name: String, state: Boolean = false) : Filter<Boolean>(name, state)
|
||||||
abstract class TriState(name: String, state: Int = STATE_IGNORE) : Filter<Int>(name, state) {
|
abstract class TriState(name: String, state: Int = STATE_IGNORE) : Filter<Int>(name, state) {
|
||||||
|
|
|
@ -21,11 +21,10 @@ import androidx.core.graphics.blue
|
||||||
import androidx.core.graphics.green
|
import androidx.core.graphics.green
|
||||||
import androidx.core.graphics.red
|
import androidx.core.graphics.red
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
|
import com.hippo.unifile.UniFile
|
||||||
import eu.kanade.tachiyomi.util.lang.truncateCenter
|
import eu.kanade.tachiyomi.util.lang.truncateCenter
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import tachiyomi.core.util.system.logcat
|
import tachiyomi.core.util.system.logcat
|
||||||
import ani.dantotsu.toast
|
|
||||||
import com.hippo.unifile.UniFile
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
@ -59,7 +58,8 @@ fun Context.copyToClipboard(label: String, content: String) {
|
||||||
* @param permission the permission to check.
|
* @param permission the permission to check.
|
||||||
* @return true if it has permissions.
|
* @return true if it has permissions.
|
||||||
*/
|
*/
|
||||||
fun Context.hasPermission(permission: String) = PermissionChecker.checkSelfPermission(this, permission) == PermissionChecker.PERMISSION_GRANTED
|
fun Context.hasPermission(permission: String) =
|
||||||
|
PermissionChecker.checkSelfPermission(this, permission) == PermissionChecker.PERMISSION_GRANTED
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the color for the given attribute.
|
* Returns the color for the given attribute.
|
||||||
|
@ -67,7 +67,8 @@ fun Context.hasPermission(permission: String) = PermissionChecker.checkSelfPermi
|
||||||
* @param resource the attribute.
|
* @param resource the attribute.
|
||||||
* @param alphaFactor the alpha number [0,1].
|
* @param alphaFactor the alpha number [0,1].
|
||||||
*/
|
*/
|
||||||
@ColorInt fun Context.getResourceColor(@AttrRes resource: Int, alphaFactor: Float = 1f): Int {
|
@ColorInt
|
||||||
|
fun Context.getResourceColor(@AttrRes resource: Int, alphaFactor: Float = 1f): Int {
|
||||||
val typedArray = obtainStyledAttributes(intArrayOf(resource))
|
val typedArray = obtainStyledAttributes(intArrayOf(resource))
|
||||||
val color = typedArray.getColor(0, 0)
|
val color = typedArray.getColor(0, 0)
|
||||||
typedArray.recycle()
|
typedArray.recycle()
|
||||||
|
@ -80,7 +81,8 @@ fun Context.hasPermission(permission: String) = PermissionChecker.checkSelfPermi
|
||||||
return color
|
return color
|
||||||
}
|
}
|
||||||
|
|
||||||
@ColorInt fun Context.getThemeColor(attr: Int): Int {
|
@ColorInt
|
||||||
|
fun Context.getThemeColor(attr: Int): Int {
|
||||||
val tv = TypedValue()
|
val tv = TypedValue()
|
||||||
return if (this.theme.resolveAttribute(attr, tv, true)) {
|
return if (this.theme.resolveAttribute(attr, tv, true)) {
|
||||||
if (tv.resourceId != 0) {
|
if (tv.resourceId != 0) {
|
||||||
|
@ -137,7 +139,10 @@ fun Context.openInBrowser(uri: Uri, forceDefaultBrowser: Boolean = false) {
|
||||||
private fun Context.defaultBrowserPackageName(): String? {
|
private fun Context.defaultBrowserPackageName(): String? {
|
||||||
val browserIntent = Intent(Intent.ACTION_VIEW, "http://".toUri())
|
val browserIntent = Intent(Intent.ACTION_VIEW, "http://".toUri())
|
||||||
val resolveInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
val resolveInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
packageManager.resolveActivity(browserIntent, PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY.toLong()))
|
packageManager.resolveActivity(
|
||||||
|
browserIntent,
|
||||||
|
PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY.toLong())
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
packageManager.resolveActivity(browserIntent, PackageManager.MATCH_DEFAULT_ONLY)
|
packageManager.resolveActivity(browserIntent, PackageManager.MATCH_DEFAULT_ONLY)
|
||||||
|
@ -184,7 +189,6 @@ val Context.hasMiuiPackageInstaller get() = isPackageInstalled("com.miui.package
|
||||||
val Context.isShizukuInstalled get() = false
|
val Context.isShizukuInstalled get() = false
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fun Context.getApplicationIcon(pkgName: String): Drawable? {
|
fun Context.getApplicationIcon(pkgName: String): Drawable? {
|
||||||
return try {
|
return try {
|
||||||
packageManager.getApplicationIcon(pkgName)
|
packageManager.getApplicationIcon(pkgName)
|
||||||
|
|
|
@ -16,6 +16,7 @@ fun Uri.toShareIntent(context: Context, type: String = "image/*", message: Strin
|
||||||
"http", "https" -> {
|
"http", "https" -> {
|
||||||
putExtra(Intent.EXTRA_TEXT, uri.toString())
|
putExtra(Intent.EXTRA_TEXT, uri.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
"content" -> {
|
"content" -> {
|
||||||
message?.let { putExtra(Intent.EXTRA_TEXT, it) }
|
message?.let { putExtra(Intent.EXTRA_TEXT, it) }
|
||||||
putExtra(Intent.EXTRA_STREAM, uri)
|
putExtra(Intent.EXTRA_STREAM, uri)
|
||||||
|
|
|
@ -10,7 +10,11 @@ import androidx.annotation.StringRes
|
||||||
* @param resource the text resource.
|
* @param resource the text resource.
|
||||||
* @param duration the duration of the toast. Defaults to short.
|
* @param duration the duration of the toast. Defaults to short.
|
||||||
*/
|
*/
|
||||||
fun Context.toast(@StringRes resource: Int, duration: Int = Toast.LENGTH_SHORT, block: (Toast) -> Unit = {}): Toast {
|
fun Context.toast(
|
||||||
|
@StringRes resource: Int,
|
||||||
|
duration: Int = Toast.LENGTH_SHORT,
|
||||||
|
block: (Toast) -> Unit = {}
|
||||||
|
): Toast {
|
||||||
return toast(getString(resource), duration, block)
|
return toast(getString(resource), duration, block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +24,11 @@ fun Context.toast(@StringRes resource: Int, duration: Int = Toast.LENGTH_SHORT,
|
||||||
* @param text the text to display.
|
* @param text the text to display.
|
||||||
* @param duration the duration of the toast. Defaults to short.
|
* @param duration the duration of the toast. Defaults to short.
|
||||||
*/
|
*/
|
||||||
fun Context.toast(text: String?, duration: Int = Toast.LENGTH_SHORT, block: (Toast) -> Unit = {}): Toast {
|
fun Context.toast(
|
||||||
|
text: String?,
|
||||||
|
duration: Int = Toast.LENGTH_SHORT,
|
||||||
|
block: (Toast) -> Unit = {}
|
||||||
|
): Toast {
|
||||||
return Toast.makeText(applicationContext, text.orEmpty(), duration).also {
|
return Toast.makeText(applicationContext, text.orEmpty(), duration).also {
|
||||||
block(it)
|
block(it)
|
||||||
it.show()
|
it.show()
|
||||||
|
|
|
@ -30,7 +30,11 @@ object EpisodeRecognition {
|
||||||
*/
|
*/
|
||||||
private val unwantedWhiteSpace = Regex("""\s(?=extra|special|omake)""")
|
private val unwantedWhiteSpace = Regex("""\s(?=extra|special|omake)""")
|
||||||
|
|
||||||
fun parseEpisodeNumber(animeTitle: String, episodeName: String, episodeNumber: Float? = null): Float {
|
fun parseEpisodeNumber(
|
||||||
|
animeTitle: String,
|
||||||
|
episodeName: String,
|
||||||
|
episodeNumber: Float? = null
|
||||||
|
): Float {
|
||||||
// If episode number is known return.
|
// If episode number is known return.
|
||||||
if (episodeNumber != null && (episodeNumber == -2f || episodeNumber > -1f)) {
|
if (episodeNumber != null && (episodeNumber == -2f || episodeNumber > -1f)) {
|
||||||
return episodeNumber
|
return episodeNumber
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package tachiyomi.source.local.entries.anime
|
package tachiyomi.source.local.entries.anime
|
||||||
|
|
||||||
|
//import eu.kanade.tachiyomi.util.storage.toFFmpegString
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource
|
import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource
|
||||||
import eu.kanade.tachiyomi.animesource.AnimeSource
|
import eu.kanade.tachiyomi.animesource.AnimeSource
|
||||||
|
@ -8,15 +9,11 @@ import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
||||||
import eu.kanade.tachiyomi.animesource.model.AnimesPage
|
import eu.kanade.tachiyomi.animesource.model.AnimesPage
|
||||||
import eu.kanade.tachiyomi.animesource.model.SAnime
|
import eu.kanade.tachiyomi.animesource.model.SAnime
|
||||||
import eu.kanade.tachiyomi.animesource.model.SEpisode
|
import eu.kanade.tachiyomi.animesource.model.SEpisode
|
||||||
import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
|
|
||||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||||
//import eu.kanade.tachiyomi.util.storage.toFFmpegString
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import tachiyomi.core.util.lang.withIOContext
|
import tachiyomi.core.util.lang.withIOContext
|
||||||
import tachiyomi.domain.entries.anime.model.Anime
|
import tachiyomi.domain.entries.anime.model.Anime
|
||||||
import tachiyomi.source.local.filter.anime.AnimeOrderBy
|
import tachiyomi.source.local.filter.anime.AnimeOrderBy
|
||||||
import uy.kohesive.injekt.injectLazy
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
@ -27,7 +24,7 @@ class LocalAnimeSource(
|
||||||
private val POPULAR_FILTERS = AnimeFilterList(AnimeOrderBy.Popular(context))
|
private val POPULAR_FILTERS = AnimeFilterList(AnimeOrderBy.Popular(context))
|
||||||
private val LATEST_FILTERS = AnimeFilterList(AnimeOrderBy.Latest(context))
|
private val LATEST_FILTERS = AnimeFilterList(AnimeOrderBy.Latest(context))
|
||||||
|
|
||||||
override val name ="Local anime source"
|
override val name = "Local anime source"
|
||||||
|
|
||||||
override val id: Long = ID
|
override val id: Long = ID
|
||||||
|
|
||||||
|
@ -42,7 +39,11 @@ class LocalAnimeSource(
|
||||||
|
|
||||||
override fun fetchLatestUpdates(page: Int) = fetchSearchAnime(page, "", LATEST_FILTERS)
|
override fun fetchLatestUpdates(page: Int) = fetchSearchAnime(page, "", LATEST_FILTERS)
|
||||||
|
|
||||||
override fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable<AnimesPage> {
|
override fun fetchSearchAnime(
|
||||||
|
page: Int,
|
||||||
|
query: String,
|
||||||
|
filters: AnimeFilterList
|
||||||
|
): Observable<AnimesPage> {
|
||||||
//return emptyObservable()
|
//return emptyObservable()
|
||||||
return Observable.just(AnimesPage(emptyList(), false))
|
return Observable.just(AnimesPage(emptyList(), false))
|
||||||
}
|
}
|
||||||
|
@ -63,7 +64,8 @@ class LocalAnimeSource(
|
||||||
override fun getFilterList() = AnimeFilterList(AnimeOrderBy.Popular(context))
|
override fun getFilterList() = AnimeFilterList(AnimeOrderBy.Popular(context))
|
||||||
|
|
||||||
// Unused stuff
|
// Unused stuff
|
||||||
override suspend fun getVideoList(episode: SEpisode) = throw UnsupportedOperationException("Unused")
|
override suspend fun getVideoList(episode: SEpisode) =
|
||||||
|
throw UnsupportedOperationException("Unused")
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val ID = 0L
|
const val ID = 0L
|
||||||
|
|
|
@ -37,7 +37,11 @@ class LocalMangaSource(
|
||||||
|
|
||||||
override fun fetchLatestUpdates(page: Int) = fetchSearchManga(page, "", LATEST_FILTERS)
|
override fun fetchLatestUpdates(page: Int) = fetchSearchManga(page, "", LATEST_FILTERS)
|
||||||
|
|
||||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
override fun fetchSearchManga(
|
||||||
|
page: Int,
|
||||||
|
query: String,
|
||||||
|
filters: FilterList
|
||||||
|
): Observable<MangasPage> {
|
||||||
return Observable.just(MangasPage(emptyList(), false))
|
return Observable.just(MangasPage(emptyList(), false))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +59,8 @@ class LocalMangaSource(
|
||||||
override fun getFilterList() = FilterList(MangaOrderBy.Popular(context))
|
override fun getFilterList() = FilterList(MangaOrderBy.Popular(context))
|
||||||
|
|
||||||
// Unused stuff
|
// Unused stuff
|
||||||
override suspend fun getPageList(chapter: SChapter) = throw UnsupportedOperationException("Unused")
|
override suspend fun getPageList(chapter: SChapter) =
|
||||||
|
throw UnsupportedOperationException("Unused")
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val ID = 0L
|
const val ID = 0L
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue