???
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
|
||||
}
|
||||
@OptIn(UnstableApi::class) private fun grid(){
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
private fun grid() {
|
||||
gridView.setOnItemClickListener { parent, view, position, id ->
|
||||
// Get the OfflineAnimeModel that was clicked
|
||||
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.setPositiveButton("Yes") { _, _ ->
|
||||
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()
|
||||
if (mediaIds.isEmpty()) {
|
||||
snackString("No media found") // if this happens, terrible things have happened
|
||||
|
@ -231,6 +236,7 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
|||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSearchQuery(query: String) {
|
||||
adapter.onSearchQuery(query)
|
||||
}
|
||||
|
@ -254,7 +260,8 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
|||
}
|
||||
}
|
||||
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
|
||||
|
||||
fun animate() {
|
||||
|
@ -401,9 +408,13 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
|||
mediaModel.status == currActivity()!!.getString(R.string.status_releasing)
|
||||
val isUserScored = mediaModel.userScore != 0
|
||||
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 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(
|
||||
title,
|
||||
score,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package ani.dantotsu.download.anime
|
||||
|
||||
import android.net.Uri
|
||||
|
||||
data class OfflineAnimeModel(
|
||||
val title: String,
|
||||
val score: String,
|
||||
|
|
|
@ -171,10 +171,12 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
|||
gridView.scheduleLayoutAnimation()
|
||||
grid()
|
||||
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
|
||||
}
|
||||
|
||||
private fun grid() {
|
||||
gridView.setOnItemClickListener { parent, view, position, id ->
|
||||
// Get the OfflineMangaModel that was clicked
|
||||
|
@ -220,6 +222,7 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
|||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSearchQuery(query: String) {
|
||||
adapter.onSearchQuery(query)
|
||||
}
|
||||
|
@ -243,7 +246,8 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
|||
}
|
||||
}
|
||||
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
|
||||
|
||||
fun animate() {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package ani.dantotsu.home
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.drawable.Animatable
|
||||
import android.os.Build
|
||||
|
|
|
@ -140,6 +140,7 @@ class MangaPageAdapter : RecyclerView.Adapter<MangaPageAdapter.MangaPageViewHold
|
|||
fun updateHeight() {
|
||||
trendingViewPager!!.updateLayoutParams { height += statusBarHeight }
|
||||
}
|
||||
|
||||
fun updateTrending(adaptor: MediaAdaptor) {
|
||||
binding.mangaTrendingProgressBar.visibility = View.GONE
|
||||
binding.mangaTrendingViewPager.adapter = adaptor
|
||||
|
|
|
@ -1,24 +1,19 @@
|
|||
package ani.dantotsu.media
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.connections.anilist.Anilist
|
||||
import ani.dantotsu.currActivity
|
||||
import ani.dantotsu.currContext
|
||||
import ani.dantotsu.loadData
|
||||
import ani.dantotsu.logger
|
||||
import ani.dantotsu.media.anime.Episode
|
||||
import ani.dantotsu.media.anime.ExoplayerView
|
||||
import ani.dantotsu.media.anime.SelectorDialogFragment
|
||||
import ani.dantotsu.media.manga.MangaChapter
|
||||
import ani.dantotsu.others.AniSkip
|
||||
|
@ -260,7 +255,12 @@ class MediaDetailsViewModel : ViewModel() {
|
|||
}
|
||||
media.selected = this.loadSelected(media)
|
||||
val selector =
|
||||
SelectorDialogFragment.newInstance(media.selected!!.server, launch, prevEp, isDownload)
|
||||
SelectorDialogFragment.newInstance(
|
||||
media.selected!!.server,
|
||||
launch,
|
||||
prevEp,
|
||||
isDownload
|
||||
)
|
||||
selector.show(manager, "dialog")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -166,11 +166,17 @@ class AnimeWatchFragment : Fragment() {
|
|||
if (!loaded) {
|
||||
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!!)
|
||||
episodeAdapter =
|
||||
EpisodeAdapter(style ?: uiSettings.animeDefaultView, media, this, offlineMode = offlineMode)
|
||||
EpisodeAdapter(
|
||||
style ?: uiSettings.animeDefaultView,
|
||||
media,
|
||||
this,
|
||||
offlineMode = offlineMode
|
||||
)
|
||||
|
||||
binding.animeSourceRecycler.adapter =
|
||||
ConcatAdapter(headerAdapter, episodeAdapter)
|
||||
|
@ -421,7 +427,10 @@ class AnimeWatchFragment : Fragment() {
|
|||
fun onAnimeEpisodeStopDownloadClick(i: String) {
|
||||
val cancelIntent = Intent().apply {
|
||||
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)
|
||||
|
||||
|
|
|
@ -75,7 +75,6 @@ import ani.dantotsu.media.MediaDetailsViewModel
|
|||
import ani.dantotsu.media.SubtitleDownloader
|
||||
import ani.dantotsu.others.AniSkip
|
||||
import ani.dantotsu.others.AniSkip.getType
|
||||
import ani.dantotsu.others.Download.download
|
||||
import ani.dantotsu.others.LangSet
|
||||
import ani.dantotsu.others.ResettableTimer
|
||||
import ani.dantotsu.others.getSerialized
|
||||
|
|
|
@ -6,7 +6,6 @@ import android.content.Intent
|
|||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import android.view.HapticFeedbackConstants
|
||||
import android.view.LayoutInflater
|
||||
|
|
|
@ -7,9 +7,7 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.fragment.app.Fragment
|
||||
import ani.dantotsu.App
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.currContext
|
||||
import ani.dantotsu.databinding.FragmentOfflineBinding
|
||||
import ani.dantotsu.isOnline
|
||||
import ani.dantotsu.navBarHeight
|
||||
|
|
|
@ -169,7 +169,8 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
|
|||
} catch (e: Exception) {
|
||||
sourceLanguage = 0
|
||||
extension.sources[sourceLanguage]
|
||||
} as? AnimeHttpSource ?: (extension.sources[sourceLanguage] as? AnimeCatalogueSource ?: return emptyList())
|
||||
} as? AnimeHttpSource ?: (extension.sources[sourceLanguage] as? AnimeCatalogueSource
|
||||
?: return emptyList())
|
||||
return try {
|
||||
val res = source.fetchSearchAnime(1, query, source.getFilterList()).awaitSingle()
|
||||
logger("query: $query")
|
||||
|
|
|
@ -13,7 +13,8 @@ class NotificationClickReceiver : BroadcastReceiver() {
|
|||
context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit()
|
||||
.putBoolean("incognito", false)
|
||||
.apply()
|
||||
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
val notificationManager =
|
||||
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
notificationManager.cancel(INCOGNITO_CHANNEL_ID)
|
||||
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import android.app.Activity
|
|||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Build
|
||||
import android.view.Window
|
||||
import android.view.WindowManager
|
||||
import ani.dantotsu.R
|
||||
|
@ -72,7 +71,7 @@ class ThemeManager(private val context: Activity) {
|
|||
} else {
|
||||
winParams.flags = winParams.flags and bits.inv()
|
||||
}
|
||||
win.setAttributes(winParams)
|
||||
win.attributes = winParams
|
||||
}
|
||||
|
||||
private fun applyDynamicColors(
|
||||
|
|
|
@ -20,7 +20,10 @@ class BasePreferences(
|
|||
|
||||
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) {
|
||||
LEGACY("Legacy"),
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package eu.kanade.domain.base
|
||||
|
||||
import android.content.Context
|
||||
import eu.kanade.tachiyomi.util.system.hasMiuiPackageInstaller
|
||||
import eu.kanade.domain.base.BasePreferences.ExtensionInstaller
|
||||
import eu.kanade.tachiyomi.util.system.hasMiuiPackageInstaller
|
||||
import eu.kanade.tachiyomi.util.system.isShizukuInstalled
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import tachiyomi.core.preference.Preference
|
||||
|
@ -19,10 +19,10 @@ class ExtensionInstallerPreference(
|
|||
override fun key() = "extension_installer"
|
||||
|
||||
|
||||
|
||||
val entries get() = BasePreferences.ExtensionInstaller.values().run {
|
||||
val entries
|
||||
get() = ExtensionInstaller.values().run {
|
||||
if (context.hasMiuiPackageInstaller) {
|
||||
filter { it != BasePreferences.ExtensionInstaller.PACKAGEINSTALLER }
|
||||
filter { it != ExtensionInstaller.PACKAGEINSTALLER }
|
||||
} else {
|
||||
toList()
|
||||
}
|
||||
|
@ -39,9 +39,11 @@ class ExtensionInstallerPreference(
|
|||
ExtensionInstaller.PACKAGEINSTALLER -> {
|
||||
if (context.hasMiuiPackageInstaller) return ExtensionInstaller.LEGACY
|
||||
}
|
||||
|
||||
ExtensionInstaller.SHIZUKU -> {
|
||||
if (!context.isShizukuInstalled) return defaultValue()
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
return value
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
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
|
||||
@Volatile
|
||||
var status: State = State.QUEUE
|
||||
set(value) {
|
||||
field = value
|
||||
}
|
||||
|
||||
@Transient
|
||||
private val _progressFlow = MutableStateFlow(0)
|
||||
|
|
|
@ -69,7 +69,11 @@ sealed class AndroidPreference<T>(
|
|||
key: String,
|
||||
defaultValue: String,
|
||||
) : 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 {
|
||||
preferences.getString(key, defaultValue) ?: defaultValue
|
||||
} catch (e: ClassCastException) {
|
||||
|
@ -145,7 +149,11 @@ sealed class AndroidPreference<T>(
|
|||
key: String,
|
||||
defaultValue: Boolean,
|
||||
) : 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 {
|
||||
preferences.getBoolean(key, defaultValue)
|
||||
} catch (e: ClassCastException) {
|
||||
|
@ -164,7 +172,11 @@ sealed class AndroidPreference<T>(
|
|||
key: String,
|
||||
defaultValue: Set<String>,
|
||||
) : 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 {
|
||||
preferences.getStringSet(key, defaultValue) ?: defaultValue
|
||||
} catch (e: ClassCastException) {
|
||||
|
|
|
@ -68,7 +68,8 @@ class AndroidPreferenceStore(
|
|||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private val SharedPreferences.keyFlow
|
||||
get() = callbackFlow {
|
||||
val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key: String? -> trySend(key) }
|
||||
val listener =
|
||||
SharedPreferences.OnSharedPreferenceChangeListener { _, key: String? -> trySend(key) }
|
||||
registerOnSharedPreferenceChangeListener(listener)
|
||||
awaitClose {
|
||||
unregisterOnSharedPreferenceChangeListener(listener)
|
||||
|
|
|
@ -1,37 +1,16 @@
|
|||
package tachiyomi.core.util.system
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.BitmapRegionDecoder
|
||||
import android.graphics.Color
|
||||
import android.graphics.Matrix
|
||||
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.blue
|
||||
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.ByteArrayOutputStream
|
||||
import java.io.InputStream
|
||||
import java.net.URLConnection
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
object ImageUtil {
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import ani.dantotsu.MainActivity
|
||||
import eu.kanade.tachiyomi.core.Constants
|
||||
|
||||
/**
|
||||
* Global [BroadcastReceiver] that runs on UI thread
|
||||
* Pending Broadcasts should be made from here.
|
||||
|
@ -28,7 +29,12 @@ class NotificationReceiver {
|
|||
action = Constants.SHORTCUT_EXTENSIONS
|
||||
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.AnimeExtensionLoader
|
||||
import eu.kanade.tachiyomi.util.preference.plusAssign
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
@ -57,20 +56,24 @@ class AnimeExtensionManager(
|
|||
|
||||
private val iconMap = mutableMapOf<String, Drawable>()
|
||||
|
||||
private val _installedAnimeExtensionsFlow = MutableStateFlow(emptyList<AnimeExtension.Installed>())
|
||||
private val _installedAnimeExtensionsFlow =
|
||||
MutableStateFlow(emptyList<AnimeExtension.Installed>())
|
||||
val installedExtensionsFlow = _installedAnimeExtensionsFlow.asStateFlow()
|
||||
|
||||
private var subLanguagesEnabledOnFirstRun = preferences.enabledLanguages().isSet()
|
||||
|
||||
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) {
|
||||
return iconMap[pkgName] ?: iconMap.getOrPut(pkgName) { context.packageManager.getApplicationIcon(pkgName) }
|
||||
return iconMap[pkgName]
|
||||
?: iconMap.getOrPut(pkgName) { context.packageManager.getApplicationIcon(pkgName) }
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private val _availableAnimeExtensionsFlow = MutableStateFlow(emptyList<AnimeExtension.Available>())
|
||||
private val _availableAnimeExtensionsFlow =
|
||||
MutableStateFlow(emptyList<AnimeExtension.Available>())
|
||||
val availableExtensionsFlow = _availableAnimeExtensionsFlow.asStateFlow()
|
||||
|
||||
private var availableAnimeExtensionsSourcesData: Map<Long, AnimeSourceData> = emptyMap()
|
||||
|
@ -84,7 +87,8 @@ class AnimeExtensionManager(
|
|||
|
||||
fun getSourceData(id: Long) = availableAnimeExtensionsSourcesData[id]
|
||||
|
||||
private val _untrustedAnimeExtensionsFlow = MutableStateFlow(emptyList<AnimeExtension.Untrusted>())
|
||||
private val _untrustedAnimeExtensionsFlow =
|
||||
MutableStateFlow(emptyList<AnimeExtension.Untrusted>())
|
||||
val untrustedExtensionsFlow = _untrustedAnimeExtensionsFlow.asStateFlow()
|
||||
|
||||
init {
|
||||
|
@ -213,7 +217,8 @@ class AnimeExtensionManager(
|
|||
* @param extension The anime extension to be updated.
|
||||
*/
|
||||
fun updateExtension(extension: AnimeExtension.Installed): Observable<InstallStep> {
|
||||
val availableExt = _availableAnimeExtensionsFlow.value.find { it.pkgName == extension.pkgName }
|
||||
val availableExt =
|
||||
_availableAnimeExtensionsFlow.value.find { it.pkgName == extension.pkgName }
|
||||
?: return Observable.empty()
|
||||
return installExtension(availableExt)
|
||||
}
|
||||
|
@ -251,20 +256,27 @@ class AnimeExtensionManager(
|
|||
* @param signature The signature to whitelist.
|
||||
*/
|
||||
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
|
||||
|
||||
AnimeExtensionLoader.trustedSignatures += signature
|
||||
preferences.trustedSignatures() += signature
|
||||
|
||||
val nowTrustedAnimeExtensions = _untrustedAnimeExtensionsFlow.value.filter { it.signatureHash == signature }
|
||||
val nowTrustedAnimeExtensions =
|
||||
_untrustedAnimeExtensionsFlow.value.filter { it.signatureHash == signature }
|
||||
_untrustedAnimeExtensionsFlow.value -= nowTrustedAnimeExtensions
|
||||
|
||||
val ctx = context
|
||||
launchNow {
|
||||
nowTrustedAnimeExtensions
|
||||
.map { animeextension ->
|
||||
async { AnimeExtensionLoader.loadExtensionFromPkgName(ctx, animeextension.pkgName) }
|
||||
async {
|
||||
AnimeExtensionLoader.loadExtensionFromPkgName(
|
||||
ctx,
|
||||
animeextension.pkgName
|
||||
)
|
||||
}
|
||||
}
|
||||
.map { it.await() }
|
||||
.forEach { result ->
|
||||
|
@ -307,11 +319,13 @@ class AnimeExtensionManager(
|
|||
* @param pkgName The package name of the uninstalled application.
|
||||
*/
|
||||
private fun unregisterAnimeExtension(pkgName: String) {
|
||||
val installedAnimeExtension = _installedAnimeExtensionsFlow.value.find { it.pkgName == pkgName }
|
||||
val installedAnimeExtension =
|
||||
_installedAnimeExtensionsFlow.value.find { it.pkgName == pkgName }
|
||||
if (installedAnimeExtension != null) {
|
||||
_installedAnimeExtensionsFlow.value -= installedAnimeExtension
|
||||
}
|
||||
val untrustedAnimeExtension = _untrustedAnimeExtensionsFlow.value.find { it.pkgName == pkgName }
|
||||
val untrustedAnimeExtension =
|
||||
_untrustedAnimeExtensionsFlow.value.find { it.pkgName == pkgName }
|
||||
if (untrustedAnimeExtension != null) {
|
||||
_untrustedAnimeExtensionsFlow.value -= untrustedAnimeExtension
|
||||
}
|
||||
|
@ -354,13 +368,15 @@ class AnimeExtensionManager(
|
|||
}
|
||||
|
||||
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
|
||||
|
||||
return (availableExt.versionCode > versionCode || availableExt.libVersion > libVersion)
|
||||
}
|
||||
|
||||
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
|
||||
if (fromAvailableExtensionList && Date().time < lastExtCheck.get() + 1.days.inWholeMilliseconds) {
|
||||
return null
|
||||
|
@ -161,8 +164,10 @@ private fun AnimeExtensionJsonObject.extractLibVersion(): Double {
|
|||
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/"
|
||||
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
|
||||
private data class AnimeExtensionJsonObject(
|
||||
|
|
|
@ -3,8 +3,8 @@ package eu.kanade.tachiyomi.extension.anime.util
|
|||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import ani.dantotsu.themes.ThemeManager
|
||||
import ani.dantotsu.others.LangSet
|
||||
import ani.dantotsu.themes.ThemeManager
|
||||
import eu.kanade.tachiyomi.extension.InstallStep
|
||||
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
|
||||
import eu.kanade.tachiyomi.util.system.hasMiuiPackageInstaller
|
||||
|
|
|
@ -62,6 +62,7 @@ internal class AnimeExtensionInstallReceiver(private val listener: Listener) :
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Intent.ACTION_PACKAGE_REPLACED -> {
|
||||
launchNow {
|
||||
when (val result = getExtensionFromIntent(context, intent)) {
|
||||
|
@ -72,6 +73,7 @@ internal class AnimeExtensionInstallReceiver(private val listener: Listener) :
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Intent.ACTION_PACKAGE_REMOVED -> {
|
||||
if (isReplacing(intent)) return
|
||||
|
||||
|
|
|
@ -77,7 +77,11 @@ internal class AnimeExtensionInstaller(private val context: Context) {
|
|||
val request = DownloadManager.Request(downloadUri)
|
||||
.setTitle(extension.name)
|
||||
.setMimeType(APK_MIME)
|
||||
.setDestinationInExternalFilesDir(context, Environment.DIRECTORY_DOWNLOADS, downloadUri.lastPathSegment)
|
||||
.setDestinationInExternalFilesDir(
|
||||
context,
|
||||
Environment.DIRECTORY_DOWNLOADS,
|
||||
downloadUri.lastPathSegment
|
||||
)
|
||||
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
|
||||
|
||||
val id = downloadManager.enqueue(request)
|
||||
|
@ -144,6 +148,7 @@ internal class AnimeExtensionInstaller(private val context: Context) {
|
|||
|
||||
context.startActivity(intent)
|
||||
}
|
||||
|
||||
else -> {
|
||||
val intent =
|
||||
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
|
||||
if (fromAvailableExtensionList && Date().time < lastExtCheck.get() + 1.days.inWholeMilliseconds) {
|
||||
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 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
|
||||
private data class ExtensionJsonObject(
|
||||
|
|
|
@ -23,7 +23,10 @@ class PackageInstallerInstallerManga(private val service: Service) : InstallerMa
|
|||
|
||||
private val packageActionReceiver = object : BroadcastReceiver() {
|
||||
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 -> {
|
||||
val userAction = intent.getParcelableExtraCompat<Intent>(Intent.EXTRA_INTENT)
|
||||
if (userAction == null) {
|
||||
|
@ -34,9 +37,11 @@ class PackageInstallerInstallerManga(private val service: Service) : InstallerMa
|
|||
userAction.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
service.startActivity(userAction)
|
||||
}
|
||||
|
||||
PackageInstaller.STATUS_FAILURE_ABORTED -> {
|
||||
continueQueue(InstallStep.Idle)
|
||||
}
|
||||
|
||||
PackageInstaller.STATUS_SUCCESS -> continueQueue(InstallStep.Installed)
|
||||
else -> continueQueue(InstallStep.Error)
|
||||
}
|
||||
|
@ -52,7 +57,8 @@ class PackageInstallerInstallerManga(private val service: Service) : InstallerMa
|
|||
super.processEntry(entry)
|
||||
activeSession = null
|
||||
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) {
|
||||
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()
|
||||
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 outputStream = session.openWrite(entry.downloadId.toString(), 0, fileSize)
|
||||
session.use {
|
||||
|
@ -108,7 +115,12 @@ class PackageInstallerInstallerManga(private val service: Service) : InstallerMa
|
|||
}
|
||||
|
||||
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.content.Intent
|
||||
import android.os.Bundle
|
||||
import ani.dantotsu.themes.ThemeManager
|
||||
import ani.dantotsu.others.LangSet
|
||||
import ani.dantotsu.themes.ThemeManager
|
||||
import eu.kanade.tachiyomi.extension.InstallStep
|
||||
import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
|
||||
import eu.kanade.tachiyomi.util.system.hasMiuiPackageInstaller
|
||||
|
|
|
@ -62,6 +62,7 @@ internal class MangaExtensionInstallReceiver(private val listener: Listener) :
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Intent.ACTION_PACKAGE_REPLACED -> {
|
||||
launchNow {
|
||||
when (val result = getExtensionFromIntent(context, intent)) {
|
||||
|
@ -72,6 +73,7 @@ internal class MangaExtensionInstallReceiver(private val listener: Listener) :
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Intent.ACTION_PACKAGE_REMOVED -> {
|
||||
if (isReplacing(intent)) return
|
||||
|
||||
|
|
|
@ -3,9 +3,7 @@ package eu.kanade.tachiyomi.extension.manga.util
|
|||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.ServiceInfo
|
||||
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
|
@ -34,7 +32,11 @@ class MangaExtensionInstallService : Service() {
|
|||
setProgress(100, 100, true)
|
||||
}.build()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
startForeground(Notifications.ID_EXTENSION_INSTALLER, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC)
|
||||
startForeground(
|
||||
Notifications.ID_EXTENSION_INSTALLER,
|
||||
notification,
|
||||
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
|
||||
)
|
||||
} else {
|
||||
startForeground(Notifications.ID_EXTENSION_INSTALLER, notification)
|
||||
}
|
||||
|
@ -53,7 +55,10 @@ class MangaExtensionInstallService : Service() {
|
|||
|
||||
if (installer == null) {
|
||||
installer = when (installerUsed) {
|
||||
BasePreferences.ExtensionInstaller.PACKAGEINSTALLER -> PackageInstallerInstallerManga(this)
|
||||
BasePreferences.ExtensionInstaller.PACKAGEINSTALLER -> PackageInstallerInstallerManga(
|
||||
this
|
||||
)
|
||||
|
||||
else -> {
|
||||
logcat(LogPriority.ERROR) { "Not implemented for installer $installerUsed" }
|
||||
stopSelf()
|
||||
|
|
|
@ -41,15 +41,18 @@ internal object MangaExtensionLoader {
|
|||
const val LIB_VERSION_MIN = 1.2
|
||||
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
|
||||
private const val officialSignature = "7ce04da7773d41b489f4693a366c36bcd0a11fc39b547168553c285bd7348e23"
|
||||
private const val officialSignature =
|
||||
"7ce04da7773d41b489f4693a366c36bcd0a11fc39b547168553c285bd7348e23"
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
@ -105,7 +108,11 @@ internal object MangaExtensionLoader {
|
|||
* @param pkgName The package name of the extension to load.
|
||||
* @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 appInfo = try {
|
||||
|
@ -116,7 +123,8 @@ internal object MangaExtensionLoader {
|
|||
return MangaLoadResult.Error
|
||||
}
|
||||
|
||||
val extName = pkgManager.getApplicationLabel(appInfo).toString().substringAfter("Tachiyomi: ")
|
||||
val extName =
|
||||
pkgManager.getApplicationLabel(appInfo).toString().substringAfter("Tachiyomi: ")
|
||||
val versionName = pkgInfo.versionName
|
||||
val versionCode = PackageInfoCompat.getLongVersionCode(pkgInfo)
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.network
|
|||
import android.content.Context
|
||||
import android.os.Build
|
||||
import ani.dantotsu.Mapper
|
||||
import ani.dantotsu.defaultHeaders
|
||||
import com.lagradost.nicehttp.Requests
|
||||
import eu.kanade.tachiyomi.network.interceptor.CloudflareInterceptor
|
||||
import eu.kanade.tachiyomi.network.interceptor.UncaughtExceptionInterceptor
|
||||
|
@ -31,8 +30,7 @@ class NetworkHelper(
|
|||
CloudflareInterceptor(context, cookieJar, ::defaultUserAgentProvider)
|
||||
}
|
||||
|
||||
private fun baseClientBuilder(callTimout: Int = 2): OkHttpClient.Builder
|
||||
{
|
||||
private fun baseClientBuilder(callTimout: Int = 2): OkHttpClient.Builder {
|
||||
val builder = OkHttpClient.Builder()
|
||||
.cookieJar(cookieJar)
|
||||
.connectTimeout(30, TimeUnit.SECONDS)
|
||||
|
@ -68,7 +66,6 @@ class NetworkHelper(
|
|||
}
|
||||
|
||||
|
||||
|
||||
val client by lazy { baseClientBuilder().cache(Cache(cacheDir, cacheSize)).build() }
|
||||
val downloadClient by lazy { baseClientBuilder(20).build() }
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package eu.kanade.tachiyomi.network
|
||||
|
||||
import eu.kanade.tachiyomi.network.ProgressListener
|
||||
import eu.kanade.tachiyomi.network.ProgressResponseBody
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.serialization.DeserializationStrategy
|
||||
|
|
|
@ -9,7 +9,10 @@ import okio.Source
|
|||
import okio.buffer
|
||||
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 {
|
||||
source(responseBody.source()).buffer()
|
||||
|
@ -36,7 +39,11 @@ class ProgressResponseBody(private val responseBody: ResponseBody, private val p
|
|||
val bytesRead = super.read(sink, byteCount)
|
||||
// read() returns the number of bytes read, or -1 if this source is exhausted.
|
||||
totalBytesRead += if (bytesRead != -1L) bytesRead else 0
|
||||
progressListener.update(totalBytesRead, responseBody.contentLength(), bytesRead == -1L)
|
||||
progressListener.update(
|
||||
totalBytesRead,
|
||||
responseBody.contentLength(),
|
||||
bytesRead == -1L
|
||||
)
|
||||
return bytesRead
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,7 +95,17 @@ private fun isRequestHeaderSafe(_name: String, _value: String): Boolean {
|
|||
val name = _name.lowercase(Locale.ENGLISH)
|
||||
val value = _value.lowercase(Locale.ENGLISH)
|
||||
if (name in unsafeHeaderNames || name.startsWith("proxy-")) return false
|
||||
if (name == "connection" && value == "upgrade") return false
|
||||
return true
|
||||
return !(name == "connection" && value == "upgrade")
|
||||
}
|
||||
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",
|
||||
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.
|
||||
|
@ -44,7 +45,8 @@ interface MangaSource {
|
|||
"Use the 1.x API instead",
|
||||
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
|
||||
|
|
|
@ -31,7 +31,8 @@ class AndroidAnimeSourceManager(
|
|||
|
||||
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 {
|
||||
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> {
|
||||
val onlineSourceIds = getOnlineSources().map { it.id }
|
||||
|
|
|
@ -31,7 +31,8 @@ class AndroidMangaSourceManager(
|
|||
|
||||
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 {
|
||||
scope.launch {
|
||||
|
@ -67,7 +68,8 @@ class AndroidMangaSourceManager(
|
|||
|
||||
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> {
|
||||
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) {
|
||||
open class Header(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 CheckBox(name: String, state: Boolean = false) : Filter<Boolean>(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.red
|
||||
import androidx.core.net.toUri
|
||||
import com.hippo.unifile.UniFile
|
||||
import eu.kanade.tachiyomi.util.lang.truncateCenter
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.core.util.system.logcat
|
||||
import ani.dantotsu.toast
|
||||
import com.hippo.unifile.UniFile
|
||||
import java.io.File
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
@ -59,7 +58,8 @@ fun Context.copyToClipboard(label: String, content: String) {
|
|||
* @param permission the permission to check.
|
||||
* @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.
|
||||
|
@ -67,7 +67,8 @@ fun Context.hasPermission(permission: String) = PermissionChecker.checkSelfPermi
|
|||
* @param resource the attribute.
|
||||
* @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 color = typedArray.getColor(0, 0)
|
||||
typedArray.recycle()
|
||||
|
@ -80,7 +81,8 @@ fun Context.hasPermission(permission: String) = PermissionChecker.checkSelfPermi
|
|||
return color
|
||||
}
|
||||
|
||||
@ColorInt fun Context.getThemeColor(attr: Int): Int {
|
||||
@ColorInt
|
||||
fun Context.getThemeColor(attr: Int): Int {
|
||||
val tv = TypedValue()
|
||||
return if (this.theme.resolveAttribute(attr, tv, true)) {
|
||||
if (tv.resourceId != 0) {
|
||||
|
@ -137,7 +139,10 @@ fun Context.openInBrowser(uri: Uri, forceDefaultBrowser: Boolean = false) {
|
|||
private fun Context.defaultBrowserPackageName(): String? {
|
||||
val browserIntent = Intent(Intent.ACTION_VIEW, "http://".toUri())
|
||||
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 {
|
||||
@Suppress("DEPRECATION")
|
||||
packageManager.resolveActivity(browserIntent, PackageManager.MATCH_DEFAULT_ONLY)
|
||||
|
@ -184,7 +189,6 @@ val Context.hasMiuiPackageInstaller get() = isPackageInstalled("com.miui.package
|
|||
val Context.isShizukuInstalled get() = false
|
||||
|
||||
|
||||
|
||||
fun Context.getApplicationIcon(pkgName: String): Drawable? {
|
||||
return try {
|
||||
packageManager.getApplicationIcon(pkgName)
|
||||
|
|
|
@ -16,6 +16,7 @@ fun Uri.toShareIntent(context: Context, type: String = "image/*", message: Strin
|
|||
"http", "https" -> {
|
||||
putExtra(Intent.EXTRA_TEXT, uri.toString())
|
||||
}
|
||||
|
||||
"content" -> {
|
||||
message?.let { putExtra(Intent.EXTRA_TEXT, it) }
|
||||
putExtra(Intent.EXTRA_STREAM, uri)
|
||||
|
|
|
@ -10,7 +10,11 @@ import androidx.annotation.StringRes
|
|||
* @param resource the text resource.
|
||||
* @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)
|
||||
}
|
||||
|
||||
|
@ -20,7 +24,11 @@ fun Context.toast(@StringRes resource: Int, duration: Int = Toast.LENGTH_SHORT,
|
|||
* @param text the text to display.
|
||||
* @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 {
|
||||
block(it)
|
||||
it.show()
|
||||
|
|
|
@ -30,7 +30,11 @@ object EpisodeRecognition {
|
|||
*/
|
||||
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 (episodeNumber != null && (episodeNumber == -2f || episodeNumber > -1f)) {
|
||||
return episodeNumber
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package tachiyomi.source.local.entries.anime
|
||||
|
||||
//import eu.kanade.tachiyomi.util.storage.toFFmpegString
|
||||
import android.content.Context
|
||||
import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource
|
||||
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.SAnime
|
||||
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.toFFmpegString
|
||||
import kotlinx.serialization.json.Json
|
||||
import rx.Observable
|
||||
import tachiyomi.core.util.lang.withIOContext
|
||||
import tachiyomi.domain.entries.anime.model.Anime
|
||||
import tachiyomi.source.local.filter.anime.AnimeOrderBy
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.io.File
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
@ -42,7 +39,11 @@ class LocalAnimeSource(
|
|||
|
||||
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 Observable.just(AnimesPage(emptyList(), false))
|
||||
}
|
||||
|
@ -63,7 +64,8 @@ class LocalAnimeSource(
|
|||
override fun getFilterList() = AnimeFilterList(AnimeOrderBy.Popular(context))
|
||||
|
||||
// Unused stuff
|
||||
override suspend fun getVideoList(episode: SEpisode) = throw UnsupportedOperationException("Unused")
|
||||
override suspend fun getVideoList(episode: SEpisode) =
|
||||
throw UnsupportedOperationException("Unused")
|
||||
|
||||
companion object {
|
||||
const val ID = 0L
|
||||
|
|
|
@ -37,7 +37,11 @@ class LocalMangaSource(
|
|||
|
||||
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))
|
||||
}
|
||||
|
||||
|
@ -55,7 +59,8 @@ class LocalMangaSource(
|
|||
override fun getFilterList() = FilterList(MangaOrderBy.Popular(context))
|
||||
|
||||
// Unused stuff
|
||||
override suspend fun getPageList(chapter: SChapter) = throw UnsupportedOperationException("Unused")
|
||||
override suspend fun getPageList(chapter: SChapter) =
|
||||
throw UnsupportedOperationException("Unused")
|
||||
|
||||
companion object {
|
||||
const val ID = 0L
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue