mirror of
https://github.com/mihonapp/mihon.git
synced 2024-11-14 20:39:59 -05:00
Address spotless lint errors (#1138)
* Add spotless (with ktlint) * Run spotlessApply * screaming case screaming case screaming case * Update PagerViewerAdapter.kt * Update ReaderTransitionView.kt
This commit is contained in:
parent
5ae8095ef1
commit
d6252ab770
230 changed files with 580 additions and 467 deletions
|
@ -17,7 +17,7 @@ if (gradle.startParameter.taskRequests.toString().contains("Standard")) {
|
||||||
|
|
||||||
shortcutHelper.setFilePath("./shortcuts.xml")
|
shortcutHelper.setFilePath("./shortcuts.xml")
|
||||||
|
|
||||||
val SUPPORTED_ABIS = setOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
|
val supportedAbis = setOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "eu.kanade.tachiyomi"
|
namespace = "eu.kanade.tachiyomi"
|
||||||
|
@ -35,7 +35,7 @@ android {
|
||||||
buildConfigField("boolean", "PREVIEW", "false")
|
buildConfigField("boolean", "PREVIEW", "false")
|
||||||
|
|
||||||
ndk {
|
ndk {
|
||||||
abiFilters += SUPPORTED_ABIS
|
abiFilters += supportedAbis
|
||||||
}
|
}
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
@ -45,7 +45,7 @@ android {
|
||||||
abi {
|
abi {
|
||||||
isEnable = true
|
isEnable = true
|
||||||
reset()
|
reset()
|
||||||
include(*SUPPORTED_ABIS.toTypedArray())
|
include(*supportedAbis.toTypedArray())
|
||||||
isUniversalApk = true
|
isUniversalApk = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,7 +158,7 @@ dependencies {
|
||||||
implementation(compose.ui.tooling.preview)
|
implementation(compose.ui.tooling.preview)
|
||||||
implementation(compose.ui.util)
|
implementation(compose.ui.util)
|
||||||
implementation(compose.accompanist.systemuicontroller)
|
implementation(compose.accompanist.systemuicontroller)
|
||||||
|
|
||||||
implementation(androidx.interpolator)
|
implementation(androidx.interpolator)
|
||||||
|
|
||||||
implementation(androidx.paging.runtime)
|
implementation(androidx.paging.runtime)
|
||||||
|
@ -236,7 +236,6 @@ dependencies {
|
||||||
implementation(libs.compose.webview)
|
implementation(libs.compose.webview)
|
||||||
implementation(libs.compose.grid)
|
implementation(libs.compose.grid)
|
||||||
|
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
implementation(libs.logcat)
|
implementation(libs.logcat)
|
||||||
|
|
||||||
|
|
|
@ -110,7 +110,10 @@ class SyncChaptersWithSource(
|
||||||
if (shouldUpdateDbChapter.await(dbChapter, chapter)) {
|
if (shouldUpdateDbChapter.await(dbChapter, chapter)) {
|
||||||
val shouldRenameChapter = downloadProvider.isChapterDirNameChanged(dbChapter, chapter) &&
|
val shouldRenameChapter = downloadProvider.isChapterDirNameChanged(dbChapter, chapter) &&
|
||||||
downloadManager.isChapterDownloaded(
|
downloadManager.isChapterDownloaded(
|
||||||
dbChapter.name, dbChapter.scanlator, manga.title, manga.source,
|
dbChapter.name,
|
||||||
|
dbChapter.scanlator,
|
||||||
|
manga.title,
|
||||||
|
manga.source,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (shouldRenameChapter) {
|
if (shouldRenameChapter) {
|
||||||
|
|
|
@ -19,7 +19,11 @@ class UiPreferences(
|
||||||
|
|
||||||
fun appTheme() = preferenceStore.getEnum(
|
fun appTheme() = preferenceStore.getEnum(
|
||||||
"pref_app_theme",
|
"pref_app_theme",
|
||||||
if (DeviceUtil.isDynamicColorAvailable) { AppTheme.MONET } else { AppTheme.DEFAULT },
|
if (DeviceUtil.isDynamicColorAvailable) {
|
||||||
|
AppTheme.MONET
|
||||||
|
} else {
|
||||||
|
AppTheme.DEFAULT
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
fun themeDarkAmoled() = preferenceStore.getBoolean("pref_theme_dark_amoled_key", false)
|
fun themeDarkAmoled() = preferenceStore.getBoolean("pref_theme_dark_amoled_key", false)
|
||||||
|
|
|
@ -232,7 +232,7 @@ private fun DetailsHeader(
|
||||||
Extension name: ${extension.name} (lang: ${extension.lang}; package: ${extension.pkgName})
|
Extension name: ${extension.name} (lang: ${extension.lang}; package: ${extension.pkgName})
|
||||||
Extension version: ${extension.versionName} (lib: ${extension.libVersion}; version code: ${extension.versionCode})
|
Extension version: ${extension.versionName} (lib: ${extension.libVersion}; version code: ${extension.versionCode})
|
||||||
NSFW: ${extension.isNsfw}
|
NSFW: ${extension.isNsfw}
|
||||||
""".trimIndent()
|
""".trimIndent(),
|
||||||
)
|
)
|
||||||
|
|
||||||
if (extension is Extension.Installed) {
|
if (extension is Extension.Installed) {
|
||||||
|
@ -242,8 +242,8 @@ private fun DetailsHeader(
|
||||||
Update available: ${extension.hasUpdate}
|
Update available: ${extension.hasUpdate}
|
||||||
Obsolete: ${extension.isObsolete}
|
Obsolete: ${extension.isObsolete}
|
||||||
Shared: ${extension.isShared}
|
Shared: ${extension.isShared}
|
||||||
Repository: ${extension.repoUrl}
|
Repository: ${extension.repoUrl}
|
||||||
""".trimIndent()
|
""".trimIndent(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -219,7 +219,9 @@ private fun ExtensionContent(
|
||||||
when (it) {
|
when (it) {
|
||||||
is Extension.Available -> onInstallExtension(it)
|
is Extension.Available -> onInstallExtension(it)
|
||||||
is Extension.Installed -> onOpenExtension(it)
|
is Extension.Installed -> onOpenExtension(it)
|
||||||
is Extension.Untrusted -> { trustState = it }
|
is Extension.Untrusted -> {
|
||||||
|
trustState = it
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLongClickItem = onLongClickItem,
|
onLongClickItem = onLongClickItem,
|
||||||
|
@ -241,7 +243,9 @@ private fun ExtensionContent(
|
||||||
onOpenExtension(it)
|
onOpenExtension(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Extension.Untrusted -> { trustState = it }
|
is Extension.Untrusted -> {
|
||||||
|
trustState = it
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -28,7 +28,7 @@ import tachiyomi.domain.source.model.Pin
|
||||||
import tachiyomi.domain.source.model.Source
|
import tachiyomi.domain.source.model.Source
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.components.ScrollbarLazyColumn
|
import tachiyomi.presentation.core.components.ScrollbarLazyColumn
|
||||||
import tachiyomi.presentation.core.components.material.SecondaryItemAlpha
|
import tachiyomi.presentation.core.components.material.SECONDARY_ALPHA
|
||||||
import tachiyomi.presentation.core.components.material.padding
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
import tachiyomi.presentation.core.components.material.topSmallPaddingValues
|
import tachiyomi.presentation.core.components.material.topSmallPaddingValues
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
|
@ -148,7 +148,7 @@ private fun SourcePinButton(
|
||||||
MaterialTheme.colorScheme.primary
|
MaterialTheme.colorScheme.primary
|
||||||
} else {
|
} else {
|
||||||
MaterialTheme.colorScheme.onBackground.copy(
|
MaterialTheme.colorScheme.onBackground.copy(
|
||||||
alpha = SecondaryItemAlpha,
|
alpha = SECONDARY_ALPHA,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val description = if (isPinned) MR.strings.action_unpin else MR.strings.action_pin
|
val description = if (isPinned) MR.strings.action_unpin else MR.strings.action_pin
|
||||||
|
|
|
@ -79,7 +79,7 @@ fun TabbedDialog(
|
||||||
modifier = Modifier.animateContentSize(),
|
modifier = Modifier.animateContentSize(),
|
||||||
state = pagerState,
|
state = pagerState,
|
||||||
verticalAlignment = Alignment.Top,
|
verticalAlignment = Alignment.Top,
|
||||||
pageContent = { page -> content(page) }
|
pageContent = { page -> content(page) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ private val ContinueReadingButtonIconSizeLarge = 20.dp
|
||||||
private val ContinueReadingButtonGridPadding = 6.dp
|
private val ContinueReadingButtonGridPadding = 6.dp
|
||||||
private val ContinueReadingButtonListSpacing = 8.dp
|
private val ContinueReadingButtonListSpacing = 8.dp
|
||||||
|
|
||||||
private const val GridSelectedCoverAlpha = 0.76f
|
private const val GRID_SELECTED_COVER_ALPHA = 0.76f
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Layout of grid list item with title overlaying the cover.
|
* Layout of grid list item with title overlaying the cover.
|
||||||
|
@ -90,7 +90,7 @@ fun MangaCompactGridItem(
|
||||||
MangaCover.Book(
|
MangaCover.Book(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.alpha(if (isSelected) GridSelectedCoverAlpha else coverAlpha),
|
.alpha(if (isSelected) GRID_SELECTED_COVER_ALPHA else coverAlpha),
|
||||||
data = coverData,
|
data = coverData,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -197,7 +197,7 @@ fun MangaComfortableGridItem(
|
||||||
MangaCover.Book(
|
MangaCover.Book(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.alpha(if (isSelected) GridSelectedCoverAlpha else coverAlpha),
|
.alpha(if (isSelected) GRID_SELECTED_COVER_ALPHA else coverAlpha),
|
||||||
data = coverData,
|
data = coverData,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -371,7 +371,7 @@ fun MangaListItem(
|
||||||
size = ContinueReadingButtonSizeSmall,
|
size = ContinueReadingButtonSizeSmall,
|
||||||
iconSize = ContinueReadingButtonIconSizeSmall,
|
iconSize = ContinueReadingButtonIconSizeSmall,
|
||||||
onClick = onClickContinueReading,
|
onClick = onClickContinueReading,
|
||||||
modifier = Modifier.padding(start = ContinueReadingButtonListSpacing)
|
modifier = Modifier.padding(start = ContinueReadingButtonListSpacing),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -392,7 +392,7 @@ private fun ContinueReadingButton(
|
||||||
containerColor = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.9f),
|
containerColor = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.9f),
|
||||||
contentColor = contentColorFor(MaterialTheme.colorScheme.primaryContainer),
|
contentColor = contentColorFor(MaterialTheme.colorScheme.primaryContainer),
|
||||||
),
|
),
|
||||||
modifier = Modifier.size(size)
|
modifier = Modifier.size(size),
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Filled.PlayArrow,
|
imageVector = Icons.Filled.PlayArrow,
|
||||||
|
|
|
@ -12,7 +12,7 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.components.material.SecondaryItemAlpha
|
import tachiyomi.presentation.core.components.material.SECONDARY_ALPHA
|
||||||
import tachiyomi.presentation.core.components.material.padding
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
import tachiyomi.presentation.core.i18n.pluralStringResource
|
import tachiyomi.presentation.core.i18n.pluralStringResource
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
|
@ -60,6 +60,6 @@ private fun MissingChaptersWarning(count: Int) {
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
color = MaterialTheme.colorScheme.error.copy(alpha = SecondaryItemAlpha),
|
color = MaterialTheme.colorScheme.error.copy(alpha = SECONDARY_ALPHA),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,8 +40,8 @@ import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
import me.saket.swipe.SwipeableActionsBox
|
import me.saket.swipe.SwipeableActionsBox
|
||||||
import tachiyomi.domain.library.service.LibraryPreferences
|
import tachiyomi.domain.library.service.LibraryPreferences
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.components.material.ReadItemAlpha
|
import tachiyomi.presentation.core.components.material.DISABLED_ALPHA
|
||||||
import tachiyomi.presentation.core.components.material.SecondaryItemAlpha
|
import tachiyomi.presentation.core.components.material.SECONDARY_ALPHA
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
import tachiyomi.presentation.core.util.selectedBackground
|
import tachiyomi.presentation.core.util.selectedBackground
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ fun MangaChapterListItem(
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
onTextLayout = { textHeight = it.size.height },
|
onTextLayout = { textHeight = it.size.height },
|
||||||
color = LocalContentColor.current.copy(alpha = if (read) ReadItemAlpha else 1f),
|
color = LocalContentColor.current.copy(alpha = if (read) DISABLED_ALPHA else 1f),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ fun MangaChapterListItem(
|
||||||
val subtitleStyle = MaterialTheme.typography.bodySmall
|
val subtitleStyle = MaterialTheme.typography.bodySmall
|
||||||
.merge(
|
.merge(
|
||||||
color = LocalContentColor.current
|
color = LocalContentColor.current
|
||||||
.copy(alpha = if (read) ReadItemAlpha else SecondaryItemAlpha)
|
.copy(alpha = if (read) DISABLED_ALPHA else SECONDARY_ALPHA),
|
||||||
)
|
)
|
||||||
ProvideTextStyle(value = subtitleStyle) {
|
ProvideTextStyle(value = subtitleStyle) {
|
||||||
if (date != null) {
|
if (date != null) {
|
||||||
|
@ -156,7 +156,7 @@ fun MangaChapterListItem(
|
||||||
text = readProgress,
|
text = readProgress,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
color = LocalContentColor.current.copy(alpha = ReadItemAlpha),
|
color = LocalContentColor.current.copy(alpha = DISABLED_ALPHA),
|
||||||
)
|
)
|
||||||
if (scanlator != null) DotSeparatorText()
|
if (scanlator != null) DotSeparatorText()
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,6 +81,7 @@ import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
|
import tachiyomi.presentation.core.components.material.DISABLED_ALPHA
|
||||||
import tachiyomi.presentation.core.components.material.TextButton
|
import tachiyomi.presentation.core.components.material.TextButton
|
||||||
import tachiyomi.presentation.core.components.material.padding
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
import tachiyomi.presentation.core.i18n.pluralStringResource
|
import tachiyomi.presentation.core.i18n.pluralStringResource
|
||||||
|
@ -177,7 +178,7 @@ fun MangaActionRow(
|
||||||
onEditCategory: (() -> Unit)?,
|
onEditCategory: (() -> Unit)?,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val defaultActionButtonColor = MaterialTheme.colorScheme.onSurface.copy(alpha = .38f)
|
val defaultActionButtonColor = MaterialTheme.colorScheme.onSurface.copy(alpha = DISABLED_ALPHA)
|
||||||
|
|
||||||
// TODO: show something better when using custom interval
|
// TODO: show something better when using custom interval
|
||||||
val nextUpdateDays = remember(nextUpdate) {
|
val nextUpdateDays = remember(nextUpdate) {
|
||||||
|
|
|
@ -75,7 +75,7 @@ private fun NewUpdateScreenPreview() {
|
||||||
changelogInfo = """
|
changelogInfo = """
|
||||||
## Yay
|
## Yay
|
||||||
Foobar
|
Foobar
|
||||||
|
|
||||||
### More info
|
### More info
|
||||||
- Hello
|
- Hello
|
||||||
- World
|
- World
|
||||||
|
|
|
@ -340,7 +340,7 @@ object SettingsAdvancedScreen : SearchableSettings {
|
||||||
chooseColorProfile.launch(arrayOf("*/*"))
|
chooseColorProfile.launch(arrayOf("*/*"))
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ class OpenSourceLicensesScreen : Screen() {
|
||||||
name = it.name,
|
name = it.name,
|
||||||
website = it.website,
|
website = it.website,
|
||||||
license = it.licenses.firstOrNull()?.htmlReadyLicenseContent.orEmpty(),
|
license = it.licenses.firstOrNull()?.htmlReadyLicenseContent.orEmpty(),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -28,7 +28,7 @@ import tachiyomi.presentation.core.i18n.stringResource
|
||||||
class BackupSchemaScreen : Screen() {
|
class BackupSchemaScreen : Screen() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val title = "Backup file schema"
|
const val TITLE = "Backup file schema"
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -41,7 +41,7 @@ class BackupSchemaScreen : Screen() {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
AppBar(
|
AppBar(
|
||||||
title = title,
|
title = TITLE,
|
||||||
navigateUp = navigator::pop,
|
navigateUp = navigator::pop,
|
||||||
actions = {
|
actions = {
|
||||||
AppBarActions(
|
AppBarActions(
|
||||||
|
@ -50,7 +50,7 @@ class BackupSchemaScreen : Screen() {
|
||||||
title = stringResource(MR.strings.action_copy_to_clipboard),
|
title = stringResource(MR.strings.action_copy_to_clipboard),
|
||||||
icon = Icons.Default.ContentCopy,
|
icon = Icons.Default.ContentCopy,
|
||||||
onClick = {
|
onClick = {
|
||||||
context.copyToClipboard(title, schema)
|
context.copyToClipboard(TITLE, schema)
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -31,11 +31,11 @@ class DebugInfoScreen : Screen() {
|
||||||
itemsProvider = {
|
itemsProvider = {
|
||||||
listOf(
|
listOf(
|
||||||
Preference.PreferenceItem.TextPreference(
|
Preference.PreferenceItem.TextPreference(
|
||||||
title = WorkerInfoScreen.title,
|
title = WorkerInfoScreen.TITLE,
|
||||||
onClick = { navigator.push(WorkerInfoScreen()) },
|
onClick = { navigator.push(WorkerInfoScreen()) },
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.TextPreference(
|
Preference.PreferenceItem.TextPreference(
|
||||||
title = BackupSchemaScreen.title,
|
title = BackupSchemaScreen.TITLE,
|
||||||
onClick = { navigator.push(BackupSchemaScreen()) },
|
onClick = { navigator.push(BackupSchemaScreen()) },
|
||||||
),
|
),
|
||||||
getAppInfoGroup(),
|
getAppInfoGroup(),
|
||||||
|
|
|
@ -49,7 +49,7 @@ import java.time.ZoneId
|
||||||
class WorkerInfoScreen : Screen() {
|
class WorkerInfoScreen : Screen() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val title = "Worker info"
|
const val TITLE = "Worker info"
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -65,7 +65,7 @@ class WorkerInfoScreen : Screen() {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
AppBar(
|
AppBar(
|
||||||
title = title,
|
title = TITLE,
|
||||||
navigateUp = navigator::pop,
|
navigateUp = navigator::pop,
|
||||||
actions = {
|
actions = {
|
||||||
AppBarActions(
|
AppBarActions(
|
||||||
|
@ -74,7 +74,7 @@ class WorkerInfoScreen : Screen() {
|
||||||
title = stringResource(MR.strings.action_copy_to_clipboard),
|
title = stringResource(MR.strings.action_copy_to_clipboard),
|
||||||
icon = Icons.Default.ContentCopy,
|
icon = Icons.Default.ContentCopy,
|
||||||
onClick = {
|
onClick = {
|
||||||
context.copyToClipboard(title, enqueued + finished + running)
|
context.copyToClipboard(TITLE, enqueued + finished + running)
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -159,7 +159,7 @@ class WorkerInfoScreen : Screen() {
|
||||||
Injekt.get<UiPreferences>().dateFormat().get(),
|
Injekt.get<UiPreferences>().dateFormat().get(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
appendLine("Next scheduled run: $timestamp",)
|
appendLine("Next scheduled run: $timestamp")
|
||||||
appendLine("Attempt #${workInfo.runAttemptCount + 1}")
|
appendLine("Attempt #${workInfo.runAttemptCount + 1}")
|
||||||
}
|
}
|
||||||
appendLine()
|
appendLine()
|
||||||
|
|
|
@ -32,7 +32,9 @@ import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
|
|
||||||
private enum class State {
|
private enum class State {
|
||||||
CHECKED, INVERSED, UNCHECKED
|
CHECKED,
|
||||||
|
INVERSED,
|
||||||
|
UNCHECKED,
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
@ -15,7 +15,7 @@ import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import tachiyomi.presentation.core.components.material.SecondaryItemAlpha
|
import tachiyomi.presentation.core.components.material.SECONDARY_ALPHA
|
||||||
import tachiyomi.presentation.core.components.material.padding
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -73,7 +73,7 @@ private fun RowScope.BaseStatsItem(
|
||||||
style = subtitleStyle
|
style = subtitleStyle
|
||||||
.copy(
|
.copy(
|
||||||
color = MaterialTheme.colorScheme.onSurface
|
color = MaterialTheme.colorScheme.onSurface
|
||||||
.copy(alpha = SecondaryItemAlpha),
|
.copy(alpha = SECONDARY_ALPHA),
|
||||||
),
|
),
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
)
|
)
|
||||||
|
|
|
@ -226,7 +226,7 @@ private fun ChapterText(
|
||||||
Text(
|
Text(
|
||||||
text = buildAnnotatedString {
|
text = buildAnnotatedString {
|
||||||
if (downloaded) {
|
if (downloaded) {
|
||||||
appendInlineContent(DownloadedIconContentId)
|
appendInlineContent(DOWNLOADED_ICON_ID)
|
||||||
append(' ')
|
append(' ')
|
||||||
}
|
}
|
||||||
append(name)
|
append(name)
|
||||||
|
@ -236,7 +236,7 @@ private fun ChapterText(
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
style = MaterialTheme.typography.titleLarge,
|
style = MaterialTheme.typography.titleLarge,
|
||||||
inlineContent = persistentMapOf(
|
inlineContent = persistentMapOf(
|
||||||
DownloadedIconContentId to InlineTextContent(
|
DOWNLOADED_ICON_ID to InlineTextContent(
|
||||||
Placeholder(
|
Placeholder(
|
||||||
width = 22.sp,
|
width = 22.sp,
|
||||||
height = 22.sp,
|
height = 22.sp,
|
||||||
|
@ -273,7 +273,7 @@ private val CardColor: CardColors
|
||||||
)
|
)
|
||||||
|
|
||||||
private val VerticalSpacerSize = 24.dp
|
private val VerticalSpacerSize = 24.dp
|
||||||
private const val DownloadedIconContentId = "downloaded"
|
private const val DOWNLOADED_ICON_ID = "downloaded"
|
||||||
|
|
||||||
private fun previewChapter(name: String, scanlator: String, chapterNumber: Double) = Chapter.create().copy(
|
private fun previewChapter(name: String, scanlator: String, chapterNumber: Double) = Chapter.create().copy(
|
||||||
id = 0L,
|
id = 0L,
|
||||||
|
|
|
@ -38,7 +38,6 @@ import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.alpha
|
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
@ -58,8 +57,6 @@ import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
private const val UnsetStatusTextAlpha = 0.5F
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TrackInfoDialogHome(
|
fun TrackInfoDialogHome(
|
||||||
trackItems: List<TrackItem>,
|
trackItems: List<TrackItem>,
|
||||||
|
@ -211,10 +208,9 @@ private fun TrackInfoItem(
|
||||||
if (onScoreClick != null) {
|
if (onScoreClick != null) {
|
||||||
VerticalDivider()
|
VerticalDivider()
|
||||||
TrackDetailsItem(
|
TrackDetailsItem(
|
||||||
modifier = Modifier
|
modifier = Modifier.weight(1f),
|
||||||
.weight(1f)
|
text = score,
|
||||||
.alpha(if (score == null) UnsetStatusTextAlpha else 1f),
|
placeholder = stringResource(MR.strings.score),
|
||||||
text = score ?: stringResource(MR.strings.score),
|
|
||||||
onClick = onScoreClick,
|
onClick = onScoreClick,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -243,6 +239,8 @@ private fun TrackInfoItem(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const val UNSET_TEXT_ALPHA = 0.5F
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun TrackDetailsItem(
|
private fun TrackDetailsItem(
|
||||||
text: String?,
|
text: String?,
|
||||||
|
@ -263,7 +261,7 @@ private fun TrackDetailsItem(
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = if (text == null) UnsetStatusTextAlpha else 1f),
|
color = MaterialTheme.colorScheme.onSurface.copy(alpha = if (text == null) UNSET_TEXT_ALPHA else 1f),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ import eu.kanade.tachiyomi.ui.updates.UpdatesItem
|
||||||
import tachiyomi.domain.updates.model.UpdatesWithRelations
|
import tachiyomi.domain.updates.model.UpdatesWithRelations
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.components.ListGroupHeader
|
import tachiyomi.presentation.core.components.ListGroupHeader
|
||||||
import tachiyomi.presentation.core.components.material.ReadItemAlpha
|
import tachiyomi.presentation.core.components.material.DISABLED_ALPHA
|
||||||
import tachiyomi.presentation.core.components.material.padding
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
import tachiyomi.presentation.core.util.selectedBackground
|
import tachiyomi.presentation.core.util.selectedBackground
|
||||||
|
@ -146,7 +146,7 @@ private fun UpdatesUiItem(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val haptic = LocalHapticFeedback.current
|
val haptic = LocalHapticFeedback.current
|
||||||
val textAlpha = if (update.read) ReadItemAlpha else 1f
|
val textAlpha = if (update.read) DISABLED_ALPHA else 1f
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
@ -220,7 +220,7 @@ private fun UpdatesUiItem(
|
||||||
Text(
|
Text(
|
||||||
text = readProgress,
|
text = readProgress,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
color = LocalContentColor.current.copy(alpha = ReadItemAlpha),
|
color = LocalContentColor.current.copy(alpha = DISABLED_ALPHA),
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package eu.kanade.presentation.util
|
package eu.kanade.presentation.util
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import androidx.compose.animation.AnimatedContent
|
import androidx.compose.animation.AnimatedContent
|
||||||
import androidx.compose.animation.AnimatedContentTransitionScope
|
import androidx.compose.animation.AnimatedContentTransitionScope
|
||||||
import androidx.compose.animation.ContentTransform
|
import androidx.compose.animation.ContentTransform
|
||||||
|
|
|
@ -167,7 +167,7 @@ class BackupRestorer(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.restoreExtensionRepos(
|
private fun CoroutineScope.restoreExtensionRepos(
|
||||||
backupExtensionRepo: List<BackupExtensionRepos>
|
backupExtensionRepo: List<BackupExtensionRepos>,
|
||||||
) = launch {
|
) = launch {
|
||||||
backupExtensionRepo
|
backupExtensionRepo
|
||||||
.forEach {
|
.forEach {
|
||||||
|
|
|
@ -9,7 +9,7 @@ data class RestoreOptions(
|
||||||
val categories: Boolean = true,
|
val categories: Boolean = true,
|
||||||
val appSettings: Boolean = true,
|
val appSettings: Boolean = true,
|
||||||
val extensionRepoSettings: Boolean = true,
|
val extensionRepoSettings: Boolean = true,
|
||||||
val sourceSettings: Boolean = true
|
val sourceSettings: Boolean = true,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun asBooleanArray() = booleanArrayOf(
|
fun asBooleanArray() = booleanArrayOf(
|
||||||
|
@ -17,7 +17,7 @@ data class RestoreOptions(
|
||||||
categories,
|
categories,
|
||||||
appSettings,
|
appSettings,
|
||||||
extensionRepoSettings,
|
extensionRepoSettings,
|
||||||
sourceSettings
|
sourceSettings,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun canRestore() = libraryEntries || categories || appSettings || extensionRepoSettings || sourceSettings
|
fun canRestore() = libraryEntries || categories || appSettings || extensionRepoSettings || sourceSettings
|
||||||
|
|
|
@ -8,7 +8,7 @@ import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
class ExtensionRepoRestorer(
|
class ExtensionRepoRestorer(
|
||||||
private val handler: DatabaseHandler = Injekt.get(),
|
private val handler: DatabaseHandler = Injekt.get(),
|
||||||
private val getExtensionRepos: GetExtensionRepo = Injekt.get()
|
private val getExtensionRepos: GetExtensionRepo = Injekt.get(),
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend operator fun invoke(
|
suspend operator fun invoke(
|
||||||
|
@ -32,7 +32,7 @@ class ExtensionRepoRestorer(
|
||||||
backupRepo.name,
|
backupRepo.name,
|
||||||
backupRepo.shortName,
|
backupRepo.shortName,
|
||||||
backupRepo.website,
|
backupRepo.website,
|
||||||
backupRepo.signingKeyFingerprint
|
backupRepo.signingKeyFingerprint,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,7 @@ class MangaCoverFetcher(
|
||||||
source = ImageSource(
|
source = ImageSource(
|
||||||
file = file.toOkioPath(),
|
file = file.toOkioPath(),
|
||||||
fileSystem = FileSystem.SYSTEM,
|
fileSystem = FileSystem.SYSTEM,
|
||||||
diskCacheKey = diskCacheKey
|
diskCacheKey = diskCacheKey,
|
||||||
),
|
),
|
||||||
mimeType = "image/*",
|
mimeType = "image/*",
|
||||||
dataSource = DataSource.DISK,
|
dataSource = DataSource.DISK,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
@file:Suppress("PropertyName", "ktlint:standard:property-naming")
|
||||||
|
|
||||||
package eu.kanade.tachiyomi.data.database.models
|
package eu.kanade.tachiyomi.data.database.models
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
@file:Suppress("PropertyName", "ktlint:standard:property-naming")
|
||||||
|
|
||||||
package eu.kanade.tachiyomi.data.database.models
|
package eu.kanade.tachiyomi.data.database.models
|
||||||
|
|
||||||
class ChapterImpl : Chapter {
|
class ChapterImpl : Chapter {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
@file:Suppress("PropertyName", "ktlint:standard:property-naming")
|
||||||
|
|
||||||
package eu.kanade.tachiyomi.data.database.models
|
package eu.kanade.tachiyomi.data.database.models
|
||||||
|
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
@file:Suppress("PropertyName", "ktlint:standard:property-naming")
|
||||||
|
|
||||||
package eu.kanade.tachiyomi.data.database.models
|
package eu.kanade.tachiyomi.data.database.models
|
||||||
|
|
||||||
class TrackImpl : Track {
|
class TrackImpl : Track {
|
||||||
|
|
|
@ -180,7 +180,7 @@ class Downloader(
|
||||||
fun clearQueue() {
|
fun clearQueue() {
|
||||||
cancelDownloaderJob()
|
cancelDownloaderJob()
|
||||||
|
|
||||||
_clearQueue()
|
internalClearQueue()
|
||||||
notifier.dismissProgress()
|
notifier.dismissProgress()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,9 +194,12 @@ class Downloader(
|
||||||
val activeDownloadsFlow = queueState.transformLatest { queue ->
|
val activeDownloadsFlow = queueState.transformLatest { queue ->
|
||||||
while (true) {
|
while (true) {
|
||||||
val activeDownloads = queue.asSequence()
|
val activeDownloads = queue.asSequence()
|
||||||
.filter { it.status.value <= Download.State.DOWNLOADING.value } // Ignore completed downloads, leave them in the queue
|
// Ignore completed downloads, leave them in the queue
|
||||||
|
.filter { it.status.value <= Download.State.DOWNLOADING.value }
|
||||||
.groupBy { it.source }
|
.groupBy { it.source }
|
||||||
.toList().take(5) // Concurrently download from 5 different sources
|
.toList()
|
||||||
|
// Concurrently download from 5 different sources
|
||||||
|
.take(5)
|
||||||
.map { (_, downloads) -> downloads.first() }
|
.map { (_, downloads) -> downloads.first() }
|
||||||
emit(activeDownloads)
|
emit(activeDownloads)
|
||||||
|
|
||||||
|
@ -616,7 +619,7 @@ class Downloader(
|
||||||
chapter,
|
chapter,
|
||||||
urls,
|
urls,
|
||||||
categories,
|
categories,
|
||||||
source.name
|
source.name,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Remove the old file
|
// Remove the old file
|
||||||
|
@ -676,7 +679,7 @@ class Downloader(
|
||||||
removeFromQueueIf { it.manga.id == manga.id }
|
removeFromQueueIf { it.manga.id == manga.id }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun _clearQueue() {
|
private fun internalClearQueue() {
|
||||||
_queueState.update {
|
_queueState.update {
|
||||||
it.forEach { download ->
|
it.forEach { download ->
|
||||||
if (download.status == Download.State.DOWNLOADING || download.status == Download.State.QUEUE) {
|
if (download.status == Download.State.DOWNLOADING || download.status == Download.State.QUEUE) {
|
||||||
|
@ -698,7 +701,7 @@ class Downloader(
|
||||||
}
|
}
|
||||||
|
|
||||||
pause()
|
pause()
|
||||||
_clearQueue()
|
internalClearQueue()
|
||||||
addAllToQueue(downloads)
|
addAllToQueue(downloads)
|
||||||
|
|
||||||
if (wasRunning) {
|
if (wasRunning) {
|
||||||
|
|
|
@ -437,7 +437,9 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||||
val constraints = Constraints(
|
val constraints = Constraints(
|
||||||
requiredNetworkType = if (DEVICE_NETWORK_NOT_METERED in restrictions) {
|
requiredNetworkType = if (DEVICE_NETWORK_NOT_METERED in restrictions) {
|
||||||
NetworkType.UNMETERED
|
NetworkType.UNMETERED
|
||||||
} else { NetworkType.CONNECTED },
|
} else {
|
||||||
|
NetworkType.CONNECTED
|
||||||
|
},
|
||||||
requiresCharging = DEVICE_CHARGING in restrictions,
|
requiresCharging = DEVICE_CHARGING in restrictions,
|
||||||
requiresBatteryNotLow = true,
|
requiresBatteryNotLow = true,
|
||||||
)
|
)
|
||||||
|
|
|
@ -47,10 +47,10 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||||
return withIOContext {
|
return withIOContext {
|
||||||
val query = """
|
val query = """
|
||||||
|mutation AddManga(${'$'}mangaId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus) {
|
|mutation AddManga(${'$'}mangaId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus) {
|
||||||
|SaveMediaListEntry (mediaId: ${'$'}mangaId, progress: ${'$'}progress, status: ${'$'}status) {
|
|SaveMediaListEntry (mediaId: ${'$'}mangaId, progress: ${'$'}progress, status: ${'$'}status) {
|
||||||
| id
|
| id
|
||||||
| status
|
| status
|
||||||
|}
|
|}
|
||||||
|}
|
|}
|
||||||
|
|
|
|
||||||
""".trimMargin()
|
""".trimMargin()
|
||||||
|
@ -65,7 +65,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||||
with(json) {
|
with(json) {
|
||||||
authClient.newCall(
|
authClient.newCall(
|
||||||
POST(
|
POST(
|
||||||
apiUrl,
|
API_URL,
|
||||||
body = payload.toString().toRequestBody(jsonMime),
|
body = payload.toString().toRequestBody(jsonMime),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -109,7 +109,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||||
put("completedAt", createDate(track.finished_reading_date))
|
put("completedAt", createDate(track.finished_reading_date))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
authClient.newCall(POST(apiUrl, body = payload.toString().toRequestBody(jsonMime)))
|
authClient.newCall(POST(API_URL, body = payload.toString().toRequestBody(jsonMime)))
|
||||||
.awaitSuccess()
|
.awaitSuccess()
|
||||||
track
|
track
|
||||||
}
|
}
|
||||||
|
@ -119,9 +119,9 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||||
withIOContext {
|
withIOContext {
|
||||||
val query = """
|
val query = """
|
||||||
|mutation DeleteManga(${'$'}listId: Int) {
|
|mutation DeleteManga(${'$'}listId: Int) {
|
||||||
|DeleteMediaListEntry(id: ${'$'}listId) {
|
|DeleteMediaListEntry(id: ${'$'}listId) {
|
||||||
|deleted
|
|deleted
|
||||||
|}
|
|}
|
||||||
|}
|
|}
|
||||||
|
|
|
|
||||||
""".trimMargin()
|
""".trimMargin()
|
||||||
|
@ -131,7 +131,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||||
put("listId", track.libraryId)
|
put("listId", track.libraryId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
authClient.newCall(POST(apiUrl, body = payload.toString().toRequestBody(jsonMime)))
|
authClient.newCall(POST(API_URL, body = payload.toString().toRequestBody(jsonMime)))
|
||||||
.awaitSuccess()
|
.awaitSuccess()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -172,7 +172,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||||
with(json) {
|
with(json) {
|
||||||
authClient.newCall(
|
authClient.newCall(
|
||||||
POST(
|
POST(
|
||||||
apiUrl,
|
API_URL,
|
||||||
body = payload.toString().toRequestBody(jsonMime),
|
body = payload.toString().toRequestBody(jsonMime),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -242,7 +242,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||||
with(json) {
|
with(json) {
|
||||||
authClient.newCall(
|
authClient.newCall(
|
||||||
POST(
|
POST(
|
||||||
apiUrl,
|
API_URL,
|
||||||
body = payload.toString().toRequestBody(jsonMime),
|
body = payload.toString().toRequestBody(jsonMime),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -286,7 +286,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||||
with(json) {
|
with(json) {
|
||||||
authClient.newCall(
|
authClient.newCall(
|
||||||
POST(
|
POST(
|
||||||
apiUrl,
|
API_URL,
|
||||||
body = payload.toString().toRequestBody(jsonMime),
|
body = payload.toString().toRequestBody(jsonMime),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -364,17 +364,17 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val clientId = "16329"
|
private const val CLIENT_ID = "16329"
|
||||||
private const val apiUrl = "https://graphql.anilist.co/"
|
private const val API_URL = "https://graphql.anilist.co/"
|
||||||
private const val baseUrl = "https://anilist.co/api/v2/"
|
private const val BASE_URL = "https://anilist.co/api/v2/"
|
||||||
private const val baseMangaUrl = "https://anilist.co/manga/"
|
private const val BASE_MANGA_URL = "https://anilist.co/manga/"
|
||||||
|
|
||||||
fun mangaUrl(mediaId: Long): String {
|
fun mangaUrl(mediaId: Long): String {
|
||||||
return baseMangaUrl + mediaId
|
return BASE_MANGA_URL + mediaId
|
||||||
}
|
}
|
||||||
|
|
||||||
fun authUrl(): Uri = "${baseUrl}oauth/authorize".toUri().buildUpon()
|
fun authUrl(): Uri = "${BASE_URL}oauth/authorize".toUri().buildUpon()
|
||||||
.appendQueryParameter("client_id", clientId)
|
.appendQueryParameter("client_id", CLIENT_ID)
|
||||||
.appendQueryParameter("response_type", "token")
|
.appendQueryParameter("response_type", "token")
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ class BangumiApi(
|
||||||
.add("rating", track.score.toInt().toString())
|
.add("rating", track.score.toInt().toString())
|
||||||
.add("status", track.toBangumiStatus())
|
.add("status", track.toBangumiStatus())
|
||||||
.build()
|
.build()
|
||||||
authClient.newCall(POST("$apiUrl/collection/${track.remote_id}/update", body = body))
|
authClient.newCall(POST("$API_URL/collection/${track.remote_id}/update", body = body))
|
||||||
.awaitSuccess()
|
.awaitSuccess()
|
||||||
track
|
track
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ class BangumiApi(
|
||||||
.add("rating", track.score.toInt().toString())
|
.add("rating", track.score.toInt().toString())
|
||||||
.add("status", track.toBangumiStatus())
|
.add("status", track.toBangumiStatus())
|
||||||
.build()
|
.build()
|
||||||
authClient.newCall(POST("$apiUrl/collection/${track.remote_id}/update", body = sbody))
|
authClient.newCall(POST("$API_URL/collection/${track.remote_id}/update", body = sbody))
|
||||||
.awaitSuccess()
|
.awaitSuccess()
|
||||||
|
|
||||||
// chapter update
|
// chapter update
|
||||||
|
@ -64,7 +64,7 @@ class BangumiApi(
|
||||||
.build()
|
.build()
|
||||||
authClient.newCall(
|
authClient.newCall(
|
||||||
POST(
|
POST(
|
||||||
"$apiUrl/subject/${track.remote_id}/update/watched_eps",
|
"$API_URL/subject/${track.remote_id}/update/watched_eps",
|
||||||
body = body,
|
body = body,
|
||||||
),
|
),
|
||||||
).awaitSuccess()
|
).awaitSuccess()
|
||||||
|
@ -75,7 +75,7 @@ class BangumiApi(
|
||||||
|
|
||||||
suspend fun search(search: String): List<TrackSearch> {
|
suspend fun search(search: String): List<TrackSearch> {
|
||||||
return withIOContext {
|
return withIOContext {
|
||||||
val url = "$apiUrl/search/subject/${URLEncoder.encode(search, StandardCharsets.UTF_8.name())}"
|
val url = "$API_URL/search/subject/${URLEncoder.encode(search, StandardCharsets.UTF_8.name())}"
|
||||||
.toUri()
|
.toUri()
|
||||||
.buildUpon()
|
.buildUpon()
|
||||||
.appendQueryParameter("max_results", "20")
|
.appendQueryParameter("max_results", "20")
|
||||||
|
@ -124,7 +124,7 @@ class BangumiApi(
|
||||||
suspend fun findLibManga(track: Track): Track? {
|
suspend fun findLibManga(track: Track): Track? {
|
||||||
return withIOContext {
|
return withIOContext {
|
||||||
with(json) {
|
with(json) {
|
||||||
authClient.newCall(GET("$apiUrl/subject/${track.remote_id}"))
|
authClient.newCall(GET("$API_URL/subject/${track.remote_id}"))
|
||||||
.awaitSuccess()
|
.awaitSuccess()
|
||||||
.parseAs<JsonObject>()
|
.parseAs<JsonObject>()
|
||||||
.let { jsonToSearch(it) }
|
.let { jsonToSearch(it) }
|
||||||
|
@ -134,7 +134,7 @@ class BangumiApi(
|
||||||
|
|
||||||
suspend fun statusLibManga(track: Track): Track? {
|
suspend fun statusLibManga(track: Track): Track? {
|
||||||
return withIOContext {
|
return withIOContext {
|
||||||
val urlUserRead = "$apiUrl/collection/${track.remote_id}"
|
val urlUserRead = "$API_URL/collection/${track.remote_id}"
|
||||||
val requestUserRead = Request.Builder()
|
val requestUserRead = Request.Builder()
|
||||||
.url(urlUserRead)
|
.url(urlUserRead)
|
||||||
.cacheControl(CacheControl.FORCE_NETWORK)
|
.cacheControl(CacheControl.FORCE_NETWORK)
|
||||||
|
@ -171,41 +171,41 @@ class BangumiApi(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun accessTokenRequest(code: String) = POST(
|
private fun accessTokenRequest(code: String) = POST(
|
||||||
oauthUrl,
|
OAUTH_URL,
|
||||||
body = FormBody.Builder()
|
body = FormBody.Builder()
|
||||||
.add("grant_type", "authorization_code")
|
.add("grant_type", "authorization_code")
|
||||||
.add("client_id", clientId)
|
.add("client_id", CLIENT_ID)
|
||||||
.add("client_secret", clientSecret)
|
.add("client_secret", CLIENT_SECRET)
|
||||||
.add("code", code)
|
.add("code", code)
|
||||||
.add("redirect_uri", redirectUrl)
|
.add("redirect_uri", REDIRECT_URL)
|
||||||
.build(),
|
.build(),
|
||||||
)
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val clientId = "bgm291665acbd06a4c28"
|
private const val CLIENT_ID = "bgm291665acbd06a4c28"
|
||||||
private const val clientSecret = "43e5ce36b207de16e5d3cfd3e79118db"
|
private const val CLIENT_SECRET = "43e5ce36b207de16e5d3cfd3e79118db"
|
||||||
|
|
||||||
private const val apiUrl = "https://api.bgm.tv"
|
private const val API_URL = "https://api.bgm.tv"
|
||||||
private const val oauthUrl = "https://bgm.tv/oauth/access_token"
|
private const val OAUTH_URL = "https://bgm.tv/oauth/access_token"
|
||||||
private const val loginUrl = "https://bgm.tv/oauth/authorize"
|
private const val LOGIN_URL = "https://bgm.tv/oauth/authorize"
|
||||||
|
|
||||||
private const val redirectUrl = "mihon://bangumi-auth"
|
private const val REDIRECT_URL = "mihon://bangumi-auth"
|
||||||
|
|
||||||
fun authUrl(): Uri =
|
fun authUrl(): Uri =
|
||||||
loginUrl.toUri().buildUpon()
|
LOGIN_URL.toUri().buildUpon()
|
||||||
.appendQueryParameter("client_id", clientId)
|
.appendQueryParameter("client_id", CLIENT_ID)
|
||||||
.appendQueryParameter("response_type", "code")
|
.appendQueryParameter("response_type", "code")
|
||||||
.appendQueryParameter("redirect_uri", redirectUrl)
|
.appendQueryParameter("redirect_uri", REDIRECT_URL)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
fun refreshTokenRequest(token: String) = POST(
|
fun refreshTokenRequest(token: String) = POST(
|
||||||
oauthUrl,
|
OAUTH_URL,
|
||||||
body = FormBody.Builder()
|
body = FormBody.Builder()
|
||||||
.add("grant_type", "refresh_token")
|
.add("grant_type", "refresh_token")
|
||||||
.add("client_id", clientId)
|
.add("client_id", CLIENT_ID)
|
||||||
.add("client_secret", clientSecret)
|
.add("client_secret", CLIENT_SECRET)
|
||||||
.add("refresh_token", token)
|
.add("refresh_token", token)
|
||||||
.add("redirect_uri", redirectUrl)
|
.add("redirect_uri", REDIRECT_URL)
|
||||||
.build(),
|
.build(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
|
||||||
with(json) {
|
with(json) {
|
||||||
authClient.newCall(
|
authClient.newCall(
|
||||||
POST(
|
POST(
|
||||||
"${baseUrl}library-entries",
|
"${BASE_URL}library-entries",
|
||||||
headers = headersOf(
|
headers = headersOf(
|
||||||
"Content-Type",
|
"Content-Type",
|
||||||
"application/vnd.api+json",
|
"application/vnd.api+json",
|
||||||
|
@ -104,7 +104,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
|
||||||
with(json) {
|
with(json) {
|
||||||
authClient.newCall(
|
authClient.newCall(
|
||||||
Request.Builder()
|
Request.Builder()
|
||||||
.url("${baseUrl}library-entries/${track.remote_id}")
|
.url("${BASE_URL}library-entries/${track.remote_id}")
|
||||||
.headers(
|
.headers(
|
||||||
headersOf(
|
headersOf(
|
||||||
"Content-Type",
|
"Content-Type",
|
||||||
|
@ -130,7 +130,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
|
||||||
authClient
|
authClient
|
||||||
.newCall(
|
.newCall(
|
||||||
DELETE(
|
DELETE(
|
||||||
"${baseUrl}library-entries/${track.remoteId}",
|
"${BASE_URL}library-entries/${track.remoteId}",
|
||||||
headers = headersOf(
|
headers = headersOf(
|
||||||
"Content-Type",
|
"Content-Type",
|
||||||
"application/vnd.api+json",
|
"application/vnd.api+json",
|
||||||
|
@ -143,7 +143,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
|
||||||
suspend fun search(query: String): List<TrackSearch> {
|
suspend fun search(query: String): List<TrackSearch> {
|
||||||
return withIOContext {
|
return withIOContext {
|
||||||
with(json) {
|
with(json) {
|
||||||
authClient.newCall(GET(algoliaKeyUrl))
|
authClient.newCall(GET(ALGOLIA_KEY_URL))
|
||||||
.awaitSuccess()
|
.awaitSuccess()
|
||||||
.parseAs<JsonObject>()
|
.parseAs<JsonObject>()
|
||||||
.let {
|
.let {
|
||||||
|
@ -157,16 +157,16 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
|
||||||
private suspend fun algoliaSearch(key: String, query: String): List<TrackSearch> {
|
private suspend fun algoliaSearch(key: String, query: String): List<TrackSearch> {
|
||||||
return withIOContext {
|
return withIOContext {
|
||||||
val jsonObject = buildJsonObject {
|
val jsonObject = buildJsonObject {
|
||||||
put("params", "query=${URLEncoder.encode(query, StandardCharsets.UTF_8.name())}$algoliaFilter")
|
put("params", "query=${URLEncoder.encode(query, StandardCharsets.UTF_8.name())}$ALGOLIA_FILTER")
|
||||||
}
|
}
|
||||||
|
|
||||||
with(json) {
|
with(json) {
|
||||||
client.newCall(
|
client.newCall(
|
||||||
POST(
|
POST(
|
||||||
algoliaUrl,
|
ALGOLIA_URL,
|
||||||
headers = headersOf(
|
headers = headersOf(
|
||||||
"X-Algolia-Application-Id",
|
"X-Algolia-Application-Id",
|
||||||
algoliaAppId,
|
ALGOLIA_APP_ID,
|
||||||
"X-Algolia-API-Key",
|
"X-Algolia-API-Key",
|
||||||
key,
|
key,
|
||||||
),
|
),
|
||||||
|
@ -187,7 +187,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
|
||||||
|
|
||||||
suspend fun findLibManga(track: Track, userId: String): Track? {
|
suspend fun findLibManga(track: Track, userId: String): Track? {
|
||||||
return withIOContext {
|
return withIOContext {
|
||||||
val url = "${baseUrl}library-entries".toUri().buildUpon()
|
val url = "${BASE_URL}library-entries".toUri().buildUpon()
|
||||||
.encodedQuery("filter[manga_id]=${track.remote_id}&filter[user_id]=$userId")
|
.encodedQuery("filter[manga_id]=${track.remote_id}&filter[user_id]=$userId")
|
||||||
.appendQueryParameter("include", "manga")
|
.appendQueryParameter("include", "manga")
|
||||||
.build()
|
.build()
|
||||||
|
@ -210,7 +210,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
|
||||||
|
|
||||||
suspend fun getLibManga(track: Track): Track {
|
suspend fun getLibManga(track: Track): Track {
|
||||||
return withIOContext {
|
return withIOContext {
|
||||||
val url = "${baseUrl}library-entries".toUri().buildUpon()
|
val url = "${BASE_URL}library-entries".toUri().buildUpon()
|
||||||
.encodedQuery("filter[id]=${track.remote_id}")
|
.encodedQuery("filter[id]=${track.remote_id}")
|
||||||
.appendQueryParameter("include", "manga")
|
.appendQueryParameter("include", "manga")
|
||||||
.build()
|
.build()
|
||||||
|
@ -237,11 +237,11 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
|
||||||
.add("username", username)
|
.add("username", username)
|
||||||
.add("password", password)
|
.add("password", password)
|
||||||
.add("grant_type", "password")
|
.add("grant_type", "password")
|
||||||
.add("client_id", clientId)
|
.add("client_id", CLIENT_ID)
|
||||||
.add("client_secret", clientSecret)
|
.add("client_secret", CLIENT_SECRET)
|
||||||
.build()
|
.build()
|
||||||
with(json) {
|
with(json) {
|
||||||
client.newCall(POST(loginUrl, body = formBody))
|
client.newCall(POST(LOGIN_URL, body = formBody))
|
||||||
.awaitSuccess()
|
.awaitSuccess()
|
||||||
.parseAs()
|
.parseAs()
|
||||||
}
|
}
|
||||||
|
@ -250,7 +250,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
|
||||||
|
|
||||||
suspend fun getCurrentUser(): String {
|
suspend fun getCurrentUser(): String {
|
||||||
return withIOContext {
|
return withIOContext {
|
||||||
val url = "${baseUrl}users".toUri().buildUpon()
|
val url = "${BASE_URL}users".toUri().buildUpon()
|
||||||
.encodedQuery("filter[self]=true")
|
.encodedQuery("filter[self]=true")
|
||||||
.build()
|
.build()
|
||||||
with(json) {
|
with(json) {
|
||||||
|
@ -265,35 +265,31 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val clientId =
|
private const val CLIENT_ID = "dd031b32d2f56c990b1425efe6c42ad847e7fe3ab46bf1299f05ecd856bdb7dd"
|
||||||
"dd031b32d2f56c990b1425efe6c42ad847e7fe3ab46bf1299f05ecd856bdb7dd"
|
private const val CLIENT_SECRET = "54d7307928f63414defd96399fc31ba847961ceaecef3a5fd93144e960c0e151"
|
||||||
private const val clientSecret =
|
|
||||||
"54d7307928f63414defd96399fc31ba847961ceaecef3a5fd93144e960c0e151"
|
|
||||||
|
|
||||||
private const val baseUrl = "https://kitsu.app/api/edge/"
|
private const val BASE_URL = "https://kitsu.app/api/edge/"
|
||||||
private const val loginUrl = "https://kitsu.app/api/oauth/token"
|
private const val LOGIN_URL = "https://kitsu.app/api/oauth/token"
|
||||||
private const val baseMangaUrl = "https://kitsu.app/manga/"
|
private const val BASE_MANGA_URL = "https://kitsu.app/manga/"
|
||||||
private const val algoliaKeyUrl = "https://kitsu.app/api/edge/algolia-keys/media/"
|
private const val ALGOLIA_KEY_URL = "https://kitsu.app/api/edge/algolia-keys/media/"
|
||||||
|
|
||||||
private const val algoliaUrl =
|
private const val ALGOLIA_APP_ID = "AWQO5J657S"
|
||||||
"https://AWQO5J657S-dsn.algolia.net/1/indexes/production_media/query/"
|
private const val ALGOLIA_URL = "https://$ALGOLIA_APP_ID-dsn.algolia.net/1/indexes/production_media/query/"
|
||||||
private const val algoliaAppId = "AWQO5J657S"
|
private const val ALGOLIA_FILTER = "&facetFilters=%5B%22kind%3Amanga%22%5D&attributesToRetrieve=" +
|
||||||
private const val algoliaFilter =
|
"%5B%22synopsis%22%2C%22averageRating%22%2C%22canonicalTitle%22%2C%22chapterCount%22%2C%22" +
|
||||||
"&facetFilters=%5B%22kind%3Amanga%22%5D&attributesToRetrieve=" +
|
"posterImage%22%2C%22startDate%22%2C%22subtype%22%2C%22endDate%22%2C%20%22id%22%5D"
|
||||||
"%5B%22synopsis%22%2C%22averageRating%22%2C%22canonicalTitle%22%2C%22chapterCount%22%2C%22" +
|
|
||||||
"posterImage%22%2C%22startDate%22%2C%22subtype%22%2C%22endDate%22%2C%20%22id%22%5D"
|
|
||||||
|
|
||||||
fun mangaUrl(remoteId: Long): String {
|
fun mangaUrl(remoteId: Long): String {
|
||||||
return baseMangaUrl + remoteId
|
return BASE_MANGA_URL + remoteId
|
||||||
}
|
}
|
||||||
|
|
||||||
fun refreshTokenRequest(token: String) = POST(
|
fun refreshTokenRequest(token: String) = POST(
|
||||||
loginUrl,
|
LOGIN_URL,
|
||||||
body = FormBody.Builder()
|
body = FormBody.Builder()
|
||||||
.add("grant_type", "refresh_token")
|
.add("grant_type", "refresh_token")
|
||||||
.add("refresh_token", token)
|
.add("refresh_token", token)
|
||||||
.add("client_id", clientId)
|
.add("client_id", CLIENT_ID)
|
||||||
.add("client_secret", clientSecret)
|
.add("client_secret", CLIENT_SECRET)
|
||||||
.build(),
|
.build(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ import java.util.Locale
|
||||||
|
|
||||||
object KitsuDateHelper {
|
object KitsuDateHelper {
|
||||||
|
|
||||||
private const val pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
|
private const val PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
|
||||||
private val formatter = SimpleDateFormat(pattern, Locale.ENGLISH)
|
private val formatter = SimpleDateFormat(PATTERN, Locale.ENGLISH)
|
||||||
|
|
||||||
fun convert(dateValue: Long): String? {
|
fun convert(dateValue: Long): String? {
|
||||||
if (dateValue == 0L) return null
|
if (dateValue == 0L) return null
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
@file:Suppress("PropertyName", "ktlint:standard:property-naming")
|
||||||
|
|
||||||
package eu.kanade.tachiyomi.data.track.model
|
package eu.kanade.tachiyomi.data.track.model
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Track
|
import eu.kanade.tachiyomi.data.database.models.Track
|
||||||
|
|
|
@ -54,7 +54,7 @@ class ShikimoriApi(
|
||||||
}
|
}
|
||||||
authClient.newCall(
|
authClient.newCall(
|
||||||
POST(
|
POST(
|
||||||
"$apiUrl/v2/user_rates",
|
"$API_URL/v2/user_rates",
|
||||||
body = payload.toString().toRequestBody(jsonMime),
|
body = payload.toString().toRequestBody(jsonMime),
|
||||||
),
|
),
|
||||||
).awaitSuccess()
|
).awaitSuccess()
|
||||||
|
@ -73,14 +73,14 @@ class ShikimoriApi(
|
||||||
suspend fun deleteLibManga(track: DomainTrack) {
|
suspend fun deleteLibManga(track: DomainTrack) {
|
||||||
withIOContext {
|
withIOContext {
|
||||||
authClient
|
authClient
|
||||||
.newCall(DELETE("$apiUrl/v2/user_rates/${track.libraryId}"))
|
.newCall(DELETE("$API_URL/v2/user_rates/${track.libraryId}"))
|
||||||
.awaitSuccess()
|
.awaitSuccess()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun search(search: String): List<TrackSearch> {
|
suspend fun search(search: String): List<TrackSearch> {
|
||||||
return withIOContext {
|
return withIOContext {
|
||||||
val url = "$apiUrl/mangas".toUri().buildUpon()
|
val url = "$API_URL/mangas".toUri().buildUpon()
|
||||||
.appendQueryParameter("order", "popularity")
|
.appendQueryParameter("order", "popularity")
|
||||||
.appendQueryParameter("search", search)
|
.appendQueryParameter("search", search)
|
||||||
.appendQueryParameter("limit", "20")
|
.appendQueryParameter("limit", "20")
|
||||||
|
@ -103,10 +103,10 @@ class ShikimoriApi(
|
||||||
remote_id = obj["id"]!!.jsonPrimitive.long
|
remote_id = obj["id"]!!.jsonPrimitive.long
|
||||||
title = obj["name"]!!.jsonPrimitive.content
|
title = obj["name"]!!.jsonPrimitive.content
|
||||||
total_chapters = obj["chapters"]!!.jsonPrimitive.long
|
total_chapters = obj["chapters"]!!.jsonPrimitive.long
|
||||||
cover_url = baseUrl + obj["image"]!!.jsonObject["preview"]!!.jsonPrimitive.content
|
cover_url = BASE_URL + obj["image"]!!.jsonObject["preview"]!!.jsonPrimitive.content
|
||||||
summary = ""
|
summary = ""
|
||||||
score = obj["score"]!!.jsonPrimitive.double
|
score = obj["score"]!!.jsonPrimitive.double
|
||||||
tracking_url = baseUrl + obj["url"]!!.jsonPrimitive.content
|
tracking_url = BASE_URL + obj["url"]!!.jsonPrimitive.content
|
||||||
publishing_status = obj["status"]!!.jsonPrimitive.content
|
publishing_status = obj["status"]!!.jsonPrimitive.content
|
||||||
publishing_type = obj["kind"]!!.jsonPrimitive.content
|
publishing_type = obj["kind"]!!.jsonPrimitive.content
|
||||||
start_date = obj["aired_on"]!!.jsonPrimitive.contentOrNull ?: ""
|
start_date = obj["aired_on"]!!.jsonPrimitive.contentOrNull ?: ""
|
||||||
|
@ -122,13 +122,13 @@ class ShikimoriApi(
|
||||||
last_chapter_read = obj["chapters"]!!.jsonPrimitive.double
|
last_chapter_read = obj["chapters"]!!.jsonPrimitive.double
|
||||||
score = obj["score"]!!.jsonPrimitive.int.toDouble()
|
score = obj["score"]!!.jsonPrimitive.int.toDouble()
|
||||||
status = toTrackStatus(obj["status"]!!.jsonPrimitive.content)
|
status = toTrackStatus(obj["status"]!!.jsonPrimitive.content)
|
||||||
tracking_url = baseUrl + mangas["url"]!!.jsonPrimitive.content
|
tracking_url = BASE_URL + mangas["url"]!!.jsonPrimitive.content
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun findLibManga(track: Track, userId: String): Track? {
|
suspend fun findLibManga(track: Track, userId: String): Track? {
|
||||||
return withIOContext {
|
return withIOContext {
|
||||||
val urlMangas = "$apiUrl/mangas".toUri().buildUpon()
|
val urlMangas = "$API_URL/mangas".toUri().buildUpon()
|
||||||
.appendPath(track.remote_id.toString())
|
.appendPath(track.remote_id.toString())
|
||||||
.build()
|
.build()
|
||||||
val mangas = with(json) {
|
val mangas = with(json) {
|
||||||
|
@ -137,7 +137,7 @@ class ShikimoriApi(
|
||||||
.parseAs<JsonObject>()
|
.parseAs<JsonObject>()
|
||||||
}
|
}
|
||||||
|
|
||||||
val url = "$apiUrl/v2/user_rates".toUri().buildUpon()
|
val url = "$API_URL/v2/user_rates".toUri().buildUpon()
|
||||||
.appendQueryParameter("user_id", userId)
|
.appendQueryParameter("user_id", userId)
|
||||||
.appendQueryParameter("target_id", track.remote_id.toString())
|
.appendQueryParameter("target_id", track.remote_id.toString())
|
||||||
.appendQueryParameter("target_type", "Manga")
|
.appendQueryParameter("target_type", "Manga")
|
||||||
|
@ -161,7 +161,7 @@ class ShikimoriApi(
|
||||||
|
|
||||||
suspend fun getCurrentUser(): Int {
|
suspend fun getCurrentUser(): Int {
|
||||||
return with(json) {
|
return with(json) {
|
||||||
authClient.newCall(GET("$apiUrl/users/whoami"))
|
authClient.newCall(GET("$API_URL/users/whoami"))
|
||||||
.awaitSuccess()
|
.awaitSuccess()
|
||||||
.parseAs<JsonObject>()
|
.parseAs<JsonObject>()
|
||||||
.let {
|
.let {
|
||||||
|
@ -181,39 +181,39 @@ class ShikimoriApi(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun accessTokenRequest(code: String) = POST(
|
private fun accessTokenRequest(code: String) = POST(
|
||||||
oauthUrl,
|
OAUTH_URL,
|
||||||
body = FormBody.Builder()
|
body = FormBody.Builder()
|
||||||
.add("grant_type", "authorization_code")
|
.add("grant_type", "authorization_code")
|
||||||
.add("client_id", clientId)
|
.add("client_id", CLIENT_ID)
|
||||||
.add("client_secret", clientSecret)
|
.add("client_secret", CLIENT_SECRET)
|
||||||
.add("code", code)
|
.add("code", code)
|
||||||
.add("redirect_uri", redirectUrl)
|
.add("redirect_uri", REDIRECT_URL)
|
||||||
.build(),
|
.build(),
|
||||||
)
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val clientId = "PB9dq8DzI405s7wdtwTdirYqHiyVMh--djnP7lBUqSA"
|
private const val CLIENT_ID = "PB9dq8DzI405s7wdtwTdirYqHiyVMh--djnP7lBUqSA"
|
||||||
private const val clientSecret = "NajpZcOBKB9sJtgNcejf8OB9jBN1OYYoo-k4h2WWZus"
|
private const val CLIENT_SECRET = "NajpZcOBKB9sJtgNcejf8OB9jBN1OYYoo-k4h2WWZus"
|
||||||
|
|
||||||
private const val baseUrl = "https://shikimori.one"
|
private const val BASE_URL = "https://shikimori.one"
|
||||||
private const val apiUrl = "$baseUrl/api"
|
private const val API_URL = "$BASE_URL/api"
|
||||||
private const val oauthUrl = "$baseUrl/oauth/token"
|
private const val OAUTH_URL = "$BASE_URL/oauth/token"
|
||||||
private const val loginUrl = "$baseUrl/oauth/authorize"
|
private const val LOGIN_URL = "$BASE_URL/oauth/authorize"
|
||||||
|
|
||||||
private const val redirectUrl = "mihon://shikimori-auth"
|
private const val REDIRECT_URL = "mihon://shikimori-auth"
|
||||||
|
|
||||||
fun authUrl(): Uri = loginUrl.toUri().buildUpon()
|
fun authUrl(): Uri = LOGIN_URL.toUri().buildUpon()
|
||||||
.appendQueryParameter("client_id", clientId)
|
.appendQueryParameter("client_id", CLIENT_ID)
|
||||||
.appendQueryParameter("redirect_uri", redirectUrl)
|
.appendQueryParameter("redirect_uri", REDIRECT_URL)
|
||||||
.appendQueryParameter("response_type", "code")
|
.appendQueryParameter("response_type", "code")
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
fun refreshTokenRequest(token: String) = POST(
|
fun refreshTokenRequest(token: String) = POST(
|
||||||
oauthUrl,
|
OAUTH_URL,
|
||||||
body = FormBody.Builder()
|
body = FormBody.Builder()
|
||||||
.add("grant_type", "refresh_token")
|
.add("grant_type", "refresh_token")
|
||||||
.add("client_id", clientId)
|
.add("client_id", CLIENT_ID)
|
||||||
.add("client_secret", clientSecret)
|
.add("client_secret", CLIENT_SECRET)
|
||||||
.add("refresh_token", token)
|
.add("refresh_token", token)
|
||||||
.build(),
|
.build(),
|
||||||
)
|
)
|
||||||
|
|
|
@ -62,14 +62,14 @@ class ExtensionManager(
|
||||||
|
|
||||||
private val iconMap = mutableMapOf<String, Drawable>()
|
private val iconMap = mutableMapOf<String, Drawable>()
|
||||||
|
|
||||||
private val _installedExtensionsMapFlow = MutableStateFlow(emptyMap<String, Extension.Installed>())
|
private val installedExtensionMapFlow = MutableStateFlow(emptyMap<String, Extension.Installed>())
|
||||||
val installedExtensionsFlow = _installedExtensionsMapFlow.mapExtensions(scope)
|
val installedExtensionsFlow = installedExtensionMapFlow.mapExtensions(scope)
|
||||||
|
|
||||||
private val _availableExtensionsMapFlow = MutableStateFlow(emptyMap<String, Extension.Available>())
|
private val availableExtensionMapFlow = MutableStateFlow(emptyMap<String, Extension.Available>())
|
||||||
val availableExtensionsFlow = _availableExtensionsMapFlow.mapExtensions(scope)
|
val availableExtensionsFlow = availableExtensionMapFlow.mapExtensions(scope)
|
||||||
|
|
||||||
private val _untrustedExtensionsMapFlow = MutableStateFlow(emptyMap<String, Extension.Untrusted>())
|
private val untrustedExtensionMapFlow = MutableStateFlow(emptyMap<String, Extension.Untrusted>())
|
||||||
val untrustedExtensionsFlow = _untrustedExtensionsMapFlow.mapExtensions(scope)
|
val untrustedExtensionsFlow = untrustedExtensionMapFlow.mapExtensions(scope)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
initExtensions()
|
initExtensions()
|
||||||
|
@ -79,7 +79,7 @@ class ExtensionManager(
|
||||||
private var subLanguagesEnabledOnFirstRun = preferences.enabledLanguages().isSet()
|
private var subLanguagesEnabledOnFirstRun = preferences.enabledLanguages().isSet()
|
||||||
|
|
||||||
fun getAppIconForSource(sourceId: Long): Drawable? {
|
fun getAppIconForSource(sourceId: Long): Drawable? {
|
||||||
val pkgName = _installedExtensionsMapFlow.value.values
|
val pkgName = installedExtensionMapFlow.value.values
|
||||||
.find { ext ->
|
.find { ext ->
|
||||||
ext.sources.any { it.id == sourceId }
|
ext.sources.any { it.id == sourceId }
|
||||||
}
|
}
|
||||||
|
@ -109,11 +109,11 @@ class ExtensionManager(
|
||||||
private fun initExtensions() {
|
private fun initExtensions() {
|
||||||
val extensions = ExtensionLoader.loadExtensions(context)
|
val extensions = ExtensionLoader.loadExtensions(context)
|
||||||
|
|
||||||
_installedExtensionsMapFlow.value = extensions
|
installedExtensionMapFlow.value = extensions
|
||||||
.filterIsInstance<LoadResult.Success>()
|
.filterIsInstance<LoadResult.Success>()
|
||||||
.associate { it.extension.pkgName to it.extension }
|
.associate { it.extension.pkgName to it.extension }
|
||||||
|
|
||||||
_untrustedExtensionsMapFlow.value = extensions
|
untrustedExtensionMapFlow.value = extensions
|
||||||
.filterIsInstance<LoadResult.Untrusted>()
|
.filterIsInstance<LoadResult.Untrusted>()
|
||||||
.associate { it.extension.pkgName to it.extension }
|
.associate { it.extension.pkgName to it.extension }
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ class ExtensionManager(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the available extensions in the [api] and updates [_availableExtensionsMapFlow].
|
* Finds the available extensions in the [api] and updates [availableExtensionMapFlow].
|
||||||
*/
|
*/
|
||||||
suspend fun findAvailableExtensions() {
|
suspend fun findAvailableExtensions() {
|
||||||
val extensions: List<Extension.Available> = try {
|
val extensions: List<Extension.Available> = try {
|
||||||
|
@ -134,7 +134,7 @@ class ExtensionManager(
|
||||||
|
|
||||||
enableAdditionalSubLanguages(extensions)
|
enableAdditionalSubLanguages(extensions)
|
||||||
|
|
||||||
_availableExtensionsMapFlow.value = extensions.associateBy { it.pkgName }
|
availableExtensionMapFlow.value = extensions.associateBy { it.pkgName }
|
||||||
updatedInstalledExtensionsStatuses(extensions)
|
updatedInstalledExtensionsStatuses(extensions)
|
||||||
setupAvailableExtensionsSourcesDataMap(extensions)
|
setupAvailableExtensionsSourcesDataMap(extensions)
|
||||||
}
|
}
|
||||||
|
@ -180,7 +180,7 @@ class ExtensionManager(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val installedExtensionsMap = _installedExtensionsMapFlow.value.toMutableMap()
|
val installedExtensionsMap = installedExtensionMapFlow.value.toMutableMap()
|
||||||
var changed = false
|
var changed = false
|
||||||
for ((pkgName, extension) in installedExtensionsMap) {
|
for ((pkgName, extension) in installedExtensionsMap) {
|
||||||
val availableExt = availableExtensions.find { it.pkgName == pkgName }
|
val availableExt = availableExtensions.find { it.pkgName == pkgName }
|
||||||
|
@ -204,7 +204,7 @@ class ExtensionManager(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (changed) {
|
if (changed) {
|
||||||
_installedExtensionsMapFlow.value = installedExtensionsMap
|
installedExtensionMapFlow.value = installedExtensionsMap
|
||||||
}
|
}
|
||||||
updatePendingUpdatesCount()
|
updatePendingUpdatesCount()
|
||||||
}
|
}
|
||||||
|
@ -228,7 +228,7 @@ class ExtensionManager(
|
||||||
* @param extension The extension to be updated.
|
* @param extension The extension to be updated.
|
||||||
*/
|
*/
|
||||||
fun updateExtension(extension: Extension.Installed): Flow<InstallStep> {
|
fun updateExtension(extension: Extension.Installed): Flow<InstallStep> {
|
||||||
val availableExt = _availableExtensionsMapFlow.value[extension.pkgName] ?: return emptyFlow()
|
val availableExt = availableExtensionMapFlow.value[extension.pkgName] ?: return emptyFlow()
|
||||||
return installExtension(availableExt)
|
return installExtension(availableExt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,11 +265,11 @@ class ExtensionManager(
|
||||||
* @param extension the extension to trust
|
* @param extension the extension to trust
|
||||||
*/
|
*/
|
||||||
suspend fun trust(extension: Extension.Untrusted) {
|
suspend fun trust(extension: Extension.Untrusted) {
|
||||||
_untrustedExtensionsMapFlow.value[extension.pkgName] ?: return
|
untrustedExtensionMapFlow.value[extension.pkgName] ?: return
|
||||||
|
|
||||||
trustExtension.trust(extension.pkgName, extension.versionCode, extension.signatureHash)
|
trustExtension.trust(extension.pkgName, extension.versionCode, extension.signatureHash)
|
||||||
|
|
||||||
_untrustedExtensionsMapFlow.value -= extension.pkgName
|
untrustedExtensionMapFlow.value -= extension.pkgName
|
||||||
|
|
||||||
ExtensionLoader.loadExtensionFromPkgName(context, extension.pkgName)
|
ExtensionLoader.loadExtensionFromPkgName(context, extension.pkgName)
|
||||||
.let { it as? LoadResult.Success }
|
.let { it as? LoadResult.Success }
|
||||||
|
@ -282,7 +282,7 @@ class ExtensionManager(
|
||||||
* @param extension The extension to be registered.
|
* @param extension The extension to be registered.
|
||||||
*/
|
*/
|
||||||
private fun registerNewExtension(extension: Extension.Installed) {
|
private fun registerNewExtension(extension: Extension.Installed) {
|
||||||
_installedExtensionsMapFlow.value += extension
|
installedExtensionMapFlow.value += extension
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -292,7 +292,7 @@ class ExtensionManager(
|
||||||
* @param extension The extension to be registered.
|
* @param extension The extension to be registered.
|
||||||
*/
|
*/
|
||||||
private fun registerUpdatedExtension(extension: Extension.Installed) {
|
private fun registerUpdatedExtension(extension: Extension.Installed) {
|
||||||
_installedExtensionsMapFlow.value += extension
|
installedExtensionMapFlow.value += extension
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -302,8 +302,8 @@ class ExtensionManager(
|
||||||
* @param pkgName The package name of the uninstalled application.
|
* @param pkgName The package name of the uninstalled application.
|
||||||
*/
|
*/
|
||||||
private fun unregisterExtension(pkgName: String) {
|
private fun unregisterExtension(pkgName: String) {
|
||||||
_installedExtensionsMapFlow.value -= pkgName
|
installedExtensionMapFlow.value -= pkgName
|
||||||
_untrustedExtensionsMapFlow.value -= pkgName
|
untrustedExtensionMapFlow.value -= pkgName
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -322,8 +322,8 @@ class ExtensionManager(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onExtensionUntrusted(extension: Extension.Untrusted) {
|
override fun onExtensionUntrusted(extension: Extension.Untrusted) {
|
||||||
_installedExtensionsMapFlow.value -= extension.pkgName
|
installedExtensionMapFlow.value -= extension.pkgName
|
||||||
_untrustedExtensionsMapFlow.value += extension
|
untrustedExtensionMapFlow.value += extension
|
||||||
updatePendingUpdatesCount()
|
updatePendingUpdatesCount()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -347,14 +347,14 @@ class ExtensionManager(
|
||||||
|
|
||||||
private fun Extension.Installed.updateExists(availableExtension: Extension.Available? = null): Boolean {
|
private fun Extension.Installed.updateExists(availableExtension: Extension.Available? = null): Boolean {
|
||||||
val availableExt = availableExtension
|
val availableExt = availableExtension
|
||||||
?: _availableExtensionsMapFlow.value[pkgName]
|
?: availableExtensionMapFlow.value[pkgName]
|
||||||
?: return false
|
?: return false
|
||||||
|
|
||||||
return (availableExt.versionCode > versionCode || availableExt.libVersion > libVersion)
|
return (availableExt.versionCode > versionCode || availableExt.libVersion > libVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updatePendingUpdatesCount() {
|
private fun updatePendingUpdatesCount() {
|
||||||
val pendingUpdateCount = _installedExtensionsMapFlow.value.values.count { it.hasUpdate }
|
val pendingUpdateCount = installedExtensionMapFlow.value.values.count { it.hasUpdate }
|
||||||
preferences.extensionUpdatesCount().set(pendingUpdateCount)
|
preferences.extensionUpdatesCount().set(pendingUpdateCount)
|
||||||
if (pendingUpdateCount == 0) {
|
if (pendingUpdateCount == 0) {
|
||||||
ExtensionUpdateNotifier(context).dismiss()
|
ExtensionUpdateNotifier(context).dismiss()
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
package eu.kanade.tachiyomi.extension.model
|
package eu.kanade.tachiyomi.extension.model
|
||||||
|
|
||||||
enum class InstallStep {
|
enum class InstallStep {
|
||||||
Idle, Pending, Downloading, Installing, Installed, Error;
|
Idle,
|
||||||
|
Pending,
|
||||||
|
Downloading,
|
||||||
|
Installing,
|
||||||
|
Installed,
|
||||||
|
Error,
|
||||||
|
;
|
||||||
|
|
||||||
fun isCompleted(): Boolean {
|
fun isCompleted(): Boolean {
|
||||||
return this == Installed || this == Error || this == Idle
|
return this == Installed || this == Error || this == Idle
|
||||||
|
|
|
@ -97,7 +97,7 @@ class SecureActivityDelegateImpl : SecureActivityDelegate, DefaultLifecycleObser
|
||||||
val incognitoModeFlow = preferences.incognitoMode().changes()
|
val incognitoModeFlow = preferences.incognitoMode().changes()
|
||||||
combine(secureScreenFlow, incognitoModeFlow) { secureScreen, incognitoMode ->
|
combine(secureScreenFlow, incognitoModeFlow) { secureScreen, incognitoMode ->
|
||||||
secureScreen == SecurityPreferences.SecureScreenMode.ALWAYS ||
|
secureScreen == SecurityPreferences.SecureScreenMode.ALWAYS ||
|
||||||
secureScreen == SecurityPreferences.SecureScreenMode.INCOGNITO && incognitoMode
|
(secureScreen == SecurityPreferences.SecureScreenMode.INCOGNITO && incognitoMode)
|
||||||
}
|
}
|
||||||
.onEach(activity.window::setSecureScreen)
|
.onEach(activity.window::setSecureScreen)
|
||||||
.launchIn(activity.lifecycleScope)
|
.launchIn(activity.lifecycleScope)
|
||||||
|
|
|
@ -41,7 +41,7 @@ class ExtensionsScreenModel(
|
||||||
private val getExtensions: GetExtensionsByType = Injekt.get(),
|
private val getExtensions: GetExtensionsByType = Injekt.get(),
|
||||||
) : StateScreenModel<ExtensionsScreenModel.State>(State()) {
|
) : StateScreenModel<ExtensionsScreenModel.State>(State()) {
|
||||||
|
|
||||||
private var _currentDownloads = MutableStateFlow<Map<String, InstallStep>>(hashMapOf())
|
private val currentDownloads = MutableStateFlow<Map<String, InstallStep>>(hashMapOf())
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val context = Injekt.get<Application>()
|
val context = Injekt.get<Application>()
|
||||||
|
@ -62,14 +62,20 @@ class ExtensionsScreenModel(
|
||||||
it.name.contains(input, ignoreCase = true) ||
|
it.name.contains(input, ignoreCase = true) ||
|
||||||
it.baseUrl.contains(input, ignoreCase = true) ||
|
it.baseUrl.contains(input, ignoreCase = true) ||
|
||||||
it.id == input.toLongOrNull()
|
it.id == input.toLongOrNull()
|
||||||
} || extension.name.contains(input, ignoreCase = true)
|
} ||
|
||||||
|
extension.name.contains(input, ignoreCase = true)
|
||||||
}
|
}
|
||||||
is Extension.Installed -> {
|
is Extension.Installed -> {
|
||||||
extension.sources.any {
|
extension.sources.any {
|
||||||
it.name.contains(input, ignoreCase = true) ||
|
it.name.contains(input, ignoreCase = true) ||
|
||||||
it.id == input.toLongOrNull() ||
|
it.id == input.toLongOrNull() ||
|
||||||
if (it is HttpSource) { it.baseUrl.contains(input, ignoreCase = true) } else false
|
if (it is HttpSource) {
|
||||||
} || extension.name.contains(input, ignoreCase = true)
|
it.baseUrl.contains(input, ignoreCase = true)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} ||
|
||||||
|
extension.name.contains(input, ignoreCase = true)
|
||||||
}
|
}
|
||||||
is Extension.Untrusted -> extension.name.contains(input, ignoreCase = true)
|
is Extension.Untrusted -> extension.name.contains(input, ignoreCase = true)
|
||||||
}
|
}
|
||||||
|
@ -80,7 +86,7 @@ class ExtensionsScreenModel(
|
||||||
screenModelScope.launchIO {
|
screenModelScope.launchIO {
|
||||||
combine(
|
combine(
|
||||||
state.map { it.searchQuery }.distinctUntilChanged().debounce(SEARCH_DEBOUNCE_MILLIS),
|
state.map { it.searchQuery }.distinctUntilChanged().debounce(SEARCH_DEBOUNCE_MILLIS),
|
||||||
_currentDownloads,
|
currentDownloads,
|
||||||
getExtensions.subscribe(),
|
getExtensions.subscribe(),
|
||||||
) { query, downloads, (_updates, _installed, _available, _untrusted) ->
|
) { query, downloads, (_updates, _installed, _available, _untrusted) ->
|
||||||
val searchQuery = query ?: ""
|
val searchQuery = query ?: ""
|
||||||
|
@ -166,11 +172,11 @@ class ExtensionsScreenModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addDownloadState(extension: Extension, installStep: InstallStep) {
|
private fun addDownloadState(extension: Extension, installStep: InstallStep) {
|
||||||
_currentDownloads.update { it + Pair(extension.pkgName, installStep) }
|
currentDownloads.update { it + Pair(extension.pkgName, installStep) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun removeDownloadState(extension: Extension) {
|
private fun removeDownloadState(extension: Extension) {
|
||||||
_currentDownloads.update { it - extension.pkgName }
|
currentDownloads.update { it - extension.pkgName }
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun Flow<InstallStep>.collectToInstallUpdate(extension: Extension) =
|
private suspend fun Flow<InstallStep>.collectToInstallUpdate(extension: Extension) =
|
||||||
|
|
|
@ -66,8 +66,8 @@ object HomeScreen : Screen() {
|
||||||
private val openTabEvent = Channel<Tab>()
|
private val openTabEvent = Channel<Tab>()
|
||||||
private val showBottomNavEvent = Channel<Boolean>()
|
private val showBottomNavEvent = Channel<Boolean>()
|
||||||
|
|
||||||
private const val TabFadeDuration = 200
|
private const val TAB_FADE_DURATION = 200
|
||||||
private const val TabNavigatorKey = "HomeTabs"
|
private const val TAB_NAVIGATOR_KEY = "HomeTabs"
|
||||||
|
|
||||||
private val tabs = listOf(
|
private val tabs = listOf(
|
||||||
LibraryTab,
|
LibraryTab,
|
||||||
|
@ -82,7 +82,7 @@ object HomeScreen : Screen() {
|
||||||
val navigator = LocalNavigator.currentOrThrow
|
val navigator = LocalNavigator.currentOrThrow
|
||||||
TabNavigator(
|
TabNavigator(
|
||||||
tab = LibraryTab,
|
tab = LibraryTab,
|
||||||
key = TabNavigatorKey,
|
key = TAB_NAVIGATOR_KEY,
|
||||||
) { tabNavigator ->
|
) { tabNavigator ->
|
||||||
// Provide usable navigator to content screen
|
// Provide usable navigator to content screen
|
||||||
CompositionLocalProvider(LocalNavigator provides navigator) {
|
CompositionLocalProvider(LocalNavigator provides navigator) {
|
||||||
|
@ -124,8 +124,11 @@ object HomeScreen : Screen() {
|
||||||
AnimatedContent(
|
AnimatedContent(
|
||||||
targetState = tabNavigator.current,
|
targetState = tabNavigator.current,
|
||||||
transitionSpec = {
|
transitionSpec = {
|
||||||
materialFadeThroughIn(initialScale = 1f, durationMillis = TabFadeDuration) togetherWith
|
materialFadeThroughIn(
|
||||||
materialFadeThroughOut(durationMillis = TabFadeDuration)
|
initialScale = 1f,
|
||||||
|
durationMillis = TAB_FADE_DURATION,
|
||||||
|
) togetherWith
|
||||||
|
materialFadeThroughOut(durationMillis = TAB_FADE_DURATION)
|
||||||
},
|
},
|
||||||
label = "tabContent",
|
label = "tabContent",
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -258,7 +258,7 @@ class LibraryScreenModel(
|
||||||
private fun LibraryMap.applySort(
|
private fun LibraryMap.applySort(
|
||||||
// Map<MangaId, List<Track>>
|
// Map<MangaId, List<Track>>
|
||||||
trackMap: Map<Long, List<Track>>,
|
trackMap: Map<Long, List<Track>>,
|
||||||
loggedInTrackerIds: Set<Long>
|
loggedInTrackerIds: Set<Long>,
|
||||||
): LibraryMap {
|
): LibraryMap {
|
||||||
val sortAlphabetically: (LibraryItem, LibraryItem) -> Int = { i1, i2 ->
|
val sortAlphabetically: (LibraryItem, LibraryItem) -> Int = { i1, i2 ->
|
||||||
i1.libraryManga.manga.title.lowercase().compareToWithCollator(i2.libraryManga.manga.title.lowercase())
|
i1.libraryManga.manga.title.lowercase().compareToWithCollator(i2.libraryManga.manga.title.lowercase())
|
||||||
|
|
|
@ -32,7 +32,7 @@ class LibrarySettingsScreenModel(
|
||||||
.stateIn(
|
.stateIn(
|
||||||
scope = screenModelScope,
|
scope = screenModelScope,
|
||||||
started = SharingStarted.WhileSubscribed(5.seconds.inWholeMilliseconds),
|
started = SharingStarted.WhileSubscribed(5.seconds.inWholeMilliseconds),
|
||||||
initialValue = trackerManager.loggedInTrackers()
|
initialValue = trackerManager.loggedInTrackers(),
|
||||||
)
|
)
|
||||||
|
|
||||||
fun toggleFilter(preference: (LibraryPreferences) -> Preference<TriState>) {
|
fun toggleFilter(preference: (LibraryPreferences) -> Preference<TriState>) {
|
||||||
|
|
|
@ -87,8 +87,8 @@ private class MoreScreenModel(
|
||||||
var downloadedOnly by preferences.downloadedOnly().asState(screenModelScope)
|
var downloadedOnly by preferences.downloadedOnly().asState(screenModelScope)
|
||||||
var incognitoMode by preferences.incognitoMode().asState(screenModelScope)
|
var incognitoMode by preferences.incognitoMode().asState(screenModelScope)
|
||||||
|
|
||||||
private var _state: MutableStateFlow<DownloadQueueState> = MutableStateFlow(DownloadQueueState.Stopped)
|
private var _downloadQueueState: MutableStateFlow<DownloadQueueState> = MutableStateFlow(DownloadQueueState.Stopped)
|
||||||
val downloadQueueState: StateFlow<DownloadQueueState> = _state.asStateFlow()
|
val downloadQueueState: StateFlow<DownloadQueueState> = _downloadQueueState.asStateFlow()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// Handle running/paused status change and queue progress updating
|
// Handle running/paused status change and queue progress updating
|
||||||
|
@ -99,7 +99,7 @@ private class MoreScreenModel(
|
||||||
) { isRunning, downloadQueue -> Pair(isRunning, downloadQueue.size) }
|
) { isRunning, downloadQueue -> Pair(isRunning, downloadQueue.size) }
|
||||||
.collectLatest { (isDownloading, downloadQueueSize) ->
|
.collectLatest { (isDownloading, downloadQueueSize) ->
|
||||||
val pendingDownloadExists = downloadQueueSize != 0
|
val pendingDownloadExists = downloadQueueSize != 0
|
||||||
_state.value = when {
|
_downloadQueueState.value = when {
|
||||||
!pendingDownloadExists -> DownloadQueueState.Stopped
|
!pendingDownloadExists -> DownloadQueueState.Stopped
|
||||||
!isDownloading -> DownloadQueueState.Paused(downloadQueueSize)
|
!isDownloading -> DownloadQueueState.Paused(downloadQueueSize)
|
||||||
else -> DownloadQueueState.Downloading(downloadQueueSize)
|
else -> DownloadQueueState.Downloading(downloadQueueSize)
|
||||||
|
|
|
@ -165,13 +165,19 @@ class ReaderViewModel @JvmOverloads constructor(
|
||||||
(
|
(
|
||||||
manga.downloadedFilterRaw == Manga.CHAPTER_SHOW_DOWNLOADED &&
|
manga.downloadedFilterRaw == Manga.CHAPTER_SHOW_DOWNLOADED &&
|
||||||
!downloadManager.isChapterDownloaded(
|
!downloadManager.isChapterDownloaded(
|
||||||
it.name, it.scanlator, manga.title, manga.source,
|
it.name,
|
||||||
|
it.scanlator,
|
||||||
|
manga.title,
|
||||||
|
manga.source,
|
||||||
)
|
)
|
||||||
) ||
|
) ||
|
||||||
(
|
(
|
||||||
manga.downloadedFilterRaw == Manga.CHAPTER_SHOW_NOT_DOWNLOADED &&
|
manga.downloadedFilterRaw == Manga.CHAPTER_SHOW_NOT_DOWNLOADED &&
|
||||||
downloadManager.isChapterDownloaded(
|
downloadManager.isChapterDownloaded(
|
||||||
it.name, it.scanlator, manga.title, manga.source,
|
it.name,
|
||||||
|
it.scanlator,
|
||||||
|
manga.title,
|
||||||
|
manga.source,
|
||||||
)
|
)
|
||||||
) ||
|
) ||
|
||||||
(manga.bookmarkedFilterRaw == Manga.CHAPTER_SHOW_BOOKMARKED && !it.bookmark) ||
|
(manga.bookmarkedFilterRaw == Manga.CHAPTER_SHOW_BOOKMARKED && !it.bookmark) ||
|
||||||
|
|
|
@ -142,7 +142,7 @@ class ReaderPreferences(
|
||||||
enum class FlashColor {
|
enum class FlashColor {
|
||||||
BLACK,
|
BLACK,
|
||||||
WHITE,
|
WHITE,
|
||||||
WHITE_BLACK
|
WHITE_BLACK,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class TappingInvertMode(
|
enum class TappingInvertMode(
|
||||||
|
|
|
@ -404,7 +404,9 @@ open class ReaderPageImageView @JvmOverloads constructor(
|
||||||
)
|
)
|
||||||
|
|
||||||
enum class ZoomStartPosition {
|
enum class ZoomStartPosition {
|
||||||
LEFT, CENTER, RIGHT
|
LEFT,
|
||||||
|
CENTER,
|
||||||
|
RIGHT,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,15 +32,16 @@ class ReaderTransitionView @JvmOverloads constructor(context: Context, attrs: At
|
||||||
Data(
|
Data(
|
||||||
transition = transition,
|
transition = transition,
|
||||||
currChapterDownloaded = transition.from.pageLoader?.isLocal == true,
|
currChapterDownloaded = transition.from.pageLoader?.isLocal == true,
|
||||||
goingToChapterDownloaded = manga.isLocal() || transition.to?.chapter?.let { goingToChapter ->
|
goingToChapterDownloaded = manga.isLocal() ||
|
||||||
downloadManager.isChapterDownloaded(
|
transition.to?.chapter?.let { goingToChapter ->
|
||||||
chapterName = goingToChapter.name,
|
downloadManager.isChapterDownloaded(
|
||||||
chapterScanlator = goingToChapter.scanlator,
|
chapterName = goingToChapter.name,
|
||||||
mangaTitle = manga.title,
|
chapterScanlator = goingToChapter.scanlator,
|
||||||
sourceId = manga.source,
|
mangaTitle = manga.title,
|
||||||
skipCache = true,
|
sourceId = manga.source,
|
||||||
)
|
skipCache = true,
|
||||||
} ?: false,
|
)
|
||||||
|
} ?: false,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
|
|
|
@ -92,7 +92,9 @@ class PagerViewerAdapter(private val viewer: PagerViewer) : ViewPagerAdapter() {
|
||||||
// Add next chapter transition and pages.
|
// Add next chapter transition and pages.
|
||||||
nextTransition = ChapterTransition.Next(chapters.currChapter, chapters.nextChapter)
|
nextTransition = ChapterTransition.Next(chapters.currChapter, chapters.nextChapter)
|
||||||
.also {
|
.also {
|
||||||
if (nextHasMissingChapters || forceTransition ||
|
if (
|
||||||
|
nextHasMissingChapters ||
|
||||||
|
forceTransition ||
|
||||||
chapters.nextChapter?.state !is ReaderChapter.State.Loaded
|
chapters.nextChapter?.state !is ReaderChapter.State.Loaded
|
||||||
) {
|
) {
|
||||||
newItems.add(it)
|
newItems.add(it)
|
||||||
|
|
|
@ -82,7 +82,7 @@ class WebtoonConfig(
|
||||||
readerPreferences.webtoonDisableZoomOut()
|
readerPreferences.webtoonDisableZoomOut()
|
||||||
.register(
|
.register(
|
||||||
{ zoomOutDisabled = it },
|
{ zoomOutDisabled = it },
|
||||||
{ zoomPropertyChangedListener?.invoke(it) }
|
{ zoomPropertyChangedListener?.invoke(it) },
|
||||||
)
|
)
|
||||||
|
|
||||||
readerPreferences.webtoonDoubleTapZoomEnabled()
|
readerPreferences.webtoonDoubleTapZoomEnabled()
|
||||||
|
|
|
@ -182,7 +182,11 @@ class WebtoonRecyclerView @JvmOverloads constructor(
|
||||||
|
|
||||||
setScaleRate(currentScale)
|
setScaleRate(currentScale)
|
||||||
|
|
||||||
layoutParams.height = if (currentScale < 1) { (originalHeight / currentScale).toInt() } else { originalHeight }
|
layoutParams.height = if (currentScale < 1) {
|
||||||
|
(originalHeight / currentScale).toInt()
|
||||||
|
} else {
|
||||||
|
originalHeight
|
||||||
|
}
|
||||||
halfHeight = layoutParams.height / 2
|
halfHeight = layoutParams.height / 2
|
||||||
|
|
||||||
if (currentScale != DEFAULT_RATE) {
|
if (currentScale != DEFAULT_RATE) {
|
||||||
|
|
|
@ -79,7 +79,7 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr
|
||||||
.threshold
|
.threshold
|
||||||
|
|
||||||
init {
|
init {
|
||||||
recycler.setItemViewCacheSize(RecyclerViewCacheSize)
|
recycler.setItemViewCacheSize(RECYCLER_VIEW_CACHE_SIZE)
|
||||||
recycler.isVisible = false // Don't let the recycler layout yet
|
recycler.isVisible = false // Don't let the recycler layout yet
|
||||||
recycler.layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)
|
recycler.layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)
|
||||||
recycler.isFocusable = false
|
recycler.isFocusable = false
|
||||||
|
@ -362,4 +362,4 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Double the cache size to reduce rebinds/recycles incurred by the extra layout space on scroll direction changes
|
// Double the cache size to reduce rebinds/recycles incurred by the extra layout space on scroll direction changes
|
||||||
private const val RecyclerViewCacheSize = 4
|
private const val RECYCLER_VIEW_CACHE_SIZE = 4
|
||||||
|
|
|
@ -15,7 +15,7 @@ import java.util.Enumeration
|
||||||
class ChildFirstPathClassLoader(
|
class ChildFirstPathClassLoader(
|
||||||
dexPath: String,
|
dexPath: String,
|
||||||
librarySearchPath: String?,
|
librarySearchPath: String?,
|
||||||
parent: ClassLoader
|
parent: ClassLoader,
|
||||||
) : PathClassLoader(dexPath, librarySearchPath, parent) {
|
) : PathClassLoader(dexPath, librarySearchPath, parent) {
|
||||||
|
|
||||||
private val systemClassLoader: ClassLoader? = getSystemClassLoader()
|
private val systemClassLoader: ClassLoader? = getSystemClassLoader()
|
||||||
|
|
|
@ -9,20 +9,24 @@ import tachiyomi.core.common.util.system.logcat
|
||||||
|
|
||||||
class MigrationJobFactory(
|
class MigrationJobFactory(
|
||||||
private val migrationContext: MigrationContext,
|
private val migrationContext: MigrationContext,
|
||||||
private val scope: CoroutineScope
|
private val scope: CoroutineScope,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun create(migrations: List<Migration>): Deferred<Boolean> = with(scope) {
|
fun create(migrations: List<Migration>): Deferred<Boolean> = with(scope) {
|
||||||
return migrations.sortedBy { it.version }
|
return migrations.sortedBy { it.version }
|
||||||
.fold(CompletableDeferred(true)) { acc: Deferred<Boolean>, migration: Migration ->
|
.fold(CompletableDeferred(true)) { acc: Deferred<Boolean>, migration: Migration ->
|
||||||
if (!migrationContext.dryrun) {
|
if (!migrationContext.dryrun) {
|
||||||
logcat { "Running migration: { name = ${migration::class.simpleName}, version = ${migration.version} }" }
|
logcat {
|
||||||
|
"Running migration: { name = ${migration::class.simpleName}, version = ${migration.version} }"
|
||||||
|
}
|
||||||
async(start = CoroutineStart.UNDISPATCHED) {
|
async(start = CoroutineStart.UNDISPATCHED) {
|
||||||
val prev = acc.await()
|
val prev = acc.await()
|
||||||
migration(migrationContext) || prev
|
migration(migrationContext) || prev
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logcat { "(Dry-run) Running migration: { name = ${migration::class.simpleName}, version = ${migration.version} }" }
|
logcat {
|
||||||
|
"(Dry-run) Running migration: { name = ${migration::class.simpleName}, version = ${migration.version} }"
|
||||||
|
}
|
||||||
CompletableDeferred(true)
|
CompletableDeferred(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ interface MigrationStrategy {
|
||||||
class DefaultMigrationStrategy(
|
class DefaultMigrationStrategy(
|
||||||
private val migrationJobFactory: MigrationJobFactory,
|
private val migrationJobFactory: MigrationJobFactory,
|
||||||
private val migrationCompletedListener: MigrationCompletedListener,
|
private val migrationCompletedListener: MigrationCompletedListener,
|
||||||
private val scope: CoroutineScope
|
private val scope: CoroutineScope,
|
||||||
) : MigrationStrategy {
|
) : MigrationStrategy {
|
||||||
|
|
||||||
override operator fun invoke(migrations: List<Migration>): Deferred<Boolean> = with(scope) {
|
override operator fun invoke(migrations: List<Migration>): Deferred<Boolean> = with(scope) {
|
||||||
|
@ -46,7 +46,7 @@ class NoopMigrationStrategy(val state: Boolean) : MigrationStrategy {
|
||||||
|
|
||||||
class VersionRangeMigrationStrategy(
|
class VersionRangeMigrationStrategy(
|
||||||
private val versions: IntRange,
|
private val versions: IntRange,
|
||||||
private val strategy: DefaultMigrationStrategy
|
private val strategy: DefaultMigrationStrategy,
|
||||||
) : MigrationStrategy {
|
) : MigrationStrategy {
|
||||||
|
|
||||||
override operator fun invoke(migrations: List<Migration>): Deferred<Boolean> {
|
override operator fun invoke(migrations: List<Migration>): Deferred<Boolean> {
|
||||||
|
|
|
@ -17,7 +17,7 @@ object Migrator {
|
||||||
new: Int,
|
new: Int,
|
||||||
migrations: List<Migration>,
|
migrations: List<Migration>,
|
||||||
dryrun: Boolean = false,
|
dryrun: Boolean = false,
|
||||||
onMigrationComplete: () -> Unit
|
onMigrationComplete: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val migrationContext = MigrationContext(dryrun)
|
val migrationContext = MigrationContext(dryrun)
|
||||||
val migrationJobFactory = MigrationJobFactory(migrationContext, scope)
|
val migrationJobFactory = MigrationJobFactory(migrationContext, scope)
|
||||||
|
|
|
@ -32,7 +32,7 @@ import java.time.temporal.WeekFields
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
private val FontSize = 16.sp
|
private val FontSize = 16.sp
|
||||||
private const val DaysOfWeek = 7
|
private const val DAYS_OF_WEEK = 7
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Calendar(
|
fun Calendar(
|
||||||
|
@ -54,7 +54,7 @@ fun Calendar(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(vertical = MaterialTheme.padding.small)
|
.padding(vertical = MaterialTheme.padding.small)
|
||||||
.padding(start = MaterialTheme.padding.medium)
|
.padding(start = MaterialTheme.padding.medium),
|
||||||
)
|
)
|
||||||
CalendarGrid(
|
CalendarGrid(
|
||||||
selectedYearMonth = selectedYearMonth,
|
selectedYearMonth = selectedYearMonth,
|
||||||
|
@ -72,8 +72,8 @@ private fun CalendarGrid(
|
||||||
) {
|
) {
|
||||||
val localeFirstDayOfWeek = WeekFields.of(Locale.getDefault()).firstDayOfWeek.value
|
val localeFirstDayOfWeek = WeekFields.of(Locale.getDefault()).firstDayOfWeek.value
|
||||||
val weekDays = remember {
|
val weekDays = remember {
|
||||||
(0 until DaysOfWeek)
|
(0 until DAYS_OF_WEEK)
|
||||||
.map { DayOfWeek.of((localeFirstDayOfWeek - 1 + it) % DaysOfWeek + 1) }
|
.map { DayOfWeek.of((localeFirstDayOfWeek - 1 + it) % DAYS_OF_WEEK + 1) }
|
||||||
.toImmutableList()
|
.toImmutableList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,12 +81,12 @@ private fun CalendarGrid(
|
||||||
val daysInMonth = selectedYearMonth.lengthOfMonth()
|
val daysInMonth = selectedYearMonth.lengthOfMonth()
|
||||||
|
|
||||||
VerticalGrid(
|
VerticalGrid(
|
||||||
columns = SimpleGridCells.Fixed(DaysOfWeek),
|
columns = SimpleGridCells.Fixed(DAYS_OF_WEEK),
|
||||||
modifier = if (isMediumWidthWindow() && !isExpandedWidthWindow()) {
|
modifier = if (isMediumWidthWindow() && !isExpandedWidthWindow()) {
|
||||||
Modifier.widthIn(max = 360.dp)
|
Modifier.widthIn(max = 360.dp)
|
||||||
} else {
|
} else {
|
||||||
Modifier
|
Modifier
|
||||||
}
|
},
|
||||||
) {
|
) {
|
||||||
weekDays.fastForEach { item ->
|
weekDays.fastForEach { item ->
|
||||||
Text(
|
Text(
|
||||||
|
|
|
@ -19,9 +19,10 @@ import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import tachiyomi.presentation.core.components.material.DISABLED_ALPHA
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
|
|
||||||
private const val MaxEvents = 3
|
private const val MAX_EVENTS = 3
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CalendarDay(
|
fun CalendarDay(
|
||||||
|
@ -39,7 +40,7 @@ fun CalendarDay(
|
||||||
Modifier.border(
|
Modifier.border(
|
||||||
border = BorderStroke(
|
border = BorderStroke(
|
||||||
width = 1.dp,
|
width = 1.dp,
|
||||||
color = MaterialTheme.colorScheme.onBackground
|
color = MaterialTheme.colorScheme.onBackground,
|
||||||
),
|
),
|
||||||
shape = CircleShape,
|
shape = CircleShape,
|
||||||
)
|
)
|
||||||
|
@ -57,14 +58,14 @@ fun CalendarDay(
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
fontSize = 16.sp,
|
fontSize = 16.sp,
|
||||||
color = if (date.isBefore(today)) {
|
color = if (date.isBefore(today)) {
|
||||||
MaterialTheme.colorScheme.onBackground.copy(alpha = 0.38f)
|
MaterialTheme.colorScheme.onBackground.copy(alpha = DISABLED_ALPHA)
|
||||||
} else {
|
} else {
|
||||||
MaterialTheme.colorScheme.onBackground
|
MaterialTheme.colorScheme.onBackground
|
||||||
},
|
},
|
||||||
fontWeight = FontWeight.SemiBold,
|
fontWeight = FontWeight.SemiBold,
|
||||||
)
|
)
|
||||||
Row(Modifier.offset(y = 12.dp)) {
|
Row(Modifier.offset(y = 12.dp)) {
|
||||||
val size = events.coerceAtMost(MaxEvents)
|
val size = events.coerceAtMost(MAX_EVENTS)
|
||||||
for (index in 0 until size) {
|
for (index in 0 until size) {
|
||||||
CalendarIndicator(
|
CalendarIndicator(
|
||||||
index = index,
|
index = index,
|
||||||
|
|
|
@ -63,20 +63,20 @@ fun CalenderHeader(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val MonthYearChangeAnimationDuration = 200
|
private const val MONTH_YEAR_CHANGE_ANIMATION_DURATION = 200
|
||||||
|
|
||||||
private fun AnimatedContentTransitionScope<YearMonth>.getAnimation(): ContentTransform {
|
private fun AnimatedContentTransitionScope<YearMonth>.getAnimation(): ContentTransform {
|
||||||
val movingForward = targetState > initialState
|
val movingForward = targetState > initialState
|
||||||
|
|
||||||
val enterTransition = slideInVertically(
|
val enterTransition = slideInVertically(
|
||||||
animationSpec = tween(durationMillis = MonthYearChangeAnimationDuration),
|
animationSpec = tween(durationMillis = MONTH_YEAR_CHANGE_ANIMATION_DURATION),
|
||||||
) { height -> if (movingForward) height else -height } + fadeIn(
|
) { height -> if (movingForward) height else -height } + fadeIn(
|
||||||
animationSpec = tween(durationMillis = MonthYearChangeAnimationDuration),
|
animationSpec = tween(durationMillis = MONTH_YEAR_CHANGE_ANIMATION_DURATION),
|
||||||
)
|
)
|
||||||
val exitTransition = slideOutVertically(
|
val exitTransition = slideOutVertically(
|
||||||
animationSpec = tween(durationMillis = MonthYearChangeAnimationDuration),
|
animationSpec = tween(durationMillis = MONTH_YEAR_CHANGE_ANIMATION_DURATION),
|
||||||
) { height -> if (movingForward) -height else height } + fadeOut(
|
) { height -> if (movingForward) -height else height } + fadeOut(
|
||||||
animationSpec = tween(durationMillis = MonthYearChangeAnimationDuration),
|
animationSpec = tween(durationMillis = MONTH_YEAR_CHANGE_ANIMATION_DURATION),
|
||||||
)
|
)
|
||||||
return (enterTransition togetherWith exitTransition)
|
return (enterTransition togetherWith exitTransition)
|
||||||
.using(SizeTransform(clip = false))
|
.using(SizeTransform(clip = false))
|
||||||
|
|
|
@ -12,8 +12,8 @@ import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
private const val IndicatorScale = 12
|
private const val INDICATOR_SCALE = 12
|
||||||
private const val IndicatorAlphaMultiplier = 0.3f
|
private const val INDICATOR_ALPHA_MULTIPLIER = 0.3f
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CalendarIndicator(
|
fun CalendarIndicator(
|
||||||
|
@ -26,7 +26,7 @@ fun CalendarIndicator(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.padding(horizontal = 1.dp)
|
.padding(horizontal = 1.dp)
|
||||||
.clip(shape = CircleShape)
|
.clip(shape = CircleShape)
|
||||||
.background(color = color.copy(alpha = (index + 1) * IndicatorAlphaMultiplier))
|
.background(color = color.copy(alpha = (index + 1) * INDICATOR_ALPHA_MULTIPLIER))
|
||||||
.size(size = size.div(IndicatorScale)),
|
.size(size = size.div(INDICATOR_SCALE)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,4 +6,4 @@
|
||||||
<path
|
<path
|
||||||
android:fillColor="#FFF"
|
android:fillColor="#FFF"
|
||||||
android:pathData="M3,10C2.76,10 2.55,10.09 2.41,10.25C2.27,10.4 2.21,10.62 2.24,10.86L2.74,13.85C2.82,14.5 3.4,15 4,15H7C7.64,15 8.36,14.44 8.5,13.82L9.56,10.63C9.6,10.5 9.57,10.31 9.5,10.19C9.39,10.07 9.22,10 9,10H3M7,17H4C2.38,17 0.96,15.74 0.76,14.14L0.26,11.15C0.15,10.3 0.39,9.5 0.91,8.92C1.43,8.34 2.19,8 3,8H9C9.83,8 10.58,8.35 11.06,8.96C11.17,9.11 11.27,9.27 11.35,9.45C11.78,9.36 12.22,9.36 12.64,9.45C12.72,9.27 12.82,9.11 12.94,8.96C13.41,8.35 14.16,8 15,8H21C21.81,8 22.57,8.34 23.09,8.92C23.6,9.5 23.84,10.3 23.74,11.11L23.23,14.18C23.04,15.74 21.61,17 20,17H17C15.44,17 13.92,15.81 13.54,14.3L12.64,11.59C12.26,11.31 11.73,11.31 11.35,11.59L10.43,14.37C10.07,15.82 8.56,17 7,17M15,10C14.78,10 14.61,10.07 14.5,10.19C14.42,10.31 14.4,10.5 14.45,10.7L15.46,13.75C15.64,14.44 16.36,15 17,15H20C20.59,15 21.18,14.5 21.25,13.89L21.76,10.82C21.79,10.62 21.73,10.4 21.59,10.25C21.45,10.09 21.24,10 21,10H15Z" />
|
android:pathData="M3,10C2.76,10 2.55,10.09 2.41,10.25C2.27,10.4 2.21,10.62 2.24,10.86L2.74,13.85C2.82,14.5 3.4,15 4,15H7C7.64,15 8.36,14.44 8.5,13.82L9.56,10.63C9.6,10.5 9.57,10.31 9.5,10.19C9.39,10.07 9.22,10 9,10H3M7,17H4C2.38,17 0.96,15.74 0.76,14.14L0.26,11.15C0.15,10.3 0.39,9.5 0.91,8.92C1.43,8.34 2.19,8 3,8H9C9.83,8 10.58,8.35 11.06,8.96C11.17,9.11 11.27,9.27 11.35,9.45C11.78,9.36 12.22,9.36 12.64,9.45C12.72,9.27 12.82,9.11 12.94,8.96C13.41,8.35 14.16,8 15,8H21C21.81,8 22.57,8.34 23.09,8.92C23.6,9.5 23.84,10.3 23.74,11.11L23.23,14.18C23.04,15.74 21.61,17 20,17H17C15.44,17 13.92,15.81 13.54,14.3L12.64,11.59C12.26,11.31 11.73,11.31 11.35,11.59L10.43,14.37C10.07,15.82 8.56,17 7,17M15,10C14.78,10 14.61,10.07 14.5,10.19C14.42,10.31 14.4,10.5 14.45,10.7L15.46,13.75C15.64,14.44 16.36,15 17,15H20C20.59,15 21.18,14.5 21.25,13.89L21.76,10.82C21.79,10.62 21.73,10.4 21.59,10.25C21.45,10.09 21.24,10 21,10H15Z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -12,4 +12,4 @@
|
||||||
<corners android:radius="@dimen/m3_alert_dialog_corner_size" />
|
<corners android:radius="@dimen/m3_alert_dialog_corner_size" />
|
||||||
</shape>
|
</shape>
|
||||||
</item>
|
</item>
|
||||||
</layer-list>
|
</layer-list>
|
||||||
|
|
|
@ -445,7 +445,7 @@
|
||||||
<item name="colorSurfaceContainerLow">@color/nord_surfaceContainerLow</item>
|
<item name="colorSurfaceContainerLow">@color/nord_surfaceContainerLow</item>
|
||||||
<item name="colorSurfaceContainerLowest">@color/nord_surfaceContainerLowest</item>
|
<item name="colorSurfaceContainerLowest">@color/nord_surfaceContainerLowest</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!--== AMOLED Mode Overlay ==-->
|
<!--== AMOLED Mode Overlay ==-->
|
||||||
<style name="ThemeOverlay.Tachiyomi.Amoled" parent="" />
|
<style name="ThemeOverlay.Tachiyomi.Amoled" parent="" />
|
||||||
|
|
||||||
|
|
|
@ -57,4 +57,4 @@
|
||||||
name="gesture"
|
name="gesture"
|
||||||
value="swipe_left" />
|
value="swipe_left" />
|
||||||
</action>
|
</action>
|
||||||
</remote-actions>
|
</remote-actions>
|
||||||
|
|
|
@ -127,8 +127,8 @@ class MigratorTest {
|
||||||
listOf(
|
listOf(
|
||||||
Migration.of(Migration.ALWAYS) { true },
|
Migration.of(Migration.ALWAYS) { true },
|
||||||
Migration.of(2f) { true },
|
Migration.of(2f) { true },
|
||||||
Migration.of(3f) { false }
|
Migration.of(3f) { false },
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
execute.await()
|
execute.await()
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest />
|
<manifest />
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest />
|
<manifest />
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
@file:Suppress("FunctionName", "ktlint:standard:function-naming")
|
||||||
|
|
||||||
package eu.kanade.tachiyomi.network
|
package eu.kanade.tachiyomi.network
|
||||||
|
|
||||||
import okhttp3.CacheControl
|
import okhttp3.CacheControl
|
||||||
|
|
|
@ -500,16 +500,20 @@ object ImageUtil {
|
||||||
darkBG -> {
|
darkBG -> {
|
||||||
return ColorDrawable(blackColor)
|
return ColorDrawable(blackColor)
|
||||||
}
|
}
|
||||||
topIsBlackStreak || (
|
topIsBlackStreak ||
|
||||||
topCornersIsDark && topOffsetCornersIsDark &&
|
(
|
||||||
(topMidIsDark || overallBlackPixels > 9)
|
topCornersIsDark &&
|
||||||
) -> {
|
topOffsetCornersIsDark &&
|
||||||
|
(topMidIsDark || overallBlackPixels > 9)
|
||||||
|
) -> {
|
||||||
intArrayOf(blackColor, blackColor, whiteColor, whiteColor)
|
intArrayOf(blackColor, blackColor, whiteColor, whiteColor)
|
||||||
}
|
}
|
||||||
bottomIsBlackStreak || (
|
bottomIsBlackStreak ||
|
||||||
botCornersIsDark && botOffsetCornersIsDark &&
|
(
|
||||||
(bottomCenterPixel.isDark() || overallBlackPixels > 9)
|
botCornersIsDark &&
|
||||||
) -> {
|
botOffsetCornersIsDark &&
|
||||||
|
(bottomCenterPixel.isDark() || overallBlackPixels > 9)
|
||||||
|
) -> {
|
||||||
intArrayOf(whiteColor, whiteColor, blackColor, blackColor)
|
intArrayOf(whiteColor, whiteColor, blackColor, blackColor)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest />
|
<manifest />
|
||||||
|
|
|
@ -42,8 +42,8 @@ class QueryPagingSource<RowType : Any>(
|
||||||
}
|
}
|
||||||
|
|
||||||
val (prevKey, nextKey) = when (params) {
|
val (prevKey, nextKey) = when (params) {
|
||||||
is LoadParams.Append -> { offset - loadSize to offset + loadSize }
|
is LoadParams.Append -> (offset - loadSize to offset + loadSize)
|
||||||
else -> { offset to offset + loadSize }
|
else -> (offset to offset + loadSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
return LoadResult.Page(
|
return LoadResult.Page(
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest />
|
<manifest />
|
||||||
|
|
|
@ -5,7 +5,8 @@ import tachiyomi.domain.chapter.model.Chapter
|
||||||
class ShouldUpdateDbChapter {
|
class ShouldUpdateDbChapter {
|
||||||
|
|
||||||
fun await(dbChapter: Chapter, sourceChapter: Chapter): Boolean {
|
fun await(dbChapter: Chapter, sourceChapter: Chapter): Boolean {
|
||||||
return dbChapter.scanlator != sourceChapter.scanlator || dbChapter.name != sourceChapter.name ||
|
return dbChapter.scanlator != sourceChapter.scanlator ||
|
||||||
|
dbChapter.name != sourceChapter.name ||
|
||||||
dbChapter.dateUpload != sourceChapter.dateUpload ||
|
dbChapter.dateUpload != sourceChapter.dateUpload ||
|
||||||
dbChapter.chapterNumber != sourceChapter.chapterNumber ||
|
dbChapter.chapterNumber != sourceChapter.chapterNumber ||
|
||||||
dbChapter.sourceOrder != sourceChapter.sourceOrder
|
dbChapter.sourceOrder != sourceChapter.sourceOrder
|
||||||
|
|
|
@ -20,10 +20,8 @@ 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 && now.isBefore(
|
val nextCheckTime = Instant.ofEpochMilli(lastChecked.get()).plus(3, ChronoUnit.DAYS)
|
||||||
Instant.ofEpochMilli(lastChecked.get()).plus(3, ChronoUnit.DAYS),
|
if (!arguments.forceCheck && now.isBefore(nextCheckTime)) {
|
||||||
)
|
|
||||||
) {
|
|
||||||
return Result.NoNewUpdate
|
return Result.NoNewUpdate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest />
|
<manifest />
|
||||||
|
|
|
@ -12,4 +12,4 @@
|
||||||
<item quantity="one">ከ %2$s ስህተት ጋር በ %1$s ውስጥ ተከናውኗል</item>
|
<item quantity="one">ከ %2$s ስህተት ጋር በ %1$s ውስጥ ተከናውኗል</item>
|
||||||
<item quantity="other">ከ %2$s ስህተት ጋር በ %1$s ውስጥ ተከናውኗል</item>
|
<item quantity="other">ከ %2$s ስህተት ጋር በ %1$s ውስጥ ተከናውኗል</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -275,4 +275,4 @@
|
||||||
<string name="backup_restore_missing_trackers">መከታተያዎች አልገቡም:</string>
|
<string name="backup_restore_missing_trackers">መከታተያዎች አልገቡም:</string>
|
||||||
<string name="backup_restore_missing_sources">የጠፋ ምንጮች:</string>
|
<string name="backup_restore_missing_sources">የጠፋ ምንጮች:</string>
|
||||||
<string name="invalid_backup_file_missing_manga">ምትኬ ማንኛውንም ማንጋ አልያዘም ፡፡</string>
|
<string name="invalid_backup_file_missing_manga">ምትኬ ማንኛውንም ማንጋ አልያዘም ፡፡</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -152,4 +152,4 @@
|
||||||
<item quantity="many">بعد %1$d أيام</item>
|
<item quantity="many">بعد %1$d أيام</item>
|
||||||
<item quantity="other">بعد %1$d أيام</item>
|
<item quantity="other">بعد %1$d أيام</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -817,4 +817,4 @@
|
||||||
\n إذا كان هذا متوقعًا، فسيتم استبدال %2$s، وإلا فاتصل بمشرف الرابطالخاص بك.</string>
|
\n إذا كان هذا متوقعًا، فسيتم استبدال %2$s، وإلا فاتصل بمشرف الرابطالخاص بك.</string>
|
||||||
<string name="action_migrate_duplicate">نقل مَدْخَل موجود</string>
|
<string name="action_migrate_duplicate">نقل مَدْخَل موجود</string>
|
||||||
<string name="pref_display_profile">ملف تعريف عرض خاص</string>
|
<string name="pref_display_profile">ملف تعريف عرض خاص</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -24,4 +24,4 @@
|
||||||
<item quantity="many">Наступныя %d непрачытаных глав</item>
|
<item quantity="many">Наступныя %d непрачытаных глав</item>
|
||||||
<item quantity="other">Наступныя %d непрачытаных глав</item>
|
<item quantity="other">Наступныя %d непрачытаных глав</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -352,4 +352,4 @@
|
||||||
<string name="label_upcoming">Наступнае</string>
|
<string name="label_upcoming">Наступнае</string>
|
||||||
<string name="scanlator">Перакладчык</string>
|
<string name="scanlator">Перакладчык</string>
|
||||||
<string name="label_downloaded">Загружана</string>
|
<string name="label_downloaded">Загружана</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -68,4 +68,4 @@
|
||||||
<item quantity="one">%d хранилище</item>
|
<item quantity="one">%d хранилище</item>
|
||||||
<item quantity="other">%d хранилища</item>
|
<item quantity="other">%d хранилища</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -642,4 +642,4 @@
|
||||||
<string name="publishing_finished">Приключено издаване</string>
|
<string name="publishing_finished">Приключено издаване</string>
|
||||||
<string name="update_check_fdroid_migration_info">Достъпна е нова версия от официалните източници. Научете как да мигрирате от неофициалните версии на F-Droid.</string>
|
<string name="update_check_fdroid_migration_info">Достъпна е нова версия от официалните източници. Научете как да мигрирате от неофициалните версии на F-Droid.</string>
|
||||||
<string name="download_notifier_split_page_not_found">Страница %d не беше намерена при разделяне</string>
|
<string name="download_notifier_split_page_not_found">Страница %d не беше намерена при разделяне</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -64,4 +64,4 @@
|
||||||
<item quantity="one">%d দিন</item>
|
<item quantity="one">%d দিন</item>
|
||||||
<item quantity="other">%d দিন</item>
|
<item quantity="other">%d দিন</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -637,4 +637,4 @@
|
||||||
<string name="sort_category_confirmation">আপনি কি বর্ণানুক্রমিকভাবে ফিল্টার করতে চান</string>
|
<string name="sort_category_confirmation">আপনি কি বর্ণানুক্রমিকভাবে ফিল্টার করতে চান</string>
|
||||||
<string name="action_ok">ঠিক আছে</string>
|
<string name="action_ok">ঠিক আছে</string>
|
||||||
<string name="action_sort_next_updated">পরবর্তী আপডেটের সম্ভাব্য সময়</string>
|
<string name="action_sort_next_updated">পরবর্তী আপডেটের সম্ভাব্য সময়</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -95,4 +95,4 @@
|
||||||
<item quantity="many">%1$s pàgines</item>
|
<item quantity="many">%1$s pàgines</item>
|
||||||
<item quantity="other">%1$s pàgines</item>
|
<item quantity="other">%1$s pàgines</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -820,4 +820,4 @@
|
||||||
<string name="invalid_backup_file_json">Còpia de seguretat en JSON no suportada</string>
|
<string name="invalid_backup_file_json">Còpia de seguretat en JSON no suportada</string>
|
||||||
<string name="invalid_backup_file_unknown">El fitxer de còpia de seguretat és corrupte</string>
|
<string name="invalid_backup_file_unknown">El fitxer de còpia de seguretat és corrupte</string>
|
||||||
<string name="extensionRepo_settings">Repositoris d’extensions</string>
|
<string name="extensionRepo_settings">Repositoris d’extensions</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -20,4 +20,4 @@
|
||||||
<item quantity="one">%1$s ang nahibilin</item>
|
<item quantity="one">%1$s ang nahibilin</item>
|
||||||
<item quantity="other">%1$s ang nahibilin</item>
|
<item quantity="other">%1$s ang nahibilin</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -443,4 +443,4 @@
|
||||||
<string name="action_not_now">Dili karon</string>
|
<string name="action_not_now">Dili karon</string>
|
||||||
<string name="information_webview_required">Gikinahanlan ang WebView alang sa Mihon</string>
|
<string name="information_webview_required">Gikinahanlan ang WebView alang sa Mihon</string>
|
||||||
<string name="information_required_plain">*gikinahanlan</string>
|
<string name="information_required_plain">*gikinahanlan</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -95,4 +95,4 @@
|
||||||
<item quantity="few">%1$s stránky</item>
|
<item quantity="few">%1$s stránky</item>
|
||||||
<item quantity="other">%1$s stránek</item>
|
<item quantity="other">%1$s stránek</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -814,4 +814,4 @@
|
||||||
<string name="pref_display_profile">Vlastní profil zobrazení</string>
|
<string name="pref_display_profile">Vlastní profil zobrazení</string>
|
||||||
<string name="action_view_upcoming">Zobrazit nadcházející aktualizace</string>
|
<string name="action_view_upcoming">Zobrazit nadcházející aktualizace</string>
|
||||||
<string name="file_picker_uri_permission_unsupported">Nepodařilo se získat trvalý přístup ke složce. Aplikace se může chovat zvláštně.</string>
|
<string name="file_picker_uri_permission_unsupported">Nepodařilo se získat trvalý přístup ke složce. Aplikace se může chovat zvláštně.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -68,4 +68,4 @@
|
||||||
<item quantity="one">%d усрав</item>
|
<item quantity="one">%d усрав</item>
|
||||||
<item quantity="other">%d усрав</item>
|
<item quantity="other">%d усрав</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -599,4 +599,4 @@
|
||||||
<string name="onboarding_storage_help_info">Кивӗ верссирен ҫӗнӗлетӗн те мӗн суламаллине пӗлместӗн? Нумайрах пӗлме усрав пӗлкӗчне кӗрсе пӑх.</string>
|
<string name="onboarding_storage_help_info">Кивӗ верссирен ҫӗнӗлетӗн те мӗн суламаллине пӗлместӗн? Нумайрах пӗлме усрав пӗлкӗчне кӗрсе пӑх.</string>
|
||||||
<string name="onboarding_guides_returning_user">%s ҫӗнӗрен лартатӑн?</string>
|
<string name="onboarding_guides_returning_user">%s ҫӗнӗрен лартатӑн?</string>
|
||||||
<string name="pref_relative_format_summary">«%1$s» вырӑнне «%2$s»</string>
|
<string name="pref_relative_format_summary">«%1$s» вырӑнне «%2$s»</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -16,4 +16,4 @@
|
||||||
<item quantity="one">Næste ulæste kapitel</item>
|
<item quantity="one">Næste ulæste kapitel</item>
|
||||||
<item quantity="other">Næste %d ulæste kapitler</item>
|
<item quantity="other">Næste %d ulæste kapitler</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -316,4 +316,4 @@
|
||||||
<string name="creating_backup">Opretter sikkerhedskopi</string>
|
<string name="creating_backup">Opretter sikkerhedskopi</string>
|
||||||
<string name="exception_unknown_host">Kunne ikke nå %s</string>
|
<string name="exception_unknown_host">Kunne ikke nå %s</string>
|
||||||
<string name="channel_new_chapters">Kapitelopdateringer</string>
|
<string name="channel_new_chapters">Kapitelopdateringer</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -76,4 +76,4 @@
|
||||||
<item quantity="one">1 Seite</item>
|
<item quantity="one">1 Seite</item>
|
||||||
<item quantity="other">%1$s Seiten</item>
|
<item quantity="other">%1$s Seiten</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -817,4 +817,4 @@
|
||||||
<string name="pref_flash_style_white_black">Weiß und Schwarz</string>
|
<string name="pref_flash_style_white_black">Weiß und Schwarz</string>
|
||||||
<string name="pref_flash_duration">Dauer des Leuchtens</string>
|
<string name="pref_flash_duration">Dauer des Leuchtens</string>
|
||||||
<string name="pref_flash_duration_summary">%1$s ms</string>
|
<string name="pref_flash_duration_summary">%1$s ms</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue