mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-11-10 04:01:58 +01:00
Switch to different ktlint plugin
Should be better at incremental builds. To format, run `./gradlew ktlintFormat`.
This commit is contained in:
parent
772db51593
commit
d29b7c4e57
69 changed files with 628 additions and 291 deletions
|
@ -1,5 +1,4 @@
|
||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
import org.jmailen.gradle.kotlinter.tasks.LintTask
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("com.android.application")
|
id("com.android.application")
|
||||||
|
@ -104,7 +103,8 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
packaging {
|
packaging {
|
||||||
resources.excludes.addAll(listOf(
|
resources.excludes.addAll(
|
||||||
|
listOf(
|
||||||
"META-INF/DEPENDENCIES",
|
"META-INF/DEPENDENCIES",
|
||||||
"LICENSE.txt",
|
"LICENSE.txt",
|
||||||
"META-INF/LICENSE",
|
"META-INF/LICENSE",
|
||||||
|
@ -112,7 +112,8 @@ android {
|
||||||
"META-INF/README.md",
|
"META-INF/README.md",
|
||||||
"META-INF/NOTICE",
|
"META-INF/NOTICE",
|
||||||
"META-INF/*.kotlin_module",
|
"META-INF/*.kotlin_module",
|
||||||
))
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
dependenciesInfo {
|
dependenciesInfo {
|
||||||
|
@ -264,7 +265,9 @@ androidComponents {
|
||||||
beforeVariants { variantBuilder ->
|
beforeVariants { variantBuilder ->
|
||||||
// Disables standardBenchmark
|
// Disables standardBenchmark
|
||||||
if (variantBuilder.buildType == "benchmark") {
|
if (variantBuilder.buildType == "benchmark") {
|
||||||
variantBuilder.enable = variantBuilder.productFlavors.containsAll(listOf("default" to "dev"))
|
variantBuilder.enable = variantBuilder.productFlavors.containsAll(
|
||||||
|
listOf("default" to "dev"),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onVariants(selector().withFlavor("default" to "standard")) {
|
onVariants(selector().withFlavor("default" to "standard")) {
|
||||||
|
@ -275,10 +278,6 @@ androidComponents {
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
withType<LintTask>().configureEach {
|
|
||||||
exclude { it.file.path.contains("generated[\\\\/]".toRegex()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
// See https://kotlinlang.org/docs/reference/experimental.html#experimental-status-of-experimental-api(-markers)
|
// See https://kotlinlang.org/docs/reference/experimental.html#experimental-status-of-experimental-api(-markers)
|
||||||
withType<KotlinCompile> {
|
withType<KotlinCompile> {
|
||||||
kotlinOptions.freeCompilerArgs += listOf(
|
kotlinOptions.freeCompilerArgs += listOf(
|
||||||
|
@ -303,12 +302,12 @@ tasks {
|
||||||
kotlinOptions.freeCompilerArgs += listOf(
|
kotlinOptions.freeCompilerArgs += listOf(
|
||||||
"-P",
|
"-P",
|
||||||
"plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" +
|
"plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" +
|
||||||
project.buildDir.absolutePath + "/compose_metrics"
|
project.buildDir.absolutePath + "/compose_metrics",
|
||||||
)
|
)
|
||||||
kotlinOptions.freeCompilerArgs += listOf(
|
kotlinOptions.freeCompilerArgs += listOf(
|
||||||
"-P",
|
"-P",
|
||||||
"plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" +
|
"plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" +
|
||||||
project.buildDir.absolutePath + "/compose_metrics"
|
project.buildDir.absolutePath + "/compose_metrics",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,7 +162,7 @@ fun CategoryDeleteDialog(
|
||||||
TextButton(onClick = {
|
TextButton(onClick = {
|
||||||
onDelete()
|
onDelete()
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
},) {
|
}) {
|
||||||
Text(text = stringResource(R.string.action_ok))
|
Text(text = stringResource(R.string.action_ok))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -217,7 +217,7 @@ fun ChangeCategoryDialog(
|
||||||
tachiyomi.presentation.core.components.material.TextButton(onClick = {
|
tachiyomi.presentation.core.components.material.TextButton(onClick = {
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
onEditCategories()
|
onEditCategories()
|
||||||
},) {
|
}) {
|
||||||
Text(text = stringResource(R.string.action_edit))
|
Text(text = stringResource(R.string.action_edit))
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
|
|
@ -61,7 +61,7 @@ fun HistoryDeleteDialog(
|
||||||
TextButton(onClick = {
|
TextButton(onClick = {
|
||||||
onDelete(removeEverything)
|
onDelete(removeEverything)
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
},) {
|
}) {
|
||||||
Text(text = stringResource(R.string.action_remove))
|
Text(text = stringResource(R.string.action_remove))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -90,7 +90,7 @@ fun HistoryDeleteAllDialog(
|
||||||
TextButton(onClick = {
|
TextButton(onClick = {
|
||||||
onDelete()
|
onDelete()
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
},) {
|
}) {
|
||||||
Text(text = stringResource(R.string.action_ok))
|
Text(text = stringResource(R.string.action_ok))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -91,7 +91,7 @@ fun SetIntervalDialog(
|
||||||
TextButton(onClick = {
|
TextButton(onClick = {
|
||||||
onValueChanged(selectedInterval)
|
onValueChanged(selectedInterval)
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
},) {
|
}) {
|
||||||
Text(text = stringResource(R.string.action_ok))
|
Text(text = stringResource(R.string.action_ok))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,7 +4,6 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import eu.kanade.presentation.more.settings.Preference.PreferenceItem
|
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.track.TrackService
|
import eu.kanade.tachiyomi.data.track.TrackService
|
||||||
import tachiyomi.core.preference.Preference as PreferenceData
|
import tachiyomi.core.preference.Preference as PreferenceData
|
|
@ -21,7 +21,7 @@ fun UpdatesDeleteConfirmationDialog(
|
||||||
TextButton(onClick = {
|
TextButton(onClick = {
|
||||||
onConfirm()
|
onConfirm()
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
},) {
|
}) {
|
||||||
Text(text = stringResource(R.string.action_ok))
|
Text(text = stringResource(R.string.action_ok))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -64,7 +64,7 @@ fun SourceFilterDialog(
|
||||||
Button(onClick = {
|
Button(onClick = {
|
||||||
onFilter()
|
onFilter()
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
},) {
|
}) {
|
||||||
Text(stringResource(R.string.action_filter))
|
Text(stringResource(R.string.action_filter))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -271,7 +271,10 @@ private data class TrackStatusSelectorScreen(
|
||||||
selection = state.selection,
|
selection = state.selection,
|
||||||
onSelectionChange = sm::setSelection,
|
onSelectionChange = sm::setSelection,
|
||||||
selections = remember { sm.getSelections() },
|
selections = remember { sm.getSelections() },
|
||||||
onConfirm = { sm.setStatus(); navigator.pop() },
|
onConfirm = {
|
||||||
|
sm.setStatus()
|
||||||
|
navigator.pop()
|
||||||
|
},
|
||||||
onDismissRequest = navigator::pop,
|
onDismissRequest = navigator::pop,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -322,7 +325,10 @@ private data class TrackChapterSelectorScreen(
|
||||||
selection = state.selection,
|
selection = state.selection,
|
||||||
onSelectionChange = sm::setSelection,
|
onSelectionChange = sm::setSelection,
|
||||||
range = remember { sm.getRange() },
|
range = remember { sm.getRange() },
|
||||||
onConfirm = { sm.setChapter(); navigator.pop() },
|
onConfirm = {
|
||||||
|
sm.setChapter()
|
||||||
|
navigator.pop()
|
||||||
|
},
|
||||||
onDismissRequest = navigator::pop,
|
onDismissRequest = navigator::pop,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -378,7 +384,10 @@ private data class TrackScoreSelectorScreen(
|
||||||
selection = state.selection,
|
selection = state.selection,
|
||||||
onSelectionChange = sm::setSelection,
|
onSelectionChange = sm::setSelection,
|
||||||
selections = remember { sm.getSelections() },
|
selections = remember { sm.getSelections() },
|
||||||
onConfirm = { sm.setScore(); navigator.pop() },
|
onConfirm = {
|
||||||
|
sm.setScore()
|
||||||
|
navigator.pop()
|
||||||
|
},
|
||||||
onDismissRequest = navigator::pop,
|
onDismissRequest = navigator::pop,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -495,7 +504,10 @@ private data class TrackDateSelectorScreen(
|
||||||
},
|
},
|
||||||
initialSelectedDateMillis = sm.initialSelection,
|
initialSelectedDateMillis = sm.initialSelection,
|
||||||
selectableDates = selectableDates,
|
selectableDates = selectableDates,
|
||||||
onConfirm = { sm.setDate(it); navigator.pop() },
|
onConfirm = {
|
||||||
|
sm.setDate(it)
|
||||||
|
navigator.pop()
|
||||||
|
},
|
||||||
onRemove = { sm.confirmRemoveDate(navigator) }.takeIf { canRemove },
|
onRemove = { sm.confirmRemoveDate(navigator) }.takeIf { canRemove },
|
||||||
onDismissRequest = navigator::pop,
|
onDismissRequest = navigator::pop,
|
||||||
)
|
)
|
||||||
|
@ -584,7 +596,10 @@ private data class TrackDateRemoverScreen(
|
||||||
Text(text = stringResource(android.R.string.cancel))
|
Text(text = stringResource(android.R.string.cancel))
|
||||||
}
|
}
|
||||||
FilledTonalButton(
|
FilledTonalButton(
|
||||||
onClick = { sm.removeDate(); navigator.popUntil { it is TrackInfoDialogHomeScreen } },
|
onClick = {
|
||||||
|
sm.removeDate()
|
||||||
|
navigator.popUntil { it is TrackInfoDialogHomeScreen }
|
||||||
|
},
|
||||||
colors = ButtonDefaults.filledTonalButtonColors(
|
colors = ButtonDefaults.filledTonalButtonColors(
|
||||||
containerColor = MaterialTheme.colorScheme.errorContainer,
|
containerColor = MaterialTheme.colorScheme.errorContainer,
|
||||||
contentColor = MaterialTheme.colorScheme.onErrorContainer,
|
contentColor = MaterialTheme.colorScheme.onErrorContainer,
|
||||||
|
@ -646,7 +661,10 @@ data class TrackServiceSearchScreen(
|
||||||
queryResult = state.queryResult,
|
queryResult = state.queryResult,
|
||||||
selected = state.selected,
|
selected = state.selected,
|
||||||
onSelectedChange = sm::updateSelection,
|
onSelectedChange = sm::updateSelection,
|
||||||
onConfirmSelection = { sm.registerTracking(state.selected!!); navigator.pop() },
|
onConfirmSelection = {
|
||||||
|
sm.registerTracking(state.selected!!)
|
||||||
|
navigator.pop()
|
||||||
|
},
|
||||||
onDismissRequest = navigator::pop,
|
onDismissRequest = navigator::pop,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ plugins {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(androidxLibs.gradle)
|
implementation(androidxLibs.gradle)
|
||||||
implementation(kotlinLibs.gradle)
|
implementation(kotlinLibs.gradle)
|
||||||
implementation(libs.kotlinter)
|
implementation(libs.ktlint)
|
||||||
implementation(gradleApi())
|
implementation(gradleApi())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,14 @@
|
||||||
import org.jmailen.gradle.kotlinter.KotlinterExtension
|
import org.jlleitschuh.gradle.ktlint.KtlintExtension
|
||||||
import org.jmailen.gradle.kotlinter.KotlinterPlugin
|
import org.jlleitschuh.gradle.ktlint.KtlintPlugin
|
||||||
|
|
||||||
apply<KotlinterPlugin>()
|
apply<KtlintPlugin>()
|
||||||
|
|
||||||
extensions.configure<KotlinterExtension>("kotlinter") {
|
extensions.configure<KtlintExtension>("ktlint") {
|
||||||
experimentalRules = true
|
version.set("0.50.0")
|
||||||
|
android.set(true)
|
||||||
|
enableExperimentalRules.set(true)
|
||||||
|
|
||||||
disabledRules = arrayOf(
|
filter {
|
||||||
"experimental:argument-list-wrapping", // Doesn't play well with Android Studio
|
exclude("**/generated/**")
|
||||||
"filename", // Often broken to give a more general name
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks {
|
|
||||||
named<DefaultTask>("preBuild").configure {
|
|
||||||
if (!System.getenv("CI").toBoolean())
|
|
||||||
dependsOn("formatKotlin")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ android {
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
consumerProguardFiles("consumer-rules.pro")
|
consumerProguardFiles("consumer-rules.pro")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
|
@ -39,7 +39,9 @@ class NetworkHelper(
|
||||||
builder.addNetworkInterceptor(httpLoggingInterceptor)
|
builder.addNetworkInterceptor(httpLoggingInterceptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.addInterceptor(CloudflareInterceptor(context, cookieJar, ::defaultUserAgentProvider))
|
builder.addInterceptor(
|
||||||
|
CloudflareInterceptor(context, cookieJar, ::defaultUserAgentProvider),
|
||||||
|
)
|
||||||
|
|
||||||
when (preferences.dohProvider().get()) {
|
when (preferences.dohProvider().get()) {
|
||||||
PREF_DOH_CLOUDFLARE -> builder.dohCloudflare()
|
PREF_DOH_CLOUDFLARE -> builder.dohCloudflare()
|
||||||
|
|
|
@ -17,6 +17,9 @@ class NetworkPreferences(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun defaultUserAgent(): Preference<String> {
|
fun defaultUserAgent(): Preference<String> {
|
||||||
return preferenceStore.getString("default_user_agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/114.0")
|
return preferenceStore.getString(
|
||||||
|
"default_user_agent",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/114.0",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,10 @@ import okio.Source
|
||||||
import okio.buffer
|
import okio.buffer
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class ProgressResponseBody(private val responseBody: ResponseBody, private val progressListener: ProgressListener) : ResponseBody() {
|
class ProgressResponseBody(
|
||||||
|
private val responseBody: ResponseBody,
|
||||||
|
private val progressListener: ProgressListener,
|
||||||
|
) : ResponseBody() {
|
||||||
|
|
||||||
private val bufferedSource: BufferedSource by lazy {
|
private val bufferedSource: BufferedSource by lazy {
|
||||||
source(responseBody.source()).buffer()
|
source(responseBody.source()).buffer()
|
||||||
|
@ -36,7 +39,11 @@ class ProgressResponseBody(private val responseBody: ResponseBody, private val p
|
||||||
val bytesRead = super.read(sink, byteCount)
|
val bytesRead = super.read(sink, byteCount)
|
||||||
// read() returns the number of bytes read, or -1 if this source is exhausted.
|
// read() returns the number of bytes read, or -1 if this source is exhausted.
|
||||||
totalBytesRead += if (bytesRead != -1L) bytesRead else 0
|
totalBytesRead += if (bytesRead != -1L) bytesRead else 0
|
||||||
progressListener.update(totalBytesRead, responseBody.contentLength(), bytesRead == -1L)
|
progressListener.update(
|
||||||
|
totalBytesRead,
|
||||||
|
responseBody.contentLength(),
|
||||||
|
bytesRead == -1L,
|
||||||
|
)
|
||||||
return bytesRead
|
return bytesRead
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,11 @@ class CloudflareInterceptor(
|
||||||
return response.code in ERROR_CODES && response.header("Server") in SERVER_CHECK
|
return response.code in ERROR_CODES && response.header("Server") in SERVER_CHECK
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun intercept(chain: Interceptor.Chain, request: Request, response: Response): Response {
|
override fun intercept(
|
||||||
|
chain: Interceptor.Chain,
|
||||||
|
request: Request,
|
||||||
|
response: Response,
|
||||||
|
): Response {
|
||||||
try {
|
try {
|
||||||
response.close()
|
response.close()
|
||||||
cookieManager.remove(request.url, COOKIE_NAMES, 0)
|
cookieManager.remove(request.url, COOKIE_NAMES, 0)
|
||||||
|
|
|
@ -33,7 +33,9 @@ fun OkHttpClient.Builder.rateLimitHost(
|
||||||
permits: Int,
|
permits: Int,
|
||||||
period: Long = 1,
|
period: Long = 1,
|
||||||
unit: TimeUnit = TimeUnit.SECONDS,
|
unit: TimeUnit = TimeUnit.SECONDS,
|
||||||
) = addInterceptor(RateLimitInterceptor(httpUrl.host, permits, period.toDuration(unit.toDurationUnit())))
|
) = addInterceptor(
|
||||||
|
RateLimitInterceptor(httpUrl.host, permits, period.toDuration(unit.toDurationUnit())),
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An OkHttp interceptor that handles given url host's rate limiting.
|
* An OkHttp interceptor that handles given url host's rate limiting.
|
||||||
|
@ -69,8 +71,5 @@ fun OkHttpClient.Builder.rateLimitHost(
|
||||||
* @param permits [Int] Number of requests allowed within a period of units.
|
* @param permits [Int] Number of requests allowed within a period of units.
|
||||||
* @param period [Duration] The limiting duration. Defaults to 1.seconds.
|
* @param period [Duration] The limiting duration. Defaults to 1.seconds.
|
||||||
*/
|
*/
|
||||||
fun OkHttpClient.Builder.rateLimitHost(
|
fun OkHttpClient.Builder.rateLimitHost(url: String, permits: Int, period: Duration = 1.seconds) =
|
||||||
url: String,
|
addInterceptor(RateLimitInterceptor(url.toHttpUrlOrNull()?.host, permits, period))
|
||||||
permits: Int,
|
|
||||||
period: Duration = 1.seconds,
|
|
||||||
) = addInterceptor(RateLimitInterceptor(url.toHttpUrlOrNull()?.host, permits, period))
|
|
||||||
|
|
|
@ -10,7 +10,11 @@ import androidx.annotation.StringRes
|
||||||
* @param resource the text resource.
|
* @param resource the text resource.
|
||||||
* @param duration the duration of the toast. Defaults to short.
|
* @param duration the duration of the toast. Defaults to short.
|
||||||
*/
|
*/
|
||||||
fun Context.toast(@StringRes resource: Int, duration: Int = Toast.LENGTH_SHORT, block: (Toast) -> Unit = {}): Toast {
|
fun Context.toast(
|
||||||
|
@StringRes resource: Int,
|
||||||
|
duration: Int = Toast.LENGTH_SHORT,
|
||||||
|
block: (Toast) -> Unit = {},
|
||||||
|
): Toast {
|
||||||
return toast(getString(resource), duration, block)
|
return toast(getString(resource), duration, block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +24,11 @@ fun Context.toast(@StringRes resource: Int, duration: Int = Toast.LENGTH_SHORT,
|
||||||
* @param text the text to display.
|
* @param text the text to display.
|
||||||
* @param duration the duration of the toast. Defaults to short.
|
* @param duration the duration of the toast. Defaults to short.
|
||||||
*/
|
*/
|
||||||
fun Context.toast(text: String?, duration: Int = Toast.LENGTH_SHORT, block: (Toast) -> Unit = {}): Toast {
|
fun Context.toast(
|
||||||
|
text: String?,
|
||||||
|
duration: Int = Toast.LENGTH_SHORT,
|
||||||
|
block: (Toast) -> Unit = {},
|
||||||
|
): Toast {
|
||||||
return Toast.makeText(applicationContext, text.orEmpty(), duration).also {
|
return Toast.makeText(applicationContext, text.orEmpty(), duration).also {
|
||||||
block(it)
|
block(it)
|
||||||
it.show()
|
it.show()
|
||||||
|
|
|
@ -47,10 +47,7 @@ abstract class WebViewClientCompat : WebViewClient() {
|
||||||
return shouldInterceptRequestCompat(view, request.url.toString())
|
return shouldInterceptRequestCompat(view, request.url.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
final override fun shouldInterceptRequest(
|
final override fun shouldInterceptRequest(view: WebView, url: String): WebResourceResponse? {
|
||||||
view: WebView,
|
|
||||||
url: String,
|
|
||||||
): WebResourceResponse? {
|
|
||||||
return shouldInterceptRequestCompat(view, url)
|
return shouldInterceptRequestCompat(view, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,11 @@ sealed class AndroidPreference<T>(
|
||||||
key: String,
|
key: String,
|
||||||
defaultValue: String,
|
defaultValue: String,
|
||||||
) : AndroidPreference<String>(preferences, keyFlow, key, defaultValue) {
|
) : AndroidPreference<String>(preferences, keyFlow, key, defaultValue) {
|
||||||
override fun read(preferences: SharedPreferences, key: String, defaultValue: String): String {
|
override fun read(
|
||||||
|
preferences: SharedPreferences,
|
||||||
|
key: String,
|
||||||
|
defaultValue: String,
|
||||||
|
): String {
|
||||||
return preferences.getString(key, defaultValue) ?: defaultValue
|
return preferences.getString(key, defaultValue) ?: defaultValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +132,11 @@ sealed class AndroidPreference<T>(
|
||||||
key: String,
|
key: String,
|
||||||
defaultValue: Boolean,
|
defaultValue: Boolean,
|
||||||
) : AndroidPreference<Boolean>(preferences, keyFlow, key, defaultValue) {
|
) : AndroidPreference<Boolean>(preferences, keyFlow, key, defaultValue) {
|
||||||
override fun read(preferences: SharedPreferences, key: String, defaultValue: Boolean): Boolean {
|
override fun read(
|
||||||
|
preferences: SharedPreferences,
|
||||||
|
key: String,
|
||||||
|
defaultValue: Boolean,
|
||||||
|
): Boolean {
|
||||||
return preferences.getBoolean(key, defaultValue)
|
return preferences.getBoolean(key, defaultValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +151,11 @@ sealed class AndroidPreference<T>(
|
||||||
key: String,
|
key: String,
|
||||||
defaultValue: Set<String>,
|
defaultValue: Set<String>,
|
||||||
) : AndroidPreference<Set<String>>(preferences, keyFlow, key, defaultValue) {
|
) : AndroidPreference<Set<String>>(preferences, keyFlow, key, defaultValue) {
|
||||||
override fun read(preferences: SharedPreferences, key: String, defaultValue: Set<String>): Set<String> {
|
override fun read(
|
||||||
|
preferences: SharedPreferences,
|
||||||
|
key: String,
|
||||||
|
defaultValue: Set<String>,
|
||||||
|
): Set<String> {
|
||||||
return preferences.getStringSet(key, defaultValue) ?: defaultValue
|
return preferences.getStringSet(key, defaultValue) ?: defaultValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,11 @@ class AndroidPreferenceStore(
|
||||||
|
|
||||||
private val SharedPreferences.keyFlow
|
private val SharedPreferences.keyFlow
|
||||||
get() = callbackFlow {
|
get() = callbackFlow {
|
||||||
val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key: String? -> trySend(key) }
|
val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key: String? ->
|
||||||
|
trySend(
|
||||||
|
key,
|
||||||
|
)
|
||||||
|
}
|
||||||
registerOnSharedPreferenceChangeListener(listener)
|
registerOnSharedPreferenceChangeListener(listener)
|
||||||
awaitClose {
|
awaitClose {
|
||||||
unregisterOnSharedPreferenceChangeListener(listener)
|
unregisterOnSharedPreferenceChangeListener(listener)
|
||||||
|
|
|
@ -23,7 +23,9 @@ interface Preference<T> {
|
||||||
fun stateIn(scope: CoroutineScope): StateFlow<T>
|
fun stateIn(scope: CoroutineScope): StateFlow<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <reified T, R : T> Preference<T>.getAndSet(crossinline block: (T) -> R) = set(block(get()))
|
inline fun <reified T, R : T> Preference<T>.getAndSet(crossinline block: (T) -> R) = set(
|
||||||
|
block(get()),
|
||||||
|
)
|
||||||
|
|
||||||
operator fun <T> Preference<Set<T>>.plusAssign(item: T) {
|
operator fun <T> Preference<Set<T>>.plusAssign(item: T) {
|
||||||
set(get() + item)
|
set(get() + item)
|
||||||
|
|
|
@ -52,9 +52,15 @@ fun CoroutineScope.launchIO(block: suspend CoroutineScope.() -> Unit): Job =
|
||||||
fun CoroutineScope.launchNonCancellable(block: suspend CoroutineScope.() -> Unit): Job =
|
fun CoroutineScope.launchNonCancellable(block: suspend CoroutineScope.() -> Unit): Job =
|
||||||
launchIO { withContext(NonCancellable, block) }
|
launchIO { withContext(NonCancellable, block) }
|
||||||
|
|
||||||
suspend fun <T> withUIContext(block: suspend CoroutineScope.() -> T) = withContext(Dispatchers.Main, block)
|
suspend fun <T> withUIContext(block: suspend CoroutineScope.() -> T) = withContext(
|
||||||
|
Dispatchers.Main,
|
||||||
|
block,
|
||||||
|
)
|
||||||
|
|
||||||
suspend fun <T> withIOContext(block: suspend CoroutineScope.() -> T) = withContext(Dispatchers.IO, block)
|
suspend fun <T> withIOContext(block: suspend CoroutineScope.() -> T) = withContext(
|
||||||
|
Dispatchers.IO,
|
||||||
|
block,
|
||||||
|
)
|
||||||
|
|
||||||
suspend fun <T> withNonCancellableContext(block: suspend CoroutineScope.() -> T) =
|
suspend fun <T> withNonCancellableContext(block: suspend CoroutineScope.() -> T) =
|
||||||
withContext(NonCancellable, block)
|
withContext(NonCancellable, block)
|
||||||
|
|
|
@ -11,13 +11,14 @@ object DateColumnAdapter : ColumnAdapter<Date, Long> {
|
||||||
|
|
||||||
private const val LIST_OF_STRINGS_SEPARATOR = ", "
|
private const val LIST_OF_STRINGS_SEPARATOR = ", "
|
||||||
object StringListColumnAdapter : ColumnAdapter<List<String>, String> {
|
object StringListColumnAdapter : ColumnAdapter<List<String>, String> {
|
||||||
override fun decode(databaseValue: String) =
|
override fun decode(databaseValue: String) = if (databaseValue.isEmpty()) {
|
||||||
if (databaseValue.isEmpty()) {
|
|
||||||
emptyList()
|
emptyList()
|
||||||
} else {
|
} else {
|
||||||
databaseValue.split(LIST_OF_STRINGS_SEPARATOR)
|
databaseValue.split(LIST_OF_STRINGS_SEPARATOR)
|
||||||
}
|
}
|
||||||
override fun encode(value: List<String>) = value.joinToString(separator = LIST_OF_STRINGS_SEPARATOR)
|
override fun encode(value: List<String>) = value.joinToString(
|
||||||
|
separator = LIST_OF_STRINGS_SEPARATOR,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
object UpdateStrategyColumnAdapter : ColumnAdapter<UpdateStrategy, Long> {
|
object UpdateStrategyColumnAdapter : ColumnAdapter<UpdateStrategy, Long> {
|
||||||
|
|
|
@ -2,7 +2,21 @@ package tachiyomi.data.chapter
|
||||||
|
|
||||||
import tachiyomi.domain.chapter.model.Chapter
|
import tachiyomi.domain.chapter.model.Chapter
|
||||||
|
|
||||||
val chapterMapper: (Long, Long, String, String, String?, Boolean, Boolean, Long, Double, Long, Long, Long, Long) -> Chapter =
|
val chapterMapper: (
|
||||||
|
Long,
|
||||||
|
Long,
|
||||||
|
String,
|
||||||
|
String,
|
||||||
|
String?,
|
||||||
|
Boolean,
|
||||||
|
Boolean,
|
||||||
|
Long,
|
||||||
|
Double,
|
||||||
|
Long,
|
||||||
|
Long,
|
||||||
|
Long,
|
||||||
|
Long,
|
||||||
|
) -> Chapter =
|
||||||
{ id, mangaId, url, name, scanlator, read, bookmark, lastPageRead, chapterNumber, sourceOrder, dateFetch, dateUpload, lastModifiedAt ->
|
{ id, mangaId, url, name, scanlator, read, bookmark, lastPageRead, chapterNumber, sourceOrder, dateFetch, dateUpload, lastModifiedAt ->
|
||||||
Chapter(
|
Chapter(
|
||||||
id = id,
|
id = id,
|
||||||
|
|
|
@ -81,7 +81,12 @@ class ChapterRepositoryImpl(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getBookmarkedChaptersByMangaId(mangaId: Long): List<Chapter> {
|
override suspend fun getBookmarkedChaptersByMangaId(mangaId: Long): List<Chapter> {
|
||||||
return handler.awaitList { chaptersQueries.getBookmarkedChaptersByMangaId(mangaId, chapterMapper) }
|
return handler.awaitList {
|
||||||
|
chaptersQueries.getBookmarkedChaptersByMangaId(
|
||||||
|
mangaId,
|
||||||
|
chapterMapper,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getChapterById(id: Long): Chapter? {
|
override suspend fun getChapterById(id: Long): Chapter? {
|
||||||
|
@ -89,10 +94,21 @@ class ChapterRepositoryImpl(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getChapterByMangaIdAsFlow(mangaId: Long): Flow<List<Chapter>> {
|
override suspend fun getChapterByMangaIdAsFlow(mangaId: Long): Flow<List<Chapter>> {
|
||||||
return handler.subscribeToList { chaptersQueries.getChaptersByMangaId(mangaId, chapterMapper) }
|
return handler.subscribeToList {
|
||||||
|
chaptersQueries.getChaptersByMangaId(
|
||||||
|
mangaId,
|
||||||
|
chapterMapper,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getChapterByUrlAndMangaId(url: String, mangaId: Long): Chapter? {
|
override suspend fun getChapterByUrlAndMangaId(url: String, mangaId: Long): Chapter? {
|
||||||
return handler.awaitOneOrNull { chaptersQueries.getChapterByUrlAndMangaId(url, mangaId, chapterMapper) }
|
return handler.awaitOneOrNull {
|
||||||
|
chaptersQueries.getChapterByUrlAndMangaId(
|
||||||
|
url,
|
||||||
|
mangaId,
|
||||||
|
chapterMapper,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,19 @@ val historyMapper: (Long, Long, Date?, Long) -> History = { id, chapterId, readA
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val historyWithRelationsMapper: (Long, Long, Long, String, String?, Long, Boolean, Long, Double, Date?, Long) -> HistoryWithRelations = {
|
val historyWithRelationsMapper: (
|
||||||
|
Long,
|
||||||
|
Long,
|
||||||
|
Long,
|
||||||
|
String,
|
||||||
|
String?,
|
||||||
|
Long,
|
||||||
|
Boolean,
|
||||||
|
Long,
|
||||||
|
Double,
|
||||||
|
Date?,
|
||||||
|
Long,
|
||||||
|
) -> HistoryWithRelations = {
|
||||||
historyId, mangaId, chapterId, title, thumbnailUrl, sourceId, isFavorite, coverLastModified, chapterNumber, readAt, readDuration ->
|
historyId, mangaId, chapterId, title, thumbnailUrl, sourceId, isFavorite, coverLastModified, chapterNumber, readAt, readDuration ->
|
||||||
HistoryWithRelations(
|
HistoryWithRelations(
|
||||||
id = historyId,
|
id = historyId,
|
||||||
|
|
|
@ -4,7 +4,30 @@ import eu.kanade.tachiyomi.source.model.UpdateStrategy
|
||||||
import tachiyomi.domain.library.model.LibraryManga
|
import tachiyomi.domain.library.model.LibraryManga
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
|
|
||||||
val mangaMapper: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long, Long, Long?) -> Manga =
|
val mangaMapper: (
|
||||||
|
Long,
|
||||||
|
Long,
|
||||||
|
String,
|
||||||
|
String?,
|
||||||
|
String?,
|
||||||
|
String?,
|
||||||
|
List<String>?,
|
||||||
|
String,
|
||||||
|
Long,
|
||||||
|
String?,
|
||||||
|
Boolean,
|
||||||
|
Long?,
|
||||||
|
Long?,
|
||||||
|
Boolean,
|
||||||
|
Long,
|
||||||
|
Long,
|
||||||
|
Long,
|
||||||
|
Long,
|
||||||
|
UpdateStrategy,
|
||||||
|
Long,
|
||||||
|
Long,
|
||||||
|
Long?,
|
||||||
|
) -> Manga =
|
||||||
{ id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval, lastModifiedAt, favoriteModifiedAt ->
|
{ id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval, lastModifiedAt, favoriteModifiedAt ->
|
||||||
Manga(
|
Manga(
|
||||||
id = id,
|
id = id,
|
||||||
|
@ -32,7 +55,37 @@ val mangaMapper: (Long, Long, String, String?, String?, String?, List<String>?,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val libraryManga: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long, Long, Long?, Long, Double, Long, Long, Long, Double, Long) -> LibraryManga =
|
val libraryManga: (
|
||||||
|
Long,
|
||||||
|
Long,
|
||||||
|
String,
|
||||||
|
String?,
|
||||||
|
String?,
|
||||||
|
String?,
|
||||||
|
List<String>?,
|
||||||
|
String,
|
||||||
|
Long,
|
||||||
|
String?,
|
||||||
|
Boolean,
|
||||||
|
Long?,
|
||||||
|
Long?,
|
||||||
|
Boolean,
|
||||||
|
Long,
|
||||||
|
Long,
|
||||||
|
Long,
|
||||||
|
Long,
|
||||||
|
UpdateStrategy,
|
||||||
|
Long,
|
||||||
|
Long,
|
||||||
|
Long?,
|
||||||
|
Long,
|
||||||
|
Double,
|
||||||
|
Long,
|
||||||
|
Long,
|
||||||
|
Long,
|
||||||
|
Double,
|
||||||
|
Long,
|
||||||
|
) -> LibraryManga =
|
||||||
{ id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval, lastModifiedAt, favoriteModifiedAt, totalCount, readCount, latestUpload, chapterFetchedAt, lastRead, bookmarkCount, category ->
|
{ id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval, lastModifiedAt, favoriteModifiedAt, totalCount, readCount, latestUpload, chapterFetchedAt, lastRead, bookmarkCount, category ->
|
||||||
LibraryManga(
|
LibraryManga(
|
||||||
manga = mangaMapper(
|
manga = mangaMapper(
|
||||||
|
|
|
@ -24,11 +24,23 @@ class MangaRepositoryImpl(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getMangaByUrlAndSourceId(url: String, sourceId: Long): Manga? {
|
override suspend fun getMangaByUrlAndSourceId(url: String, sourceId: Long): Manga? {
|
||||||
return handler.awaitOneOrNull(inTransaction = true) { mangasQueries.getMangaByUrlAndSource(url, sourceId, mangaMapper) }
|
return handler.awaitOneOrNull(inTransaction = true) {
|
||||||
|
mangasQueries.getMangaByUrlAndSource(
|
||||||
|
url,
|
||||||
|
sourceId,
|
||||||
|
mangaMapper,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getMangaByUrlAndSourceIdAsFlow(url: String, sourceId: Long): Flow<Manga?> {
|
override fun getMangaByUrlAndSourceIdAsFlow(url: String, sourceId: Long): Flow<Manga?> {
|
||||||
return handler.subscribeToOneOrNull { mangasQueries.getMangaByUrlAndSource(url, sourceId, mangaMapper) }
|
return handler.subscribeToOneOrNull {
|
||||||
|
mangasQueries.getMangaByUrlAndSource(
|
||||||
|
url,
|
||||||
|
sourceId,
|
||||||
|
mangaMapper,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getFavorites(): List<Manga> {
|
override suspend fun getFavorites(): List<Manga> {
|
||||||
|
|
|
@ -32,7 +32,9 @@ data class GitHubAssets(@SerialName("browser_download_url") val downloadLink: St
|
||||||
* Reference: https://stackoverflow.com/a/30281147
|
* Reference: https://stackoverflow.com/a/30281147
|
||||||
*/
|
*/
|
||||||
val gitHubUsernameMentionRegex =
|
val gitHubUsernameMentionRegex =
|
||||||
"""\B@([a-z0-9](?:-(?=[a-z0-9])|[a-z0-9]){0,38}(?<=[a-z0-9]))""".toRegex(RegexOption.IGNORE_CASE)
|
"""\B@([a-z0-9](?:-(?=[a-z0-9])|[a-z0-9]){0,38}(?<=[a-z0-9]))""".toRegex(
|
||||||
|
RegexOption.IGNORE_CASE,
|
||||||
|
)
|
||||||
|
|
||||||
val releaseMapper: (GithubRelease) -> Release = {
|
val releaseMapper: (GithubRelease) -> Release = {
|
||||||
Release(
|
Release(
|
||||||
|
|
|
@ -9,7 +9,9 @@ import tachiyomi.core.util.lang.awaitSingle
|
||||||
import tachiyomi.core.util.lang.withIOContext
|
import tachiyomi.core.util.lang.withIOContext
|
||||||
import tachiyomi.domain.source.repository.SourcePagingSourceType
|
import tachiyomi.domain.source.repository.SourcePagingSourceType
|
||||||
|
|
||||||
class SourceSearchPagingSource(source: CatalogueSource, val query: String, val filters: FilterList) : SourcePagingSource(source) {
|
class SourceSearchPagingSource(source: CatalogueSource, val query: String, val filters: FilterList) : SourcePagingSource(
|
||||||
|
source,
|
||||||
|
) {
|
||||||
override suspend fun requestNextPage(currentPage: Int): MangasPage {
|
override suspend fun requestNextPage(currentPage: Int): MangasPage {
|
||||||
return source.fetchSearchManga(currentPage, query, filters).awaitSingle()
|
return source.fetchSearchManga(currentPage, query, filters).awaitSingle()
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,21 @@ package tachiyomi.data.track
|
||||||
|
|
||||||
import tachiyomi.domain.track.model.Track
|
import tachiyomi.domain.track.model.Track
|
||||||
|
|
||||||
val trackMapper: (Long, Long, Long, Long, Long?, String, Double, Long, Long, Double, String, Long, Long) -> Track =
|
val trackMapper: (
|
||||||
|
Long,
|
||||||
|
Long,
|
||||||
|
Long,
|
||||||
|
Long,
|
||||||
|
Long?,
|
||||||
|
String,
|
||||||
|
Double,
|
||||||
|
Long,
|
||||||
|
Long,
|
||||||
|
Double,
|
||||||
|
String,
|
||||||
|
Long,
|
||||||
|
Long,
|
||||||
|
) -> Track =
|
||||||
{ id, mangaId, syncId, remoteId, libraryId, title, lastChapterRead, totalChapters, status, score, remoteUrl, startDate, finishDate ->
|
{ id, mangaId, syncId, remoteId, libraryId, title, lastChapterRead, totalChapters, status, score, remoteUrl, startDate, finishDate ->
|
||||||
Track(
|
Track(
|
||||||
id = id,
|
id = id,
|
||||||
|
|
|
@ -3,7 +3,22 @@ package tachiyomi.data.updates
|
||||||
import tachiyomi.domain.manga.model.MangaCover
|
import tachiyomi.domain.manga.model.MangaCover
|
||||||
import tachiyomi.domain.updates.model.UpdatesWithRelations
|
import tachiyomi.domain.updates.model.UpdatesWithRelations
|
||||||
|
|
||||||
val updateWithRelationMapper: (Long, String, Long, String, String?, Boolean, Boolean, Long, Long, Boolean, String?, Long, Long, Long) -> UpdatesWithRelations = {
|
val updateWithRelationMapper: (
|
||||||
|
Long,
|
||||||
|
String,
|
||||||
|
Long,
|
||||||
|
String,
|
||||||
|
String?,
|
||||||
|
Boolean,
|
||||||
|
Boolean,
|
||||||
|
Long,
|
||||||
|
Long,
|
||||||
|
Boolean,
|
||||||
|
String?,
|
||||||
|
Long,
|
||||||
|
Long,
|
||||||
|
Long,
|
||||||
|
) -> UpdatesWithRelations = {
|
||||||
mangaId, mangaTitle, chapterId, chapterName, scanlator, read, bookmark, lastPageRead, sourceId, favorite, thumbnailUrl, coverLastModified, _, dateFetch ->
|
mangaId, mangaTitle, chapterId, chapterName, scanlator, read, bookmark, lastPageRead, sourceId, favorite, thumbnailUrl, coverLastModified, _, dateFetch ->
|
||||||
UpdatesWithRelations(
|
UpdatesWithRelations(
|
||||||
mangaId = mangaId,
|
mangaId = mangaId,
|
||||||
|
|
|
@ -9,7 +9,11 @@ class UpdatesRepositoryImpl(
|
||||||
private val databaseHandler: DatabaseHandler,
|
private val databaseHandler: DatabaseHandler,
|
||||||
) : UpdatesRepository {
|
) : UpdatesRepository {
|
||||||
|
|
||||||
override suspend fun awaitWithRead(read: Boolean, after: Long, limit: Long): List<UpdatesWithRelations> {
|
override suspend fun awaitWithRead(
|
||||||
|
read: Boolean,
|
||||||
|
after: Long,
|
||||||
|
limit: Long,
|
||||||
|
): List<UpdatesWithRelations> {
|
||||||
return databaseHandler.awaitList {
|
return databaseHandler.awaitList {
|
||||||
updatesViewQueries.getUpdatesByReadStatus(
|
updatesViewQueries.getUpdatesByReadStatus(
|
||||||
read = read,
|
read = read,
|
||||||
|
@ -26,7 +30,11 @@ class UpdatesRepositoryImpl(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun subscribeWithRead(read: Boolean, after: Long, limit: Long): Flow<List<UpdatesWithRelations>> {
|
override fun subscribeWithRead(
|
||||||
|
read: Boolean,
|
||||||
|
after: Long,
|
||||||
|
limit: Long,
|
||||||
|
): Flow<List<UpdatesWithRelations>> {
|
||||||
return databaseHandler.subscribeToList {
|
return databaseHandler.subscribeToList {
|
||||||
updatesViewQueries.getUpdatesByReadStatus(
|
updatesViewQueries.getUpdatesByReadStatus(
|
||||||
read = read,
|
read = read,
|
||||||
|
|
|
@ -16,11 +16,9 @@ class ReorderCategory(
|
||||||
|
|
||||||
private val mutex = Mutex()
|
private val mutex = Mutex()
|
||||||
|
|
||||||
suspend fun moveUp(category: Category): Result =
|
suspend fun moveUp(category: Category): Result = await(category, MoveTo.UP)
|
||||||
await(category, MoveTo.UP)
|
|
||||||
|
|
||||||
suspend fun moveDown(category: Category): Result =
|
suspend fun moveDown(category: Category): Result = await(category, MoveTo.DOWN)
|
||||||
await(category, MoveTo.DOWN)
|
|
||||||
|
|
||||||
private suspend fun await(category: Category, moveTo: MoveTo) = withNonCancellableContext {
|
private suspend fun await(category: Category, moveTo: MoveTo) = withNonCancellableContext {
|
||||||
mutex.withLock {
|
mutex.withLock {
|
||||||
|
|
|
@ -28,7 +28,11 @@ class SetSortModeForCategory(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun await(category: Category?, type: LibrarySort.Type, direction: LibrarySort.Direction) {
|
suspend fun await(
|
||||||
|
category: Category?,
|
||||||
|
type: LibrarySort.Type,
|
||||||
|
direction: LibrarySort.Direction,
|
||||||
|
) {
|
||||||
await(category?.id, type, direction)
|
await(category?.id, type, direction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,11 @@ object ChapterRecognition {
|
||||||
*/
|
*/
|
||||||
private val unwantedWhiteSpace = Regex("""\s(?=extra|special|omake)""")
|
private val unwantedWhiteSpace = Regex("""\s(?=extra|special|omake)""")
|
||||||
|
|
||||||
fun parseChapterNumber(mangaTitle: String, chapterName: String, chapterNumber: Double? = null): Double {
|
fun parseChapterNumber(
|
||||||
|
mangaTitle: String,
|
||||||
|
chapterName: String,
|
||||||
|
chapterNumber: Double? = null,
|
||||||
|
): Double {
|
||||||
// If chapter number is known return.
|
// If chapter number is known return.
|
||||||
if (chapterNumber != null && (chapterNumber == -2.0 || chapterNumber > -1.0)) {
|
if (chapterNumber != null && (chapterNumber == -2.0 || chapterNumber > -1.0)) {
|
||||||
return chapterNumber
|
return chapterNumber
|
||||||
|
|
|
@ -3,7 +3,13 @@ package tachiyomi.domain.chapter.service
|
||||||
import tachiyomi.domain.chapter.model.Chapter
|
import tachiyomi.domain.chapter.model.Chapter
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
|
|
||||||
fun getChapterSort(manga: Manga, sortDescending: Boolean = manga.sortDescending()): (Chapter, Chapter) -> Int {
|
fun getChapterSort(
|
||||||
|
manga: Manga,
|
||||||
|
sortDescending: Boolean = manga.sortDescending(),
|
||||||
|
): (
|
||||||
|
Chapter,
|
||||||
|
Chapter,
|
||||||
|
) -> Int {
|
||||||
return when (manga.sorting) {
|
return when (manga.sorting) {
|
||||||
Manga.CHAPTER_SORTING_SOURCE -> when (sortDescending) {
|
Manga.CHAPTER_SORTING_SOURCE -> when (sortDescending) {
|
||||||
true -> { c1, c2 -> c1.sourceOrder.compareTo(c2.sourceOrder) }
|
true -> { c1, c2 -> c1.sourceOrder.compareTo(c2.sourceOrder) }
|
||||||
|
|
|
@ -8,9 +8,15 @@ class DownloadPreferences(
|
||||||
private val preferenceStore: PreferenceStore,
|
private val preferenceStore: PreferenceStore,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun downloadsDirectory() = preferenceStore.getString("download_directory", folderProvider.path())
|
fun downloadsDirectory() = preferenceStore.getString(
|
||||||
|
"download_directory",
|
||||||
|
folderProvider.path(),
|
||||||
|
)
|
||||||
|
|
||||||
fun downloadOnlyOverWifi() = preferenceStore.getBoolean("pref_download_only_over_wifi_key", true)
|
fun downloadOnlyOverWifi() = preferenceStore.getBoolean(
|
||||||
|
"pref_download_only_over_wifi_key",
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
|
||||||
fun saveChaptersAsCBZ() = preferenceStore.getBoolean("save_chapter_as_cbz", true)
|
fun saveChaptersAsCBZ() = preferenceStore.getBoolean("save_chapter_as_cbz", true)
|
||||||
|
|
||||||
|
@ -20,15 +26,27 @@ class DownloadPreferences(
|
||||||
|
|
||||||
fun removeAfterReadSlots() = preferenceStore.getInt("remove_after_read_slots", -1)
|
fun removeAfterReadSlots() = preferenceStore.getInt("remove_after_read_slots", -1)
|
||||||
|
|
||||||
fun removeAfterMarkedAsRead() = preferenceStore.getBoolean("pref_remove_after_marked_as_read_key", false)
|
fun removeAfterMarkedAsRead() = preferenceStore.getBoolean(
|
||||||
|
"pref_remove_after_marked_as_read_key",
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
|
||||||
fun removeBookmarkedChapters() = preferenceStore.getBoolean("pref_remove_bookmarked", false)
|
fun removeBookmarkedChapters() = preferenceStore.getBoolean("pref_remove_bookmarked", false)
|
||||||
|
|
||||||
fun removeExcludeCategories() = preferenceStore.getStringSet("remove_exclude_categories", emptySet())
|
fun removeExcludeCategories() = preferenceStore.getStringSet(
|
||||||
|
"remove_exclude_categories",
|
||||||
|
emptySet(),
|
||||||
|
)
|
||||||
|
|
||||||
fun downloadNewChapters() = preferenceStore.getBoolean("download_new", false)
|
fun downloadNewChapters() = preferenceStore.getBoolean("download_new", false)
|
||||||
|
|
||||||
fun downloadNewChapterCategories() = preferenceStore.getStringSet("download_new_categories", emptySet())
|
fun downloadNewChapterCategories() = preferenceStore.getStringSet(
|
||||||
|
"download_new_categories",
|
||||||
|
emptySet(),
|
||||||
|
)
|
||||||
|
|
||||||
fun downloadNewChapterCategoriesExclude() = preferenceStore.getStringSet("download_new_categories_exclude", emptySet())
|
fun downloadNewChapterCategoriesExclude() = preferenceStore.getStringSet(
|
||||||
|
"download_new_categories_exclude",
|
||||||
|
emptySet(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,11 @@ class GetNextChapters(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun await(mangaId: Long, fromChapterId: Long, onlyUnread: Boolean = true): List<Chapter> {
|
suspend fun await(
|
||||||
|
mangaId: Long,
|
||||||
|
fromChapterId: Long,
|
||||||
|
onlyUnread: Boolean = true,
|
||||||
|
): List<Chapter> {
|
||||||
val chapters = await(mangaId, onlyUnread)
|
val chapters = await(mangaId, onlyUnread)
|
||||||
val currChapterIndex = chapters.indexOfFirst { it.id == fromChapterId }
|
val currChapterIndex = chapters.indexOfFirst { it.id == fromChapterId }
|
||||||
val nextChapters = chapters.subList(max(0, currChapterIndex), chapters.size)
|
val nextChapters = chapters.subList(max(0, currChapterIndex), chapters.size)
|
||||||
|
|
|
@ -65,7 +65,18 @@ data class LibrarySort(
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val types by lazy { setOf(Type.Alphabetical, Type.LastRead, Type.LastUpdate, Type.UnreadCount, Type.TotalChapters, Type.LatestChapter, Type.ChapterFetchDate, Type.DateAdded) }
|
val types by lazy {
|
||||||
|
setOf(
|
||||||
|
Type.Alphabetical,
|
||||||
|
Type.LastRead,
|
||||||
|
Type.LastUpdate,
|
||||||
|
Type.UnreadCount,
|
||||||
|
Type.TotalChapters,
|
||||||
|
Type.LatestChapter,
|
||||||
|
Type.ChapterFetchDate,
|
||||||
|
Type.DateAdded,
|
||||||
|
)
|
||||||
|
}
|
||||||
val directions by lazy { setOf(Direction.Ascending, Direction.Descending) }
|
val directions by lazy { setOf(Direction.Ascending, Direction.Descending) }
|
||||||
val default = LibrarySort(Type.Alphabetical, Direction.Ascending)
|
val default = LibrarySort(Type.Alphabetical, Direction.Ascending)
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,19 @@ class LibraryPreferences(
|
||||||
private val preferenceStore: PreferenceStore,
|
private val preferenceStore: PreferenceStore,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun displayMode() = preferenceStore.getObject("pref_display_mode_library", LibraryDisplayMode.default, LibraryDisplayMode.Serializer::serialize, LibraryDisplayMode.Serializer::deserialize)
|
fun displayMode() = preferenceStore.getObject(
|
||||||
|
"pref_display_mode_library",
|
||||||
|
LibraryDisplayMode.default,
|
||||||
|
LibraryDisplayMode.Serializer::serialize,
|
||||||
|
LibraryDisplayMode.Serializer::deserialize,
|
||||||
|
)
|
||||||
|
|
||||||
fun sortingMode() = preferenceStore.getObject("library_sorting_mode", LibrarySort.default, LibrarySort.Serializer::serialize, LibrarySort.Serializer::deserialize)
|
fun sortingMode() = preferenceStore.getObject(
|
||||||
|
"library_sorting_mode",
|
||||||
|
LibrarySort.default,
|
||||||
|
LibrarySort.Serializer::serialize,
|
||||||
|
LibrarySort.Serializer::deserialize,
|
||||||
|
)
|
||||||
|
|
||||||
fun portraitColumns() = preferenceStore.getInt("pref_library_columns_portrait_key", 0)
|
fun portraitColumns() = preferenceStore.getInt("pref_library_columns_portrait_key", 0)
|
||||||
|
|
||||||
|
@ -42,31 +52,64 @@ class LibraryPreferences(
|
||||||
|
|
||||||
fun autoUpdateTrackers() = preferenceStore.getBoolean("auto_update_trackers", false)
|
fun autoUpdateTrackers() = preferenceStore.getBoolean("auto_update_trackers", false)
|
||||||
|
|
||||||
fun showContinueReadingButton() = preferenceStore.getBoolean("display_continue_reading_button", false)
|
fun showContinueReadingButton() = preferenceStore.getBoolean(
|
||||||
|
"display_continue_reading_button",
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
|
||||||
// region Filter
|
// region Filter
|
||||||
|
|
||||||
fun filterDownloaded() = preferenceStore.getEnum("pref_filter_library_downloaded_v2", TriState.DISABLED)
|
fun filterDownloaded() = preferenceStore.getEnum(
|
||||||
|
"pref_filter_library_downloaded_v2",
|
||||||
|
TriState.DISABLED,
|
||||||
|
)
|
||||||
|
|
||||||
fun filterUnread() = preferenceStore.getEnum("pref_filter_library_unread_v2", TriState.DISABLED)
|
fun filterUnread() = preferenceStore.getEnum("pref_filter_library_unread_v2", TriState.DISABLED)
|
||||||
|
|
||||||
fun filterStarted() = preferenceStore.getEnum("pref_filter_library_started_v2", TriState.DISABLED)
|
fun filterStarted() = preferenceStore.getEnum(
|
||||||
|
"pref_filter_library_started_v2",
|
||||||
|
TriState.DISABLED,
|
||||||
|
)
|
||||||
|
|
||||||
fun filterBookmarked() = preferenceStore.getEnum("pref_filter_library_bookmarked_v2", TriState.DISABLED)
|
fun filterBookmarked() = preferenceStore.getEnum(
|
||||||
|
"pref_filter_library_bookmarked_v2",
|
||||||
|
TriState.DISABLED,
|
||||||
|
)
|
||||||
|
|
||||||
fun filterCompleted() = preferenceStore.getEnum("pref_filter_library_completed_v2", TriState.DISABLED)
|
fun filterCompleted() = preferenceStore.getEnum(
|
||||||
|
"pref_filter_library_completed_v2",
|
||||||
|
TriState.DISABLED,
|
||||||
|
)
|
||||||
|
|
||||||
fun filterIntervalCustom() = preferenceStore.getEnum("pref_filter_library_interval_custom", TriState.DISABLED)
|
fun filterIntervalCustom() = preferenceStore.getEnum(
|
||||||
|
"pref_filter_library_interval_custom",
|
||||||
|
TriState.DISABLED,
|
||||||
|
)
|
||||||
|
|
||||||
fun filterIntervalLong() = preferenceStore.getEnum("pref_filter_library_interval_long", TriState.DISABLED)
|
fun filterIntervalLong() = preferenceStore.getEnum(
|
||||||
|
"pref_filter_library_interval_long",
|
||||||
|
TriState.DISABLED,
|
||||||
|
)
|
||||||
|
|
||||||
fun filterIntervalLate() = preferenceStore.getEnum("pref_filter_library_interval_late", TriState.DISABLED)
|
fun filterIntervalLate() = preferenceStore.getEnum(
|
||||||
|
"pref_filter_library_interval_late",
|
||||||
|
TriState.DISABLED,
|
||||||
|
)
|
||||||
|
|
||||||
fun filterIntervalDropped() = preferenceStore.getEnum("pref_filter_library_interval_dropped", TriState.DISABLED)
|
fun filterIntervalDropped() = preferenceStore.getEnum(
|
||||||
|
"pref_filter_library_interval_dropped",
|
||||||
|
TriState.DISABLED,
|
||||||
|
)
|
||||||
|
|
||||||
fun filterIntervalPassed() = preferenceStore.getEnum("pref_filter_library_interval_passed", TriState.DISABLED)
|
fun filterIntervalPassed() = preferenceStore.getEnum(
|
||||||
|
"pref_filter_library_interval_passed",
|
||||||
|
TriState.DISABLED,
|
||||||
|
)
|
||||||
|
|
||||||
fun filterTracking(id: Int) = preferenceStore.getEnum("pref_filter_library_tracked_${id}_v2", TriState.DISABLED)
|
fun filterTracking(id: Int) = preferenceStore.getEnum(
|
||||||
|
"pref_filter_library_tracked_${id}_v2",
|
||||||
|
TriState.DISABLED,
|
||||||
|
)
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
|
@ -97,24 +140,45 @@ class LibraryPreferences(
|
||||||
|
|
||||||
fun updateCategories() = preferenceStore.getStringSet("library_update_categories", emptySet())
|
fun updateCategories() = preferenceStore.getStringSet("library_update_categories", emptySet())
|
||||||
|
|
||||||
fun updateCategoriesExclude() = preferenceStore.getStringSet("library_update_categories_exclude", emptySet())
|
fun updateCategoriesExclude() = preferenceStore.getStringSet(
|
||||||
|
"library_update_categories_exclude",
|
||||||
|
emptySet(),
|
||||||
|
)
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region Chapter
|
// region Chapter
|
||||||
|
|
||||||
fun filterChapterByRead() = preferenceStore.getLong("default_chapter_filter_by_read", Manga.SHOW_ALL)
|
fun filterChapterByRead() = preferenceStore.getLong(
|
||||||
|
"default_chapter_filter_by_read",
|
||||||
|
Manga.SHOW_ALL,
|
||||||
|
)
|
||||||
|
|
||||||
fun filterChapterByDownloaded() = preferenceStore.getLong("default_chapter_filter_by_downloaded", Manga.SHOW_ALL)
|
fun filterChapterByDownloaded() = preferenceStore.getLong(
|
||||||
|
"default_chapter_filter_by_downloaded",
|
||||||
|
Manga.SHOW_ALL,
|
||||||
|
)
|
||||||
|
|
||||||
fun filterChapterByBookmarked() = preferenceStore.getLong("default_chapter_filter_by_bookmarked", Manga.SHOW_ALL)
|
fun filterChapterByBookmarked() = preferenceStore.getLong(
|
||||||
|
"default_chapter_filter_by_bookmarked",
|
||||||
|
Manga.SHOW_ALL,
|
||||||
|
)
|
||||||
|
|
||||||
// and upload date
|
// and upload date
|
||||||
fun sortChapterBySourceOrNumber() = preferenceStore.getLong("default_chapter_sort_by_source_or_number", Manga.CHAPTER_SORTING_SOURCE)
|
fun sortChapterBySourceOrNumber() = preferenceStore.getLong(
|
||||||
|
"default_chapter_sort_by_source_or_number",
|
||||||
|
Manga.CHAPTER_SORTING_SOURCE,
|
||||||
|
)
|
||||||
|
|
||||||
fun displayChapterByNameOrNumber() = preferenceStore.getLong("default_chapter_display_by_name_or_number", Manga.CHAPTER_DISPLAY_NAME)
|
fun displayChapterByNameOrNumber() = preferenceStore.getLong(
|
||||||
|
"default_chapter_display_by_name_or_number",
|
||||||
|
Manga.CHAPTER_DISPLAY_NAME,
|
||||||
|
)
|
||||||
|
|
||||||
fun sortChapterByAscendingOrDescending() = preferenceStore.getLong("default_chapter_sort_by_ascending_or_descending", Manga.CHAPTER_SORT_DESC)
|
fun sortChapterByAscendingOrDescending() = preferenceStore.getLong(
|
||||||
|
"default_chapter_sort_by_ascending_or_descending",
|
||||||
|
Manga.CHAPTER_SORT_DESC,
|
||||||
|
)
|
||||||
|
|
||||||
fun setChapterSettingsDefault(manga: Manga) {
|
fun setChapterSettingsDefault(manga: Manga) {
|
||||||
filterChapterByRead().set(manga.unreadFilterRaw)
|
filterChapterByRead().set(manga.unreadFilterRaw)
|
||||||
|
@ -122,7 +186,9 @@ class LibraryPreferences(
|
||||||
filterChapterByBookmarked().set(manga.bookmarkedFilterRaw)
|
filterChapterByBookmarked().set(manga.bookmarkedFilterRaw)
|
||||||
sortChapterBySourceOrNumber().set(manga.sorting)
|
sortChapterBySourceOrNumber().set(manga.sorting)
|
||||||
displayChapterByNameOrNumber().set(manga.displayMode)
|
displayChapterByNameOrNumber().set(manga.displayMode)
|
||||||
sortChapterByAscendingOrDescending().set(if (manga.sortDescending()) Manga.CHAPTER_SORT_DESC else Manga.CHAPTER_SORT_ASC)
|
sortChapterByAscendingOrDescending().set(
|
||||||
|
if (manga.sortDescending()) Manga.CHAPTER_SORT_DESC else Manga.CHAPTER_SORT_ASC,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun autoClearChapterCache() = preferenceStore.getBoolean("auto_clear_chapter_cache", false)
|
fun autoClearChapterCache() = preferenceStore.getBoolean("auto_clear_chapter_cache", false)
|
||||||
|
@ -131,9 +197,15 @@ class LibraryPreferences(
|
||||||
|
|
||||||
// region Swipe Actions
|
// region Swipe Actions
|
||||||
|
|
||||||
fun swipeToStartAction() = preferenceStore.getEnum("pref_chapter_swipe_end_action", ChapterSwipeAction.ToggleBookmark)
|
fun swipeToStartAction() = preferenceStore.getEnum(
|
||||||
|
"pref_chapter_swipe_end_action",
|
||||||
|
ChapterSwipeAction.ToggleBookmark,
|
||||||
|
)
|
||||||
|
|
||||||
fun swipeToEndAction() = preferenceStore.getEnum("pref_chapter_swipe_start_action", ChapterSwipeAction.ToggleRead)
|
fun swipeToEndAction() = preferenceStore.getEnum(
|
||||||
|
"pref_chapter_swipe_start_action",
|
||||||
|
ChapterSwipeAction.ToggleRead,
|
||||||
|
)
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,10 @@ class SetFetchInterval(
|
||||||
window
|
window
|
||||||
}
|
}
|
||||||
val chapters = getChapterByMangaId.await(manga.id)
|
val chapters = getChapterByMangaId.await(manga.id)
|
||||||
val interval = manga.fetchInterval.takeIf { it < 0 } ?: calculateInterval(chapters, dateTime)
|
val interval = manga.fetchInterval.takeIf { it < 0 } ?: calculateInterval(
|
||||||
|
chapters,
|
||||||
|
dateTime,
|
||||||
|
)
|
||||||
val nextUpdate = calculateNextUpdate(manga, interval, dateTime, currentWindow)
|
val nextUpdate = calculateNextUpdate(manga, interval, dateTime, currentWindow)
|
||||||
|
|
||||||
return if (manga.nextUpdate == nextUpdate && manga.fetchInterval == interval) {
|
return if (manga.nextUpdate == nextUpdate && manga.fetchInterval == interval) {
|
||||||
|
@ -46,7 +49,9 @@ class SetFetchInterval(
|
||||||
|
|
||||||
internal fun calculateInterval(chapters: List<Chapter>, zonedDateTime: ZonedDateTime): Int {
|
internal fun calculateInterval(chapters: List<Chapter>, zonedDateTime: ZonedDateTime): Int {
|
||||||
val sortedChapters = chapters
|
val sortedChapters = chapters
|
||||||
.sortedWith(compareByDescending<Chapter> { it.dateUpload }.thenByDescending { it.dateFetch })
|
.sortedWith(
|
||||||
|
compareByDescending<Chapter> { it.dateUpload }.thenByDescending { it.dateFetch },
|
||||||
|
)
|
||||||
.take(50)
|
.take(50)
|
||||||
|
|
||||||
val uploadDates = sortedChapters
|
val uploadDates = sortedChapters
|
||||||
|
@ -95,7 +100,10 @@ class SetFetchInterval(
|
||||||
manga.nextUpdate !in window.first.rangeTo(window.second + 1) ||
|
manga.nextUpdate !in window.first.rangeTo(window.second + 1) ||
|
||||||
manga.fetchInterval == 0
|
manga.fetchInterval == 0
|
||||||
) {
|
) {
|
||||||
val latestDate = ZonedDateTime.ofInstant(Instant.ofEpochMilli(manga.lastUpdate), dateTime.zone)
|
val latestDate = ZonedDateTime.ofInstant(
|
||||||
|
Instant.ofEpochMilli(manga.lastUpdate),
|
||||||
|
dateTime.zone,
|
||||||
|
)
|
||||||
.toLocalDate()
|
.toLocalDate()
|
||||||
.atStartOfDay()
|
.atStartOfDay()
|
||||||
val timeSinceLatest = ChronoUnit.DAYS.between(latestDate, dateTime).toInt()
|
val timeSinceLatest = ChronoUnit.DAYS.between(latestDate, dateTime).toInt()
|
||||||
|
|
|
@ -20,7 +20,10 @@ class GetApplicationRelease(
|
||||||
val now = Instant.now()
|
val now = Instant.now()
|
||||||
|
|
||||||
// Limit checks to once every 3 days at most
|
// Limit checks to once every 3 days at most
|
||||||
if (arguments.forceCheck.not() && now.isBefore(Instant.ofEpochMilli(lastChecked.get()).plus(3, ChronoUnit.DAYS))) {
|
if (arguments.forceCheck.not() && now.isBefore(
|
||||||
|
Instant.ofEpochMilli(lastChecked.get()).plus(3, ChronoUnit.DAYS),
|
||||||
|
)
|
||||||
|
) {
|
||||||
return Result.NoNewUpdate
|
return Result.NoNewUpdate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +32,12 @@ class GetApplicationRelease(
|
||||||
lastChecked.set(now.toEpochMilli())
|
lastChecked.set(now.toEpochMilli())
|
||||||
|
|
||||||
// Check if latest version is different from current version
|
// Check if latest version is different from current version
|
||||||
val isNewVersion = isNewVersion(arguments.isPreview, arguments.commitCount, arguments.versionName, release.version)
|
val isNewVersion = isNewVersion(
|
||||||
|
arguments.isPreview,
|
||||||
|
arguments.commitCount,
|
||||||
|
arguments.versionName,
|
||||||
|
release.version,
|
||||||
|
)
|
||||||
return when {
|
return when {
|
||||||
isNewVersion && arguments.isThirdParty -> Result.ThirdPartyInstallation
|
isNewVersion && arguments.isThirdParty -> Result.ThirdPartyInstallation
|
||||||
isNewVersion -> Result.NewUpdate(release)
|
isNewVersion -> Result.NewUpdate(release)
|
||||||
|
@ -37,7 +45,12 @@ class GetApplicationRelease(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isNewVersion(isPreview: Boolean, commitCount: Int, versionName: String, versionTag: String): Boolean {
|
private fun isNewVersion(
|
||||||
|
isPreview: Boolean,
|
||||||
|
commitCount: Int,
|
||||||
|
versionName: String,
|
||||||
|
versionTag: String,
|
||||||
|
): Boolean {
|
||||||
// Removes prefixes like "r" or "v"
|
// Removes prefixes like "r" or "v"
|
||||||
val newVersion = versionTag.replace("[^\\d.]".toRegex(), "")
|
val newVersion = versionTag.replace("[^\\d.]".toRegex(), "")
|
||||||
return if (isPreview) {
|
return if (isPreview) {
|
||||||
|
|
|
@ -34,7 +34,10 @@ class LibraryFlagsTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Test Flag plus operator with old flag as base`() {
|
fun `Test Flag plus operator with old flag as base`() {
|
||||||
val currentSort = LibrarySort(LibrarySort.Type.UnreadCount, LibrarySort.Direction.Descending)
|
val currentSort = LibrarySort(
|
||||||
|
LibrarySort.Type.UnreadCount,
|
||||||
|
LibrarySort.Direction.Descending,
|
||||||
|
)
|
||||||
currentSort.flag shouldBe 0b00001100
|
currentSort.flag shouldBe 0b00001100
|
||||||
|
|
||||||
val sort = LibrarySort(LibrarySort.Type.DateAdded, LibrarySort.Direction.Ascending)
|
val sort = LibrarySort(LibrarySort.Type.DateAdded, LibrarySort.Direction.Ascending)
|
||||||
|
|
|
@ -79,7 +79,9 @@ class GetApplicationReleaseTest {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
(result as GetApplicationRelease.Result.NewUpdate).release shouldBe GetApplicationRelease.Result.NewUpdate(release).release
|
(result as GetApplicationRelease.Result.NewUpdate).release shouldBe GetApplicationRelease.Result.NewUpdate(
|
||||||
|
release,
|
||||||
|
).release
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -106,7 +108,9 @@ class GetApplicationReleaseTest {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
(result as GetApplicationRelease.Result.NewUpdate).release shouldBe GetApplicationRelease.Result.NewUpdate(release).release
|
(result as GetApplicationRelease.Result.NewUpdate).release shouldBe GetApplicationRelease.Result.NewUpdate(
|
||||||
|
release,
|
||||||
|
).release
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -90,7 +90,7 @@ voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.
|
||||||
voyager-tab-navigator = { module = "cafe.adriel.voyager:voyager-tab-navigator", version.ref = "voyager" }
|
voyager-tab-navigator = { module = "cafe.adriel.voyager:voyager-tab-navigator", version.ref = "voyager" }
|
||||||
voyager-transitions = { module = "cafe.adriel.voyager:voyager-transitions", version.ref = "voyager" }
|
voyager-transitions = { module = "cafe.adriel.voyager:voyager-transitions", version.ref = "voyager" }
|
||||||
|
|
||||||
kotlinter = "org.jmailen.gradle:kotlinter-gradle:3.13.0"
|
ktlint = "org.jlleitschuh.gradle:ktlint-gradle:11.5.1"
|
||||||
|
|
||||||
[bundles]
|
[bundles]
|
||||||
okhttp = ["okhttp-core", "okhttp-logging", "okhttp-dnsoverhttps"]
|
okhttp = ["okhttp-core", "okhttp-logging", "okhttp-dnsoverhttps"]
|
||||||
|
|
|
@ -60,11 +60,16 @@ abstract class AbstractStartupBenchmark(private val startupMode: StartupMode) {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun startupBaselineProfileDisabled() = startup(
|
fun startupBaselineProfileDisabled() = startup(
|
||||||
CompilationMode.Partial(baselineProfileMode = BaselineProfileMode.Disable, warmupIterations = 1),
|
CompilationMode.Partial(
|
||||||
|
baselineProfileMode = BaselineProfileMode.Disable,
|
||||||
|
warmupIterations = 1,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun startupBaselineProfile() = startup(CompilationMode.Partial(baselineProfileMode = BaselineProfileMode.Require))
|
fun startupBaselineProfile() = startup(
|
||||||
|
CompilationMode.Partial(baselineProfileMode = BaselineProfileMode.Require),
|
||||||
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun startupFullCompilation() = startup(CompilationMode.Full())
|
fun startupFullCompilation() = startup(CompilationMode.Full())
|
||||||
|
|
|
@ -182,7 +182,10 @@ fun AdaptiveSheet(
|
||||||
shape = MaterialTheme.shapes.extraLarge,
|
shape = MaterialTheme.shapes.extraLarge,
|
||||||
tonalElevation = tonalElevation,
|
tonalElevation = tonalElevation,
|
||||||
content = {
|
content = {
|
||||||
BackHandler(enabled = anchoredDraggableState.targetValue == 0, onBack = internalOnDismissRequest)
|
BackHandler(
|
||||||
|
enabled = anchoredDraggableState.targetValue == 0,
|
||||||
|
onBack = internalOnDismissRequest,
|
||||||
|
)
|
||||||
content()
|
content()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -200,7 +203,8 @@ fun AdaptiveSheet(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T> AnchoredDraggableState<T>.preUpPostDownNestedScrollConnection() = object : NestedScrollConnection {
|
private fun <T> AnchoredDraggableState<T>.preUpPostDownNestedScrollConnection() =
|
||||||
|
object : NestedScrollConnection {
|
||||||
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
|
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
|
||||||
val delta = available.toFloat()
|
val delta = available.toFloat()
|
||||||
return if (delta < 0 && source == NestedScrollSource.Drag) {
|
return if (delta < 0 && source == NestedScrollSource.Drag) {
|
||||||
|
|
|
@ -37,9 +37,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||||
* By always rotating we give the feedback to the user that the application isn't 'stuck'.
|
* By always rotating we give the feedback to the user that the application isn't 'stuck'.
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun CombinedCircularProgressIndicator(
|
fun CombinedCircularProgressIndicator(progress: Float) {
|
||||||
progress: Float,
|
|
||||||
) {
|
|
||||||
val animatedProgress by animateFloatAsState(
|
val animatedProgress by animateFloatAsState(
|
||||||
targetValue = progress,
|
targetValue = progress,
|
||||||
animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec,
|
animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec,
|
||||||
|
|
|
@ -23,10 +23,7 @@ import androidx.compose.ui.unit.dp
|
||||||
import tachiyomi.presentation.core.theme.header
|
import tachiyomi.presentation.core.theme.header
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CollapsibleBox(
|
fun CollapsibleBox(heading: String, content: @Composable () -> Unit) {
|
||||||
heading: String,
|
|
||||||
content: @Composable () -> Unit,
|
|
||||||
) {
|
|
||||||
var expanded by remember { mutableStateOf(false) }
|
var expanded by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
|
|
|
@ -11,12 +11,7 @@ import androidx.compose.ui.platform.LocalUriHandler
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LinkIcon(
|
fun LinkIcon(modifier: Modifier = Modifier, label: String, icon: ImageVector, url: String) {
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
label: String,
|
|
||||||
icon: ImageVector,
|
|
||||||
url: String,
|
|
||||||
) {
|
|
||||||
val uriHandler = LocalUriHandler.current
|
val uriHandler = LocalUriHandler.current
|
||||||
IconButton(
|
IconButton(
|
||||||
modifier = modifier.padding(4.dp),
|
modifier = modifier.padding(4.dp),
|
||||||
|
|
|
@ -9,10 +9,7 @@ import androidx.compose.ui.text.font.FontWeight
|
||||||
import tachiyomi.presentation.core.components.material.padding
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ListGroupHeader(
|
fun ListGroupHeader(modifier: Modifier = Modifier, text: String) {
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
text: String,
|
|
||||||
) {
|
|
||||||
Text(
|
Text(
|
||||||
text = text,
|
text = text,
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
|
|
@ -51,16 +51,12 @@ object SettingsItemsPaddings {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HeadingItem(
|
fun HeadingItem(@StringRes labelRes: Int) {
|
||||||
@StringRes labelRes: Int,
|
|
||||||
) {
|
|
||||||
HeadingItem(stringResource(labelRes))
|
HeadingItem(stringResource(labelRes))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HeadingItem(
|
fun HeadingItem(text: String) {
|
||||||
text: String,
|
|
||||||
) {
|
|
||||||
Text(
|
Text(
|
||||||
text = text,
|
text = text,
|
||||||
style = MaterialTheme.typography.header,
|
style = MaterialTheme.typography.header,
|
||||||
|
@ -74,11 +70,7 @@ fun HeadingItem(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun IconItem(
|
fun IconItem(label: String, icon: ImageVector, onClick: () -> Unit) {
|
||||||
label: String,
|
|
||||||
icon: ImageVector,
|
|
||||||
onClick: () -> Unit,
|
|
||||||
) {
|
|
||||||
BaseSettingsItem(
|
BaseSettingsItem(
|
||||||
label = label,
|
label = label,
|
||||||
widget = {
|
widget = {
|
||||||
|
@ -93,11 +85,7 @@ fun IconItem(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SortItem(
|
fun SortItem(label: String, sortDescending: Boolean?, onClick: () -> Unit) {
|
||||||
label: String,
|
|
||||||
sortDescending: Boolean?,
|
|
||||||
onClick: () -> Unit,
|
|
||||||
) {
|
|
||||||
val arrowIcon = when (sortDescending) {
|
val arrowIcon = when (sortDescending) {
|
||||||
true -> Icons.Default.ArrowDownward
|
true -> Icons.Default.ArrowDownward
|
||||||
false -> Icons.Default.ArrowUpward
|
false -> Icons.Default.ArrowUpward
|
||||||
|
@ -122,10 +110,7 @@ fun SortItem(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CheckboxItem(
|
fun CheckboxItem(label: String, pref: Preference<Boolean>) {
|
||||||
label: String,
|
|
||||||
pref: Preference<Boolean>,
|
|
||||||
) {
|
|
||||||
val checked by pref.collectAsState()
|
val checked by pref.collectAsState()
|
||||||
CheckboxItem(
|
CheckboxItem(
|
||||||
label = label,
|
label = label,
|
||||||
|
@ -135,11 +120,7 @@ fun CheckboxItem(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CheckboxItem(
|
fun CheckboxItem(label: String, checked: Boolean, onClick: () -> Unit) {
|
||||||
label: String,
|
|
||||||
checked: Boolean,
|
|
||||||
onClick: () -> Unit,
|
|
||||||
) {
|
|
||||||
BaseSettingsItem(
|
BaseSettingsItem(
|
||||||
label = label,
|
label = label,
|
||||||
widget = {
|
widget = {
|
||||||
|
@ -153,11 +134,7 @@ fun CheckboxItem(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RadioItem(
|
fun RadioItem(label: String, selected: Boolean, onClick: () -> Unit) {
|
||||||
label: String,
|
|
||||||
selected: Boolean,
|
|
||||||
onClick: () -> Unit,
|
|
||||||
) {
|
|
||||||
BaseSettingsItem(
|
BaseSettingsItem(
|
||||||
label = label,
|
label = label,
|
||||||
widget = {
|
widget = {
|
||||||
|
@ -314,11 +291,7 @@ fun TriStateItem(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TextItem(
|
fun TextItem(label: String, value: String, onChange: (String) -> Unit) {
|
||||||
label: String,
|
|
||||||
value: String,
|
|
||||||
onChange: (String) -> Unit,
|
|
||||||
) {
|
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
@ -331,10 +304,7 @@ fun TextItem(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingsChipRow(
|
fun SettingsChipRow(@StringRes labelRes: Int, content: @Composable FlowRowScope.() -> Unit) {
|
||||||
@StringRes labelRes: Int,
|
|
||||||
content: @Composable FlowRowScope.() -> Unit,
|
|
||||||
) {
|
|
||||||
Column {
|
Column {
|
||||||
HeadingItem(labelRes)
|
HeadingItem(labelRes)
|
||||||
FlowRow(
|
FlowRow(
|
||||||
|
|
|
@ -197,7 +197,8 @@ private fun rememberColumnWidthSums(
|
||||||
horizontalArrangement,
|
horizontalArrangement,
|
||||||
contentPadding,
|
contentPadding,
|
||||||
) {
|
) {
|
||||||
{ constraints ->
|
{
|
||||||
|
constraints ->
|
||||||
require(constraints.maxWidth != Constraints.Infinity) {
|
require(constraints.maxWidth != Constraints.Infinity) {
|
||||||
"LazyVerticalGrid's width should be bound by parent"
|
"LazyVerticalGrid's width should be bound by parent"
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,9 @@ fun AlertDialogContent(
|
||||||
title = title,
|
title = title,
|
||||||
content = {
|
content = {
|
||||||
Column {
|
Column {
|
||||||
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurfaceVariant) {
|
CompositionLocalProvider(
|
||||||
|
LocalContentColor provides MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
) {
|
||||||
val textStyle = MaterialTheme.typography.bodyMedium
|
val textStyle = MaterialTheme.typography.bodyMedium
|
||||||
ProvideTextStyle(textStyle) {
|
ProvideTextStyle(textStyle) {
|
||||||
Box(
|
Box(
|
||||||
|
@ -54,7 +56,9 @@ fun AlertDialogContent(
|
||||||
)
|
)
|
||||||
.align(Alignment.End),
|
.align(Alignment.End),
|
||||||
) {
|
) {
|
||||||
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.primary) {
|
CompositionLocalProvider(
|
||||||
|
LocalContentColor provides MaterialTheme.colorScheme.primary,
|
||||||
|
) {
|
||||||
val textStyle = MaterialTheme.typography.labelLarge
|
val textStyle = MaterialTheme.typography.labelLarge
|
||||||
ProvideTextStyle(value = textStyle, content = buttons)
|
ProvideTextStyle(value = textStyle, content = buttons)
|
||||||
}
|
}
|
||||||
|
@ -86,7 +90,9 @@ fun AlertDialogContent(
|
||||||
.fillMaxWidth(),
|
.fillMaxWidth(),
|
||||||
) {
|
) {
|
||||||
icon?.let {
|
icon?.let {
|
||||||
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.secondary) {
|
CompositionLocalProvider(
|
||||||
|
LocalContentColor provides MaterialTheme.colorScheme.secondary,
|
||||||
|
) {
|
||||||
Box(
|
Box(
|
||||||
Modifier
|
Modifier
|
||||||
.padding(IconPadding)
|
.padding(IconPadding)
|
||||||
|
@ -97,7 +103,9 @@ fun AlertDialogContent(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
title?.let {
|
title?.let {
|
||||||
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurface) {
|
CompositionLocalProvider(
|
||||||
|
LocalContentColor provides MaterialTheme.colorScheme.onSurface,
|
||||||
|
) {
|
||||||
val textStyle = MaterialTheme.typography.headlineSmall
|
val textStyle = MaterialTheme.typography.headlineSmall
|
||||||
ProvideTextStyle(textStyle) {
|
ProvideTextStyle(textStyle) {
|
||||||
Box(
|
Box(
|
||||||
|
|
|
@ -64,8 +64,7 @@ fun TextButton(
|
||||||
),
|
),
|
||||||
contentPadding: PaddingValues = M3ButtonDefaults.TextButtonContentPadding,
|
contentPadding: PaddingValues = M3ButtonDefaults.TextButtonContentPadding,
|
||||||
content: @Composable RowScope.() -> Unit,
|
content: @Composable RowScope.() -> Unit,
|
||||||
) =
|
) = Button(
|
||||||
Button(
|
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
onLongClick = onLongClick,
|
onLongClick = onLongClick,
|
||||||
|
|
|
@ -48,7 +48,10 @@ fun NavigationRail(
|
||||||
.padding(vertical = MaterialTheme.padding.tiny)
|
.padding(vertical = MaterialTheme.padding.tiny)
|
||||||
.selectableGroup(),
|
.selectableGroup(),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.tiny, alignment = Alignment.CenterVertically),
|
verticalArrangement = Arrangement.spacedBy(
|
||||||
|
MaterialTheme.padding.tiny,
|
||||||
|
alignment = Alignment.CenterVertically,
|
||||||
|
),
|
||||||
) {
|
) {
|
||||||
if (header != null) {
|
if (header != null) {
|
||||||
header()
|
header()
|
||||||
|
|
|
@ -99,7 +99,9 @@ import kotlin.math.max
|
||||||
@Composable
|
@Composable
|
||||||
fun Scaffold(
|
fun Scaffold(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
topBarScrollBehavior: TopAppBarScrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()),
|
topBarScrollBehavior: TopAppBarScrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(
|
||||||
|
rememberTopAppBarState(),
|
||||||
|
),
|
||||||
topBar: @Composable (TopAppBarScrollBehavior) -> Unit = {},
|
topBar: @Composable (TopAppBarScrollBehavior) -> Unit = {},
|
||||||
bottomBar: @Composable () -> Unit = {},
|
bottomBar: @Composable () -> Unit = {},
|
||||||
startBar: @Composable () -> Unit = {},
|
startBar: @Composable () -> Unit = {},
|
||||||
|
@ -116,7 +118,11 @@ fun Scaffold(
|
||||||
androidx.compose.material3.Surface(
|
androidx.compose.material3.Surface(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.nestedScroll(topBarScrollBehavior.nestedScrollConnection)
|
.nestedScroll(topBarScrollBehavior.nestedScrollConnection)
|
||||||
.onConsumedWindowInsetsChanged { remainingWindowInsets.insets = contentWindowInsets.exclude(it) }
|
.onConsumedWindowInsetsChanged {
|
||||||
|
remainingWindowInsets.insets = contentWindowInsets.exclude(
|
||||||
|
it,
|
||||||
|
)
|
||||||
|
}
|
||||||
.then(modifier),
|
.then(modifier),
|
||||||
color = containerColor,
|
color = containerColor,
|
||||||
contentColor = contentColor,
|
contentColor = contentColor,
|
||||||
|
@ -271,7 +277,10 @@ private fun ScaffoldLayout(
|
||||||
} else {
|
} else {
|
||||||
max(bottomBarHeightPx.toDp(), fabOffsetDp)
|
max(bottomBarHeightPx.toDp(), fabOffsetDp)
|
||||||
},
|
},
|
||||||
start = max(insets.calculateStartPadding((this@SubcomposeLayout).layoutDirection), startBarWidth.toDp()),
|
start = max(
|
||||||
|
insets.calculateStartPadding((this@SubcomposeLayout).layoutDirection),
|
||||||
|
startBarWidth.toDp(),
|
||||||
|
),
|
||||||
end = insets.calculateEndPadding((this@SubcomposeLayout).layoutDirection),
|
end = insets.calculateEndPadding((this@SubcomposeLayout).layoutDirection),
|
||||||
)
|
)
|
||||||
content(innerPadding)
|
content(innerPadding)
|
||||||
|
|
|
@ -104,9 +104,7 @@ private fun surfaceColorAtElevation(color: Color, elevation: Dp): Color {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ColorScheme.surfaceColorAtElevation(
|
private fun ColorScheme.surfaceColorAtElevation(elevation: Dp): Color {
|
||||||
elevation: Dp,
|
|
||||||
): Color {
|
|
||||||
if (elevation == 0.dp) return surface
|
if (elevation == 0.dp) return surface
|
||||||
val alpha = ((4.5f * ln(elevation.value + 1)) + 2f) / 100f
|
val alpha = ((4.5f * ln(elevation.value + 1)) + 2f) / 100f
|
||||||
return surfaceTint.copy(alpha = alpha).compositeOver(surface)
|
return surfaceTint.copy(alpha = alpha).compositeOver(surface)
|
||||||
|
|
|
@ -46,10 +46,7 @@ private fun Modifier.tabIndicatorOffset(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TabIndicator(
|
fun TabIndicator(currentTabPosition: TabPosition, currentPageOffsetFraction: Float) {
|
||||||
currentTabPosition: TabPosition,
|
|
||||||
currentPageOffsetFraction: Float,
|
|
||||||
) {
|
|
||||||
SecondaryIndicator(
|
SecondaryIndicator(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.tabIndicatorOffset(currentTabPosition, currentPageOffsetFraction)
|
.tabIndicatorOffset(currentTabPosition, currentPageOffsetFraction)
|
||||||
|
@ -59,10 +56,7 @@ fun TabIndicator(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TabText(
|
fun TabText(text: String, badgeCount: Int? = null) {
|
||||||
text: String,
|
|
||||||
badgeCount: Int? = null,
|
|
||||||
) {
|
|
||||||
val pillAlpha = if (isSystemInDarkTheme()) 0.12f else 0.08f
|
val pillAlpha = if (isSystemInDarkTheme()) 0.12f else 0.08f
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
|
|
|
@ -17,10 +17,7 @@ val CoverWidth = 58.dp
|
||||||
val CoverHeight = 87.dp
|
val CoverHeight = 87.dp
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun UpdatesMangaCover(
|
fun UpdatesMangaCover(modifier: GlanceModifier = GlanceModifier, cover: Bitmap?) {
|
||||||
modifier: GlanceModifier = GlanceModifier,
|
|
||||||
cover: Bitmap?,
|
|
||||||
) {
|
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.size(width = CoverWidth, height = CoverHeight)
|
.size(width = CoverWidth, height = CoverHeight)
|
||||||
|
|
|
@ -64,7 +64,9 @@ interface Source {
|
||||||
"Use the non-RxJava API instead",
|
"Use the non-RxJava API instead",
|
||||||
ReplaceWith("getMangaDetails"),
|
ReplaceWith("getMangaDetails"),
|
||||||
)
|
)
|
||||||
fun fetchMangaDetails(manga: SManga): Observable<SManga> = throw IllegalStateException("Not used")
|
fun fetchMangaDetails(manga: SManga): Observable<SManga> = throw IllegalStateException(
|
||||||
|
"Not used",
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an observable with all the available chapters for a manga.
|
* Returns an observable with all the available chapters for a manga.
|
||||||
|
@ -75,7 +77,9 @@ interface Source {
|
||||||
"Use the non-RxJava API instead",
|
"Use the non-RxJava API instead",
|
||||||
ReplaceWith("getChapterList"),
|
ReplaceWith("getChapterList"),
|
||||||
)
|
)
|
||||||
fun fetchChapterList(manga: SManga): Observable<List<SChapter>> = throw IllegalStateException("Not used")
|
fun fetchChapterList(manga: SManga): Observable<List<SChapter>> = throw IllegalStateException(
|
||||||
|
"Not used",
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an observable with the list of pages a chapter has. Pages should be returned
|
* Returns an observable with the list of pages a chapter has. Pages should be returned
|
||||||
|
|
|
@ -3,7 +3,10 @@ package eu.kanade.tachiyomi.source.model
|
||||||
sealed class Filter<T>(val name: String, var state: T) {
|
sealed class Filter<T>(val name: String, var state: T) {
|
||||||
open class Header(name: String) : Filter<Any>(name, 0)
|
open class Header(name: String) : Filter<Any>(name, 0)
|
||||||
open class Separator(name: String = "") : Filter<Any>(name, 0)
|
open class Separator(name: String = "") : Filter<Any>(name, 0)
|
||||||
abstract class Select<V>(name: String, val values: Array<V>, state: Int = 0) : Filter<Int>(name, state)
|
abstract class Select<V>(name: String, val values: Array<V>, state: Int = 0) : Filter<Int>(
|
||||||
|
name,
|
||||||
|
state,
|
||||||
|
)
|
||||||
abstract class Text(name: String, state: String = "") : Filter<String>(name, state)
|
abstract class Text(name: String, state: String = "") : Filter<String>(name, state)
|
||||||
abstract class CheckBox(name: String, state: Boolean = false) : Filter<Boolean>(name, state)
|
abstract class CheckBox(name: String, state: Boolean = false) : Filter<Boolean>(name, state)
|
||||||
abstract class TriState(name: String, state: Int = STATE_IGNORE) : Filter<Int>(name, state) {
|
abstract class TriState(name: String, state: Int = STATE_IGNORE) : Filter<Int>(name, state) {
|
||||||
|
|
|
@ -135,7 +135,11 @@ abstract class HttpSource : CatalogueSource {
|
||||||
* @param query the search query.
|
* @param query the search query.
|
||||||
* @param filters the list of filters to apply.
|
* @param filters the list of filters to apply.
|
||||||
*/
|
*/
|
||||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
override fun fetchSearchManga(
|
||||||
|
page: Int,
|
||||||
|
query: String,
|
||||||
|
filters: FilterList,
|
||||||
|
): Observable<MangasPage> {
|
||||||
return Observable.defer {
|
return Observable.defer {
|
||||||
try {
|
try {
|
||||||
client.newCall(searchMangaRequest(page, query, filters)).asObservableSuccess()
|
client.newCall(searchMangaRequest(page, query, filters)).asObservableSuccess()
|
||||||
|
@ -157,7 +161,11 @@ abstract class HttpSource : CatalogueSource {
|
||||||
* @param query the search query.
|
* @param query the search query.
|
||||||
* @param filters the list of filters to apply.
|
* @param filters the list of filters to apply.
|
||||||
*/
|
*/
|
||||||
protected abstract fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request
|
protected abstract fun searchMangaRequest(
|
||||||
|
page: Int,
|
||||||
|
query: String,
|
||||||
|
filters: FilterList,
|
||||||
|
): Request
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the response from the site and returns a [MangasPage] object.
|
* Parses the response from the site and returns a [MangasPage] object.
|
||||||
|
|
Loading…
Reference in a new issue