mirror of
https://github.com/aniyomiorg/aniyomi.git
synced 2024-10-23 04:17:16 +03:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
a3d060ff42
266 changed files with 3448 additions and 3072 deletions
3
.github/workflows/build_push.yml
vendored
3
.github/workflows/build_push.yml
vendored
|
@ -13,9 +13,10 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Cancel previous runs
|
||||
uses: styfle/cancel-workflow-action@0.5.0
|
||||
uses: styfle/cancel-workflow-action@0.9.1
|
||||
with:
|
||||
access_token: ${{ github.token }}
|
||||
all_but_latest: true
|
||||
|
||||
- name: Clone repo
|
||||
uses: actions/checkout@v2
|
||||
|
|
3
.github/workflows/cancel_pull_request.yml
vendored
3
.github/workflows/cancel_pull_request.yml
vendored
|
@ -10,6 +10,7 @@ jobs:
|
|||
cancel:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: styfle/cancel-workflow-action@0.8.0
|
||||
- uses: styfle/cancel-workflow-action@0.9.1
|
||||
with:
|
||||
all_but_latest: true
|
||||
workflow_id: ${{ github.event.workflow.id }}
|
||||
|
|
8
.github/workflows/lock.yml
vendored
8
.github/workflows/lock.yml
vendored
|
@ -3,7 +3,7 @@ name: Lock threads
|
|||
on:
|
||||
# Daily
|
||||
schedule:
|
||||
- cron: '0 * * * *'
|
||||
- cron: '0 0 * * *'
|
||||
# Manual trigger
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
|
@ -12,8 +12,8 @@ jobs:
|
|||
lock:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v2
|
||||
- uses: dessant/lock-threads@v3
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
issue-lock-inactive-days: '2'
|
||||
pr-lock-inactive-days: '2'
|
||||
issue-inactive-days: '2'
|
||||
pr-inactive-days: '2'
|
||||
|
|
|
@ -29,7 +29,7 @@ android {
|
|||
applicationId = "xyz.jmir.tachiyomi.mi"
|
||||
minSdk = AndroidConfig.minSdk
|
||||
targetSdk = AndroidConfig.targetSdk
|
||||
versionCode = 71
|
||||
versionCode = 72
|
||||
versionName = "0.12.3.5"
|
||||
|
||||
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
||||
|
@ -140,7 +140,7 @@ android {
|
|||
dependencies {
|
||||
implementation(kotlin("reflect", version = BuildPluginsVersion.KOTLIN))
|
||||
|
||||
val coroutinesVersion = "1.5.2"
|
||||
val coroutinesVersion = "1.6.0"
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion")
|
||||
|
||||
|
@ -148,13 +148,13 @@ dependencies {
|
|||
implementation("org.tachiyomi:source-api:1.1")
|
||||
|
||||
// AndroidX libraries
|
||||
implementation("androidx.annotation:annotation:1.3.0")
|
||||
implementation("androidx.annotation:annotation:1.4.0-alpha01")
|
||||
implementation("androidx.appcompat:appcompat:1.4.0")
|
||||
implementation("androidx.biometric:biometric-ktx:1.2.0-alpha04")
|
||||
implementation("androidx.browser:browser:1.4.0")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.1.2")
|
||||
implementation("androidx.coordinatorlayout:coordinatorlayout:1.2.0-beta01")
|
||||
implementation("androidx.core:core-ktx:1.8.0-alpha01")
|
||||
implementation("androidx.coordinatorlayout:coordinatorlayout:1.2.0-rc01")
|
||||
implementation("androidx.core:core-ktx:1.8.0-alpha02")
|
||||
implementation("androidx.core:core-splashscreen:1.0.0-alpha02")
|
||||
implementation("androidx.recyclerview:recyclerview:1.3.0-alpha01")
|
||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01")
|
||||
|
@ -185,11 +185,13 @@ dependencies {
|
|||
implementation("org.conscrypt:conscrypt-android:2.5.2")
|
||||
|
||||
// Data serialization (JSON, protobuf)
|
||||
val kotlinSerializationVersion = "1.3.1"
|
||||
val kotlinSerializationVersion = "1.3.2"
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinSerializationVersion")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:$kotlinSerializationVersion")
|
||||
|
||||
// JavaScript engine
|
||||
implementation("app.cash.quickjs:quickjs-android:0.9.2")
|
||||
// TODO: remove Duktape once all extensions are using QuickJS
|
||||
implementation("com.squareup.duktape:duktape-android:1.4.0")
|
||||
|
||||
// HTML parser
|
||||
|
@ -201,13 +203,13 @@ dependencies {
|
|||
implementation("com.github.junrar:junrar:7.4.0")
|
||||
|
||||
// Database
|
||||
implementation("androidx.sqlite:sqlite-ktx:2.2.0-rc01")
|
||||
implementation("androidx.sqlite:sqlite-ktx:2.2.0")
|
||||
implementation("com.github.inorichi.storio:storio-common:8be19de@aar")
|
||||
implementation("com.github.inorichi.storio:storio-sqlite:8be19de@aar")
|
||||
implementation("com.github.requery:sqlite-android:3.36.0")
|
||||
|
||||
// Preferences
|
||||
implementation("androidx.preference:preference-ktx:1.2.0-beta01")
|
||||
implementation("androidx.preference:preference-ktx:1.2.0-rc01")
|
||||
implementation("com.github.tfcporciuncula.flow-preferences:flow-preferences:1.4.0")
|
||||
|
||||
// Model View Presenter
|
||||
|
@ -261,9 +263,9 @@ dependencies {
|
|||
implementation("com.squareup.logcat:logcat:0.1")
|
||||
|
||||
// Crash reports/analytics
|
||||
implementation("ch.acra:acra-http:5.8.1")
|
||||
"standardImplementation"("com.google.firebase:firebase-crashlytics-ktx:18.2.4")
|
||||
"standardImplementation"("com.google.firebase:firebase-analytics-ktx:20.0.0")
|
||||
implementation("ch.acra:acra-http:5.8.4")
|
||||
"standardImplementation"("com.google.firebase:firebase-crashlytics-ktx:18.2.6")
|
||||
"standardImplementation"("com.google.firebase:firebase-analytics-ktx:20.0.2")
|
||||
|
||||
// Licenses
|
||||
implementation("com.mikepenz:aboutlibraries-core:${BuildPluginsVersion.ABOUTLIB_PLUGIN}")
|
||||
|
|
2
app/proguard-rules.pro
vendored
2
app/proguard-rules.pro
vendored
|
@ -16,6 +16,8 @@
|
|||
-keep,allowoptimization class com.google.gson.** { public protected *; }
|
||||
-keep,allowoptimization class com.github.salomonbrys.kotson.** { public protected *; }
|
||||
-keep,allowoptimization class com.squareup.duktape.** { public protected *; }
|
||||
-keep,allowoptimization class app.cash.quickjs.** { public protected *; }
|
||||
-keep,allowoptimization class uy.kohesive.injekt.** { public protected *; }
|
||||
|
||||
##---------------Begin: proguard configuration for RxJava 1.x ----------
|
||||
-dontwarn sun.misc.**
|
||||
|
|
|
@ -1,93 +0,0 @@
|
|||
package com.google.android.material.appbar
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.view.View
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.marginTop
|
||||
import eu.kanade.tachiyomi.util.system.animatorDurationScale
|
||||
import eu.kanade.tachiyomi.util.view.findChild
|
||||
import kotlin.math.roundToLong
|
||||
|
||||
/**
|
||||
* Hide toolbar on scroll behavior for [AppBarLayout].
|
||||
*
|
||||
* Inside this package to access some package-private methods.
|
||||
*/
|
||||
class HideToolbarOnScrollBehavior : AppBarLayout.Behavior() {
|
||||
|
||||
@ViewCompat.NestedScrollType
|
||||
private var lastStartedType: Int = 0
|
||||
|
||||
private var offsetAnimator: ValueAnimator? = null
|
||||
|
||||
private var toolbarHeight: Int = 0
|
||||
|
||||
override fun onStartNestedScroll(
|
||||
parent: CoordinatorLayout,
|
||||
child: AppBarLayout,
|
||||
directTargetChild: View,
|
||||
target: View,
|
||||
nestedScrollAxes: Int,
|
||||
type: Int
|
||||
): Boolean {
|
||||
lastStartedType = type
|
||||
offsetAnimator?.cancel()
|
||||
return super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes, type)
|
||||
}
|
||||
|
||||
override fun onStopNestedScroll(
|
||||
parent: CoordinatorLayout,
|
||||
layout: AppBarLayout,
|
||||
target: View,
|
||||
type: Int
|
||||
) {
|
||||
super.onStopNestedScroll(parent, layout, target, type)
|
||||
if (toolbarHeight == 0) {
|
||||
toolbarHeight = layout.findChild<Toolbar>()?.height ?: 0
|
||||
}
|
||||
if (lastStartedType == ViewCompat.TYPE_TOUCH || type == ViewCompat.TYPE_NON_TOUCH) {
|
||||
animateToolbarVisibility(
|
||||
parent,
|
||||
layout,
|
||||
getTopBottomOffsetForScrollingSibling(layout) > -toolbarHeight / 2
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFlingFinished(parent: CoordinatorLayout, layout: AppBarLayout) {
|
||||
super.onFlingFinished(parent, layout)
|
||||
animateToolbarVisibility(
|
||||
parent,
|
||||
layout,
|
||||
getTopBottomOffsetForScrollingSibling(layout) > -toolbarHeight / 2
|
||||
)
|
||||
}
|
||||
|
||||
private fun getTopBottomOffsetForScrollingSibling(abl: AppBarLayout): Int {
|
||||
return topBottomOffsetForScrollingSibling - abl.marginTop
|
||||
}
|
||||
|
||||
private fun animateToolbarVisibility(
|
||||
coordinatorLayout: CoordinatorLayout,
|
||||
child: AppBarLayout,
|
||||
isVisible: Boolean
|
||||
) {
|
||||
val current = getTopBottomOffsetForScrollingSibling(child)
|
||||
val target = if (isVisible) 0 else -toolbarHeight
|
||||
if (current == target) return
|
||||
|
||||
offsetAnimator?.cancel()
|
||||
offsetAnimator = ValueAnimator().apply {
|
||||
interpolator = DecelerateInterpolator()
|
||||
duration = (150 * child.context.animatorDurationScale).roundToLong()
|
||||
addUpdateListener {
|
||||
setHeaderTopBottomOffset(coordinatorLayout, child, it.animatedValue as Int)
|
||||
}
|
||||
setIntValues(current, target)
|
||||
start()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,12 +7,10 @@ import android.content.Context
|
|||
import android.util.AttributeSet
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.FloatRange
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.lifecycle.coroutineScope
|
||||
import androidx.lifecycle.findViewTreeLifecycleOwner
|
||||
import com.google.android.material.animation.AnimationUtils
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import com.google.android.material.shape.getStateAlpha
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.util.view.findChild
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
|
@ -53,7 +51,7 @@ class TachiyomiAppBarLayout @JvmOverloads constructor(
|
|||
private val offsetListener = OnOffsetChangedListener { appBarLayout, verticalOffset ->
|
||||
// Show status bar foreground when offset
|
||||
val foreground = (appBarLayout?.statusBarForeground as? MaterialShapeDrawable) ?: return@OnOffsetChangedListener
|
||||
val start = foreground.getStateAlpha()
|
||||
val start = foreground.alpha
|
||||
val end = if (verticalOffset != 0) 255 else 0
|
||||
|
||||
statusBarForegroundAnimator?.cancel()
|
||||
|
@ -81,8 +79,6 @@ class TachiyomiAppBarLayout @JvmOverloads constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun getBehavior(): CoordinatorLayout.Behavior<AppBarLayout> = HideToolbarOnScrollBehavior()
|
||||
|
||||
/**
|
||||
* Disabled. Lift on scroll is handled manually with [eu.kanade.tachiyomi.widget.TachiyomiCoordinatorLayout]
|
||||
*/
|
||||
|
@ -154,7 +150,7 @@ class TachiyomiAppBarLayout @JvmOverloads constructor(
|
|||
}
|
||||
|
||||
val transparent = if (lifted) false else isTransparentWhenNotLifted
|
||||
val fromAlpha = (background as? MaterialShapeDrawable)?.getStateAlpha() ?: background.alpha
|
||||
val fromAlpha = (background as? MaterialShapeDrawable)?.alpha ?: background.alpha
|
||||
val toAlpha = if (transparent) 0 else 255
|
||||
if (fromAlpha != toAlpha) {
|
||||
ValueAnimator.ofInt(fromAlpha, toAlpha).apply {
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
package com.google.android.material.shape
|
||||
|
||||
/**
|
||||
* Use this instead of [MaterialShapeDrawable.getAlpha].
|
||||
*
|
||||
* https://github.com/material-components/material-components-android/issues/1796
|
||||
*/
|
||||
fun MaterialShapeDrawable.getStateAlpha(): Int {
|
||||
return (constantState as? MaterialShapeDrawable.MaterialShapeDrawableState)?.alpha ?: alpha
|
||||
}
|
|
@ -28,9 +28,9 @@ import eu.kanade.tachiyomi.data.coil.TachiyomiImageDecoder
|
|||
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||
import eu.kanade.tachiyomi.data.preference.PreferenceValues
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.asImmediateFlow
|
||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
|
||||
import eu.kanade.tachiyomi.util.preference.asImmediateFlow
|
||||
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil
|
||||
import eu.kanade.tachiyomi.util.system.animatorDurationScale
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
|
|
11
app/src/main/java/eu/kanade/tachiyomi/AppInfo.kt
Normal file
11
app/src/main/java/eu/kanade/tachiyomi/AppInfo.kt
Normal file
|
@ -0,0 +1,11 @@
|
|||
package eu.kanade.tachiyomi
|
||||
|
||||
/**
|
||||
* Used by extensions.
|
||||
*
|
||||
* @since extension-lib 1.3
|
||||
*/
|
||||
object AppInfo {
|
||||
fun getVersionCode() = BuildConfig.VERSION_CODE
|
||||
fun getVersionName() = BuildConfig.VERSION_NAME
|
||||
}
|
|
@ -6,9 +6,9 @@ import androidx.preference.PreferenceManager
|
|||
import eu.kanade.tachiyomi.data.animelib.AnimelibUpdateJob
|
||||
import eu.kanade.tachiyomi.data.backup.BackupCreatorJob
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
||||
import eu.kanade.tachiyomi.data.preference.MANGA_ONGOING
|
||||
import eu.kanade.tachiyomi.data.preference.PreferenceKeys
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.plusAssign
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import eu.kanade.tachiyomi.data.updater.AppUpdateJob
|
||||
import eu.kanade.tachiyomi.extension.AnimeExtensionUpdateJob
|
||||
|
@ -18,6 +18,8 @@ import eu.kanade.tachiyomi.ui.library.LibrarySort
|
|||
import eu.kanade.tachiyomi.ui.library.setting.SortDirectionSetting
|
||||
import eu.kanade.tachiyomi.ui.library.setting.SortModeSetting
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
|
||||
import eu.kanade.tachiyomi.util.preference.minusAssign
|
||||
import eu.kanade.tachiyomi.util.preference.plusAssign
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.widget.ExtendedNavigationView
|
||||
import uy.kohesive.injekt.Injekt
|
||||
|
@ -54,6 +56,8 @@ object Migrations {
|
|||
return false
|
||||
}
|
||||
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
||||
if (oldVersion < 14) {
|
||||
// Restore jobs after upgrading to Evernote's job scheduler.
|
||||
if (BuildConfig.INCLUDE_UPDATER) {
|
||||
|
@ -100,8 +104,6 @@ object Migrations {
|
|||
}
|
||||
if (oldVersion < 44) {
|
||||
// Reset sorting preference if using removed sort by source
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
||||
val oldSortingMode = prefs.getInt(PreferenceKeys.librarySortingMode, 0)
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
|
@ -113,7 +115,6 @@ object Migrations {
|
|||
}
|
||||
if (oldVersion < 52) {
|
||||
// Migrate library filters to tri-state versions
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
fun convertBooleanPrefToTriState(key: String): Int {
|
||||
val oldPrefValue = prefs.getBoolean(key, false)
|
||||
return if (oldPrefValue) ExtendedNavigationView.Item.TriStateGroup.State.INCLUDE.value
|
||||
|
@ -142,7 +143,6 @@ object Migrations {
|
|||
}
|
||||
if (oldVersion < 57) {
|
||||
// Migrate DNS over HTTPS setting
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val wasDohEnabled = prefs.getBoolean("enable_doh", false)
|
||||
if (wasDohEnabled) {
|
||||
prefs.edit {
|
||||
|
@ -153,7 +153,6 @@ object Migrations {
|
|||
}
|
||||
if (oldVersion < 59) {
|
||||
// Reset rotation to Free after replacing Lock
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
if (prefs.contains("pref_rotation_type_key")) {
|
||||
prefs.edit {
|
||||
putInt("pref_rotation_type_key", 1)
|
||||
|
@ -172,7 +171,6 @@ object Migrations {
|
|||
}
|
||||
|
||||
// Migrate Rotation and Viewer values to default values for viewer_flags
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val newOrientation = when (prefs.getInt("pref_rotation_type_key", 1)) {
|
||||
1 -> OrientationType.FREE.flagValue
|
||||
2 -> OrientationType.PORTRAIT.flagValue
|
||||
|
@ -216,8 +214,6 @@ object Migrations {
|
|||
AnimelibUpdateJob.setupTask(context)
|
||||
}
|
||||
if (oldVersion < 64) {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
||||
val oldSortingMode = prefs.getInt(PreferenceKeys.librarySortingMode, 0)
|
||||
val oldSortingDirection = prefs.getBoolean(PreferenceKeys.librarySortingDirection, true)
|
||||
|
||||
|
@ -263,6 +259,13 @@ object Migrations {
|
|||
AnimelibUpdateJob.setupTask(context, 12)
|
||||
}
|
||||
}
|
||||
if (oldVersion < 72) {
|
||||
val oldUpdateOngoingOnly = prefs.getBoolean("pref_update_only_non_completed_key", true)
|
||||
if (!oldUpdateOngoingOnly) {
|
||||
preferences.libraryUpdateMangaRestriction() -= MANGA_ONGOING
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,6 @@ import uy.kohesive.injekt.injectLazy
|
|||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.InputStream
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class LocalAnimeSource(private val context: Context) : AnimeCatalogueSource, UnmeteredSource {
|
||||
|
@ -104,9 +103,9 @@ class LocalAnimeSource(private val context: Context) : AnimeCatalogueSource, Unm
|
|||
when (state?.index) {
|
||||
0 -> {
|
||||
animeDirs = if (state.ascending) {
|
||||
animeDirs.sortedBy { it.name.lowercase(Locale.ENGLISH) }
|
||||
animeDirs.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, { it.name }))
|
||||
} else {
|
||||
animeDirs.sortedByDescending { it.name.lowercase(Locale.ENGLISH) }
|
||||
animeDirs.sortedWith(compareByDescending(String.CASE_INSENSITIVE_ORDER, { it.name }))
|
||||
}
|
||||
}
|
||||
1 -> {
|
||||
|
@ -144,7 +143,7 @@ class LocalAnimeSource(private val context: Context) : AnimeCatalogueSource, Unm
|
|||
.asSequence()
|
||||
.mapNotNull { File(it, anime.key).listFiles()?.toList() }
|
||||
.flatten()
|
||||
.firstOrNull { it.extension.lowercase() == "json" }
|
||||
.firstOrNull { it.extension.equals("json", ignoreCase = true) }
|
||||
|
||||
return if (localDetails != null) {
|
||||
val obj = json.decodeFromStream<JsonObject>(localDetails.inputStream())
|
||||
|
@ -203,7 +202,7 @@ class LocalAnimeSource(private val context: Context) : AnimeCatalogueSource, Unm
|
|||
}
|
||||
|
||||
private fun isSupportedFile(extension: String): Boolean {
|
||||
return extension.lowercase(Locale.ROOT) in SUPPORTED_FILE_TYPES
|
||||
return extension.lowercase() in SUPPORTED_FILE_TYPES
|
||||
}
|
||||
|
||||
fun getFormat(episode: SEpisode): Format {
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
package eu.kanade.tachiyomi.annotations
|
||||
|
||||
// TODO: remove this when no longer used in extensions
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
annotation class Nsfw
|
|
@ -8,8 +8,8 @@ import androidx.work.PeriodicWorkRequestBuilder
|
|||
import androidx.work.WorkManager
|
||||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
import eu.kanade.tachiyomi.data.preference.CHARGING
|
||||
import eu.kanade.tachiyomi.data.preference.ONLY_ON_WIFI
|
||||
import eu.kanade.tachiyomi.data.preference.DEVICE_CHARGING
|
||||
import eu.kanade.tachiyomi.data.preference.DEVICE_ONLY_ON_WIFI
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.util.system.isConnectedToWifi
|
||||
import uy.kohesive.injekt.Injekt
|
||||
|
@ -39,10 +39,10 @@ class AnimelibUpdateJob(private val context: Context, workerParams: WorkerParame
|
|||
val preferences = Injekt.get<PreferencesHelper>()
|
||||
val interval = prefInterval ?: preferences.libraryUpdateInterval().get()
|
||||
if (interval > 0) {
|
||||
val restrictions = preferences.libraryUpdateRestriction().get()
|
||||
val restrictions = preferences.libraryUpdateDeviceRestriction().get()
|
||||
val constraints = Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.setRequiresCharging(CHARGING in restrictions)
|
||||
.setRequiresCharging(DEVICE_CHARGING in restrictions)
|
||||
.build()
|
||||
|
||||
val request = PeriodicWorkRequestBuilder<AnimelibUpdateJob>(
|
||||
|
@ -62,8 +62,8 @@ class AnimelibUpdateJob(private val context: Context, workerParams: WorkerParame
|
|||
}
|
||||
|
||||
fun requiresWifiConnection(preferences: PreferencesHelper): Boolean {
|
||||
val restrictions = preferences.libraryUpdateRestriction().get()
|
||||
return ONLY_ON_WIFI in restrictions
|
||||
val restrictions = preferences.libraryUpdateDeviceRestriction().get()
|
||||
return DEVICE_ONLY_ON_WIFI in restrictions
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
package eu.kanade.tachiyomi.data.animelib
|
||||
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import java.util.Collections
|
||||
import kotlin.math.abs
|
||||
|
||||
/**
|
||||
* This class will provide various functions to rank manga to efficiently schedule manga to update.
|
||||
*/
|
||||
object AnimelibUpdateRanker {
|
||||
|
||||
val rankingScheme = listOf(
|
||||
(this::lexicographicRanking)(),
|
||||
(this::latestFirstRanking)(),
|
||||
(this::nextFirstRanking)()
|
||||
)
|
||||
|
||||
/**
|
||||
* Provides a total ordering over all the Mangas.
|
||||
*
|
||||
* Orders the manga based on the distance between the next expected update and now.
|
||||
* The comparator is reversed, placing the smallest (and thus closest to updating now) first.
|
||||
*/
|
||||
fun nextFirstRanking(): Comparator<Anime> {
|
||||
val time = System.currentTimeMillis()
|
||||
return Collections.reverseOrder(
|
||||
Comparator { animeFirst: Anime,
|
||||
animeSecond: Anime ->
|
||||
compareValues(abs(animeSecond.next_update - time), abs(animeFirst.next_update - time))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a total ordering over all the [Manga]s.
|
||||
*
|
||||
* Assumption: An active [Manga] mActive is expected to have been last updated after an
|
||||
* inactive [Manga] mInactive.
|
||||
*
|
||||
* Using this insight, function returns a Comparator for which mActive appears before mInactive.
|
||||
* @return a Comparator that ranks manga based on relevance.
|
||||
*/
|
||||
private fun latestFirstRanking(): Comparator<Anime> =
|
||||
Comparator { first: Anime, second: Anime ->
|
||||
compareValues(second.last_update, first.last_update)
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a total ordering over all the [Anime]s.
|
||||
*
|
||||
* Order the manga lexicographically.
|
||||
* @return a Comparator that ranks manga lexicographically based on the title.
|
||||
*/
|
||||
private fun lexicographicRanking(): Comparator<Anime> =
|
||||
Comparator { first: Anime, second: Anime ->
|
||||
compareValues(first.title, second.title)
|
||||
}
|
||||
}
|
|
@ -12,7 +12,6 @@ import eu.kanade.tachiyomi.animesource.AnimeSourceManager
|
|||
import eu.kanade.tachiyomi.animesource.model.SAnime
|
||||
import eu.kanade.tachiyomi.animesource.model.toSAnime
|
||||
import eu.kanade.tachiyomi.animesource.model.toSEpisode
|
||||
import eu.kanade.tachiyomi.data.animelib.AnimelibUpdateRanker.rankingScheme
|
||||
import eu.kanade.tachiyomi.data.animelib.AnimelibUpdateService.Companion.start
|
||||
import eu.kanade.tachiyomi.data.cache.AnimeCoverCache
|
||||
import eu.kanade.tachiyomi.data.database.AnimeDatabaseHelper
|
||||
|
@ -24,6 +23,8 @@ import eu.kanade.tachiyomi.data.database.models.toAnimeInfo
|
|||
import eu.kanade.tachiyomi.data.download.AnimeDownloadManager
|
||||
import eu.kanade.tachiyomi.data.download.AnimeDownloadService
|
||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||
import eu.kanade.tachiyomi.data.preference.MANGA_FULLY_READ
|
||||
import eu.kanade.tachiyomi.data.preference.MANGA_ONGOING
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.track.EnhancedTrackService
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
|
@ -258,14 +259,20 @@ class AnimelibUpdateService(
|
|||
|
||||
listToInclude.minus(listToExclude)
|
||||
}
|
||||
if (target == Target.CHAPTERS && preferences.updateOnlyNonCompleted()) {
|
||||
listToUpdate = listToUpdate.filterNot { it.status == SAnime.COMPLETED }
|
||||
|
||||
if (target == Target.CHAPTERS) {
|
||||
val restrictions = preferences.libraryUpdateMangaRestriction().get()
|
||||
if (MANGA_ONGOING in restrictions) {
|
||||
listToUpdate = listToUpdate.filterNot { it.status == SAnime.COMPLETED }
|
||||
}
|
||||
if (MANGA_FULLY_READ in restrictions) {
|
||||
listToUpdate = listToUpdate.filter { it.unseen == 0 }
|
||||
}
|
||||
}
|
||||
|
||||
val selectedScheme = preferences.libraryUpdatePrioritization().get()
|
||||
animeToUpdate = listToUpdate
|
||||
.distinctBy { it.id }
|
||||
.sortedWith(rankingScheme[selectedScheme])
|
||||
.sortedBy { it.title }
|
||||
|
||||
// Warn when excessively checking a single source
|
||||
val maxUpdatesFromSource = animeToUpdate
|
||||
|
@ -545,6 +552,7 @@ class AnimelibUpdateService(
|
|||
if (errors.isNotEmpty()) {
|
||||
val file = createFileInCacheDir("aniyomi_update_errors.txt")
|
||||
file.bufferedWriter().use { out ->
|
||||
out.write(getString(R.string.library_errors_help, ERROR_LOG_HELP_URL) + "\n\n")
|
||||
// Error file format:
|
||||
// ! Error
|
||||
// # Source
|
||||
|
@ -570,3 +578,4 @@ class AnimelibUpdateService(
|
|||
}
|
||||
|
||||
private const val ANIME_PER_SOURCE_QUEUE_WARNING_THRESHOLD = 60
|
||||
private const val ERROR_LOG_HELP_URL = "https://aniyomi.jmir.xyz/help/guides/troubleshooting"
|
||||
|
|
|
@ -20,7 +20,7 @@ class AnimeDbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) {
|
|||
/**
|
||||
* Version of the database.
|
||||
*/
|
||||
const val DATABASE_VERSION = 113
|
||||
const val DATABASE_VERSION = 114
|
||||
}
|
||||
|
||||
override fun onCreate(db: SupportSQLiteDatabase) = with(db) {
|
||||
|
@ -91,6 +91,9 @@ class AnimeDbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) {
|
|||
db.execSQL(AnimeTrackTable.insertFromTempTable)
|
||||
db.execSQL(AnimeTrackTable.dropTempTable)
|
||||
}
|
||||
if (oldVersion < 14) {
|
||||
db.execSQL(EpisodeTable.fixDateUploadIfNeeded)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConfigure(db: SupportSQLiteDatabase) {
|
||||
|
|
|
@ -20,7 +20,7 @@ class DbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) {
|
|||
/**
|
||||
* Version of the database.
|
||||
*/
|
||||
const val DATABASE_VERSION = 13
|
||||
const val DATABASE_VERSION = 14
|
||||
}
|
||||
|
||||
override fun onCreate(db: SupportSQLiteDatabase) = with(db) {
|
||||
|
@ -91,6 +91,9 @@ class DbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) {
|
|||
db.execSQL(TrackTable.insertFromTempTable)
|
||||
db.execSQL(TrackTable.dropTempTable)
|
||||
}
|
||||
if (oldVersion < 14) {
|
||||
db.execSQL(ChapterTable.fixDateUploadIfNeeded)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConfigure(db: SupportSQLiteDatabase) {
|
||||
|
|
|
@ -44,9 +44,9 @@ class AnimeCategoryPutResolver : DefaultPutResolver<AnimeCategory>() {
|
|||
class AnimeCategoryGetResolver : DefaultGetResolver<AnimeCategory>() {
|
||||
|
||||
override fun mapFromCursor(cursor: Cursor): AnimeCategory = AnimeCategory().apply {
|
||||
id = cursor.getLong(cursor.getColumnIndex(COL_ID))
|
||||
anime_id = cursor.getLong(cursor.getColumnIndex(COL_ANIME_ID))
|
||||
category_id = cursor.getInt(cursor.getColumnIndex(COL_CATEGORY_ID))
|
||||
id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_ID))
|
||||
anime_id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_ANIME_ID))
|
||||
category_id = cursor.getInt(cursor.getColumnIndexOrThrow(COL_CATEGORY_ID))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,10 +47,10 @@ open class AnimeHistoryPutResolver : DefaultPutResolver<AnimeHistory>() {
|
|||
class AnimeHistoryGetResolver : DefaultGetResolver<AnimeHistory>() {
|
||||
|
||||
override fun mapFromCursor(cursor: Cursor): AnimeHistory = AnimeHistoryImpl().apply {
|
||||
id = cursor.getLong(cursor.getColumnIndex(COL_ID))
|
||||
episode_id = cursor.getLong(cursor.getColumnIndex(COL_EPISODE_ID))
|
||||
last_seen = cursor.getLong(cursor.getColumnIndex(COL_LAST_SEEN))
|
||||
time_seen = cursor.getLong(cursor.getColumnIndex(COL_TIME_SEEN))
|
||||
id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_ID))
|
||||
episode_id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_EPISODE_ID))
|
||||
last_seen = cursor.getLong(cursor.getColumnIndexOrThrow(COL_LAST_SEEN))
|
||||
time_seen = cursor.getLong(cursor.getColumnIndexOrThrow(COL_TIME_SEEN))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -65,19 +65,19 @@ class AnimeTrackPutResolver : DefaultPutResolver<AnimeTrack>() {
|
|||
class AnimeTrackGetResolver : DefaultGetResolver<AnimeTrack>() {
|
||||
|
||||
override fun mapFromCursor(cursor: Cursor): AnimeTrack = AnimeTrackImpl().apply {
|
||||
id = cursor.getLong(cursor.getColumnIndex(COL_ID))
|
||||
anime_id = cursor.getLong(cursor.getColumnIndex(COL_ANIME_ID))
|
||||
sync_id = cursor.getInt(cursor.getColumnIndex(COL_SYNC_ID))
|
||||
media_id = cursor.getInt(cursor.getColumnIndex(COL_MEDIA_ID))
|
||||
library_id = cursor.getLong(cursor.getColumnIndex(COL_LIBRARY_ID))
|
||||
title = cursor.getString(cursor.getColumnIndex(COL_TITLE))
|
||||
last_episode_seen = cursor.getFloat(cursor.getColumnIndex(COL_LAST_EPISODE_SEEN))
|
||||
total_episodes = cursor.getInt(cursor.getColumnIndex(COL_TOTAL_EPISODES))
|
||||
status = cursor.getInt(cursor.getColumnIndex(COL_STATUS))
|
||||
score = cursor.getFloat(cursor.getColumnIndex(COL_SCORE))
|
||||
tracking_url = cursor.getString(cursor.getColumnIndex(COL_TRACKING_URL))
|
||||
started_watching_date = cursor.getLong(cursor.getColumnIndex(COL_START_DATE))
|
||||
finished_watching_date = cursor.getLong(cursor.getColumnIndex(COL_FINISH_DATE))
|
||||
id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_ID))
|
||||
anime_id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_ANIME_ID))
|
||||
sync_id = cursor.getInt(cursor.getColumnIndexOrThrow(COL_SYNC_ID))
|
||||
media_id = cursor.getInt(cursor.getColumnIndexOrThrow(COL_MEDIA_ID))
|
||||
library_id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_LIBRARY_ID))
|
||||
title = cursor.getString(cursor.getColumnIndexOrThrow(COL_TITLE))
|
||||
last_episode_seen = cursor.getFloat(cursor.getColumnIndexOrThrow(COL_LAST_EPISODE_SEEN))
|
||||
total_episodes = cursor.getInt(cursor.getColumnIndexOrThrow(COL_TOTAL_EPISODES))
|
||||
status = cursor.getInt(cursor.getColumnIndexOrThrow(COL_STATUS))
|
||||
score = cursor.getFloat(cursor.getColumnIndexOrThrow(COL_SCORE))
|
||||
tracking_url = cursor.getString(cursor.getColumnIndexOrThrow(COL_TRACKING_URL))
|
||||
started_watching_date = cursor.getLong(cursor.getColumnIndexOrThrow(COL_START_DATE))
|
||||
finished_watching_date = cursor.getLong(cursor.getColumnIndexOrThrow(COL_FINISH_DATE))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,6 @@ import eu.kanade.tachiyomi.data.database.tables.AnimeTable.COL_TITLE
|
|||
import eu.kanade.tachiyomi.data.database.tables.AnimeTable.COL_URL
|
||||
import eu.kanade.tachiyomi.data.database.tables.AnimeTable.COL_VIEWER
|
||||
import eu.kanade.tachiyomi.data.database.tables.AnimeTable.TABLE
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_NEXT_UPDATE
|
||||
|
||||
class AnimeTypeMapping : SQLiteTypeMapping<Anime>(
|
||||
AnimePutResolver(),
|
||||
|
@ -63,7 +62,6 @@ class AnimePutResolver : DefaultPutResolver<Anime>() {
|
|||
COL_THUMBNAIL_URL to obj.thumbnail_url,
|
||||
COL_FAVORITE to obj.favorite,
|
||||
COL_LAST_UPDATE to obj.last_update,
|
||||
COL_NEXT_UPDATE to obj.next_update,
|
||||
COL_INITIALIZED to obj.initialized,
|
||||
COL_VIEWER to obj.viewer_flags,
|
||||
COL_EPISODE_FLAGS to obj.episode_flags,
|
||||
|
@ -74,24 +72,23 @@ class AnimePutResolver : DefaultPutResolver<Anime>() {
|
|||
|
||||
interface BaseAnimeGetResolver {
|
||||
fun mapBaseFromCursor(anime: Anime, cursor: Cursor) = anime.apply {
|
||||
id = cursor.getLong(cursor.getColumnIndex(COL_ID))
|
||||
source = cursor.getLong(cursor.getColumnIndex(COL_SOURCE))
|
||||
url = cursor.getString(cursor.getColumnIndex(COL_URL))
|
||||
artist = cursor.getString(cursor.getColumnIndex(COL_ARTIST))
|
||||
author = cursor.getString(cursor.getColumnIndex(COL_AUTHOR))
|
||||
description = cursor.getString(cursor.getColumnIndex(COL_DESCRIPTION))
|
||||
genre = cursor.getString(cursor.getColumnIndex(COL_GENRE))
|
||||
title = cursor.getString(cursor.getColumnIndex(COL_TITLE))
|
||||
status = cursor.getInt(cursor.getColumnIndex(COL_STATUS))
|
||||
thumbnail_url = cursor.getString(cursor.getColumnIndex(COL_THUMBNAIL_URL))
|
||||
favorite = cursor.getInt(cursor.getColumnIndex(COL_FAVORITE)) == 1
|
||||
last_update = cursor.getLong(cursor.getColumnIndex(COL_LAST_UPDATE))
|
||||
next_update = cursor.getLong(cursor.getColumnIndex(COL_NEXT_UPDATE))
|
||||
initialized = cursor.getInt(cursor.getColumnIndex(COL_INITIALIZED)) == 1
|
||||
viewer_flags = cursor.getInt(cursor.getColumnIndex(COL_VIEWER))
|
||||
episode_flags = cursor.getInt(cursor.getColumnIndex(COL_EPISODE_FLAGS))
|
||||
cover_last_modified = cursor.getLong(cursor.getColumnIndex(COL_COVER_LAST_MODIFIED))
|
||||
date_added = cursor.getLong(cursor.getColumnIndex(COL_DATE_ADDED))
|
||||
id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_ID))
|
||||
source = cursor.getLong(cursor.getColumnIndexOrThrow(COL_SOURCE))
|
||||
url = cursor.getString(cursor.getColumnIndexOrThrow(COL_URL))
|
||||
artist = cursor.getString(cursor.getColumnIndexOrThrow(COL_ARTIST))
|
||||
author = cursor.getString(cursor.getColumnIndexOrThrow(COL_AUTHOR))
|
||||
description = cursor.getString(cursor.getColumnIndexOrThrow(COL_DESCRIPTION))
|
||||
genre = cursor.getString(cursor.getColumnIndexOrThrow(COL_GENRE))
|
||||
title = cursor.getString(cursor.getColumnIndexOrThrow(COL_TITLE))
|
||||
status = cursor.getInt(cursor.getColumnIndexOrThrow(COL_STATUS))
|
||||
thumbnail_url = cursor.getString(cursor.getColumnIndexOrThrow(COL_THUMBNAIL_URL))
|
||||
favorite = cursor.getInt(cursor.getColumnIndexOrThrow(COL_FAVORITE)) == 1
|
||||
last_update = cursor.getLong(cursor.getColumnIndexOrThrow(COL_LAST_UPDATE))
|
||||
initialized = cursor.getInt(cursor.getColumnIndexOrThrow(COL_INITIALIZED)) == 1
|
||||
viewer_flags = cursor.getInt(cursor.getColumnIndexOrThrow(COL_VIEWER))
|
||||
episode_flags = cursor.getInt(cursor.getColumnIndexOrThrow(COL_EPISODE_FLAGS))
|
||||
cover_last_modified = cursor.getLong(cursor.getColumnIndexOrThrow(COL_COVER_LAST_MODIFIED))
|
||||
date_added = cursor.getLong(cursor.getColumnIndexOrThrow(COL_DATE_ADDED))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,10 +47,10 @@ class CategoryPutResolver : DefaultPutResolver<Category>() {
|
|||
class CategoryGetResolver : DefaultGetResolver<Category>() {
|
||||
|
||||
override fun mapFromCursor(cursor: Cursor): Category = CategoryImpl().apply {
|
||||
id = cursor.getInt(cursor.getColumnIndex(COL_ID))
|
||||
name = cursor.getString(cursor.getColumnIndex(COL_NAME))
|
||||
order = cursor.getInt(cursor.getColumnIndex(COL_ORDER))
|
||||
flags = cursor.getInt(cursor.getColumnIndex(COL_FLAGS))
|
||||
id = cursor.getInt(cursor.getColumnIndexOrThrow(COL_ID))
|
||||
name = cursor.getString(cursor.getColumnIndexOrThrow(COL_NAME))
|
||||
order = cursor.getInt(cursor.getColumnIndexOrThrow(COL_ORDER))
|
||||
flags = cursor.getInt(cursor.getColumnIndexOrThrow(COL_FLAGS))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,18 +63,18 @@ class ChapterPutResolver : DefaultPutResolver<Chapter>() {
|
|||
class ChapterGetResolver : DefaultGetResolver<Chapter>() {
|
||||
|
||||
override fun mapFromCursor(cursor: Cursor): Chapter = ChapterImpl().apply {
|
||||
id = cursor.getLong(cursor.getColumnIndex(COL_ID))
|
||||
manga_id = cursor.getLong(cursor.getColumnIndex(COL_MANGA_ID))
|
||||
url = cursor.getString(cursor.getColumnIndex(COL_URL))
|
||||
name = cursor.getString(cursor.getColumnIndex(COL_NAME))
|
||||
scanlator = cursor.getString(cursor.getColumnIndex(COL_SCANLATOR))
|
||||
read = cursor.getInt(cursor.getColumnIndex(COL_READ)) == 1
|
||||
bookmark = cursor.getInt(cursor.getColumnIndex(COL_BOOKMARK)) == 1
|
||||
date_fetch = cursor.getLong(cursor.getColumnIndex(COL_DATE_FETCH))
|
||||
date_upload = cursor.getLong(cursor.getColumnIndex(COL_DATE_UPLOAD))
|
||||
last_page_read = cursor.getInt(cursor.getColumnIndex(COL_LAST_PAGE_READ))
|
||||
chapter_number = cursor.getFloat(cursor.getColumnIndex(COL_CHAPTER_NUMBER))
|
||||
source_order = cursor.getInt(cursor.getColumnIndex(COL_SOURCE_ORDER))
|
||||
id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_ID))
|
||||
manga_id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_MANGA_ID))
|
||||
url = cursor.getString(cursor.getColumnIndexOrThrow(COL_URL))
|
||||
name = cursor.getString(cursor.getColumnIndexOrThrow(COL_NAME))
|
||||
scanlator = cursor.getString(cursor.getColumnIndexOrThrow(COL_SCANLATOR))
|
||||
read = cursor.getInt(cursor.getColumnIndexOrThrow(COL_READ)) == 1
|
||||
bookmark = cursor.getInt(cursor.getColumnIndexOrThrow(COL_BOOKMARK)) == 1
|
||||
date_fetch = cursor.getLong(cursor.getColumnIndexOrThrow(COL_DATE_FETCH))
|
||||
date_upload = cursor.getLong(cursor.getColumnIndexOrThrow(COL_DATE_UPLOAD))
|
||||
last_page_read = cursor.getInt(cursor.getColumnIndexOrThrow(COL_LAST_PAGE_READ))
|
||||
chapter_number = cursor.getFloat(cursor.getColumnIndexOrThrow(COL_CHAPTER_NUMBER))
|
||||
source_order = cursor.getInt(cursor.getColumnIndexOrThrow(COL_SOURCE_ORDER))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -65,19 +65,19 @@ class EpisodePutResolver : DefaultPutResolver<Episode>() {
|
|||
class EpisodeGetResolver : DefaultGetResolver<Episode>() {
|
||||
|
||||
override fun mapFromCursor(cursor: Cursor): Episode = EpisodeImpl().apply {
|
||||
id = cursor.getLong(cursor.getColumnIndex(COL_ID))
|
||||
anime_id = cursor.getLong(cursor.getColumnIndex(COL_ANIME_ID))
|
||||
url = cursor.getString(cursor.getColumnIndex(COL_URL))
|
||||
name = cursor.getString(cursor.getColumnIndex(COL_NAME))
|
||||
scanlator = cursor.getString(cursor.getColumnIndex(COL_SCANLATOR))
|
||||
seen = cursor.getInt(cursor.getColumnIndex(COL_SEEN)) == 1
|
||||
bookmark = cursor.getInt(cursor.getColumnIndex(COL_BOOKMARK)) == 1
|
||||
date_fetch = cursor.getLong(cursor.getColumnIndex(COL_DATE_FETCH))
|
||||
date_upload = cursor.getLong(cursor.getColumnIndex(COL_DATE_UPLOAD))
|
||||
last_second_seen = cursor.getLong(cursor.getColumnIndex(COL_LAST_SECOND_SEEN))
|
||||
total_seconds = cursor.getLong(cursor.getColumnIndex(COL_TOTAL_SECONDS))
|
||||
episode_number = cursor.getFloat(cursor.getColumnIndex(COL_EPISODE_NUMBER))
|
||||
source_order = cursor.getInt(cursor.getColumnIndex(COL_SOURCE_ORDER))
|
||||
id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_ID))
|
||||
anime_id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_ANIME_ID))
|
||||
url = cursor.getString(cursor.getColumnIndexOrThrow(COL_URL))
|
||||
name = cursor.getString(cursor.getColumnIndexOrThrow(COL_NAME))
|
||||
scanlator = cursor.getString(cursor.getColumnIndexOrThrow(COL_SCANLATOR))
|
||||
seen = cursor.getInt(cursor.getColumnIndexOrThrow(COL_SEEN)) == 1
|
||||
bookmark = cursor.getInt(cursor.getColumnIndexOrThrow(COL_BOOKMARK)) == 1
|
||||
date_fetch = cursor.getLong(cursor.getColumnIndexOrThrow(COL_DATE_FETCH))
|
||||
date_upload = cursor.getLong(cursor.getColumnIndexOrThrow(COL_DATE_UPLOAD))
|
||||
last_second_seen = cursor.getLong(cursor.getColumnIndexOrThrow(COL_LAST_SECOND_SEEN))
|
||||
total_seconds = cursor.getLong(cursor.getColumnIndexOrThrow(COL_TOTAL_SECONDS))
|
||||
episode_number = cursor.getFloat(cursor.getColumnIndexOrThrow(COL_EPISODE_NUMBER))
|
||||
source_order = cursor.getInt(cursor.getColumnIndexOrThrow(COL_SOURCE_ORDER))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,10 +47,10 @@ open class HistoryPutResolver : DefaultPutResolver<History>() {
|
|||
class HistoryGetResolver : DefaultGetResolver<History>() {
|
||||
|
||||
override fun mapFromCursor(cursor: Cursor): History = HistoryImpl().apply {
|
||||
id = cursor.getLong(cursor.getColumnIndex(COL_ID))
|
||||
chapter_id = cursor.getLong(cursor.getColumnIndex(COL_CHAPTER_ID))
|
||||
last_read = cursor.getLong(cursor.getColumnIndex(COL_LAST_READ))
|
||||
time_read = cursor.getLong(cursor.getColumnIndex(COL_TIME_READ))
|
||||
id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_ID))
|
||||
chapter_id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_CHAPTER_ID))
|
||||
last_read = cursor.getLong(cursor.getColumnIndexOrThrow(COL_LAST_READ))
|
||||
time_read = cursor.getLong(cursor.getColumnIndexOrThrow(COL_TIME_READ))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,9 +44,9 @@ class MangaCategoryPutResolver : DefaultPutResolver<MangaCategory>() {
|
|||
class MangaCategoryGetResolver : DefaultGetResolver<MangaCategory>() {
|
||||
|
||||
override fun mapFromCursor(cursor: Cursor): MangaCategory = MangaCategory().apply {
|
||||
id = cursor.getLong(cursor.getColumnIndex(COL_ID))
|
||||
manga_id = cursor.getLong(cursor.getColumnIndex(COL_MANGA_ID))
|
||||
category_id = cursor.getInt(cursor.getColumnIndex(COL_CATEGORY_ID))
|
||||
id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_ID))
|
||||
manga_id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_MANGA_ID))
|
||||
category_id = cursor.getInt(cursor.getColumnIndexOrThrow(COL_CATEGORY_ID))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@ import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_GENRE
|
|||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_ID
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_INITIALIZED
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_LAST_UPDATE
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_NEXT_UPDATE
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_SOURCE
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_STATUS
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_THUMBNAIL_URL
|
||||
|
@ -63,7 +62,6 @@ class MangaPutResolver : DefaultPutResolver<Manga>() {
|
|||
COL_THUMBNAIL_URL to obj.thumbnail_url,
|
||||
COL_FAVORITE to obj.favorite,
|
||||
COL_LAST_UPDATE to obj.last_update,
|
||||
COL_NEXT_UPDATE to obj.next_update,
|
||||
COL_INITIALIZED to obj.initialized,
|
||||
COL_VIEWER to obj.viewer_flags,
|
||||
COL_CHAPTER_FLAGS to obj.chapter_flags,
|
||||
|
@ -74,24 +72,23 @@ class MangaPutResolver : DefaultPutResolver<Manga>() {
|
|||
|
||||
interface BaseMangaGetResolver {
|
||||
fun mapBaseFromCursor(manga: Manga, cursor: Cursor) = manga.apply {
|
||||
id = cursor.getLong(cursor.getColumnIndex(COL_ID))
|
||||
source = cursor.getLong(cursor.getColumnIndex(COL_SOURCE))
|
||||
url = cursor.getString(cursor.getColumnIndex(COL_URL))
|
||||
artist = cursor.getString(cursor.getColumnIndex(COL_ARTIST))
|
||||
author = cursor.getString(cursor.getColumnIndex(COL_AUTHOR))
|
||||
description = cursor.getString(cursor.getColumnIndex(COL_DESCRIPTION))
|
||||
genre = cursor.getString(cursor.getColumnIndex(COL_GENRE))
|
||||
title = cursor.getString(cursor.getColumnIndex(COL_TITLE))
|
||||
status = cursor.getInt(cursor.getColumnIndex(COL_STATUS))
|
||||
thumbnail_url = cursor.getString(cursor.getColumnIndex(COL_THUMBNAIL_URL))
|
||||
favorite = cursor.getInt(cursor.getColumnIndex(COL_FAVORITE)) == 1
|
||||
last_update = cursor.getLong(cursor.getColumnIndex(COL_LAST_UPDATE))
|
||||
next_update = cursor.getLong(cursor.getColumnIndex(COL_NEXT_UPDATE))
|
||||
initialized = cursor.getInt(cursor.getColumnIndex(COL_INITIALIZED)) == 1
|
||||
viewer_flags = cursor.getInt(cursor.getColumnIndex(COL_VIEWER))
|
||||
chapter_flags = cursor.getInt(cursor.getColumnIndex(COL_CHAPTER_FLAGS))
|
||||
cover_last_modified = cursor.getLong(cursor.getColumnIndex(COL_COVER_LAST_MODIFIED))
|
||||
date_added = cursor.getLong(cursor.getColumnIndex(COL_DATE_ADDED))
|
||||
id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_ID))
|
||||
source = cursor.getLong(cursor.getColumnIndexOrThrow(COL_SOURCE))
|
||||
url = cursor.getString(cursor.getColumnIndexOrThrow(COL_URL))
|
||||
artist = cursor.getString(cursor.getColumnIndexOrThrow(COL_ARTIST))
|
||||
author = cursor.getString(cursor.getColumnIndexOrThrow(COL_AUTHOR))
|
||||
description = cursor.getString(cursor.getColumnIndexOrThrow(COL_DESCRIPTION))
|
||||
genre = cursor.getString(cursor.getColumnIndexOrThrow(COL_GENRE))
|
||||
title = cursor.getString(cursor.getColumnIndexOrThrow(COL_TITLE))
|
||||
status = cursor.getInt(cursor.getColumnIndexOrThrow(COL_STATUS))
|
||||
thumbnail_url = cursor.getString(cursor.getColumnIndexOrThrow(COL_THUMBNAIL_URL))
|
||||
favorite = cursor.getInt(cursor.getColumnIndexOrThrow(COL_FAVORITE)) == 1
|
||||
last_update = cursor.getLong(cursor.getColumnIndexOrThrow(COL_LAST_UPDATE))
|
||||
initialized = cursor.getInt(cursor.getColumnIndexOrThrow(COL_INITIALIZED)) == 1
|
||||
viewer_flags = cursor.getInt(cursor.getColumnIndexOrThrow(COL_VIEWER))
|
||||
chapter_flags = cursor.getInt(cursor.getColumnIndexOrThrow(COL_CHAPTER_FLAGS))
|
||||
cover_last_modified = cursor.getLong(cursor.getColumnIndexOrThrow(COL_COVER_LAST_MODIFIED))
|
||||
date_added = cursor.getLong(cursor.getColumnIndexOrThrow(COL_DATE_ADDED))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -65,19 +65,19 @@ class TrackPutResolver : DefaultPutResolver<Track>() {
|
|||
class TrackGetResolver : DefaultGetResolver<Track>() {
|
||||
|
||||
override fun mapFromCursor(cursor: Cursor): Track = TrackImpl().apply {
|
||||
id = cursor.getLong(cursor.getColumnIndex(COL_ID))
|
||||
manga_id = cursor.getLong(cursor.getColumnIndex(COL_MANGA_ID))
|
||||
sync_id = cursor.getInt(cursor.getColumnIndex(COL_SYNC_ID))
|
||||
media_id = cursor.getInt(cursor.getColumnIndex(COL_MEDIA_ID))
|
||||
library_id = cursor.getLong(cursor.getColumnIndex(COL_LIBRARY_ID))
|
||||
title = cursor.getString(cursor.getColumnIndex(COL_TITLE))
|
||||
last_chapter_read = cursor.getFloat(cursor.getColumnIndex(COL_LAST_CHAPTER_READ))
|
||||
total_chapters = cursor.getInt(cursor.getColumnIndex(COL_TOTAL_CHAPTERS))
|
||||
status = cursor.getInt(cursor.getColumnIndex(COL_STATUS))
|
||||
score = cursor.getFloat(cursor.getColumnIndex(COL_SCORE))
|
||||
tracking_url = cursor.getString(cursor.getColumnIndex(COL_TRACKING_URL))
|
||||
started_reading_date = cursor.getLong(cursor.getColumnIndex(COL_START_DATE))
|
||||
finished_reading_date = cursor.getLong(cursor.getColumnIndex(COL_FINISH_DATE))
|
||||
id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_ID))
|
||||
manga_id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_MANGA_ID))
|
||||
sync_id = cursor.getInt(cursor.getColumnIndexOrThrow(COL_SYNC_ID))
|
||||
media_id = cursor.getInt(cursor.getColumnIndexOrThrow(COL_MEDIA_ID))
|
||||
library_id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_LIBRARY_ID))
|
||||
title = cursor.getString(cursor.getColumnIndexOrThrow(COL_TITLE))
|
||||
last_chapter_read = cursor.getFloat(cursor.getColumnIndexOrThrow(COL_LAST_CHAPTER_READ))
|
||||
total_chapters = cursor.getInt(cursor.getColumnIndexOrThrow(COL_TOTAL_CHAPTERS))
|
||||
status = cursor.getInt(cursor.getColumnIndexOrThrow(COL_STATUS))
|
||||
score = cursor.getFloat(cursor.getColumnIndexOrThrow(COL_SCORE))
|
||||
tracking_url = cursor.getString(cursor.getColumnIndexOrThrow(COL_TRACKING_URL))
|
||||
started_reading_date = cursor.getLong(cursor.getColumnIndexOrThrow(COL_START_DATE))
|
||||
finished_reading_date = cursor.getLong(cursor.getColumnIndexOrThrow(COL_FINISH_DATE))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,9 +14,6 @@ interface Anime : SAnime {
|
|||
// last time the episode list changed in any way
|
||||
var last_update: Long
|
||||
|
||||
// predicted next update time based on latest (by date) 4 episodes' deltas
|
||||
var next_update: Long
|
||||
|
||||
var date_added: Long
|
||||
|
||||
var viewer_flags: Int
|
||||
|
|
|
@ -26,8 +26,6 @@ open class AnimeImpl : Anime {
|
|||
|
||||
override var last_update: Long = 0
|
||||
|
||||
override var next_update: Long = 0
|
||||
|
||||
override var date_added: Long = 0
|
||||
|
||||
override var initialized: Boolean = false
|
||||
|
|
|
@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.data.database.models
|
|||
|
||||
class AnimelibAnime : AnimeImpl() {
|
||||
|
||||
var unread: Int = 0
|
||||
var unseen: Int = 0
|
||||
|
||||
var category: Int = 0
|
||||
}
|
||||
|
|
|
@ -16,9 +16,6 @@ interface Manga : SManga {
|
|||
// last time the chapter list changed in any way
|
||||
var last_update: Long
|
||||
|
||||
// predicted next update time based on latest (by date) 4 chapters' deltas
|
||||
var next_update: Long
|
||||
|
||||
var date_added: Long
|
||||
|
||||
var viewer_flags: Int
|
||||
|
|
|
@ -26,8 +26,6 @@ open class MangaImpl : Manga {
|
|||
|
||||
override var last_update: Long = 0
|
||||
|
||||
override var next_update: Long = 0
|
||||
|
||||
override var date_added: Long = 0
|
||||
|
||||
override var initialized: Boolean = false
|
||||
|
|
|
@ -13,7 +13,6 @@ import eu.kanade.tachiyomi.data.database.resolvers.AnimeCoverLastModifiedPutReso
|
|||
import eu.kanade.tachiyomi.data.database.resolvers.AnimeFavoritePutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.AnimeFlagsPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.AnimeLastUpdatedPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.AnimeNextUpdatedPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.AnimeTitlePutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.AnimelibAnimeGetResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.SourceIdAnimeCountGetResolver
|
||||
|
@ -108,11 +107,6 @@ interface AnimeQueries : DbProvider {
|
|||
.withPutResolver(AnimeFlagsPutResolver(AnimeTable.COL_VIEWER, Anime::viewer_flags))
|
||||
.prepare()
|
||||
|
||||
fun updateNextUpdated(manga: Anime) = db.put()
|
||||
.`object`(manga)
|
||||
.withPutResolver(AnimeNextUpdatedPutResolver())
|
||||
.prepare()
|
||||
|
||||
fun updateLastUpdated(anime: Anime) = db.put()
|
||||
.`object`(anime)
|
||||
.withPutResolver(AnimeLastUpdatedPutResolver())
|
||||
|
|
|
@ -14,7 +14,6 @@ import eu.kanade.tachiyomi.data.database.resolvers.MangaCoverLastModifiedPutReso
|
|||
import eu.kanade.tachiyomi.data.database.resolvers.MangaFavoritePutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaFlagsPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaLastUpdatedPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaNextUpdatedPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaTitlePutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.SourceIdMangaCountGetResolver
|
||||
import eu.kanade.tachiyomi.data.database.tables.CategoryTable
|
||||
|
@ -108,11 +107,6 @@ interface MangaQueries : DbProvider {
|
|||
.withPutResolver(MangaFlagsPutResolver(MangaTable.COL_VIEWER, Manga::viewer_flags))
|
||||
.prepare()
|
||||
|
||||
fun updateNextUpdated(manga: Manga) = db.put()
|
||||
.`object`(manga)
|
||||
.withPutResolver(MangaNextUpdatedPutResolver())
|
||||
.prepare()
|
||||
|
||||
fun updateLastUpdated(manga: Manga) = db.put()
|
||||
.`object`(manga)
|
||||
.withPutResolver(MangaLastUpdatedPutResolver())
|
||||
|
|
|
@ -20,7 +20,7 @@ class AnimeEpisodeGetResolver : DefaultGetResolver<AnimeEpisode>() {
|
|||
val anime = animeGetResolver.mapFromCursor(cursor)
|
||||
val episode = episodeGetResolver.mapFromCursor(cursor)
|
||||
anime.id = episode.anime_id
|
||||
anime.url = cursor.getString(cursor.getColumnIndex("animeUrl"))
|
||||
anime.url = cursor.getString(cursor.getColumnIndexOrThrow("animeUrl"))
|
||||
|
||||
return AnimeEpisode(anime, episode)
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ class AnimeEpisodeHistoryGetResolver : DefaultGetResolver<AnimeEpisodeHistory>()
|
|||
|
||||
// Make certain column conflicts are dealt with
|
||||
anime.id = episode.anime_id
|
||||
anime.url = cursor.getString(cursor.getColumnIndex("animeUrl"))
|
||||
anime.url = cursor.getString(cursor.getColumnIndexOrThrow("animeUrl"))
|
||||
episode.id = history.episode_id
|
||||
|
||||
// Return result
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
package eu.kanade.tachiyomi.data.database.resolvers
|
||||
|
||||
import androidx.core.content.contentValuesOf
|
||||
import com.pushtorefresh.storio.sqlite.StorIOSQLite
|
||||
import com.pushtorefresh.storio.sqlite.operations.put.PutResolver
|
||||
import com.pushtorefresh.storio.sqlite.operations.put.PutResult
|
||||
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
|
||||
import eu.kanade.tachiyomi.data.database.inTransactionReturn
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.database.tables.AnimeTable
|
||||
|
||||
class AnimeNextUpdatedPutResolver : PutResolver<Anime>() {
|
||||
|
||||
override fun performPut(db: StorIOSQLite, anime: Anime) = db.inTransactionReturn {
|
||||
val updateQuery = mapToUpdateQuery(anime)
|
||||
val contentValues = mapToContentValues(anime)
|
||||
|
||||
val numberOfRowsUpdated = db.lowLevel().update(updateQuery, contentValues)
|
||||
PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table())
|
||||
}
|
||||
|
||||
fun mapToUpdateQuery(anime: Anime) = UpdateQuery.builder()
|
||||
.table(AnimeTable.TABLE)
|
||||
.where("${AnimeTable.COL_ID} = ?")
|
||||
.whereArgs(anime.id)
|
||||
.build()
|
||||
|
||||
fun mapToContentValues(anime: Anime) = contentValuesOf(
|
||||
AnimeTable.COL_NEXT_UPDATE to anime.next_update
|
||||
)
|
||||
}
|
|
@ -16,8 +16,8 @@ class AnimelibAnimeGetResolver : DefaultGetResolver<AnimelibAnime>(), BaseAnimeG
|
|||
val manga = AnimelibAnime()
|
||||
|
||||
mapBaseFromCursor(manga, cursor)
|
||||
manga.unread = cursor.getInt(cursor.getColumnIndex(AnimeTable.COL_UNREAD))
|
||||
manga.category = cursor.getInt(cursor.getColumnIndex(AnimeTable.COL_CATEGORY))
|
||||
manga.unseen = cursor.getInt(cursor.getColumnIndexOrThrow(AnimeTable.COL_UNREAD))
|
||||
manga.category = cursor.getInt(cursor.getColumnIndexOrThrow(AnimeTable.COL_CATEGORY))
|
||||
|
||||
return manga
|
||||
}
|
||||
|
|
|
@ -16,8 +16,8 @@ class LibraryMangaGetResolver : DefaultGetResolver<LibraryManga>(), BaseMangaGet
|
|||
val manga = LibraryManga()
|
||||
|
||||
mapBaseFromCursor(manga, cursor)
|
||||
manga.unread = cursor.getInt(cursor.getColumnIndex(MangaTable.COL_UNREAD))
|
||||
manga.category = cursor.getInt(cursor.getColumnIndex(MangaTable.COL_CATEGORY))
|
||||
manga.unread = cursor.getInt(cursor.getColumnIndexOrThrow(MangaTable.COL_UNREAD))
|
||||
manga.category = cursor.getInt(cursor.getColumnIndexOrThrow(MangaTable.COL_CATEGORY))
|
||||
|
||||
return manga
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ class MangaChapterGetResolver : DefaultGetResolver<MangaChapter>() {
|
|||
val manga = mangaGetResolver.mapFromCursor(cursor)
|
||||
val chapter = chapterGetResolver.mapFromCursor(cursor)
|
||||
manga.id = chapter.manga_id
|
||||
manga.url = cursor.getString(cursor.getColumnIndex("mangaUrl"))
|
||||
manga.url = cursor.getString(cursor.getColumnIndexOrThrow("mangaUrl"))
|
||||
|
||||
return MangaChapter(manga, chapter)
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ class MangaChapterHistoryGetResolver : DefaultGetResolver<MangaChapterHistory>()
|
|||
|
||||
// Make certain column conflicts are dealt with
|
||||
manga.id = chapter.manga_id
|
||||
manga.url = cursor.getString(cursor.getColumnIndex("mangaUrl"))
|
||||
manga.url = cursor.getString(cursor.getColumnIndexOrThrow("mangaUrl"))
|
||||
chapter.id = history.chapter_id
|
||||
|
||||
// Return result
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
package eu.kanade.tachiyomi.data.database.resolvers
|
||||
|
||||
import androidx.core.content.contentValuesOf
|
||||
import com.pushtorefresh.storio.sqlite.StorIOSQLite
|
||||
import com.pushtorefresh.storio.sqlite.operations.put.PutResolver
|
||||
import com.pushtorefresh.storio.sqlite.operations.put.PutResult
|
||||
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
|
||||
import eu.kanade.tachiyomi.data.database.inTransactionReturn
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable
|
||||
|
||||
class MangaNextUpdatedPutResolver : PutResolver<Manga>() {
|
||||
|
||||
override fun performPut(db: StorIOSQLite, manga: Manga) = db.inTransactionReturn {
|
||||
val updateQuery = mapToUpdateQuery(manga)
|
||||
val contentValues = mapToContentValues(manga)
|
||||
|
||||
val numberOfRowsUpdated = db.lowLevel().update(updateQuery, contentValues)
|
||||
PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table())
|
||||
}
|
||||
|
||||
fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder()
|
||||
.table(MangaTable.TABLE)
|
||||
.where("${MangaTable.COL_ID} = ?")
|
||||
.whereArgs(manga.id)
|
||||
.build()
|
||||
|
||||
fun mapToContentValues(manga: Manga) = contentValuesOf(
|
||||
MangaTable.COL_NEXT_UPDATE to manga.next_update
|
||||
)
|
||||
}
|
|
@ -15,8 +15,8 @@ class SourceIdAnimeCountGetResolver : DefaultGetResolver<SourceIdAnimeCount>() {
|
|||
|
||||
@SuppressLint("Range")
|
||||
override fun mapFromCursor(cursor: Cursor): SourceIdAnimeCount {
|
||||
val sourceID = cursor.getLong(cursor.getColumnIndex(AnimeTable.COL_SOURCE))
|
||||
val count = cursor.getInt(cursor.getColumnIndex(COL_COUNT))
|
||||
val sourceID = cursor.getLong(cursor.getColumnIndexOrThrow(AnimeTable.COL_SOURCE))
|
||||
val count = cursor.getInt(cursor.getColumnIndexOrThrow(COL_COUNT))
|
||||
|
||||
return SourceIdAnimeCount(sourceID, count)
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@ class SourceIdMangaCountGetResolver : DefaultGetResolver<SourceIdMangaCount>() {
|
|||
|
||||
@SuppressLint("Range")
|
||||
override fun mapFromCursor(cursor: Cursor): SourceIdMangaCount {
|
||||
val sourceID = cursor.getLong(cursor.getColumnIndex(MangaTable.COL_SOURCE))
|
||||
val count = cursor.getInt(cursor.getColumnIndex(COL_COUNT))
|
||||
val sourceID = cursor.getLong(cursor.getColumnIndexOrThrow(MangaTable.COL_SOURCE))
|
||||
val count = cursor.getInt(cursor.getColumnIndexOrThrow(COL_COUNT))
|
||||
|
||||
return SourceIdMangaCount(sourceID, count)
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ object AnimeTable {
|
|||
|
||||
const val COL_FAVORITE = "favorite"
|
||||
|
||||
// Not actually used anymore
|
||||
const val COL_LAST_UPDATE = "last_update"
|
||||
|
||||
const val COL_NEXT_UPDATE = "next_update"
|
||||
|
|
|
@ -62,4 +62,7 @@ object ChapterTable {
|
|||
|
||||
val addScanlator: String
|
||||
get() = "ALTER TABLE $TABLE ADD COLUMN $COL_SCANLATOR TEXT DEFAULT NULL"
|
||||
|
||||
val fixDateUploadIfNeeded: String
|
||||
get() = "UPDATE $TABLE SET $COL_DATE_UPLOAD = $COL_DATE_FETCH WHERE $COL_DATE_UPLOAD = 0"
|
||||
}
|
||||
|
|
|
@ -65,4 +65,7 @@ object EpisodeTable {
|
|||
|
||||
val addScanlator: String
|
||||
get() = "ALTER TABLE $TABLE ADD COLUMN $COL_SCANLATOR TEXT DEFAULT NULL"
|
||||
|
||||
val fixDateUploadIfNeeded: String
|
||||
get() = "UPDATE $TABLE SET $COL_DATE_UPLOAD = $COL_DATE_FETCH WHERE $COL_DATE_UPLOAD = 0"
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ object MangaTable {
|
|||
|
||||
const val COL_LAST_UPDATE = "last_update"
|
||||
|
||||
// Not actually used anymore
|
||||
const val COL_NEXT_UPDATE = "next_update"
|
||||
|
||||
const val COL_DATE_ADDED = "date_added"
|
||||
|
|
|
@ -206,14 +206,13 @@ internal class AnimeDownloadNotifier(private val context: Context) {
|
|||
* being overwritten.
|
||||
*
|
||||
* @param error string containing error information.
|
||||
* @param chapter string containing chapter title.
|
||||
* @param episode string containing episode title.
|
||||
*/
|
||||
fun onError(error: String? = null, chapter: String? = null) {
|
||||
fun onError(error: String? = null, episode: String? = null, animeTitle: String? = null) {
|
||||
// Create notification
|
||||
with(errorNotificationBuilder) {
|
||||
setContentTitle(
|
||||
chapter
|
||||
?: context.getString(R.string.download_notifier_downloader_title)
|
||||
animeTitle?.plus(": $episode") ?: context.getString(R.string.download_notifier_downloader_title)
|
||||
)
|
||||
setContentText(error ?: context.getString(R.string.download_notifier_unknown_error))
|
||||
setSmallIcon(R.drawable.ic_warning_white_24dp)
|
||||
|
|
|
@ -179,13 +179,15 @@ class AnimeDownloadService : Service() {
|
|||
* Listens to downloader status. Enables or disables the wake lock depending on the status.
|
||||
*/
|
||||
private fun listenDownloaderState() {
|
||||
subscriptions += downloadManager.runningRelay.subscribe { running ->
|
||||
if (running) {
|
||||
wakeLock.acquireIfNeeded()
|
||||
} else {
|
||||
wakeLock.releaseIfNeeded()
|
||||
subscriptions += downloadManager.runningRelay
|
||||
.doOnError { /* Swallow wakelock error */ }
|
||||
.subscribe { running ->
|
||||
if (running) {
|
||||
wakeLock.acquireIfNeeded()
|
||||
} else {
|
||||
wakeLock.releaseIfNeeded()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -143,7 +143,7 @@ class DownloadCache(
|
|||
mangaDirs.values.forEach { mangaDir ->
|
||||
val chapterDirs = mangaDir.dir.listFiles()
|
||||
.orEmpty()
|
||||
.mapNotNull { it.name }
|
||||
.mapNotNull { it.name?.replace(".cbz", "") }
|
||||
.toHashSet()
|
||||
|
||||
mangaDir.files = chapterDirs
|
||||
|
|
|
@ -39,7 +39,7 @@ class DownloadManager(
|
|||
/**
|
||||
* Downloads provider, used to retrieve the folders where the chapters are or should be stored.
|
||||
*/
|
||||
private val provider = DownloadProvider(context)
|
||||
val provider = DownloadProvider(context)
|
||||
|
||||
/**
|
||||
* Cache of downloaded chapters.
|
||||
|
@ -327,15 +327,19 @@ class DownloadManager(
|
|||
*/
|
||||
fun renameChapter(source: Source, manga: Manga, oldChapter: Chapter, newChapter: Chapter) {
|
||||
val oldNames = provider.getValidChapterDirNames(oldChapter)
|
||||
val newName = provider.getChapterDirName(newChapter)
|
||||
val mangaDir = provider.getMangaDir(manga, source)
|
||||
|
||||
// Assume there's only 1 version of the chapter name formats present
|
||||
val oldFolder = oldNames.asSequence()
|
||||
val oldDownload = oldNames.asSequence()
|
||||
.mapNotNull { mangaDir.findFile(it) }
|
||||
.firstOrNull()
|
||||
.firstOrNull() ?: return
|
||||
|
||||
if (oldFolder?.renameTo(newName) == true) {
|
||||
var newName = provider.getChapterDirName(newChapter)
|
||||
if (oldDownload.isFile && oldDownload.name?.endsWith(".cbz") == true) {
|
||||
newName += ".cbz"
|
||||
}
|
||||
|
||||
if (oldDownload.renameTo(newName)) {
|
||||
cache.removeChapter(oldChapter, manga)
|
||||
cache.addChapter(newName, mangaDir, manga)
|
||||
} else {
|
||||
|
|
|
@ -209,12 +209,11 @@ internal class DownloadNotifier(private val context: Context) {
|
|||
* @param error string containing error information.
|
||||
* @param chapter string containing chapter title.
|
||||
*/
|
||||
fun onError(error: String? = null, chapter: String? = null) {
|
||||
fun onError(error: String? = null, chapter: String? = null, mangaTitle: String? = null) {
|
||||
// Create notification
|
||||
with(errorNotificationBuilder) {
|
||||
setContentTitle(
|
||||
chapter
|
||||
?: context.getString(R.string.download_notifier_downloader_title)
|
||||
mangaTitle?.plus(": $chapter") ?: context.getString(R.string.download_notifier_downloader_title)
|
||||
)
|
||||
setContentText(error ?: context.getString(R.string.download_notifier_unknown_error))
|
||||
setSmallIcon(R.drawable.ic_warning_white_24dp)
|
||||
|
|
|
@ -148,10 +148,14 @@ class DownloadProvider(private val context: Context) {
|
|||
* @param chapter the chapter to query.
|
||||
*/
|
||||
fun getValidChapterDirNames(chapter: Chapter): List<String> {
|
||||
val chapterName = getChapterDirName(chapter)
|
||||
return listOf(
|
||||
getChapterDirName(chapter),
|
||||
// Folder of images
|
||||
chapterName,
|
||||
|
||||
// Archived chapters
|
||||
"$chapterName.cbz",
|
||||
|
||||
// TODO: remove this
|
||||
// Legacy chapter directory name used in v0.9.2 and before
|
||||
DiskUtil.buildValidFilename(chapter.name)
|
||||
)
|
||||
|
|
|
@ -176,13 +176,15 @@ class DownloadService : Service() {
|
|||
* Listens to downloader status. Enables or disables the wake lock depending on the status.
|
||||
*/
|
||||
private fun listenDownloaderState() {
|
||||
subscriptions += downloadManager.runningRelay.subscribe { running ->
|
||||
if (running) {
|
||||
wakeLock.acquireIfNeeded()
|
||||
} else {
|
||||
wakeLock.releaseIfNeeded()
|
||||
subscriptions += downloadManager.runningRelay
|
||||
.doOnError { /* Swallow wakelock error */ }
|
||||
.subscribe { running ->
|
||||
if (running) {
|
||||
wakeLock.acquireIfNeeded()
|
||||
} else {
|
||||
wakeLock.releaseIfNeeded()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,6 +12,7 @@ import eu.kanade.tachiyomi.data.database.models.Chapter
|
|||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.download.model.Download
|
||||
import eu.kanade.tachiyomi.data.download.model.DownloadQueue
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.UnmeteredSource
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
|
@ -35,7 +36,11 @@ import rx.android.schedulers.AndroidSchedulers
|
|||
import rx.schedulers.Schedulers
|
||||
import rx.subscriptions.CompositeSubscription
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.File
|
||||
import java.util.zip.CRC32
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
|
||||
/**
|
||||
* This class is the one in charge of downloading chapters.
|
||||
|
@ -60,6 +65,8 @@ class Downloader(
|
|||
|
||||
private val chapterCache: ChapterCache by injectLazy()
|
||||
|
||||
private val preferences: PreferencesHelper by injectLazy()
|
||||
|
||||
/**
|
||||
* Store for persisting downloads across restarts.
|
||||
*/
|
||||
|
@ -292,7 +299,7 @@ class Downloader(
|
|||
val availSpace = DiskUtil.getAvailableStorageSpace(mangaDir)
|
||||
if (availSpace != -1L && availSpace < MIN_DISK_SPACE) {
|
||||
download.status = Download.State.ERROR
|
||||
notifier.onError(context.getString(R.string.download_insufficient_space), download.chapter.name)
|
||||
notifier.onError(context.getString(R.string.download_insufficient_space), download.chapter.name, download.manga.title)
|
||||
return@defer Observable.just(download)
|
||||
}
|
||||
|
||||
|
@ -338,7 +345,7 @@ class Downloader(
|
|||
// If the page list threw, it will resume here
|
||||
.onErrorReturn { error ->
|
||||
download.status = Download.State.ERROR
|
||||
notifier.onError(error.message, download.chapter.name)
|
||||
notifier.onError(error.message, download.chapter.name, download.manga.title)
|
||||
download
|
||||
}
|
||||
}
|
||||
|
@ -484,13 +491,51 @@ class Downloader(
|
|||
|
||||
// Only rename the directory if it's downloaded.
|
||||
if (download.status == Download.State.DOWNLOADED) {
|
||||
tmpDir.renameTo(dirname)
|
||||
if (preferences.saveChaptersAsCBZ().get()) {
|
||||
archiveChapter(mangaDir, dirname, tmpDir)
|
||||
} else {
|
||||
tmpDir.renameTo(dirname)
|
||||
}
|
||||
cache.addChapter(dirname, mangaDir, download.manga)
|
||||
|
||||
DiskUtil.createNoMediaFile(tmpDir, context)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Archive the chapter pages as a CBZ.
|
||||
*/
|
||||
private fun archiveChapter(
|
||||
mangaDir: UniFile,
|
||||
dirname: String,
|
||||
tmpDir: UniFile,
|
||||
) {
|
||||
val zip = mangaDir.createFile("$dirname.cbz.tmp")
|
||||
ZipOutputStream(BufferedOutputStream(zip.openOutputStream())).use { zipOut ->
|
||||
zipOut.setMethod(ZipEntry.STORED)
|
||||
|
||||
tmpDir.listFiles()?.forEach { img ->
|
||||
img.openInputStream().use { input ->
|
||||
val data = input.readBytes()
|
||||
val size = img.length()
|
||||
val entry = ZipEntry(img.name).apply {
|
||||
val crc = CRC32().apply {
|
||||
update(data)
|
||||
}
|
||||
setCrc(crc.value)
|
||||
|
||||
compressedSize = size
|
||||
setSize(size)
|
||||
}
|
||||
zipOut.putNextEntry(entry)
|
||||
zipOut.write(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
zip.renameTo("$dirname.cbz")
|
||||
tmpDir.delete()
|
||||
}
|
||||
|
||||
/**
|
||||
* Completes a download. This method is called in the main thread.
|
||||
*/
|
||||
|
|
|
@ -8,8 +8,8 @@ import androidx.work.PeriodicWorkRequestBuilder
|
|||
import androidx.work.WorkManager
|
||||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
import eu.kanade.tachiyomi.data.preference.CHARGING
|
||||
import eu.kanade.tachiyomi.data.preference.ONLY_ON_WIFI
|
||||
import eu.kanade.tachiyomi.data.preference.DEVICE_CHARGING
|
||||
import eu.kanade.tachiyomi.data.preference.DEVICE_ONLY_ON_WIFI
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.util.system.isConnectedToWifi
|
||||
import uy.kohesive.injekt.Injekt
|
||||
|
@ -39,10 +39,10 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
|||
val preferences = Injekt.get<PreferencesHelper>()
|
||||
val interval = prefInterval ?: preferences.libraryUpdateInterval().get()
|
||||
if (interval > 0) {
|
||||
val restrictions = preferences.libraryUpdateRestriction().get()
|
||||
val restrictions = preferences.libraryUpdateDeviceRestriction().get()
|
||||
val constraints = Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.setRequiresCharging(CHARGING in restrictions)
|
||||
.setRequiresCharging(DEVICE_CHARGING in restrictions)
|
||||
.build()
|
||||
|
||||
val request = PeriodicWorkRequestBuilder<LibraryUpdateJob>(
|
||||
|
@ -62,8 +62,8 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
|||
}
|
||||
|
||||
fun requiresWifiConnection(preferences: PreferencesHelper): Boolean {
|
||||
val restrictions = preferences.libraryUpdateRestriction().get()
|
||||
return ONLY_ON_WIFI in restrictions
|
||||
val restrictions = preferences.libraryUpdateDeviceRestriction().get()
|
||||
return DEVICE_ONLY_ON_WIFI in restrictions
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
package eu.kanade.tachiyomi.data.library
|
||||
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import java.util.Collections
|
||||
import kotlin.math.abs
|
||||
|
||||
/**
|
||||
* This class will provide various functions to rank manga to efficiently schedule manga to update.
|
||||
*/
|
||||
object LibraryUpdateRanker {
|
||||
|
||||
val rankingScheme = listOf(
|
||||
(this::lexicographicRanking)(),
|
||||
(this::latestFirstRanking)(),
|
||||
(this::nextFirstRanking)()
|
||||
)
|
||||
|
||||
/**
|
||||
* Provides a total ordering over all the Mangas.
|
||||
*
|
||||
* Orders the manga based on the distance between the next expected update and now.
|
||||
* The comparator is reversed, placing the smallest (and thus closest to updating now) first.
|
||||
*/
|
||||
fun nextFirstRanking(): Comparator<Manga> {
|
||||
val time = System.currentTimeMillis()
|
||||
return Collections.reverseOrder(
|
||||
Comparator { mangaFirst: Manga,
|
||||
mangaSecond: Manga ->
|
||||
compareValues(abs(mangaSecond.next_update - time), abs(mangaFirst.next_update - time))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a total ordering over all the [Manga]s.
|
||||
*
|
||||
* Assumption: An active [Manga] mActive is expected to have been last updated after an
|
||||
* inactive [Manga] mInactive.
|
||||
*
|
||||
* Using this insight, function returns a Comparator for which mActive appears before mInactive.
|
||||
* @return a Comparator that ranks manga based on relevance.
|
||||
*/
|
||||
private fun latestFirstRanking(): Comparator<Manga> =
|
||||
Comparator { first: Manga, second: Manga ->
|
||||
compareValues(second.last_update, first.last_update)
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a total ordering over all the [Manga]s.
|
||||
*
|
||||
* Order the manga lexicographically.
|
||||
* @return a Comparator that ranks manga lexicographically based on the title.
|
||||
*/
|
||||
private fun lexicographicRanking(): Comparator<Manga> =
|
||||
Comparator { first: Manga, second: Manga ->
|
||||
compareValues(first.title, second.title)
|
||||
}
|
||||
}
|
|
@ -17,9 +17,10 @@ import eu.kanade.tachiyomi.data.database.models.Manga
|
|||
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
|
||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||
import eu.kanade.tachiyomi.data.download.DownloadService
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateRanker.rankingScheme
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService.Companion.start
|
||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||
import eu.kanade.tachiyomi.data.preference.MANGA_FULLY_READ
|
||||
import eu.kanade.tachiyomi.data.preference.MANGA_ONGOING
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.track.EnhancedTrackService
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
|
@ -258,14 +259,20 @@ class LibraryUpdateService(
|
|||
|
||||
listToInclude.minus(listToExclude)
|
||||
}
|
||||
if (target == Target.CHAPTERS && preferences.updateOnlyNonCompleted()) {
|
||||
listToUpdate = listToUpdate.filterNot { it.status == SManga.COMPLETED }
|
||||
|
||||
if (target == Target.CHAPTERS) {
|
||||
val restrictions = preferences.libraryUpdateMangaRestriction().get()
|
||||
if (MANGA_ONGOING in restrictions) {
|
||||
listToUpdate = listToUpdate.filterNot { it.status == SManga.COMPLETED }
|
||||
}
|
||||
if (MANGA_FULLY_READ in restrictions) {
|
||||
listToUpdate = listToUpdate.filter { it.unread == 0 }
|
||||
}
|
||||
}
|
||||
|
||||
val selectedScheme = preferences.libraryUpdatePrioritization().get()
|
||||
mangaToUpdate = listToUpdate
|
||||
.distinctBy { it.id }
|
||||
.sortedWith(rankingScheme[selectedScheme])
|
||||
.sortedBy { it.title }
|
||||
|
||||
// Warn when excessively checking a single source
|
||||
val maxUpdatesFromSource = mangaToUpdate
|
||||
|
@ -545,6 +552,7 @@ class LibraryUpdateService(
|
|||
if (errors.isNotEmpty()) {
|
||||
val file = createFileInCacheDir("tachiyomi_update_errors.txt")
|
||||
file.bufferedWriter().use { out ->
|
||||
out.write(getString(R.string.library_errors_help, ERROR_LOG_HELP_URL) + "\n\n")
|
||||
// Error file format:
|
||||
// ! Error
|
||||
// # Source
|
||||
|
@ -570,3 +578,4 @@ class LibraryUpdateService(
|
|||
}
|
||||
|
||||
private const val MANGA_PER_SOURCE_QUEUE_WARNING_THRESHOLD = 60
|
||||
private const val ERROR_LOG_HELP_URL = "https://tachiyomi.org/help/guides/troubleshooting"
|
||||
|
|
|
@ -5,56 +5,10 @@ package eu.kanade.tachiyomi.data.preference
|
|||
*/
|
||||
object PreferenceKeys {
|
||||
|
||||
const val themeMode = "pref_theme_mode_key"
|
||||
|
||||
const val appTheme = "pref_app_theme"
|
||||
|
||||
const val themeDarkAmoled = "pref_theme_dark_amoled_key"
|
||||
|
||||
const val confirmExit = "pref_confirm_exit"
|
||||
|
||||
const val hideBottomBarOnScroll = "pref_hide_bottom_bar_on_scroll"
|
||||
|
||||
const val sideNavIconAlignment = "pref_side_nav_icon_alignment"
|
||||
|
||||
const val enableTransitions = "pref_enable_transitions_key"
|
||||
|
||||
const val doubleTapAnimationSpeed = "pref_double_tap_anim_speed"
|
||||
|
||||
const val showPageNumber = "pref_show_page_number_key"
|
||||
|
||||
const val dualPageSplitPaged = "pref_dual_page_split"
|
||||
|
||||
const val dualPageSplitWebtoon = "pref_dual_page_split_webtoon"
|
||||
|
||||
const val dualPageInvertPaged = "pref_dual_page_invert"
|
||||
|
||||
const val dualPageInvertWebtoon = "pref_dual_page_invert_webtoon"
|
||||
|
||||
const val showReadingMode = "pref_show_reading_mode"
|
||||
|
||||
const val trueColor = "pref_true_color_key"
|
||||
|
||||
const val fullscreen = "fullscreen"
|
||||
|
||||
const val cutoutShort = "cutout_short"
|
||||
|
||||
const val keepScreenOn = "pref_keep_screen_on_key"
|
||||
|
||||
const val customBrightness = "pref_custom_brightness_key"
|
||||
|
||||
const val customBrightnessValue = "custom_brightness_value"
|
||||
|
||||
const val colorFilter = "pref_color_filter_key"
|
||||
|
||||
const val colorFilterValue = "color_filter_value"
|
||||
|
||||
const val colorFilterMode = "color_filter_mode"
|
||||
|
||||
const val grayscale = "pref_grayscale"
|
||||
|
||||
const val invertedColors = "pref_inverted_colors"
|
||||
|
||||
const val defaultReadingMode = "pref_default_reading_mode_key"
|
||||
|
||||
const val defaultOrientationType = "pref_default_orientation_type_key"
|
||||
|
@ -75,69 +29,10 @@ object PreferenceKeys {
|
|||
|
||||
const val externalPlayerPreference = "external_player_preference"
|
||||
|
||||
const val imageScaleType = "pref_image_scale_type_key"
|
||||
|
||||
const val zoomStart = "pref_zoom_start_key"
|
||||
|
||||
const val readerTheme = "pref_reader_theme_key"
|
||||
|
||||
const val cropBorders = "crop_borders"
|
||||
|
||||
const val cropBordersWebtoon = "crop_borders_webtoon"
|
||||
|
||||
const val readWithTapping = "reader_tap"
|
||||
|
||||
const val pagerNavInverted = "reader_tapping_inverted"
|
||||
|
||||
const val webtoonNavInverted = "reader_tapping_inverted_webtoon"
|
||||
|
||||
const val readWithLongTap = "reader_long_tap"
|
||||
|
||||
const val readWithVolumeKeys = "reader_volume_keys"
|
||||
|
||||
const val readWithVolumeKeysInverted = "reader_volume_keys_inverted"
|
||||
|
||||
const val navigationModePager = "reader_navigation_mode_pager"
|
||||
|
||||
const val navigationModeWebtoon = "reader_navigation_mode_webtoon"
|
||||
|
||||
const val showNavigationOverlayNewUser = "reader_navigation_overlay_new_user"
|
||||
|
||||
const val showNavigationOverlayOnStart = "reader_navigation_overlay_on_start"
|
||||
|
||||
const val readerHideThreshold = "reader_hide_threshold"
|
||||
|
||||
const val webtoonSidePadding = "webtoon_side_padding"
|
||||
|
||||
const val portraitColumns = "pref_library_columns_portrait_key"
|
||||
|
||||
const val landscapeColumns = "pref_library_columns_landscape_key"
|
||||
|
||||
const val jumpToChapters = "jump_to_chapters"
|
||||
|
||||
const val jumpToEpisodes = "jump_to_episodes"
|
||||
|
||||
const val updateOnlyNonCompleted = "pref_update_only_non_completed_key"
|
||||
|
||||
const val autoUpdateTrack = "pref_auto_update_manga_sync_key"
|
||||
|
||||
const val lastUsedSource = "last_catalogue_source"
|
||||
|
||||
const val lastUsedAnimeSource = "last_anime_catalogue_source"
|
||||
|
||||
const val lastUsedCategory = "last_used_category"
|
||||
|
||||
const val lastUsedAnimeCategory = "last_used_anime_category"
|
||||
|
||||
const val sourceDisplayMode = "pref_display_mode_catalogue"
|
||||
const val animesourceDisplayMode = "pref_display_mode_anime_catalogue"
|
||||
|
||||
const val enabledLanguages = "source_languages"
|
||||
|
||||
const val backupDirectory = "backup_directory"
|
||||
|
||||
const val downloadsDirectory = "download_directory"
|
||||
|
||||
const val useExternalDownloader = "use_external_downloader"
|
||||
|
||||
const val externalDownloaderSelection = "external_downloader_selection"
|
||||
|
@ -148,31 +43,12 @@ object PreferenceKeys {
|
|||
|
||||
const val folderPerAnime = "create_folder_per_anime"
|
||||
|
||||
const val numberOfBackups = "backup_slots"
|
||||
|
||||
const val backupInterval = "backup_interval"
|
||||
|
||||
const val removeAfterReadSlots = "remove_after_read_slots"
|
||||
|
||||
const val removeAfterMarkedAsRead = "pref_remove_after_marked_as_read_key"
|
||||
|
||||
const val removeBookmarkedChapters = "pref_remove_bookmarked"
|
||||
|
||||
const val libraryUpdateInterval = "pref_library_update_interval_key"
|
||||
|
||||
const val libraryUpdateRestriction = "library_update_restriction"
|
||||
|
||||
const val showUpdatesNavBadge = "library_update_show_tab_badge"
|
||||
|
||||
const val libraryUpdateCategories = "library_update_categories"
|
||||
const val animelibUpdateCategories = "animelib_update_categories"
|
||||
const val libraryUpdateCategoriesExclude = "library_update_categories_exclude"
|
||||
const val animelibUpdateCategoriesExclude = "animelib_update_categories_exclude"
|
||||
|
||||
const val libraryUpdatePrioritization = "library_update_prioritization"
|
||||
|
||||
const val downloadedOnly = "pref_downloaded_only"
|
||||
|
||||
const val filterDownloaded = "pref_filter_library_downloaded"
|
||||
|
||||
const val filterUnread = "pref_filter_library_unread"
|
||||
|
@ -187,20 +63,8 @@ object PreferenceKeys {
|
|||
const val migrationSortingMode = "pref_migration_sorting"
|
||||
const val migrationSortingDirection = "pref_migration_direction"
|
||||
|
||||
const val automaticExtUpdates = "automatic_ext_updates"
|
||||
|
||||
const val showNsfwSource = "show_nsfw_source"
|
||||
|
||||
const val startScreen = "start_screen"
|
||||
|
||||
const val useAuthenticator = "use_biometric_lock"
|
||||
|
||||
const val lockAppAfter = "lock_app_after"
|
||||
|
||||
const val lastAppUnlock = "last_app_unlock"
|
||||
|
||||
const val secureScreen = "secure_screen"
|
||||
|
||||
const val hideNotificationContent = "hide_notification_content"
|
||||
|
||||
const val autoUpdateMetadata = "auto_update_metadata"
|
||||
|
@ -209,48 +73,15 @@ object PreferenceKeys {
|
|||
|
||||
const val downloadNew = "download_new"
|
||||
|
||||
const val downloadNewCategories = "download_new_categories"
|
||||
const val downloadNewCategoriesAnime = "download_new_categories_anime"
|
||||
const val downloadNewCategoriesExclude = "download_new_categories_exclude"
|
||||
const val downloadNewCategoriesExcludeAnime = "download_new_categories_exclude_anime"
|
||||
const val removeExcludeCategories = "remove_exclude_categories"
|
||||
const val removeExcludeCategoriesAnime = "remove_exclude_categories_anime"
|
||||
|
||||
const val libraryDisplayMode = "pref_display_mode_library"
|
||||
|
||||
const val lang = "app_language"
|
||||
|
||||
const val relativeTime: String = "relative_time"
|
||||
|
||||
const val dateFormat = "app_date_format"
|
||||
|
||||
const val defaultCategory = "default_category"
|
||||
const val defaultAnimeCategory = "default_anime_category"
|
||||
|
||||
const val categorizedDisplay = "categorized_display"
|
||||
|
||||
const val skipRead = "skip_read"
|
||||
|
||||
const val skipFiltered = "skip_filtered"
|
||||
|
||||
const val downloadBadge = "display_download_badge"
|
||||
|
||||
const val unreadBadge = "display_unread_badge"
|
||||
|
||||
const val languageBadge = "display_language_badge"
|
||||
|
||||
const val localBadge = "display_local_badge"
|
||||
|
||||
const val categoryTabs = "display_category_tabs"
|
||||
|
||||
const val animeCategoryTabs = "display_anime_category_tabs"
|
||||
|
||||
const val categoryNumberOfItems = "display_number_of_items"
|
||||
|
||||
const val animeCategoryNumberOfItems = "display_number_of_items_anime"
|
||||
|
||||
const val alwaysShowChapterTransition = "always_show_chapter_transition"
|
||||
|
||||
const val searchPinnedSourcesOnly = "search_pinned_sources_only"
|
||||
|
||||
const val dohProvider = "doh_provider"
|
||||
|
@ -279,12 +110,6 @@ object PreferenceKeys {
|
|||
|
||||
const val defaultEpisodeDisplayByNameOrNumber = "default_episode_display_by_name_or_number"
|
||||
|
||||
const val incognitoMode = "incognito_mode"
|
||||
|
||||
const val tabletUiMode = "tablet_ui_mode"
|
||||
|
||||
const val extensionInstaller = "extension_installer"
|
||||
|
||||
const val verboseLogging = "verbose_logging"
|
||||
|
||||
const val autoClearChapterCache = "auto_clear_chapter_cache"
|
||||
|
|
|
@ -2,8 +2,11 @@ package eu.kanade.tachiyomi.data.preference
|
|||
|
||||
import eu.kanade.tachiyomi.R
|
||||
|
||||
const val ONLY_ON_WIFI = "wifi"
|
||||
const val CHARGING = "ac"
|
||||
const val DEVICE_ONLY_ON_WIFI = "wifi"
|
||||
const val DEVICE_CHARGING = "ac"
|
||||
|
||||
const val MANGA_ONGOING = "manga_ongoing"
|
||||
const val MANGA_FULLY_READ = "manga_fully_read"
|
||||
|
||||
/**
|
||||
* This class stores the values for the preferences in the application.
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
package eu.kanade.tachiyomi.data.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.net.toUri
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.android.material.color.DynamicColors
|
||||
import com.tfcporciuncula.flow.FlowSharedPreferences
|
||||
import com.tfcporciuncula.flow.Preference
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.preference.PreferenceValues.ThemeMode.system
|
||||
import eu.kanade.tachiyomi.data.track.TrackService
|
||||
import eu.kanade.tachiyomi.data.track.anilist.Anilist
|
||||
import eu.kanade.tachiyomi.ui.browse.migration.sources.MigrationSourcesController
|
||||
|
@ -19,10 +19,8 @@ import eu.kanade.tachiyomi.ui.library.setting.SortDirectionSetting
|
|||
import eu.kanade.tachiyomi.ui.library.setting.SortModeSetting
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
|
||||
import eu.kanade.tachiyomi.util.system.MiuiUtil
|
||||
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
||||
import eu.kanade.tachiyomi.widget.ExtendedNavigationView
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import java.io.File
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
|
@ -30,25 +28,6 @@ import java.util.Locale
|
|||
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
|
||||
import eu.kanade.tachiyomi.data.preference.PreferenceValues as Values
|
||||
|
||||
fun <T> Preference<T>.asImmediateFlow(block: (T) -> Unit): Flow<T> {
|
||||
block(get())
|
||||
return asFlow()
|
||||
.onEach { block(it) }
|
||||
}
|
||||
|
||||
operator fun <T> Preference<Set<T>>.plusAssign(item: T) {
|
||||
set(get() + item)
|
||||
}
|
||||
|
||||
operator fun <T> Preference<Set<T>>.minusAssign(item: T) {
|
||||
set(get() - item)
|
||||
}
|
||||
|
||||
fun Preference<Boolean>.toggle(): Boolean {
|
||||
set(!get())
|
||||
return get()
|
||||
}
|
||||
|
||||
class PreferencesHelper(val context: Context) {
|
||||
|
||||
private val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
@ -70,17 +49,17 @@ class PreferencesHelper(val context: Context) {
|
|||
|
||||
fun confirmExit() = prefs.getBoolean(Keys.confirmExit, false)
|
||||
|
||||
fun hideBottomBarOnScroll() = flowPrefs.getBoolean(Keys.hideBottomBarOnScroll, true)
|
||||
fun hideBottomBarOnScroll() = flowPrefs.getBoolean("pref_hide_bottom_bar_on_scroll", true)
|
||||
|
||||
fun sideNavIconAlignment() = flowPrefs.getInt(Keys.sideNavIconAlignment, 0)
|
||||
fun sideNavIconAlignment() = flowPrefs.getInt("pref_side_nav_icon_alignment", 0)
|
||||
|
||||
fun useAuthenticator() = flowPrefs.getBoolean(Keys.useAuthenticator, false)
|
||||
fun useAuthenticator() = flowPrefs.getBoolean("use_biometric_lock", false)
|
||||
|
||||
fun lockAppAfter() = flowPrefs.getInt(Keys.lockAppAfter, 0)
|
||||
fun lockAppAfter() = flowPrefs.getInt("lock_app_after", 0)
|
||||
|
||||
fun lastAppUnlock() = flowPrefs.getLong(Keys.lastAppUnlock, 0)
|
||||
fun lastAppUnlock() = flowPrefs.getLong("last_app_unlock", 0)
|
||||
|
||||
fun secureScreen() = flowPrefs.getBoolean(Keys.secureScreen, false)
|
||||
fun secureScreen() = flowPrefs.getBoolean("secure_screen", false)
|
||||
|
||||
fun hideNotificationContent() = prefs.getBoolean(Keys.hideNotificationContent, false)
|
||||
|
||||
|
@ -88,49 +67,55 @@ class PreferencesHelper(val context: Context) {
|
|||
|
||||
fun autoUpdateTrackers() = prefs.getBoolean(Keys.autoUpdateTrackers, false)
|
||||
|
||||
fun themeMode() = flowPrefs.getEnum(Keys.themeMode, system)
|
||||
fun themeMode() = flowPrefs.getEnum(
|
||||
"pref_theme_mode_key",
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { Values.ThemeMode.system } else { Values.ThemeMode.light }
|
||||
)
|
||||
|
||||
fun appTheme() = flowPrefs.getEnum(Keys.appTheme, Values.AppTheme.DEFAULT)
|
||||
fun appTheme() = flowPrefs.getEnum(
|
||||
"pref_app_theme",
|
||||
if (DynamicColors.isDynamicColorAvailable()) { Values.AppTheme.MONET } else { Values.AppTheme.DEFAULT }
|
||||
)
|
||||
|
||||
fun themeDarkAmoled() = flowPrefs.getBoolean(Keys.themeDarkAmoled, false)
|
||||
fun themeDarkAmoled() = flowPrefs.getBoolean("pref_theme_dark_amoled_key", false)
|
||||
|
||||
fun pageTransitions() = flowPrefs.getBoolean(Keys.enableTransitions, true)
|
||||
fun pageTransitions() = flowPrefs.getBoolean("pref_enable_transitions_key", true)
|
||||
|
||||
fun doubleTapAnimSpeed() = flowPrefs.getInt(Keys.doubleTapAnimationSpeed, 500)
|
||||
fun doubleTapAnimSpeed() = flowPrefs.getInt("pref_double_tap_anim_speed", 500)
|
||||
|
||||
fun showPageNumber() = flowPrefs.getBoolean(Keys.showPageNumber, true)
|
||||
fun showPageNumber() = flowPrefs.getBoolean("pref_show_page_number_key", true)
|
||||
|
||||
fun dualPageSplitPaged() = flowPrefs.getBoolean(Keys.dualPageSplitPaged, false)
|
||||
fun dualPageSplitPaged() = flowPrefs.getBoolean("pref_dual_page_split", false)
|
||||
|
||||
fun dualPageSplitWebtoon() = flowPrefs.getBoolean(Keys.dualPageSplitWebtoon, false)
|
||||
fun dualPageSplitWebtoon() = flowPrefs.getBoolean("pref_dual_page_split_webtoon", false)
|
||||
|
||||
fun dualPageInvertPaged() = flowPrefs.getBoolean(Keys.dualPageInvertPaged, false)
|
||||
fun dualPageInvertPaged() = flowPrefs.getBoolean("pref_dual_page_invert", false)
|
||||
|
||||
fun dualPageInvertWebtoon() = flowPrefs.getBoolean(Keys.dualPageInvertWebtoon, false)
|
||||
fun dualPageInvertWebtoon() = flowPrefs.getBoolean("pref_dual_page_invert_webtoon", false)
|
||||
|
||||
fun showReadingMode() = prefs.getBoolean(Keys.showReadingMode, true)
|
||||
|
||||
fun trueColor() = flowPrefs.getBoolean(Keys.trueColor, false)
|
||||
fun trueColor() = flowPrefs.getBoolean("pref_true_color_key", false)
|
||||
|
||||
fun fullscreen() = flowPrefs.getBoolean(Keys.fullscreen, true)
|
||||
fun fullscreen() = flowPrefs.getBoolean("fullscreen", true)
|
||||
|
||||
fun cutoutShort() = flowPrefs.getBoolean(Keys.cutoutShort, true)
|
||||
fun cutoutShort() = flowPrefs.getBoolean("cutout_short", true)
|
||||
|
||||
fun keepScreenOn() = flowPrefs.getBoolean(Keys.keepScreenOn, true)
|
||||
fun keepScreenOn() = flowPrefs.getBoolean("pref_keep_screen_on_key", true)
|
||||
|
||||
fun customBrightness() = flowPrefs.getBoolean(Keys.customBrightness, false)
|
||||
fun customBrightness() = flowPrefs.getBoolean("pref_custom_brightness_key", false)
|
||||
|
||||
fun customBrightnessValue() = flowPrefs.getInt(Keys.customBrightnessValue, 0)
|
||||
fun customBrightnessValue() = flowPrefs.getInt("custom_brightness_value", 0)
|
||||
|
||||
fun colorFilter() = flowPrefs.getBoolean(Keys.colorFilter, false)
|
||||
fun colorFilter() = flowPrefs.getBoolean("pref_color_filter_key", false)
|
||||
|
||||
fun colorFilterValue() = flowPrefs.getInt(Keys.colorFilterValue, 0)
|
||||
fun colorFilterValue() = flowPrefs.getInt("color_filter_value", 0)
|
||||
|
||||
fun colorFilterMode() = flowPrefs.getInt(Keys.colorFilterMode, 0)
|
||||
fun colorFilterMode() = flowPrefs.getInt("color_filter_mode", 0)
|
||||
|
||||
fun grayscale() = flowPrefs.getBoolean(Keys.grayscale, false)
|
||||
fun grayscale() = flowPrefs.getBoolean("pref_grayscale", false)
|
||||
|
||||
fun invertedColors() = flowPrefs.getBoolean(Keys.invertedColors, false)
|
||||
fun invertedColors() = flowPrefs.getBoolean("pref_inverted_colors", false)
|
||||
|
||||
fun defaultReadingMode() = prefs.getInt(Keys.defaultReadingMode, ReadingModeType.RIGHT_TO_LEFT.flagValue)
|
||||
|
||||
|
@ -160,72 +145,63 @@ class PreferencesHelper(val context: Context) {
|
|||
|
||||
fun skipLengthPreference() = prefs.getString(Keys.skipLengthPreference, "10")!!.toInt()
|
||||
|
||||
fun imageScaleType() = flowPrefs.getInt(Keys.imageScaleType, 1)
|
||||
fun imageScaleType() = flowPrefs.getInt("pref_image_scale_type_key", 1)
|
||||
|
||||
fun zoomStart() = flowPrefs.getInt(Keys.zoomStart, 1)
|
||||
fun zoomStart() = flowPrefs.getInt("pref_zoom_start_key", 1)
|
||||
|
||||
fun readerTheme() = flowPrefs.getInt(Keys.readerTheme, 1)
|
||||
fun readerTheme() = flowPrefs.getInt("pref_reader_theme_key", 1)
|
||||
|
||||
fun watcherTheme() = flowPrefs.getInt(Keys.readerTheme, 1)
|
||||
fun alwaysShowChapterTransition() = flowPrefs.getBoolean("always_show_chapter_transition", true)
|
||||
|
||||
fun alwaysShowChapterTransition() = flowPrefs.getBoolean(Keys.alwaysShowChapterTransition, true)
|
||||
fun cropBorders() = flowPrefs.getBoolean("crop_borders", false)
|
||||
|
||||
fun alwaysShowEpisodeTransition() = flowPrefs.getBoolean(Keys.alwaysShowChapterTransition, true)
|
||||
fun cropBordersWebtoon() = flowPrefs.getBoolean("crop_borders_webtoon", false)
|
||||
|
||||
fun cropBorders() = flowPrefs.getBoolean(Keys.cropBorders, false)
|
||||
fun webtoonSidePadding() = flowPrefs.getInt("webtoon_side_padding", 0)
|
||||
|
||||
fun cropBordersWebtoon() = flowPrefs.getBoolean(Keys.cropBordersWebtoon, false)
|
||||
fun readWithTapping() = flowPrefs.getBoolean("reader_tap", true)
|
||||
|
||||
fun webtoonSidePadding() = flowPrefs.getInt(Keys.webtoonSidePadding, 0)
|
||||
fun pagerNavInverted() = flowPrefs.getEnum("reader_tapping_inverted", Values.TappingInvertMode.NONE)
|
||||
|
||||
fun readWithTapping() = flowPrefs.getBoolean(Keys.readWithTapping, true)
|
||||
fun webtoonNavInverted() = flowPrefs.getEnum("reader_tapping_inverted_webtoon", Values.TappingInvertMode.NONE)
|
||||
|
||||
fun pagerNavInverted() = flowPrefs.getEnum(Keys.pagerNavInverted, Values.TappingInvertMode.NONE)
|
||||
fun readWithLongTap() = flowPrefs.getBoolean("reader_long_tap", true)
|
||||
|
||||
fun webtoonNavInverted() = flowPrefs.getEnum(Keys.webtoonNavInverted, Values.TappingInvertMode.NONE)
|
||||
fun readWithVolumeKeys() = flowPrefs.getBoolean("reader_volume_keys", false)
|
||||
|
||||
fun readWithLongTap() = flowPrefs.getBoolean(Keys.readWithLongTap, true)
|
||||
fun readWithVolumeKeysInverted() = flowPrefs.getBoolean("reader_volume_keys_inverted", false)
|
||||
|
||||
fun readWithVolumeKeys() = flowPrefs.getBoolean(Keys.readWithVolumeKeys, false)
|
||||
fun navigationModePager() = flowPrefs.getInt("reader_navigation_mode_pager", 0)
|
||||
|
||||
fun readWithVolumeKeysInverted() = flowPrefs.getBoolean(Keys.readWithVolumeKeysInverted, false)
|
||||
fun navigationModeWebtoon() = flowPrefs.getInt("reader_navigation_mode_webtoon", 0)
|
||||
|
||||
fun navigationModePager() = flowPrefs.getInt(Keys.navigationModePager, 0)
|
||||
fun showNavigationOverlayNewUser() = flowPrefs.getBoolean("reader_navigation_overlay_new_user", true)
|
||||
|
||||
fun navigationModeWebtoon() = flowPrefs.getInt(Keys.navigationModeWebtoon, 0)
|
||||
fun showNavigationOverlayOnStart() = flowPrefs.getBoolean("reader_navigation_overlay_on_start", false)
|
||||
|
||||
fun showNavigationOverlayNewUser() = flowPrefs.getBoolean(Keys.showNavigationOverlayNewUser, true)
|
||||
fun readerHideThreshold() = flowPrefs.getEnum("reader_hide_threshold", Values.ReaderHideThreshold.LOW)
|
||||
|
||||
fun showNavigationOverlayOnStart() = flowPrefs.getBoolean(Keys.showNavigationOverlayOnStart, false)
|
||||
fun portraitColumns() = flowPrefs.getInt("pref_library_columns_portrait_key", 0)
|
||||
|
||||
fun readerHideTreshold() = flowPrefs.getEnum(Keys.readerHideThreshold, Values.ReaderHideThreshold.LOW)
|
||||
|
||||
fun portraitColumns() = flowPrefs.getInt(Keys.portraitColumns, 0)
|
||||
|
||||
fun landscapeColumns() = flowPrefs.getInt(Keys.landscapeColumns, 0)
|
||||
fun landscapeColumns() = flowPrefs.getInt("pref_library_columns_landscape_key", 0)
|
||||
|
||||
fun jumpToChapters() = prefs.getBoolean(Keys.jumpToChapters, false)
|
||||
|
||||
fun jumpToEpisodes() = prefs.getBoolean(Keys.jumpToEpisodes, false)
|
||||
|
||||
fun updateOnlyNonCompleted() = prefs.getBoolean(Keys.updateOnlyNonCompleted, true)
|
||||
|
||||
fun autoUpdateTrack() = prefs.getBoolean(Keys.autoUpdateTrack, true)
|
||||
|
||||
fun lastUsedSource() = flowPrefs.getLong(Keys.lastUsedSource, -1)
|
||||
fun lastUsedSource() = flowPrefs.getLong("last_catalogue_source", -1)
|
||||
|
||||
fun lastUsedAnimeSource() = flowPrefs.getLong(Keys.lastUsedAnimeSource, -1)
|
||||
fun lastUsedAnimeSource() = flowPrefs.getLong("last_anime_catalogue_source", -1)
|
||||
|
||||
fun lastUsedCategory() = flowPrefs.getInt(Keys.lastUsedCategory, 0)
|
||||
fun lastUsedCategory() = flowPrefs.getInt("last_used_category", 0)
|
||||
|
||||
fun lastUsedAnimeCategory() = flowPrefs.getInt(Keys.lastUsedAnimeCategory, 0)
|
||||
fun lastUsedAnimeCategory() = flowPrefs.getInt("last_used_anime_category", 0)
|
||||
|
||||
fun lastVersionCode() = flowPrefs.getInt("last_version_code", 0)
|
||||
|
||||
fun sourceDisplayMode() = flowPrefs.getEnum(Keys.sourceDisplayMode, DisplayModeSetting.COMPACT_GRID)
|
||||
fun animesourceDisplayMode() = flowPrefs.getEnum(Keys.animesourceDisplayMode, DisplayModeSetting.COMPACT_GRID)
|
||||
fun sourceDisplayMode() = flowPrefs.getEnum("pref_display_mode_catalogue", DisplayModeSetting.COMPACT_GRID)
|
||||
|
||||
fun enabledLanguages() = flowPrefs.getStringSet(Keys.enabledLanguages, setOf("all", "en", Locale.getDefault().language))
|
||||
fun enabledLanguages() = flowPrefs.getStringSet("source_languages", setOf("all", "en", Locale.getDefault().language))
|
||||
|
||||
fun trackUsername(sync: TrackService) = prefs.getString(Keys.trackUsername(sync.id), "")
|
||||
|
||||
|
@ -242,16 +218,16 @@ class PreferencesHelper(val context: Context) {
|
|||
|
||||
fun anilistScoreType() = flowPrefs.getString("anilist_score_type", Anilist.POINT_10)
|
||||
|
||||
fun backupsDirectory() = flowPrefs.getString(Keys.backupDirectory, defaultBackupDir.toString())
|
||||
fun backupsDirectory() = flowPrefs.getString("backup_directory", defaultBackupDir.toString())
|
||||
|
||||
fun relativeTime() = flowPrefs.getInt(Keys.relativeTime, 7)
|
||||
fun relativeTime() = flowPrefs.getInt("relative_time", 7)
|
||||
|
||||
fun dateFormat(format: String = flowPrefs.getString(Keys.dateFormat, "").get()): DateFormat = when (format) {
|
||||
"" -> DateFormat.getDateInstance(DateFormat.SHORT)
|
||||
else -> SimpleDateFormat(format, Locale.getDefault())
|
||||
}
|
||||
|
||||
fun downloadsDirectory() = flowPrefs.getString(Keys.downloadsDirectory, defaultDownloadsDir.toString())
|
||||
fun downloadsDirectory() = flowPrefs.getString("download_directory", defaultDownloadsDir.toString())
|
||||
|
||||
fun useExternalDownloader() = prefs.getBoolean(Keys.useExternalDownloader, false)
|
||||
|
||||
|
@ -259,13 +235,13 @@ class PreferencesHelper(val context: Context) {
|
|||
|
||||
fun downloadOnlyOverWifi() = prefs.getBoolean(Keys.downloadOnlyOverWifi, true)
|
||||
|
||||
fun saveChaptersAsCBZ() = flowPrefs.getBoolean("save_chapter_as_cbz", false)
|
||||
|
||||
fun folderPerManga() = prefs.getBoolean(Keys.folderPerManga, false)
|
||||
|
||||
fun folderPerAnime() = prefs.getBoolean(Keys.folderPerAnime, false)
|
||||
fun numberOfBackups() = flowPrefs.getInt("backup_slots", 1)
|
||||
|
||||
fun numberOfBackups() = flowPrefs.getInt(Keys.numberOfBackups, 1)
|
||||
|
||||
fun backupInterval() = flowPrefs.getInt(Keys.backupInterval, 0)
|
||||
fun backupInterval() = flowPrefs.getInt("backup_interval", 0)
|
||||
|
||||
fun removeAfterReadSlots() = prefs.getInt(Keys.removeAfterReadSlots, -1)
|
||||
|
||||
|
@ -273,44 +249,43 @@ class PreferencesHelper(val context: Context) {
|
|||
|
||||
fun removeBookmarkedChapters() = prefs.getBoolean(Keys.removeBookmarkedChapters, false)
|
||||
|
||||
fun removeExcludeCategories() = flowPrefs.getStringSet(Keys.removeExcludeCategories, emptySet())
|
||||
fun removeExcludeAnimeCategories() = flowPrefs.getStringSet(Keys.removeExcludeCategoriesAnime, emptySet())
|
||||
fun removeExcludeCategories() = flowPrefs.getStringSet("remove_exclude_categories", emptySet())
|
||||
fun removeExcludeAnimeCategories() = flowPrefs.getStringSet("remove_exclude_categories_anime", emptySet())
|
||||
|
||||
fun libraryUpdateInterval() = flowPrefs.getInt(Keys.libraryUpdateInterval, 24)
|
||||
fun libraryUpdateInterval() = flowPrefs.getInt("pref_library_update_interval_key", 24)
|
||||
|
||||
fun libraryUpdateRestriction() = flowPrefs.getStringSet(Keys.libraryUpdateRestriction, setOf(ONLY_ON_WIFI))
|
||||
fun libraryUpdateDeviceRestriction() = flowPrefs.getStringSet("library_update_restriction", setOf(DEVICE_ONLY_ON_WIFI))
|
||||
fun libraryUpdateMangaRestriction() = flowPrefs.getStringSet("library_update_manga_restriction", setOf(MANGA_FULLY_READ, MANGA_ONGOING))
|
||||
|
||||
fun showUpdatesNavBadge() = flowPrefs.getBoolean(Keys.showUpdatesNavBadge, false)
|
||||
fun showUpdatesNavBadge() = flowPrefs.getBoolean("library_update_show_tab_badge", false)
|
||||
fun unreadUpdatesCount() = flowPrefs.getInt("library_unread_updates_count", 0)
|
||||
fun unseenUpdatesCount() = flowPrefs.getInt("library_unseen_updates_count", 0)
|
||||
|
||||
fun libraryUpdateCategories() = flowPrefs.getStringSet(Keys.libraryUpdateCategories, emptySet())
|
||||
fun animelibUpdateCategories() = flowPrefs.getStringSet(Keys.animelibUpdateCategories, emptySet())
|
||||
fun libraryUpdateCategories() = flowPrefs.getStringSet("library_update_categories", emptySet())
|
||||
fun animelibUpdateCategories() = flowPrefs.getStringSet("animelib_update_categories", emptySet())
|
||||
|
||||
fun libraryUpdateCategoriesExclude() = flowPrefs.getStringSet(Keys.libraryUpdateCategoriesExclude, emptySet())
|
||||
fun animelibUpdateCategoriesExclude() = flowPrefs.getStringSet(Keys.animelibUpdateCategoriesExclude, emptySet())
|
||||
fun libraryUpdateCategoriesExclude() = flowPrefs.getStringSet("library_update_categories_exclude", emptySet())
|
||||
fun animelibUpdateCategoriesExclude() = flowPrefs.getStringSet("animelib_update_categories_exclude", emptySet())
|
||||
|
||||
fun libraryUpdatePrioritization() = flowPrefs.getInt(Keys.libraryUpdatePrioritization, 0)
|
||||
fun libraryDisplayMode() = flowPrefs.getEnum("pref_display_mode_library", DisplayModeSetting.COMPACT_GRID)
|
||||
|
||||
fun libraryDisplayMode() = flowPrefs.getEnum(Keys.libraryDisplayMode, DisplayModeSetting.COMPACT_GRID)
|
||||
fun downloadBadge() = flowPrefs.getBoolean("display_download_badge", false)
|
||||
|
||||
fun downloadBadge() = flowPrefs.getBoolean(Keys.downloadBadge, false)
|
||||
fun localBadge() = flowPrefs.getBoolean("display_local_badge", true)
|
||||
|
||||
fun localBadge() = flowPrefs.getBoolean(Keys.localBadge, true)
|
||||
fun downloadedOnly() = flowPrefs.getBoolean("pref_downloaded_only", false)
|
||||
|
||||
fun downloadedOnly() = flowPrefs.getBoolean(Keys.downloadedOnly, false)
|
||||
fun unreadBadge() = flowPrefs.getBoolean("display_unread_badge", true)
|
||||
|
||||
fun unreadBadge() = flowPrefs.getBoolean(Keys.unreadBadge, true)
|
||||
fun languageBadge() = flowPrefs.getBoolean("display_language_badge", false)
|
||||
|
||||
fun languageBadge() = flowPrefs.getBoolean(Keys.languageBadge, false)
|
||||
fun categoryTabs() = flowPrefs.getBoolean("display_category_tabs", true)
|
||||
|
||||
fun categoryTabs() = flowPrefs.getBoolean(Keys.categoryTabs, true)
|
||||
fun animeCategoryTabs() = flowPrefs.getBoolean("display_anime_category_tabs", true)
|
||||
|
||||
fun animeCategoryTabs() = flowPrefs.getBoolean(Keys.animeCategoryTabs, true)
|
||||
fun categoryNumberOfItems() = flowPrefs.getBoolean("display_number_of_items", false)
|
||||
|
||||
fun categoryNumberOfItems() = flowPrefs.getBoolean(Keys.categoryNumberOfItems, false)
|
||||
|
||||
fun animeCategoryNumberOfItems() = flowPrefs.getBoolean(Keys.animeCategoryNumberOfItems, false)
|
||||
fun animeCategoryNumberOfItems() = flowPrefs.getBoolean("display_number_of_items_anime", false)
|
||||
|
||||
fun filterDownloaded() = flowPrefs.getInt(Keys.filterDownloaded, ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
|
||||
|
||||
|
@ -326,9 +301,9 @@ class PreferencesHelper(val context: Context) {
|
|||
fun migrationSortingMode() = flowPrefs.getEnum(Keys.migrationSortingMode, MigrationSourcesController.SortSetting.ALPHABETICAL)
|
||||
fun migrationSortingDirection() = flowPrefs.getEnum(Keys.migrationSortingDirection, MigrationSourcesController.DirectionSetting.ASCENDING)
|
||||
|
||||
fun automaticExtUpdates() = flowPrefs.getBoolean(Keys.automaticExtUpdates, true)
|
||||
fun automaticExtUpdates() = flowPrefs.getBoolean("automatic_ext_updates", true)
|
||||
|
||||
fun showNsfwSource() = flowPrefs.getBoolean(Keys.showNsfwSource, true)
|
||||
fun showNsfwSource() = flowPrefs.getBoolean("show_nsfw_source", true)
|
||||
|
||||
fun extensionUpdatesCount() = flowPrefs.getInt("ext_updates_count", 0)
|
||||
|
||||
|
@ -350,19 +325,19 @@ class PreferencesHelper(val context: Context) {
|
|||
|
||||
fun pinnedAnimeSources() = flowPrefs.getStringSet("pinned_anime_catalogues", emptySet())
|
||||
|
||||
fun downloadNew() = flowPrefs.getBoolean(Keys.downloadNew, false)
|
||||
fun downloadNew() = flowPrefs.getBoolean("download_new", false)
|
||||
|
||||
fun downloadNewCategories() = flowPrefs.getStringSet(Keys.downloadNewCategories, emptySet())
|
||||
fun downloadNewCategoriesAnime() = flowPrefs.getStringSet(Keys.downloadNewCategoriesAnime, emptySet())
|
||||
fun downloadNewCategoriesExclude() = flowPrefs.getStringSet(Keys.downloadNewCategoriesExclude, emptySet())
|
||||
fun downloadNewCategoriesAnimeExclude() = flowPrefs.getStringSet(Keys.downloadNewCategoriesExcludeAnime, emptySet())
|
||||
fun downloadNewCategories() = flowPrefs.getStringSet("download_new_categories", emptySet())
|
||||
fun downloadNewCategoriesAnime() = flowPrefs.getStringSet("download_new_categories_anime", emptySet())
|
||||
fun downloadNewCategoriesExclude() = flowPrefs.getStringSet("download_new_categories_exclude", emptySet())
|
||||
fun downloadNewCategoriesAnimeExclude() = flowPrefs.getStringSet("download_new_categories_exclude_anime", emptySet())
|
||||
|
||||
fun lang() = flowPrefs.getString(Keys.lang, "")
|
||||
fun lang() = flowPrefs.getString("app_language", "")
|
||||
|
||||
fun defaultCategory() = prefs.getInt(Keys.defaultCategory, -1)
|
||||
fun defaultAnimeCategory() = prefs.getInt(Keys.defaultAnimeCategory, -1)
|
||||
|
||||
fun categorisedDisplaySettings() = flowPrefs.getBoolean(Keys.categorizedDisplay, false)
|
||||
fun categorizedDisplaySettings() = flowPrefs.getBoolean("categorized_display", false)
|
||||
|
||||
fun skipRead() = prefs.getBoolean(Keys.skipRead, false)
|
||||
|
||||
|
@ -400,13 +375,13 @@ class PreferencesHelper(val context: Context) {
|
|||
|
||||
fun sortEpisodeByAscendingOrDescending() = prefs.getInt(Keys.defaultEpisodeSortByAscendingOrDescending, Anime.EPISODE_SORT_DESC)
|
||||
|
||||
fun incognitoMode() = flowPrefs.getBoolean(Keys.incognitoMode, false)
|
||||
fun incognitoMode() = flowPrefs.getBoolean("incognito_mode", false)
|
||||
|
||||
fun tabletUiMode() = flowPrefs.getEnum(Keys.tabletUiMode, Values.TabletUiMode.AUTOMATIC)
|
||||
fun tabletUiMode() = flowPrefs.getEnum("tablet_ui_mode", Values.TabletUiMode.AUTOMATIC)
|
||||
|
||||
fun extensionInstaller() = flowPrefs.getEnum(
|
||||
Keys.extensionInstaller,
|
||||
if (MiuiUtil.isMiui()) Values.ExtensionInstaller.LEGACY else Values.ExtensionInstaller.PACKAGEINSTALLER
|
||||
"extension_installer",
|
||||
if (DeviceUtil.isMiui) Values.ExtensionInstaller.LEGACY else Values.ExtensionInstaller.PACKAGEINSTALLER
|
||||
)
|
||||
|
||||
fun verboseLogging() = prefs.getBoolean(Keys.verboseLogging, false)
|
||||
|
|
|
@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.data.track.anilist
|
|||
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
import java.io.IOException
|
||||
|
||||
class AnilistInterceptor(val anilist: Anilist, private var token: String?) : Interceptor {
|
||||
|
||||
|
@ -28,12 +29,12 @@ class AnilistInterceptor(val anilist: Anilist, private var token: String?) : Int
|
|||
// Refresh access token if null or expired.
|
||||
if (oauth!!.isExpired()) {
|
||||
anilist.logout()
|
||||
throw Exception("Token expired")
|
||||
throw IOException("Token expired")
|
||||
}
|
||||
|
||||
// Throw on null auth.
|
||||
if (oauth == null) {
|
||||
throw Exception("No authentication token")
|
||||
throw IOException("No authentication token")
|
||||
}
|
||||
|
||||
// Add the authorization header to the original request.
|
||||
|
|
|
@ -16,15 +16,6 @@ class BangumiInterceptor(val bangumi: Bangumi) : Interceptor {
|
|||
*/
|
||||
private var oauth: OAuth? = bangumi.restoreToken()
|
||||
|
||||
fun addToken(token: String, oidFormBody: FormBody): FormBody {
|
||||
val newFormBody = FormBody.Builder()
|
||||
for (i in 0 until oidFormBody.size) {
|
||||
newFormBody.add(oidFormBody.name(i), oidFormBody.value(i))
|
||||
}
|
||||
newFormBody.add("access_token", token)
|
||||
return newFormBody.build()
|
||||
}
|
||||
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val originalRequest = chain.request()
|
||||
|
||||
|
@ -65,4 +56,13 @@ class BangumiInterceptor(val bangumi: Bangumi) : Interceptor {
|
|||
|
||||
bangumi.saveToken(oauth)
|
||||
}
|
||||
|
||||
private fun addToken(token: String, oidFormBody: FormBody): FormBody {
|
||||
val newFormBody = FormBody.Builder()
|
||||
for (i in 0 until oidFormBody.size) {
|
||||
newFormBody.add(oidFormBody.name(i), oidFormBody.value(i))
|
||||
}
|
||||
newFormBody.add("access_token", token)
|
||||
return newFormBody.build()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,7 +64,8 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
|
|||
suspend fun search(query: String): List<TrackSearch> {
|
||||
return withIOContext {
|
||||
val url = "$baseApiUrl/manga".toUri().buildUpon()
|
||||
.appendQueryParameter("q", query)
|
||||
// MAL API throws a 400 when the query is over 64 characters...
|
||||
.appendQueryParameter("q", query.take(64))
|
||||
.appendQueryParameter("nsfw", "true")
|
||||
.build()
|
||||
authClient.newCall(GET(url.toString()))
|
||||
|
@ -86,6 +87,8 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
|
|||
suspend fun searchAnime(query: String): List<AnimeTrackSearch> {
|
||||
return withIOContext {
|
||||
val url = "$baseApiUrl/anime".toUri().buildUpon()
|
||||
// MAL API throws a 400 when the query is over 64 characters...
|
||||
.appendQueryParameter("q", query.take(64))
|
||||
.appendQueryParameter("q", query)
|
||||
.appendQueryParameter("nsfw", "true")
|
||||
.build()
|
||||
|
|
|
@ -5,6 +5,7 @@ import kotlinx.serialization.json.Json
|
|||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.io.IOException
|
||||
|
||||
class MyAnimeListInterceptor(private val myanimelist: MyAnimeList, private var token: String?) : Interceptor {
|
||||
|
||||
|
@ -16,7 +17,7 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList, private var t
|
|||
val originalRequest = chain.request()
|
||||
|
||||
if (token.isNullOrEmpty()) {
|
||||
throw Exception("Not authenticated with MyAnimeList")
|
||||
throw IOException("Not authenticated with MyAnimeList")
|
||||
}
|
||||
if (oauth == null) {
|
||||
oauth = myanimelist.loadOAuth()
|
||||
|
@ -30,7 +31,7 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList, private var t
|
|||
}
|
||||
}
|
||||
if (oauth == null) {
|
||||
throw Exception("No authentication token")
|
||||
throw IOException("No authentication token")
|
||||
}
|
||||
|
||||
// Add the authorization header to the original request
|
||||
|
|
|
@ -7,7 +7,6 @@ import eu.kanade.tachiyomi.R
|
|||
import eu.kanade.tachiyomi.animesource.AnimeSource
|
||||
import eu.kanade.tachiyomi.animesource.AnimeSourceManager
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.plusAssign
|
||||
import eu.kanade.tachiyomi.extension.api.AnimeExtensionGithubApi
|
||||
import eu.kanade.tachiyomi.extension.model.AnimeExtension
|
||||
import eu.kanade.tachiyomi.extension.model.AnimeLoadResult
|
||||
|
@ -16,6 +15,7 @@ import eu.kanade.tachiyomi.extension.util.AnimeExtensionInstallReceiver
|
|||
import eu.kanade.tachiyomi.extension.util.AnimeExtensionInstaller
|
||||
import eu.kanade.tachiyomi.extension.util.AnimeExtensionLoader
|
||||
import eu.kanade.tachiyomi.util.lang.launchNow
|
||||
import eu.kanade.tachiyomi.util.preference.plusAssign
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.coroutines.async
|
||||
|
|
|
@ -5,7 +5,6 @@ import android.graphics.drawable.Drawable
|
|||
import com.jakewharton.rxrelay.BehaviorRelay
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.plusAssign
|
||||
import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi
|
||||
import eu.kanade.tachiyomi.extension.model.Extension
|
||||
import eu.kanade.tachiyomi.extension.model.InstallStep
|
||||
|
@ -16,6 +15,7 @@ import eu.kanade.tachiyomi.extension.util.ExtensionLoader
|
|||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.util.lang.launchNow
|
||||
import eu.kanade.tachiyomi.util.preference.plusAssign
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.coroutines.async
|
||||
|
|
|
@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.extension.api
|
|||
|
||||
import android.content.Context
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.extension.model.AvailableExtensionSources
|
||||
import eu.kanade.tachiyomi.extension.model.Extension
|
||||
import eu.kanade.tachiyomi.extension.model.LoadResult
|
||||
import eu.kanade.tachiyomi.extension.util.ExtensionLoader
|
||||
|
@ -80,12 +81,23 @@ internal class ExtensionGithubApi {
|
|||
versionCode = it.code,
|
||||
lang = it.lang,
|
||||
isNsfw = it.nsfw == 1,
|
||||
sources = it.sources?.toExtensionSources() ?: emptyList(),
|
||||
apkName = it.apk,
|
||||
iconUrl = "${REPO_URL_PREFIX}icon/${it.apk.replace(".apk", ".png")}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<ExtensionSourceJsonObject>.toExtensionSources(): List<AvailableExtensionSources> {
|
||||
return this.map {
|
||||
AvailableExtensionSources(
|
||||
name = it.name,
|
||||
id = it.id,
|
||||
baseUrl = it.baseUrl
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun getApkUrl(extension: Extension.Available): String {
|
||||
return "${REPO_URL_PREFIX}apk/${extension.apkName}"
|
||||
}
|
||||
|
@ -94,12 +106,21 @@ internal class ExtensionGithubApi {
|
|||
private const val REPO_URL_PREFIX = "https://raw.githubusercontent.com/tachiyomiorg/tachiyomi-extensions/repo/"
|
||||
|
||||
@Serializable
|
||||
data class ExtensionJsonObject(
|
||||
private data class ExtensionJsonObject(
|
||||
val name: String,
|
||||
val pkg: String,
|
||||
val apk: String,
|
||||
val version: String,
|
||||
val code: Long,
|
||||
val lang: String,
|
||||
val code: Long,
|
||||
val version: String,
|
||||
val nsfw: Int,
|
||||
val sources: List<ExtensionSourceJsonObject>?,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
private data class ExtensionSourceJsonObject(
|
||||
val name: String,
|
||||
val id: Long,
|
||||
val baseUrl: String
|
||||
|
||||
)
|
||||
|
|
|
@ -32,6 +32,7 @@ sealed class Extension {
|
|||
override val versionCode: Long,
|
||||
override val lang: String,
|
||||
override val isNsfw: Boolean,
|
||||
val sources: List<AvailableExtensionSources>,
|
||||
val apkName: String,
|
||||
val iconUrl: String
|
||||
) : Extension()
|
||||
|
@ -46,3 +47,9 @@ sealed class Extension {
|
|||
override val isNsfw: Boolean = false
|
||||
) : Extension()
|
||||
}
|
||||
|
||||
data class AvailableExtensionSources(
|
||||
val name: String,
|
||||
val id: Long,
|
||||
val baseUrl: String
|
||||
)
|
||||
|
|
|
@ -110,7 +110,7 @@ internal class AnimeExtensionInstaller(private val context: Context) {
|
|||
.map {
|
||||
downloadManager.query(query).use { cursor ->
|
||||
cursor.moveToFirst()
|
||||
cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))
|
||||
cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS))
|
||||
}
|
||||
}
|
||||
// Ignore duplicate results
|
||||
|
@ -249,7 +249,7 @@ internal class AnimeExtensionInstaller(private val context: Context) {
|
|||
downloadManager.query(query).use { cursor ->
|
||||
if (cursor.moveToFirst()) {
|
||||
val localUri = cursor.getString(
|
||||
cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)
|
||||
cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_URI)
|
||||
).removePrefix(FILE_SCHEME)
|
||||
|
||||
installApk(id, File(localUri).getUriCompat(context))
|
||||
|
|
|
@ -110,7 +110,7 @@ internal class ExtensionInstaller(private val context: Context) {
|
|||
.map {
|
||||
downloadManager.query(query).use { cursor ->
|
||||
cursor.moveToFirst()
|
||||
cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))
|
||||
cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS))
|
||||
}
|
||||
}
|
||||
// Ignore duplicate results
|
||||
|
@ -249,7 +249,7 @@ internal class ExtensionInstaller(private val context: Context) {
|
|||
downloadManager.query(query).use { cursor ->
|
||||
if (cursor.moveToFirst()) {
|
||||
val localUri = cursor.getString(
|
||||
cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)
|
||||
cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_URI)
|
||||
).removePrefix(FILE_SCHEME)
|
||||
|
||||
installApk(id, File(localUri).getUriCompat(context))
|
||||
|
|
|
@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.network.interceptor
|
|||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.webkit.WebSettings
|
||||
import android.webkit.WebView
|
||||
import android.widget.Toast
|
||||
|
@ -10,6 +11,7 @@ import eu.kanade.tachiyomi.R
|
|||
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.util.lang.launchUI
|
||||
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
||||
import eu.kanade.tachiyomi.util.system.WebViewClientCompat
|
||||
import eu.kanade.tachiyomi.util.system.WebViewUtil
|
||||
import eu.kanade.tachiyomi.util.system.isOutdated
|
||||
|
@ -37,6 +39,13 @@ class CloudflareInterceptor(private val context: Context) : Interceptor {
|
|||
* Application class.
|
||||
*/
|
||||
private val initWebView by lazy {
|
||||
// Crashes on some devices. We skip this in some cases since the only impact is slower
|
||||
// WebView init in those rare cases.
|
||||
// See https://bugs.chromium.org/p/chromium/issues/detail?id=1279562
|
||||
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.S && DeviceUtil.isSamsung) {
|
||||
return@lazy
|
||||
}
|
||||
|
||||
WebSettings.getDefaultUserAgent(context)
|
||||
}
|
||||
|
||||
|
@ -68,9 +77,12 @@ class CloudflareInterceptor(private val context: Context) : Interceptor {
|
|||
resolveWithWebView(originalRequest, oldCookie)
|
||||
|
||||
return chain.proceed(originalRequest)
|
||||
}
|
||||
// Because OkHttp's enqueue only handles IOExceptions, wrap the exception so that
|
||||
// we don't crash the entire app
|
||||
catch (e: CloudflareBypassException) {
|
||||
throw IOException(context.getString(R.string.information_cloudflare_bypass_failure))
|
||||
} catch (e: Exception) {
|
||||
// Because OkHttp's enqueue only handles IOExceptions, wrap the exception so that
|
||||
// we don't crash the entire app
|
||||
throw IOException(e)
|
||||
}
|
||||
}
|
||||
|
@ -162,7 +174,7 @@ class CloudflareInterceptor(private val context: Context) : Interceptor {
|
|||
context.toast(R.string.information_webview_outdated, Toast.LENGTH_LONG)
|
||||
}
|
||||
|
||||
throw Exception(context.getString(R.string.information_cloudflare_bypass_failure))
|
||||
throw CloudflareBypassException()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,3 +184,5 @@ class CloudflareInterceptor(private val context: Context) : Interceptor {
|
|||
private val COOKIE_NAMES = listOf("cf_clearance")
|
||||
}
|
||||
}
|
||||
|
||||
private class CloudflareBypassException : Exception()
|
||||
|
|
|
@ -14,6 +14,8 @@ import java.util.concurrent.TimeUnit
|
|||
* permits = 5, period = 1, unit = seconds => 5 requests per second
|
||||
* permits = 10, period = 2, unit = minutes => 10 requests per 2 minutes
|
||||
*
|
||||
* @since extension-lib 1.3
|
||||
*
|
||||
* @param permits {Int} Number of requests allowed within a period of units.
|
||||
* @param period {Long} The limiting duration. Defaults to 1.
|
||||
* @param unit {TimeUnit} The unit of time for the period. Defaults to seconds.
|
||||
|
|
|
@ -15,6 +15,8 @@ import java.util.concurrent.TimeUnit
|
|||
* httpUrl = "api.manga.com".toHttpUrlOrNull(), permits = 5, period = 1, unit = seconds => 5 requests per second to api.manga.com
|
||||
* httpUrl = "imagecdn.manga.com".toHttpUrlOrNull(), permits = 10, period = 2, unit = minutes => 10 requests per 2 minutes to imagecdn.manga.com
|
||||
*
|
||||
* @since extension-lib 1.3
|
||||
*
|
||||
* @param httpUrl {HttpUrl} The url host that this interceptor should handle. Will get url's host by using HttpUrl.host()
|
||||
* @param permits {Int} Number of requests allowed within a period of units.
|
||||
* @param period {Long} The limiting duration. Defaults to 1.
|
||||
|
|
|
@ -34,7 +34,6 @@ import uy.kohesive.injekt.injectLazy
|
|||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.InputStream
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.zip.ZipFile
|
||||
|
||||
|
@ -111,9 +110,9 @@ class LocalSource(private val context: Context) : CatalogueSource, UnmeteredSour
|
|||
when (state?.index) {
|
||||
0 -> {
|
||||
mangaDirs = if (state.ascending) {
|
||||
mangaDirs.sortedBy { it.name.lowercase(Locale.ENGLISH) }
|
||||
mangaDirs.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, { it.name }))
|
||||
} else {
|
||||
mangaDirs.sortedByDescending { it.name.lowercase(Locale.ENGLISH) }
|
||||
mangaDirs.sortedWith(compareByDescending(String.CASE_INSENSITIVE_ORDER, { it.name }))
|
||||
}
|
||||
}
|
||||
1 -> {
|
||||
|
@ -176,7 +175,7 @@ class LocalSource(private val context: Context) : CatalogueSource, UnmeteredSour
|
|||
.asSequence()
|
||||
.mapNotNull { File(it, manga.key).listFiles()?.toList() }
|
||||
.flatten()
|
||||
.firstOrNull { it.extension.lowercase() == "json" }
|
||||
.firstOrNull { it.extension.equals("json", ignoreCase = true) }
|
||||
|
||||
return if (localDetails != null) {
|
||||
val obj = json.decodeFromStream<JsonObject>(localDetails.inputStream())
|
||||
|
|
|
@ -15,7 +15,6 @@ import android.view.MenuItem
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.FloatRange
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.ViewCompat
|
||||
|
@ -102,6 +101,7 @@ import eu.kanade.tachiyomi.util.system.toast
|
|||
import eu.kanade.tachiyomi.util.view.getCoordinates
|
||||
import eu.kanade.tachiyomi.util.view.shrinkOnScroll
|
||||
import eu.kanade.tachiyomi.util.view.snack
|
||||
import eu.kanade.tachiyomi.widget.ActionModeWithToolbar
|
||||
import eu.kanade.tachiyomi.widget.materialdialogs.QuadStateTextView
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
|
@ -121,7 +121,7 @@ import kotlin.math.min
|
|||
class AnimeController :
|
||||
NucleusController<MangaControllerBinding, AnimePresenter>,
|
||||
FabController,
|
||||
ActionMode.Callback,
|
||||
ActionModeWithToolbar.Callback,
|
||||
FlexibleAdapter.OnItemClickListener,
|
||||
FlexibleAdapter.OnItemLongClickListener,
|
||||
BaseEpisodesAdapter.OnEpisodeClickListener,
|
||||
|
@ -179,7 +179,7 @@ class AnimeController :
|
|||
/**
|
||||
* Action mode for multiple selection.
|
||||
*/
|
||||
private var actionMode: ActionMode? = null
|
||||
private var actionMode: ActionModeWithToolbar? = null
|
||||
|
||||
/**
|
||||
* Selected items. Used to restore selections after a rotation.
|
||||
|
@ -260,11 +260,6 @@ class AnimeController :
|
|||
it.layoutManager = LinearLayoutManager(view.context)
|
||||
it.setHasFixedSize(true)
|
||||
}
|
||||
binding.actionToolbar.applyInsetter {
|
||||
type(navigationBars = true) {
|
||||
margin(bottom = true, horizontal = true)
|
||||
}
|
||||
}
|
||||
|
||||
if (anime == null || source == null) return
|
||||
|
||||
|
@ -330,7 +325,7 @@ class AnimeController :
|
|||
|
||||
actionFabScrollListener = actionFab?.shrinkOnScroll(episodeRecycler)
|
||||
// Initially set FAB invisible; will become visible if unseen episodes are present
|
||||
actionFab?.isVisible = false
|
||||
actionFab?.hide()
|
||||
|
||||
binding.swipeRefresh.refreshes()
|
||||
.onEach {
|
||||
|
@ -365,6 +360,9 @@ class AnimeController :
|
|||
}
|
||||
|
||||
private fun updateToolbarTitleAlpha(@FloatRange(from = 0.0, to = 1.0) alpha: Float? = null) {
|
||||
// Controller may actually already be destroyed by the time this gets run
|
||||
binding ?: return
|
||||
|
||||
val scrolledList = binding.fullRecycler ?: binding.infoRecycler!!
|
||||
(activity as? MainActivity)?.binding?.appbar?.titleTextAlpha = when {
|
||||
// Specific alpha provided
|
||||
|
@ -417,16 +415,19 @@ class AnimeController :
|
|||
val context = view?.context ?: return
|
||||
val adapter = episodesAdapter ?: return
|
||||
val fab = actionFab ?: return
|
||||
fab.isVisible = adapter.items.any { !it.seen }
|
||||
if (adapter.items.any { it.seen }) {
|
||||
fab.text = context.getString(R.string.action_resume)
|
||||
}
|
||||
if (adapter.items.any { !it.seen }) {
|
||||
fab.show()
|
||||
} else {
|
||||
fab.hide()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView(view: View) {
|
||||
recyclerViewUpdatesToolbarTitleAlpha(false)
|
||||
destroyActionModeIfNeeded()
|
||||
binding.actionToolbar.destroy()
|
||||
animeInfoAdapter = null
|
||||
episodesHeaderAdapter = null
|
||||
episodesAdapter = null
|
||||
|
@ -1157,11 +1158,7 @@ class AnimeController :
|
|||
|
||||
private fun createActionModeIfNeeded() {
|
||||
if (actionMode == null) {
|
||||
actionMode = (activity as? AppCompatActivity)?.startSupportActionMode(this)
|
||||
binding.actionToolbar.show(
|
||||
actionMode!!,
|
||||
R.menu.episode_selection
|
||||
) { onActionItemClicked(it!!) }
|
||||
actionMode = (activity as MainActivity).startActionModeAndToolbar(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1177,6 +1174,10 @@ class AnimeController :
|
|||
return true
|
||||
}
|
||||
|
||||
override fun onCreateActionToolbar(menuInflater: MenuInflater, menu: Menu) {
|
||||
menuInflater.inflate(R.menu.chapter_selection, menu)
|
||||
}
|
||||
|
||||
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
|
||||
val count = episodesAdapter?.selectedItemCount ?: 0
|
||||
if (count == 0) {
|
||||
|
@ -1185,27 +1186,24 @@ class AnimeController :
|
|||
} else {
|
||||
mode.title = count.toString()
|
||||
|
||||
val episodes = getSelectedEpisodes()
|
||||
binding.actionToolbar.findItem(R.id.action_download)?.isVisible = !isLocalSource && episodes.any { !it.isDownloaded }
|
||||
binding.actionToolbar.findItem(R.id.action_delete)?.isVisible = !isLocalSource && episodes.any { it.isDownloaded }
|
||||
binding.actionToolbar.findItem(R.id.action_bookmark)?.isVisible = episodes.any { !it.episode.bookmark }
|
||||
binding.actionToolbar.findItem(R.id.action_remove_bookmark)?.isVisible = episodes.all { it.episode.bookmark }
|
||||
binding.actionToolbar.findItem(R.id.action_mark_as_read)?.isVisible = episodes.any { !it.episode.seen }
|
||||
binding.actionToolbar.findItem(R.id.action_mark_as_unread)?.isVisible = episodes.all { it.episode.seen }
|
||||
binding.actionToolbar.findItem(R.id.action_play_externally)?.isVisible = !preferences.alwaysUseExternalPlayer()
|
||||
binding.actionToolbar.findItem(R.id.action_play_internally)?.isVisible = preferences.alwaysUseExternalPlayer()
|
||||
|
||||
// Hide FAB to avoid interfering with the bottom action toolbar
|
||||
actionFab?.isVisible = false
|
||||
actionFab?.hide()
|
||||
}
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onPrepareActionToolbar(toolbar: ActionModeWithToolbar, menu: Menu) {
|
||||
val episodes = getSelectedEpisodes()
|
||||
if (episodes.isEmpty()) return
|
||||
toolbar.findToolbarItem(R.id.action_download)?.isVisible = !isLocalSource && episodes.any { !it.isDownloaded }
|
||||
toolbar.findToolbarItem(R.id.action_delete)?.isVisible = !isLocalSource && episodes.any { it.isDownloaded }
|
||||
toolbar.findToolbarItem(R.id.action_bookmark)?.isVisible = episodes.any { !it.episode.bookmark }
|
||||
toolbar.findToolbarItem(R.id.action_remove_bookmark)?.isVisible = episodes.all { it.episode.bookmark }
|
||||
toolbar.findToolbarItem(R.id.action_mark_as_read)?.isVisible = episodes.any { !it.episode.seen }
|
||||
toolbar.findToolbarItem(R.id.action_mark_as_unread)?.isVisible = episodes.all { it.episode.seen }
|
||||
}
|
||||
|
||||
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
|
||||
return onActionItemClicked(item)
|
||||
}
|
||||
|
||||
private fun onActionItemClicked(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_select_all -> selectAll()
|
||||
R.id.action_select_inverse -> selectInverse()
|
||||
|
@ -1224,11 +1222,13 @@ class AnimeController :
|
|||
}
|
||||
|
||||
override fun onDestroyActionMode(mode: ActionMode) {
|
||||
binding.actionToolbar.hide()
|
||||
episodesAdapter?.mode = SelectableAdapter.Mode.SINGLE
|
||||
episodesAdapter?.clearSelection()
|
||||
selectedEpisodes.clear()
|
||||
actionMode = null
|
||||
}
|
||||
|
||||
override fun onDestroyActionToolbar() {
|
||||
updateFabVisibility()
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ class AnimelibAdapter(
|
|||
|
||||
private var boundViews = arrayListOf<View>()
|
||||
|
||||
private val isPerCategory by lazy { preferences.categorisedDisplaySettings().get() }
|
||||
private val isPerCategory by lazy { preferences.categorizedDisplaySettings().get() }
|
||||
private var currentDisplayMode = preferences.libraryDisplayMode().get()
|
||||
|
||||
init {
|
||||
|
|
|
@ -7,7 +7,6 @@ import android.view.Menu
|
|||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.core.view.isVisible
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||
|
@ -16,14 +15,12 @@ import com.google.android.material.tabs.TabLayout
|
|||
import com.jakewharton.rxrelay.BehaviorRelay
|
||||
import com.jakewharton.rxrelay.PublishRelay
|
||||
import com.tfcporciuncula.flow.Preference
|
||||
import dev.chrisbanes.insetter.applyInsetter
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.animesource.LocalAnimeSource
|
||||
import eu.kanade.tachiyomi.data.animelib.AnimelibUpdateService
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.asImmediateFlow
|
||||
import eu.kanade.tachiyomi.databinding.LibraryControllerBinding
|
||||
import eu.kanade.tachiyomi.ui.anime.AnimeController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.RootController
|
||||
|
@ -32,9 +29,11 @@ import eu.kanade.tachiyomi.ui.base.controller.TabbedController
|
|||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||
import eu.kanade.tachiyomi.ui.browse.animesource.globalsearch.GlobalAnimeSearchController
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.util.preference.asImmediateFlow
|
||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
import eu.kanade.tachiyomi.util.system.openInBrowser
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.widget.ActionModeWithToolbar
|
||||
import eu.kanade.tachiyomi.widget.EmptyView
|
||||
import eu.kanade.tachiyomi.widget.materialdialogs.QuadStateTextView
|
||||
import kotlinx.coroutines.flow.drop
|
||||
|
@ -55,7 +54,7 @@ class AnimelibController(
|
|||
) : SearchableNucleusController<LibraryControllerBinding, AnimelibPresenter>(bundle),
|
||||
RootController,
|
||||
TabbedController,
|
||||
ActionMode.Callback,
|
||||
ActionModeWithToolbar.Callback,
|
||||
ChangeAnimeCategoriesDialog.Listener,
|
||||
DeleteAnimelibAnimesDialog.Listener {
|
||||
|
||||
|
@ -67,7 +66,7 @@ class AnimelibController(
|
|||
/**
|
||||
* Action mode for selections.
|
||||
*/
|
||||
private var actionMode: ActionMode? = null
|
||||
private var actionMode: ActionModeWithToolbar? = null
|
||||
|
||||
/**
|
||||
* Currently selected animes.
|
||||
|
@ -170,12 +169,6 @@ class AnimelibController(
|
|||
override fun onViewCreated(view: View) {
|
||||
super.onViewCreated(view)
|
||||
|
||||
binding.actionToolbar.applyInsetter {
|
||||
type(navigationBars = true) {
|
||||
margin(bottom = true, horizontal = true)
|
||||
}
|
||||
}
|
||||
|
||||
adapter = AnimelibAdapter(this)
|
||||
binding.libraryPager.adapter = adapter
|
||||
binding.libraryPager.pageSelections()
|
||||
|
@ -202,7 +195,7 @@ class AnimelibController(
|
|||
is AnimelibSettingsSheet.Filter.FilterGroup -> onFilterChanged()
|
||||
is AnimelibSettingsSheet.Sort.SortGroup -> onSortChanged()
|
||||
is AnimelibSettingsSheet.Display.DisplayGroup -> {
|
||||
val delay = if (preferences.categorisedDisplaySettings().get()) 125L else 0L
|
||||
val delay = if (preferences.categorizedDisplaySettings().get()) 125L else 0L
|
||||
|
||||
Observable.timer(delay, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
|
@ -233,7 +226,6 @@ class AnimelibController(
|
|||
|
||||
override fun onDestroyView(view: View) {
|
||||
destroyActionModeIfNeeded()
|
||||
binding.actionToolbar.destroy()
|
||||
adapter?.onDestroy()
|
||||
adapter = null
|
||||
settingsSheet = null
|
||||
|
@ -377,13 +369,10 @@ class AnimelibController(
|
|||
* Creates the action mode if it's not created already.
|
||||
*/
|
||||
fun createActionModeIfNeeded() {
|
||||
if (actionMode == null) {
|
||||
actionMode = (activity as AppCompatActivity).startSupportActionMode(this)
|
||||
binding.actionToolbar.show(
|
||||
actionMode!!,
|
||||
R.menu.library_selection
|
||||
) { onActionItemClicked(it!!) }
|
||||
(activity as? MainActivity)?.showBottomNav(false)
|
||||
val activity = activity
|
||||
if (actionMode == null && activity is MainActivity) {
|
||||
actionMode = activity.startActionModeAndToolbar(this)
|
||||
activity.showBottomNav(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -455,6 +444,10 @@ class AnimelibController(
|
|||
return true
|
||||
}
|
||||
|
||||
override fun onCreateActionToolbar(menuInflater: MenuInflater, menu: Menu) {
|
||||
menuInflater.inflate(R.menu.library_selection, menu)
|
||||
}
|
||||
|
||||
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
|
||||
val count = selectedAnimes.size
|
||||
if (count == 0) {
|
||||
|
@ -462,17 +455,17 @@ class AnimelibController(
|
|||
destroyActionModeIfNeeded()
|
||||
} else {
|
||||
mode.title = count.toString()
|
||||
|
||||
binding.actionToolbar.findItem(R.id.action_download_unread)?.isVisible = selectedAnimes.any { it.source != LocalAnimeSource.ID }
|
||||
}
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onPrepareActionToolbar(toolbar: ActionModeWithToolbar, menu: Menu) {
|
||||
if (selectedAnimes.isEmpty()) return
|
||||
toolbar.findToolbarItem(R.id.action_download_unread)?.isVisible =
|
||||
selectedAnimes.any { it.source != LocalAnimeSource.ID }
|
||||
}
|
||||
|
||||
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
|
||||
return onActionItemClicked(item)
|
||||
}
|
||||
|
||||
private fun onActionItemClicked(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_move_to_category -> showChangeAnimeCategoriesDialog()
|
||||
R.id.action_download_unseen -> downloadUnseenEpisodes()
|
||||
|
@ -486,12 +479,11 @@ class AnimelibController(
|
|||
return true
|
||||
}
|
||||
|
||||
override fun onDestroyActionMode(mode: ActionMode?) {
|
||||
override fun onDestroyActionMode(mode: ActionMode) {
|
||||
// Clear all the anime selections and notify child views.
|
||||
selectedAnimes.clear()
|
||||
selectionRelay.call(AnimelibSelectionEvent.Cleared())
|
||||
|
||||
binding.actionToolbar.hide()
|
||||
(activity as? MainActivity)?.showBottomNav(true)
|
||||
|
||||
actionMode = null
|
||||
|
|
|
@ -129,7 +129,7 @@ class AnimelibPresenter(
|
|||
|
||||
val filterFnUnread: (AnimelibItem) -> Boolean = unread@{ item ->
|
||||
if (filterUnread == State.IGNORE.value) return@unread true
|
||||
val isUnread = item.anime.unread != 0
|
||||
val isUnread = item.anime.unseen != 0
|
||||
|
||||
return@unread if (filterUnread == State.INCLUDE.value) isUnread
|
||||
else !isUnread
|
||||
|
@ -212,7 +212,7 @@ class AnimelibPresenter(
|
|||
}
|
||||
|
||||
item.unreadCount = if (showUnreadBadges) {
|
||||
item.anime.unread
|
||||
item.anime.unseen
|
||||
} else {
|
||||
// Unset unread count if not enabled
|
||||
-1
|
||||
|
@ -286,10 +286,10 @@ class AnimelibPresenter(
|
|||
SortModeSetting.LAST_CHECKED -> i2.anime.last_update.compareTo(i1.anime.last_update)
|
||||
SortModeSetting.UNREAD -> when {
|
||||
// Ensure unread content comes first
|
||||
i1.anime.unread == i2.anime.unread -> 0
|
||||
i1.anime.unread == 0 -> if (sortAscending) 1 else -1
|
||||
i2.anime.unread == 0 -> if (sortAscending) -1 else 1
|
||||
else -> i1.anime.unread.compareTo(i2.anime.unread)
|
||||
i1.anime.unseen == i2.anime.unseen -> 0
|
||||
i1.anime.unseen == 0 -> if (sortAscending) 1 else -1
|
||||
i2.anime.unseen == 0 -> if (sortAscending) -1 else 1
|
||||
else -> i1.anime.unseen.compareTo(i2.anime.unseen)
|
||||
}
|
||||
SortModeSetting.TOTAL_CHAPTERS -> {
|
||||
val anime1TotalEpisode = totalEpisodeAnime[i1.anime.id!!] ?: 0
|
||||
|
@ -369,7 +369,7 @@ class AnimelibPresenter(
|
|||
*/
|
||||
private fun getAnimelibAnimesObservable(): Observable<AnimelibMap> {
|
||||
val defaultLibraryDisplayMode = preferences.libraryDisplayMode()
|
||||
val shouldSetFromCategory = preferences.categorisedDisplaySettings()
|
||||
val shouldSetFromCategory = preferences.categorizedDisplaySettings()
|
||||
return db.getAnimelibAnimes().asRxObservable()
|
||||
.map { list ->
|
||||
list.map { animelibAnime ->
|
||||
|
|
|
@ -245,7 +245,7 @@ class AnimelibSettingsSheet(
|
|||
SortDirectionSetting.DESCENDING
|
||||
}
|
||||
|
||||
if (preferences.categorisedDisplaySettings().get() && currentCategory != null && currentCategory?.id != 0) {
|
||||
if (preferences.categorizedDisplaySettings().get() && currentCategory != null && currentCategory?.id != 0) {
|
||||
currentCategory?.sortDirection = flag.flag
|
||||
|
||||
db.insertCategory(currentCategory!!).executeAsBlocking()
|
||||
|
@ -267,7 +267,7 @@ class AnimelibSettingsSheet(
|
|||
else -> throw NotImplementedError("Unknown display mode")
|
||||
}
|
||||
|
||||
if (preferences.categorisedDisplaySettings().get() && currentCategory != null && currentCategory?.id != 0) {
|
||||
if (preferences.categorizedDisplaySettings().get() && currentCategory != null && currentCategory?.id != 0) {
|
||||
currentCategory?.sortMode = flag.flag
|
||||
|
||||
db.insertCategory(currentCategory!!).executeAsBlocking()
|
||||
|
@ -304,7 +304,7 @@ class AnimelibSettingsSheet(
|
|||
|
||||
// Gets user preference of currently selected display mode at current category
|
||||
private fun getDisplayModePreference(): DisplayModeSetting {
|
||||
return if (preferences.categorisedDisplaySettings().get() && currentCategory != null && currentCategory?.id != 0) {
|
||||
return if (preferences.categorizedDisplaySettings().get() && currentCategory != null && currentCategory?.id != 0) {
|
||||
DisplayModeSetting.fromFlag(currentCategory?.displayMode)
|
||||
} else {
|
||||
preferences.libraryDisplayMode().get()
|
||||
|
@ -353,7 +353,7 @@ class AnimelibSettingsSheet(
|
|||
else -> throw NotImplementedError("Unknown display mode")
|
||||
}
|
||||
|
||||
if (preferences.categorisedDisplaySettings().get() && currentCategory != null && currentCategory?.id != 0) {
|
||||
if (preferences.categorizedDisplaySettings().get() && currentCategory != null && currentCategory?.id != 0) {
|
||||
currentCategory?.displayMode = flag.flag
|
||||
|
||||
db.insertCategory(currentCategory!!).executeAsBlocking()
|
||||
|
|
|
@ -45,7 +45,7 @@ open class BasePresenter<V> : RxPresenter<V>() {
|
|||
* @param onNext function to execute when the observable emits an item.
|
||||
* @param onError function to execute when the observable throws an error.
|
||||
*/
|
||||
fun <T> Observable<T>.subscribeFirst(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit)? = null) = compose(deliverFirst<T>()).subscribe(split(onNext, onError)).apply { add(this) }
|
||||
fun <T> Observable<T>.subscribeFirst(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit) = { _, _ -> }) = compose(deliverFirst<T>()).subscribe(split(onNext, onError)).apply { add(this) }
|
||||
|
||||
/**
|
||||
* Subscribes an observable with [deliverLatestCache] and adds it to the presenter's lifecycle
|
||||
|
@ -54,7 +54,7 @@ open class BasePresenter<V> : RxPresenter<V>() {
|
|||
* @param onNext function to execute when the observable emits an item.
|
||||
* @param onError function to execute when the observable throws an error.
|
||||
*/
|
||||
fun <T> Observable<T>.subscribeLatestCache(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit)? = null) = compose(deliverLatestCache<T>()).subscribe(split(onNext, onError)).apply { add(this) }
|
||||
fun <T> Observable<T>.subscribeLatestCache(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit) = { _, _ -> }) = compose(deliverLatestCache<T>()).subscribe(split(onNext, onError)).apply { add(this) }
|
||||
|
||||
/**
|
||||
* Subscribes an observable with [deliverLatestCache] and adds it to the presenter's lifecycle
|
||||
|
@ -72,7 +72,7 @@ open class BasePresenter<V> : RxPresenter<V>() {
|
|||
* @param onNext function to execute when the observable emits an item.
|
||||
* @param onError function to execute when the observable throws an error.
|
||||
*/
|
||||
fun <T> Observable<T>.subscribeReplay(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit)? = null) = compose(deliverReplay<T>()).subscribe(split(onNext, onError)).apply { add(this) }
|
||||
fun <T> Observable<T>.subscribeReplay(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit) = { _, _ -> }) = compose(deliverReplay<T>()).subscribe(split(onNext, onError)).apply { add(this) }
|
||||
|
||||
/**
|
||||
* Subscribes an observable with [DeliverWithView] and adds it to the presenter's lifecycle
|
||||
|
@ -81,7 +81,7 @@ open class BasePresenter<V> : RxPresenter<V>() {
|
|||
* @param onNext function to execute when the observable emits an item.
|
||||
* @param onError function to execute when the observable throws an error.
|
||||
*/
|
||||
fun <T> Observable<T>.subscribeWithView(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit)? = null) = compose(DeliverWithView<V, T>(view())).subscribe(split(onNext, onError)).apply { add(this) }
|
||||
fun <T> Observable<T>.subscribeWithView(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit) = { _, _ -> }) = compose(DeliverWithView<V, T>(view())).subscribe(split(onNext, onError)).apply { add(this) }
|
||||
|
||||
/**
|
||||
* A deliverable that only emits to the view if attached, otherwise the event is ignored.
|
||||
|
|
|
@ -13,6 +13,7 @@ import dev.chrisbanes.insetter.applyInsetter
|
|||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
|
||||
import eu.kanade.tachiyomi.databinding.ExtensionControllerBinding
|
||||
import eu.kanade.tachiyomi.extension.model.AnimeExtension
|
||||
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
||||
|
@ -56,7 +57,8 @@ open class AnimeExtensionController :
|
|||
return AnimeExtensionPresenter(activity!!)
|
||||
}
|
||||
|
||||
override fun createBinding(inflater: LayoutInflater) = ExtensionControllerBinding.inflate(inflater)
|
||||
override fun createBinding(inflater: LayoutInflater) =
|
||||
ExtensionControllerBinding.inflate(inflater)
|
||||
|
||||
override fun onViewCreated(view: View) {
|
||||
super.onViewCreated(view)
|
||||
|
@ -189,11 +191,22 @@ open class AnimeExtensionController :
|
|||
|
||||
private fun updateExtensionsList() {
|
||||
if (query.isNotBlank()) {
|
||||
val extensionNames = query.split(",")
|
||||
val queries = query.split(",")
|
||||
adapter?.updateDataSet(
|
||||
extensions.filter {
|
||||
extensionNames.any { queriedName ->
|
||||
it.extension.name.contains(queriedName, ignoreCase = true)
|
||||
queries.any { query ->
|
||||
when (it.extension) {
|
||||
is AnimeExtension.Installed -> {
|
||||
it.extension.sources.any {
|
||||
it.name.contains(query, ignoreCase = true) ||
|
||||
it.id == query.toLongOrNull() ||
|
||||
if (it is AnimeHttpSource) { it.baseUrl.contains(query, ignoreCase = true) } else false
|
||||
} || it.extension.name.contains(query, ignoreCase = true)
|
||||
}
|
||||
is AnimeExtension.Untrusted, is AnimeExtension.Available -> {
|
||||
it.extension.name.contains(query, ignoreCase = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
@ -2,11 +2,11 @@ package eu.kanade.tachiyomi.ui.browse.animeextension
|
|||
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.minusAssign
|
||||
import eu.kanade.tachiyomi.data.preference.plusAssign
|
||||
import eu.kanade.tachiyomi.extension.AnimeExtensionManager
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsController
|
||||
import eu.kanade.tachiyomi.util.preference.minusAssign
|
||||
import eu.kanade.tachiyomi.util.preference.onChange
|
||||
import eu.kanade.tachiyomi.util.preference.plusAssign
|
||||
import eu.kanade.tachiyomi.util.preference.switchPreference
|
||||
import eu.kanade.tachiyomi.util.preference.titleRes
|
||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
|
|
|
@ -63,9 +63,17 @@ open class AnimeExtensionPresenter(
|
|||
|
||||
val items = mutableListOf<AnimeExtensionItem>()
|
||||
|
||||
val updatesSorted = installed.filter { it.hasUpdate && (showNsfwSources || !it.isNsfw) }.sortedBy { it.name }
|
||||
val installedSorted = installed.filter { !it.hasUpdate && (showNsfwSources || !it.isNsfw) }.sortedWith(compareBy({ !it.isObsolete }, { it.name }))
|
||||
val untrustedSorted = untrusted.sortedBy { it.name }
|
||||
val updatesSorted = installed.filter { it.hasUpdate && (showNsfwSources || !it.isNsfw) }
|
||||
.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, { it.name }))
|
||||
|
||||
val installedSorted = installed.filter { !it.hasUpdate && (showNsfwSources || !it.isNsfw) }
|
||||
.sortedWith(
|
||||
compareBy<AnimeExtension.Installed> { !it.isObsolete }
|
||||
.thenBy(String.CASE_INSENSITIVE_ORDER) { it.name }
|
||||
)
|
||||
|
||||
val untrustedSorted = untrusted.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, { it.name }))
|
||||
|
||||
val availableSorted = available
|
||||
// Filter out already installed extensions and disabled languages
|
||||
.filter { avail ->
|
||||
|
@ -74,7 +82,7 @@ open class AnimeExtensionPresenter(
|
|||
avail.lang in activeLangs &&
|
||||
(showNsfwSources || !avail.isNsfw)
|
||||
}
|
||||
.sortedBy { it.name }
|
||||
.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, { it.name }))
|
||||
|
||||
if (updatesSorted.isNotEmpty()) {
|
||||
val header = AnimeExtensionGroupItem(context.getString(R.string.ext_updates_pending), updatesSorted.size, true)
|
||||
|
|
|
@ -26,8 +26,6 @@ import eu.kanade.tachiyomi.animesource.getPreferenceKey
|
|||
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
|
||||
import eu.kanade.tachiyomi.data.preference.EmptyPreferenceDataStore
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.minusAssign
|
||||
import eu.kanade.tachiyomi.data.preference.plusAssign
|
||||
import eu.kanade.tachiyomi.databinding.ExtensionDetailControllerBinding
|
||||
import eu.kanade.tachiyomi.extension.model.AnimeExtension
|
||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
|
@ -35,8 +33,9 @@ import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
|||
import eu.kanade.tachiyomi.ui.base.controller.openInBrowser
|
||||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||
import eu.kanade.tachiyomi.util.preference.DSL
|
||||
import eu.kanade.tachiyomi.util.preference.minusAssign
|
||||
import eu.kanade.tachiyomi.util.preference.onChange
|
||||
import eu.kanade.tachiyomi.util.preference.preferenceCategory
|
||||
import eu.kanade.tachiyomi.util.preference.plusAssign
|
||||
import eu.kanade.tachiyomi.util.preference.switchPreference
|
||||
import eu.kanade.tachiyomi.util.preference.switchSettingsPreference
|
||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
|
@ -122,11 +121,7 @@ class AnimeExtensionDetailsController(bundle: Bundle? = null) :
|
|||
.map { source -> LocaleHelper.getSourceDisplayName(source.lang, context) to source }
|
||||
.sortedWith(compareBy({ (_, source) -> !source.isEnabled() }, { (lang, _) -> lang.lowercase() }))
|
||||
.forEach { (lang, source) ->
|
||||
val preferenceBlock = {
|
||||
sourceSwitchPreference(source, LocaleHelper.getSourceDisplayName(lang, context))
|
||||
}
|
||||
|
||||
preferenceBlock()
|
||||
sourceSwitchPreference(source, lang)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,19 +130,11 @@ class AnimeExtensionDetailsController(bundle: Bundle? = null) :
|
|||
.groupBy { (it as AnimeCatalogueSource).lang }
|
||||
.toSortedMap(compareBy { LocaleHelper.getSourceDisplayName(it, context) })
|
||||
.forEach { entry ->
|
||||
val preferenceBlock = {
|
||||
entry.value
|
||||
.sortedWith(compareBy({ source -> !source.isEnabled() }, { source -> source.name.lowercase() }))
|
||||
.forEach { source ->
|
||||
sourceSwitchPreference(source, source.toString())
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
title = LocaleHelper.getSourceDisplayName(entry.key, context)
|
||||
|
||||
preferenceBlock()
|
||||
}
|
||||
entry.value
|
||||
.sortedWith(compareBy({ source -> !source.isEnabled() }, { source -> source.name.lowercase() }))
|
||||
.forEach { source ->
|
||||
sourceSwitchPreference(source, source.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,8 +20,6 @@ import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource
|
|||
import eu.kanade.tachiyomi.animesource.AnimeSource
|
||||
import eu.kanade.tachiyomi.animesource.LocalAnimeSource
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.minusAssign
|
||||
import eu.kanade.tachiyomi.data.preference.plusAssign
|
||||
import eu.kanade.tachiyomi.databinding.SourceMainControllerBinding
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.SearchableNucleusController
|
||||
|
@ -32,6 +30,8 @@ import eu.kanade.tachiyomi.ui.browse.animesource.browse.BrowseAnimeSourceControl
|
|||
import eu.kanade.tachiyomi.ui.browse.animesource.globalsearch.GlobalAnimeSearchController
|
||||
import eu.kanade.tachiyomi.ui.browse.animesource.latest.LatestUpdatesController
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.util.preference.minusAssign
|
||||
import eu.kanade.tachiyomi.util.preference.plusAssign
|
||||
import eu.kanade.tachiyomi.util.view.onAnimationsFinished
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
|
|
@ -9,10 +9,10 @@ import eu.kanade.tachiyomi.animesource.AnimeSourceManager
|
|||
import eu.kanade.tachiyomi.animesource.getPreferenceKey
|
||||
import eu.kanade.tachiyomi.animesource.icon
|
||||
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
|
||||
import eu.kanade.tachiyomi.data.preference.minusAssign
|
||||
import eu.kanade.tachiyomi.data.preference.plusAssign
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsController
|
||||
import eu.kanade.tachiyomi.util.preference.minusAssign
|
||||
import eu.kanade.tachiyomi.util.preference.onChange
|
||||
import eu.kanade.tachiyomi.util.preference.plusAssign
|
||||
import eu.kanade.tachiyomi.util.preference.switchPreferenceCategory
|
||||
import eu.kanade.tachiyomi.util.preference.titleRes
|
||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
|
@ -42,7 +42,7 @@ class AnimeSourceFilterController : SettingsController() {
|
|||
)
|
||||
|
||||
orderedLangs.forEach { lang ->
|
||||
val sources = sourcesByLang[lang].orEmpty().sortedBy { it.name.lowercase() }
|
||||
val sources = sourcesByLang[lang].orEmpty().sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, { it.name }))
|
||||
|
||||
// Create a preference group and set initial state and change listener
|
||||
switchPreferenceCategory {
|
||||
|
|
|
@ -29,7 +29,6 @@ import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
|
|||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.asImmediateFlow
|
||||
import eu.kanade.tachiyomi.databinding.SourceControllerBinding
|
||||
import eu.kanade.tachiyomi.ui.anime.AnimeController
|
||||
import eu.kanade.tachiyomi.ui.animelib.ChangeAnimeCategoriesDialog
|
||||
|
@ -41,6 +40,7 @@ import eu.kanade.tachiyomi.ui.library.setting.DisplayModeSetting
|
|||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.ui.more.MoreController
|
||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
||||
import eu.kanade.tachiyomi.util.preference.asImmediateFlow
|
||||
import eu.kanade.tachiyomi.util.system.connectivityManager
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import eu.kanade.tachiyomi.util.system.openInBrowser
|
||||
|
@ -159,13 +159,12 @@ open class BrowseAnimeSourceController(bundle: Bundle) :
|
|||
)
|
||||
filterSheet?.setFilters(presenter.filterItems)
|
||||
|
||||
// TODO: [ExtendedFloatingActionButton] hide/show methods don't work properly
|
||||
filterSheet?.setOnShowListener { actionFab?.isVisible = false }
|
||||
filterSheet?.setOnDismissListener { actionFab?.isVisible = true }
|
||||
filterSheet?.setOnShowListener { actionFab?.hide() }
|
||||
filterSheet?.setOnDismissListener { actionFab?.show() }
|
||||
|
||||
actionFab?.setOnClickListener { filterSheet?.show() }
|
||||
|
||||
actionFab?.isVisible = true
|
||||
actionFab?.show()
|
||||
}
|
||||
|
||||
override fun configureFab(fab: ExtendedFloatingActionButton) {
|
||||
|
@ -175,7 +174,7 @@ open class BrowseAnimeSourceController(bundle: Bundle) :
|
|||
fab.setIconResource(R.drawable.ic_filter_list_24dp)
|
||||
|
||||
// Controlled by initFilterSheet()
|
||||
fab.isVisible = false
|
||||
fab.hide()
|
||||
initFilterSheet()
|
||||
}
|
||||
|
||||
|
|
|
@ -209,7 +209,9 @@ open class GlobalAnimeSearchController(
|
|||
* Opens a catalogue with the given search.
|
||||
*/
|
||||
override fun onTitleClick(source: AnimeCatalogueSource) {
|
||||
presenter.preferences.lastUsedAnimeSource().set(source.id)
|
||||
if (!preferences.incognitoMode().get()) {
|
||||
preferences.lastUsedSource().set(source.id)
|
||||
}
|
||||
router.pushController(BrowseAnimeSourceController(source, presenter.query).withFadeTransaction())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import eu.davidea.flexibleadapter.items.IFlexible
|
|||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.databinding.ExtensionControllerBinding
|
||||
import eu.kanade.tachiyomi.extension.model.Extension
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||
import eu.kanade.tachiyomi.ui.browse.BrowseController
|
||||
|
@ -56,7 +57,8 @@ open class ExtensionController :
|
|||
return ExtensionPresenter(activity!!)
|
||||
}
|
||||
|
||||
override fun createBinding(inflater: LayoutInflater) = ExtensionControllerBinding.inflate(inflater)
|
||||
override fun createBinding(inflater: LayoutInflater) =
|
||||
ExtensionControllerBinding.inflate(inflater)
|
||||
|
||||
override fun onViewCreated(view: View) {
|
||||
super.onViewCreated(view)
|
||||
|
@ -189,11 +191,27 @@ open class ExtensionController :
|
|||
|
||||
private fun updateExtensionsList() {
|
||||
if (query.isNotBlank()) {
|
||||
val extensionNames = query.split(",")
|
||||
val queries = query.split(",")
|
||||
adapter?.updateDataSet(
|
||||
extensions.filter {
|
||||
extensionNames.any { queriedName ->
|
||||
it.extension.name.contains(queriedName, ignoreCase = true)
|
||||
queries.any { query ->
|
||||
when (it.extension) {
|
||||
is Extension.Available -> {
|
||||
it.extension.sources.any {
|
||||
it.name.contains(query, ignoreCase = true) ||
|
||||
it.baseUrl.contains(query, ignoreCase = true) ||
|
||||
it.id == query.toLongOrNull()
|
||||
} || it.extension.name.contains(query, ignoreCase = true)
|
||||
}
|
||||
is Extension.Installed -> {
|
||||
it.extension.sources.any {
|
||||
it.name.contains(query, ignoreCase = true) ||
|
||||
it.id == query.toLongOrNull() ||
|
||||
if (it is HttpSource) { it.baseUrl.contains(query, ignoreCase = true) } else false
|
||||
} || it.extension.name.contains(query, ignoreCase = true)
|
||||
}
|
||||
is Extension.Untrusted -> it.extension.name.contains(query, ignoreCase = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
@ -2,11 +2,11 @@ package eu.kanade.tachiyomi.ui.browse.extension
|
|||
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.minusAssign
|
||||
import eu.kanade.tachiyomi.data.preference.plusAssign
|
||||
import eu.kanade.tachiyomi.extension.ExtensionManager
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsController
|
||||
import eu.kanade.tachiyomi.util.preference.minusAssign
|
||||
import eu.kanade.tachiyomi.util.preference.onChange
|
||||
import eu.kanade.tachiyomi.util.preference.plusAssign
|
||||
import eu.kanade.tachiyomi.util.preference.switchPreference
|
||||
import eu.kanade.tachiyomi.util.preference.titleRes
|
||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
|
|
|
@ -63,9 +63,17 @@ open class ExtensionPresenter(
|
|||
|
||||
val items = mutableListOf<ExtensionItem>()
|
||||
|
||||
val updatesSorted = installed.filter { it.hasUpdate && (showNsfwSources || !it.isNsfw) }.sortedBy { it.name }
|
||||
val installedSorted = installed.filter { !it.hasUpdate && (showNsfwSources || !it.isNsfw) }.sortedWith(compareBy({ !it.isObsolete }, { it.name }))
|
||||
val untrustedSorted = untrusted.sortedBy { it.name }
|
||||
val updatesSorted = installed.filter { it.hasUpdate && (showNsfwSources || !it.isNsfw) }
|
||||
.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, { it.name }))
|
||||
|
||||
val installedSorted = installed.filter { !it.hasUpdate && (showNsfwSources || !it.isNsfw) }
|
||||
.sortedWith(
|
||||
compareBy<Extension.Installed> { !it.isObsolete }
|
||||
.thenBy(String.CASE_INSENSITIVE_ORDER) { it.name }
|
||||
)
|
||||
|
||||
val untrustedSorted = untrusted.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, { it.name }))
|
||||
|
||||
val availableSorted = available
|
||||
// Filter out already installed extensions and disabled languages
|
||||
.filter { avail ->
|
||||
|
@ -74,7 +82,7 @@ open class ExtensionPresenter(
|
|||
avail.lang in activeLangs &&
|
||||
(showNsfwSources || !avail.isNsfw)
|
||||
}
|
||||
.sortedBy { it.name }
|
||||
.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, { it.name }))
|
||||
|
||||
if (updatesSorted.isNotEmpty()) {
|
||||
val header = ExtensionGroupItem(context.getString(R.string.ext_updates_pending), updatesSorted.size, true)
|
||||
|
|
|
@ -21,8 +21,6 @@ import dev.chrisbanes.insetter.applyInsetter
|
|||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.EmptyPreferenceDataStore
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.minusAssign
|
||||
import eu.kanade.tachiyomi.data.preference.plusAssign
|
||||
import eu.kanade.tachiyomi.databinding.ExtensionDetailControllerBinding
|
||||
import eu.kanade.tachiyomi.extension.model.Extension
|
||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
|
@ -35,8 +33,9 @@ import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
|||
import eu.kanade.tachiyomi.ui.base.controller.openInBrowser
|
||||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||
import eu.kanade.tachiyomi.util.preference.DSL
|
||||
import eu.kanade.tachiyomi.util.preference.minusAssign
|
||||
import eu.kanade.tachiyomi.util.preference.onChange
|
||||
import eu.kanade.tachiyomi.util.preference.preferenceCategory
|
||||
import eu.kanade.tachiyomi.util.preference.plusAssign
|
||||
import eu.kanade.tachiyomi.util.preference.switchPreference
|
||||
import eu.kanade.tachiyomi.util.preference.switchSettingsPreference
|
||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
|
@ -122,11 +121,7 @@ class ExtensionDetailsController(bundle: Bundle? = null) :
|
|||
.map { source -> LocaleHelper.getSourceDisplayName(source.lang, context) to source }
|
||||
.sortedWith(compareBy({ (_, source) -> !source.isEnabled() }, { (lang, _) -> lang.lowercase() }))
|
||||
.forEach { (lang, source) ->
|
||||
val preferenceBlock = {
|
||||
sourceSwitchPreference(source, LocaleHelper.getSourceDisplayName(lang, context))
|
||||
}
|
||||
|
||||
preferenceBlock()
|
||||
sourceSwitchPreference(source, lang)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,19 +130,11 @@ class ExtensionDetailsController(bundle: Bundle? = null) :
|
|||
.groupBy { (it as CatalogueSource).lang }
|
||||
.toSortedMap(compareBy { LocaleHelper.getSourceDisplayName(it, context) })
|
||||
.forEach { entry ->
|
||||
val preferenceBlock = {
|
||||
entry.value
|
||||
.sortedWith(compareBy({ source -> !source.isEnabled() }, { source -> source.name.lowercase() }))
|
||||
.forEach { source ->
|
||||
sourceSwitchPreference(source, source.toString())
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
title = LocaleHelper.getSourceDisplayName(entry.key, context)
|
||||
|
||||
preferenceBlock()
|
||||
}
|
||||
entry.value
|
||||
.sortedWith(compareBy({ source -> !source.isEnabled() }, { source -> source.name.lowercase() }))
|
||||
.forEach { source ->
|
||||
sourceSwitchPreference(source, source.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,8 +17,6 @@ import eu.davidea.flexibleadapter.FlexibleAdapter
|
|||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.minusAssign
|
||||
import eu.kanade.tachiyomi.data.preference.plusAssign
|
||||
import eu.kanade.tachiyomi.databinding.SourceMainControllerBinding
|
||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
import eu.kanade.tachiyomi.source.LocalSource
|
||||
|
@ -32,6 +30,8 @@ import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController
|
|||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController
|
||||
import eu.kanade.tachiyomi.ui.browse.source.latest.LatestUpdatesController
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.util.preference.minusAssign
|
||||
import eu.kanade.tachiyomi.util.preference.plusAssign
|
||||
import eu.kanade.tachiyomi.util.view.onAnimationsFinished
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
|
|
@ -5,14 +5,14 @@ import androidx.preference.CheckBoxPreference
|
|||
import androidx.preference.PreferenceGroup
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.minusAssign
|
||||
import eu.kanade.tachiyomi.data.preference.plusAssign
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.getPreferenceKey
|
||||
import eu.kanade.tachiyomi.source.icon
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsController
|
||||
import eu.kanade.tachiyomi.util.preference.minusAssign
|
||||
import eu.kanade.tachiyomi.util.preference.onChange
|
||||
import eu.kanade.tachiyomi.util.preference.plusAssign
|
||||
import eu.kanade.tachiyomi.util.preference.switchPreferenceCategory
|
||||
import eu.kanade.tachiyomi.util.preference.titleRes
|
||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
|
@ -42,7 +42,7 @@ class SourceFilterController : SettingsController() {
|
|||
)
|
||||
|
||||
orderedLangs.forEach { lang ->
|
||||
val sources = sourcesByLang[lang].orEmpty().sortedBy { it.name.lowercase() }
|
||||
val sources = sourcesByLang[lang].orEmpty().sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, { it.name }))
|
||||
|
||||
// Create a preference group and set initial state and change listener
|
||||
switchPreferenceCategory {
|
||||
|
|
|
@ -24,7 +24,6 @@ import eu.kanade.tachiyomi.R
|
|||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.asImmediateFlow
|
||||
import eu.kanade.tachiyomi.databinding.SourceControllerBinding
|
||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
import eu.kanade.tachiyomi.source.LocalSource
|
||||
|
@ -41,6 +40,7 @@ import eu.kanade.tachiyomi.ui.main.MainActivity
|
|||
import eu.kanade.tachiyomi.ui.manga.MangaController
|
||||
import eu.kanade.tachiyomi.ui.more.MoreController
|
||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
||||
import eu.kanade.tachiyomi.util.preference.asImmediateFlow
|
||||
import eu.kanade.tachiyomi.util.system.connectivityManager
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import eu.kanade.tachiyomi.util.system.openInBrowser
|
||||
|
@ -159,13 +159,12 @@ open class BrowseSourceController(bundle: Bundle) :
|
|||
)
|
||||
filterSheet?.setFilters(presenter.filterItems)
|
||||
|
||||
// TODO: [ExtendedFloatingActionButton] hide/show methods don't work properly
|
||||
filterSheet?.setOnShowListener { actionFab?.isVisible = false }
|
||||
filterSheet?.setOnDismissListener { actionFab?.isVisible = true }
|
||||
filterSheet?.setOnShowListener { actionFab?.hide() }
|
||||
filterSheet?.setOnDismissListener { actionFab?.show() }
|
||||
|
||||
actionFab?.setOnClickListener { filterSheet?.show() }
|
||||
|
||||
actionFab?.isVisible = true
|
||||
actionFab?.show()
|
||||
}
|
||||
|
||||
override fun configureFab(fab: ExtendedFloatingActionButton) {
|
||||
|
@ -175,7 +174,7 @@ open class BrowseSourceController(bundle: Bundle) :
|
|||
fab.setIconResource(R.drawable.ic_filter_list_24dp)
|
||||
|
||||
// Controlled by initFilterSheet()
|
||||
fab.isVisible = false
|
||||
fab.hide()
|
||||
initFilterSheet()
|
||||
}
|
||||
|
||||
|
|
|
@ -209,7 +209,9 @@ open class GlobalSearchController(
|
|||
* Opens a catalogue with the given search.
|
||||
*/
|
||||
override fun onTitleClick(source: CatalogueSource) {
|
||||
presenter.preferences.lastUsedSource().set(source.id)
|
||||
if (!preferences.incognitoMode().get()) {
|
||||
preferences.lastUsedSource().set(source.id)
|
||||
}
|
||||
router.pushController(BrowseSourceController(source, presenter.query).withFadeTransaction())
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue