Address some build warnings

This commit is contained in:
arkon 2023-12-25 16:31:40 -05:00
parent 80d6d412f3
commit 2d7650537d
33 changed files with 102 additions and 144 deletions

View file

@ -2,7 +2,6 @@
|-------|----------|---------|------------|---------|
| [![CI](https://github.com/tachiyomiorg/tachiyomi/actions/workflows/build_push.yml/badge.svg)](https://github.com/tachiyomiorg/tachiyomi/actions/workflows/build_push.yml) | [![stable release](https://img.shields.io/github/release/tachiyomiorg/tachiyomi.svg?maxAge=3600&label=download)](https://github.com/tachiyomiorg/tachiyomi/releases) | [![latest preview build](https://img.shields.io/github/v/release/tachiyomiorg/tachiyomi-preview.svg?maxAge=3600&label=download)](https://github.com/tachiyomiorg/tachiyomi-preview/releases) | [![Translation status](https://hosted.weblate.org/widgets/tachiyomi/-/svg-badge.svg)](https://hosted.weblate.org/engage/tachiyomi/?utm_source=widget) | [![Discord](https://img.shields.io/discord/349436576037732353.svg?label=discord&labelColor=7289da&color=2c2f33&style=flat)](https://discord.gg/tachiyomi) |
# ![app icon](./.github/readme-images/app-icon.png)Tachiyomi
Tachiyomi is a free and open source manga reader for Android 6.0 and above.

View file

@ -45,7 +45,7 @@
##---------------Begin: proguard configuration for kotlinx.serialization ----------
-keepattributes *Annotation*, InnerClasses
-dontnote kotlinx.serialization.AnnotationsKt # core serialization annotations
-dontnote kotlinx.serialization.** # core serialization annotations
# kotlinx-serialization-json specific. Add this if you have java.lang.NoClassDefFoundError kotlinx.serialization.json.JsonObjectSerializer
-keepclassmembers class kotlinx.serialization.json.** {

View file

@ -8,7 +8,8 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!-- Storage -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<!-- For background jobs -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
@ -20,10 +21,12 @@
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" />
<!-- To view extension packages in API 30+ -->
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.READ_APP_SPECIFIC_LOCALES" />
<uses-permission android:name="android.permission.READ_APP_SPECIFIC_LOCALES"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />

View file

@ -27,6 +27,7 @@ import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import eu.kanade.core.preference.asToggleableState
import eu.kanade.presentation.category.visualName
import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.delay
import tachiyomi.core.preference.CheckboxState
import tachiyomi.domain.category.model.Category
@ -39,7 +40,7 @@ import kotlin.time.Duration.Companion.seconds
fun CategoryCreateDialog(
onDismissRequest: () -> Unit,
onCreate: (String) -> Unit,
categories: List<Category>,
categories: ImmutableList<Category>,
) {
var name by remember { mutableStateOf("") }
@ -98,7 +99,7 @@ fun CategoryCreateDialog(
fun CategoryRenameDialog(
onDismissRequest: () -> Unit,
onRename: (String) -> Unit,
categories: List<Category>,
categories: ImmutableList<Category>,
category: Category,
) {
var name by remember { mutableStateOf(category.name) }

View file

@ -6,6 +6,7 @@ import androidx.compose.material.icons.outlined.Add
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton
import tachiyomi.presentation.core.i18n.stringResource
@ -16,11 +17,13 @@ import tachiyomi.presentation.core.util.isScrollingUp
fun CategoryFloatingActionButton(
lazyListState: LazyListState,
onCreate: () -> Unit,
modifier: Modifier = Modifier,
) {
ExtendedFloatingActionButton(
text = { Text(text = stringResource(MR.strings.action_add)) },
icon = { Icon(imageVector = Icons.Outlined.Add, contentDescription = null) },
onClick = onCreate,
expanded = lazyListState.isScrollingUp() || lazyListState.isScrolledToEnd(),
modifier = modifier,
)
}

View file

@ -3,7 +3,9 @@ package eu.kanade.presentation.components
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import eu.kanade.presentation.manga.DownloadAction
import kotlinx.collections.immutable.persistentListOf
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.pluralStringResource
import tachiyomi.presentation.core.i18n.stringResource
@ -13,18 +15,22 @@ fun DownloadDropdownMenu(
expanded: Boolean,
onDismissRequest: () -> Unit,
onDownloadClicked: (DownloadAction) -> Unit,
modifier: Modifier = Modifier,
) {
DropdownMenu(
expanded = expanded,
onDismissRequest = onDismissRequest,
) {
listOfNotNull(
val options = persistentListOf(
DownloadAction.NEXT_1_CHAPTER to pluralStringResource(MR.plurals.download_amount, 1, 1),
DownloadAction.NEXT_5_CHAPTERS to pluralStringResource(MR.plurals.download_amount, 5, 5),
DownloadAction.NEXT_10_CHAPTERS to pluralStringResource(MR.plurals.download_amount, 10, 10),
DownloadAction.NEXT_25_CHAPTERS to pluralStringResource(MR.plurals.download_amount, 25, 25),
DownloadAction.UNREAD_CHAPTERS to stringResource(MR.strings.download_unread),
).map { (downloadAction, string) ->
)
DropdownMenu(
expanded = expanded,
onDismissRequest = onDismissRequest,
modifier = modifier,
) {
options.map { (downloadAction, string) ->
DropdownMenuItem(
text = { Text(text = string) },
onClick = {

View file

@ -1,7 +1,10 @@
package eu.kanade.presentation.components
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.ArrowRight
import androidx.compose.material.icons.outlined.RadioButtonChecked
@ -22,12 +25,17 @@ import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.stringResource
import androidx.compose.material3.DropdownMenu as ComposeDropdownMenu
/**
* DropdownMenu but overlaps anchor and has width constraints to better
* match non-Compose implementation.
*/
@Composable
fun DropdownMenu(
expanded: Boolean,
onDismissRequest: () -> Unit,
modifier: Modifier = Modifier,
offset: DpOffset = DpOffset(8.dp, (-56).dp),
scrollState: ScrollState = rememberScrollState(),
properties: PopupProperties = PopupProperties(focusable = true),
content: @Composable ColumnScope.() -> Unit,
) {
@ -36,6 +44,7 @@ fun DropdownMenu(
onDismissRequest = onDismissRequest,
modifier = modifier.sizeIn(minWidth = 196.dp, maxWidth = 196.dp),
offset = offset,
scrollState = scrollState,
properties = properties,
content = content,
)
@ -45,6 +54,7 @@ fun DropdownMenu(
fun RadioMenuItem(
text: @Composable () -> Unit,
isChecked: Boolean,
modifier: Modifier = Modifier,
onClick: () -> Unit,
) {
DropdownMenuItem(
@ -64,6 +74,7 @@ fun RadioMenuItem(
)
}
},
modifier = modifier,
)
}
@ -71,10 +82,12 @@ fun RadioMenuItem(
fun NestedMenuItem(
text: @Composable () -> Unit,
children: @Composable ColumnScope.(() -> Unit) -> Unit,
modifier: Modifier = Modifier,
) {
var nestedExpanded by remember { mutableStateOf(false) }
val closeMenu = { nestedExpanded = false }
Box {
DropdownMenuItem(
text = text,
onClick = { nestedExpanded = true },
@ -89,7 +102,9 @@ fun NestedMenuItem(
DropdownMenu(
expanded = nestedExpanded,
onDismissRequest = closeMenu,
modifier = modifier,
) {
children(closeMenu)
}
}
}

View file

@ -49,10 +49,10 @@ enum class ChapterDownloadAction {
@Composable
fun ChapterDownloadIndicator(
enabled: Boolean,
modifier: Modifier = Modifier,
downloadStateProvider: () -> Download.State,
downloadProgressProvider: () -> Int,
onClick: (ChapterDownloadAction) -> Unit,
modifier: Modifier = Modifier,
) {
when (val downloadState = downloadStateProvider()) {
Download.State.NOT_DOWNLOADED -> NotDownloadedIndicator(
@ -109,10 +109,10 @@ private fun NotDownloadedIndicator(
@Composable
private fun DownloadingIndicator(
enabled: Boolean,
modifier: Modifier = Modifier,
downloadState: Download.State,
downloadProgressProvider: () -> Int,
onClick: (ChapterDownloadAction) -> Unit,
modifier: Modifier = Modifier,
) {
var isMenuExpanded by remember { mutableStateOf(false) }
Box(

View file

@ -182,7 +182,10 @@ private fun RowScope.Button(
onClick: () -> Unit,
content: (@Composable () -> Unit)? = null,
) {
val animatedWeight by animateFloatAsState(if (toConfirm) 2f else 1f)
val animatedWeight by animateFloatAsState(
targetValue = if (toConfirm) 2f else 1f,
label = "weight",
)
Column(
modifier = Modifier
.size(48.dp)

View file

@ -20,7 +20,6 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import eu.kanade.presentation.components.AppBar

View file

@ -11,7 +11,6 @@ import tachiyomi.presentation.core.i18n.stringResource
/**
* Returns a string of categories name for settings subtitle
*/
@ReadOnlyComposable
@Composable
fun getCategoriesLabel(

View file

@ -65,7 +65,6 @@ object SettingsLibraryScreen : SearchableSettings {
allCategories: List<Category>,
libraryPreferences: LibraryPreferences,
): Preference.PreferenceGroup {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val userCategoriesCount = allCategories.filterNot(Category::isSystemCategory).size
@ -76,7 +75,7 @@ object SettingsLibraryScreen : SearchableSettings {
val ids = listOf(libraryPreferences.defaultCategory().defaultValue()) +
allCategories.fastMap { it.id.toInt() }
val labels = listOf(stringResource(MR.strings.default_category_summary)) +
allCategories.fastMap { it.visualName(context) }
allCategories.fastMap { it.visualName }
return Preference.PreferenceGroup(
title = stringResource(MR.strings.categories),

View file

@ -114,6 +114,7 @@ internal fun Modifier.highlightBackground(highlighted: Boolean): Modifier = comp
} else {
tween(200)
},
label = "highlight",
)
Modifier.background(color = highlight)
}

View file

@ -20,17 +20,20 @@ class BackupDecoder(
* Decode a potentially-gzipped backup.
*/
fun decode(uri: Uri): Backup {
val backupStringSource = context.contentResolver.openInputStream(uri)!!.source().buffer()
return context.contentResolver.openInputStream(uri)!!.use { inputStream ->
val source = inputStream.source().buffer()
val peeked = backupStringSource.peek()
peeked.require(2)
val peeked = source.peek().apply {
require(2)
}
val id1id2 = peeked.readShort()
val backupString = if (id1id2.toInt() == 0x1f8b) { // 0x1f8b is gzip magic bytes
backupStringSource.gzip().buffer()
source.gzip().buffer()
} else {
backupStringSource
source
}.use { it.readByteArray() }
return parser.decodeFromByteArray(BackupSerializer, backupString)
parser.decodeFromByteArray(BackupSerializer, backupString)
}
}
}

View file

@ -68,7 +68,7 @@ class BackupCreator(
.forEach { it.delete() }
// Create new file to place backup
dir?.createFile(BackupCreator.getFilename())
dir?.createFile(getFilename())
} else {
UniFile.fromUri(context, uri)
}

View file

@ -25,7 +25,7 @@ class DownloadProvider(
private val storageManager: StorageManager = Injekt.get(),
) {
val downloadsDir: UniFile?
private val downloadsDir: UniFile?
get() = storageManager.getDownloadsDirectory()
/**

View file

@ -246,7 +246,7 @@ class LibraryUpdateNotifier(private val context: Context) {
// Mark chapters as read action
addAction(
R.drawable.ic_glasses_24dp,
R.drawable.ic_done_24dp,
context.stringResource(MR.strings.action_mark_as_read),
NotificationReceiver.markAsReadPendingBroadcast(
context,

View file

@ -235,7 +235,6 @@ class NotificationReceiver : BroadcastReceiver() {
private const val NAME = "NotificationReceiver"
private const val ACTION_SHARE_IMAGE = "$ID.$NAME.SHARE_IMAGE"
private const val ACTION_DELETE_IMAGE = "$ID.$NAME.DELETE_IMAGE"
private const val ACTION_SHARE_BACKUP = "$ID.$NAME.SEND_BACKUP"
@ -256,7 +255,6 @@ class NotificationReceiver : BroadcastReceiver() {
private const val ACTION_DISMISS_NOTIFICATION = "$ID.$NAME.ACTION_DISMISS_NOTIFICATION"
private const val EXTRA_FILE_LOCATION = "$ID.$NAME.FILE_LOCATION"
private const val EXTRA_URI = "$ID.$NAME.URI"
private const val EXTRA_NOTIFICATION_ID = "$ID.$NAME.NOTIFICATION_ID"
private const val EXTRA_GROUP_ID = "$ID.$NAME.EXTRA_GROUP_ID"
@ -361,7 +359,7 @@ class NotificationReceiver : BroadcastReceiver() {
it.id == notificationId
}?.groupKey
if (groupId != null && groupId != 0 && groupKey != null && groupKey.isNotEmpty()) {
if (groupId != null && groupId != 0 && !groupKey.isNullOrEmpty()) {
val notifications = context.notificationManager.activeNotifications.filter {
it.groupKey == groupKey
}

View file

@ -6,7 +6,7 @@ import okhttp3.Interceptor
import okhttp3.Response
import uy.kohesive.injekt.injectLazy
class BangumiInterceptor(val bangumi: Bangumi) : Interceptor {
class BangumiInterceptor(private val bangumi: Bangumi) : Interceptor {
private val json: Json by injectLazy()

View file

@ -62,12 +62,3 @@ fun Track.toBangumiStatus() = when (status) {
Bangumi.PLAN_TO_READ -> "wish"
else -> throw NotImplementedError("Unknown status: $status")
}
fun toTrackStatus(status: String) = when (status) {
"do" -> Bangumi.READING
"collect" -> Bangumi.COMPLETED
"on_hold" -> Bangumi.ON_HOLD
"dropped" -> Bangumi.DROPPED
"wish" -> Bangumi.PLAN_TO_READ
else -> throw NotImplementedError("Unknown status: $status")
}

View file

@ -5,7 +5,7 @@ import okhttp3.Interceptor
import okhttp3.Response
import uy.kohesive.injekt.injectLazy
class KitsuInterceptor(val kitsu: Kitsu) : Interceptor {
class KitsuInterceptor(private val kitsu: Kitsu) : Interceptor {
private val json: Json by injectLazy()

View file

@ -5,7 +5,7 @@ import okhttp3.Interceptor
import okhttp3.Response
import uy.kohesive.injekt.injectLazy
class ShikimoriInterceptor(val shikimori: Shikimori) : Interceptor {
class ShikimoriInterceptor(private val shikimori: Shikimori) : Interceptor {
private val json: Json by injectLazy()

View file

@ -2,9 +2,6 @@ package eu.kanade.tachiyomi.util.system
import android.content.Context
import android.provider.Settings
import android.view.ViewPropertyAnimator
import android.view.animation.Animation
import androidx.constraintlayout.motion.widget.MotionScene.Transition
/**
* Gets the duration multiplier for general animations on the device
@ -12,19 +9,3 @@ import androidx.constraintlayout.motion.widget.MotionScene.Transition
*/
val Context.animatorDurationScale: Float
get() = Settings.Global.getFloat(this.contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1f)
/** Scale the duration of this [Animation] by [Context.animatorDurationScale] */
fun Animation.applySystemAnimatorScale(context: Context) {
this.duration = (this.duration * context.animatorDurationScale).toLong()
}
/** Scale the duration of this [Transition] by [Context.animatorDurationScale] */
fun Transition.applySystemAnimatorScale(context: Context) {
// End layout of cover expanding animation tends to break when the transition is less than ~25ms
this.duration = (this.duration * context.animatorDurationScale).toInt().coerceAtLeast(25)
}
/** Scale the duration of this [ViewPropertyAnimator] by [Context.animatorDurationScale] */
fun ViewPropertyAnimator.applySystemAnimatorScale(context: Context): ViewPropertyAnimator = apply {
this.duration = (this.duration * context.animatorDurationScale).toLong()
}

View file

@ -17,18 +17,10 @@ private const val TABLET_UI_MIN_SCREEN_WIDTH_PORTRAIT_DP = 700
// make sure icons on the nav rail fit
private const val TABLET_UI_MIN_SCREEN_WIDTH_LANDSCAPE_DP = 600
fun Context.isTabletUi(): Boolean {
return resources.configuration.isTabletUi()
}
fun Configuration.isTabletUi(): Boolean {
return smallestScreenWidthDp >= TABLET_UI_REQUIRED_SCREEN_WIDTH_DP
}
fun Configuration.isAutoTabletUiAvailable(): Boolean {
return smallestScreenWidthDp >= TABLET_UI_MIN_SCREEN_WIDTH_LANDSCAPE_DP
}
// TODO: move the logic to `isTabletUi()` when main activity is rewritten in Compose
fun Context.prepareTabletUiContext(): Context {
val configuration = resources.configuration

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFF"
android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z" />
</vector>

View file

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View file

@ -51,6 +51,7 @@ fun OkHttpClient.Builder.rateLimitHost(
* @param permits [Int] Number of requests allowed within a period of units.
* @param period [Duration] The limiting duration. Defaults to 1.seconds.
*/
@Suppress("UNUSED")
fun OkHttpClient.Builder.rateLimitHost(
httpUrl: HttpUrl,
permits: Int,
@ -71,5 +72,6 @@ fun OkHttpClient.Builder.rateLimitHost(
* @param permits [Int] Number of requests allowed within a period of units.
* @param period [Duration] The limiting duration. Defaults to 1.seconds.
*/
@Suppress("UNUSED")
fun OkHttpClient.Builder.rateLimitHost(url: String, permits: Int, period: Duration = 1.seconds) =
addInterceptor(RateLimitInterceptor(url.toHttpUrlOrNull()?.host, permits, period))

View file

@ -51,6 +51,7 @@ class InMemoryPreferenceStore(
TODO("Not yet implemented")
}
@Suppress("UNCHECKED_CAST")
override fun <T> getObject(
key: String,
defaultValue: T,
@ -59,7 +60,7 @@ class InMemoryPreferenceStore(
): Preference<T> {
val default = InMemoryPreference(key, null, defaultValue)
val data: T? = preferences[key]?.get() as? T
return if (data == null) default else InMemoryPreference<T>(key, data, defaultValue)
return if (data == null) default else InMemoryPreference(key, data, defaultValue)
}
override fun getAll(): Map<String, *> {

View file

@ -11,7 +11,6 @@ import androidx.compose.foundation.gestures.anchoredDraggable
import androidx.compose.foundation.gestures.animateTo
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.fillMaxSize
@ -78,7 +77,7 @@ fun AdaptiveSheet(
onDismissRequest()
}
}
BoxWithConstraints(
Box(
modifier = Modifier
.clickable(
enabled = true,

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<resources>
<color name="splash">@color/accent_blue</color>
<color name="cover_placeholder">#1F888888</color>
@ -20,21 +20,7 @@
<color name="background_amoled">#000000</color>
<!-- Material Design Colors -->
<color name="md_black_1000">#000000</color>
<color name="md_black_1000_87">#DE000000</color>
<color name="md_black_1000_54">#8A000000</color>
<color name="md_black_1000_38">#61000000</color>
<color name="md_black_1000_12">#1F000000</color>
<color name="md_black_1000_8">#14000000</color>
<color name="md_black_1000_6">#0F000000</color>
<color name="md_white_1000">#FFFFFFFF</color>
<color name="md_white_1000_70">#B3FFFFFF</color>
<color name="md_white_1000_54">#8AFFFFFF</color>
<color name="md_white_1000_50">#80FFFFFF</color>
<color name="md_white_1000_20">#33FFFFFF</color>
<color name="md_white_1000_12">#1FFFFFFF</color>
<color name="md_white_1000_8">#14FFFFFF</color>
<color name="md_white_1000_6">#0FFFFFFF</color>
</resources>

View file

@ -1,10 +1,12 @@
package tachiyomi.presentation.widget
import android.annotation.SuppressLint
import androidx.compose.ui.unit.dp
import androidx.glance.ImageProvider
import androidx.glance.unit.ColorProvider
class UpdatesGridGlanceWidget : BaseUpdatesGridGlanceWidget() {
@SuppressLint("RestrictedApi")
override val foreground = ColorProvider(R.color.appwidget_on_secondary_container)
override val background = ImageProvider(R.drawable.appwidget_background)
override val topPadding = 0.dp

View file

@ -29,36 +29,6 @@ interface SManga : Serializable {
return genre?.split(", ")?.map { it.trim() }?.filterNot { it.isBlank() }?.distinct()
}
fun copyFrom(other: SManga) {
if (other.author != null) {
author = other.author
}
if (other.artist != null) {
artist = other.artist
}
if (other.description != null) {
description = other.description
}
if (other.genre != null) {
genre = other.genre
}
if (other.thumbnail_url != null) {
thumbnail_url = other.thumbnail_url
}
status = other.status
update_strategy = other.update_strategy
if (!initialized) {
initialized = other.initialized
}
}
fun copy() = create().also {
it.url = url
it.title = title

View file

@ -6,6 +6,7 @@ package eu.kanade.tachiyomi.source.model
*
* @since extensions-lib 1.4
*/
@Suppress("UNUSED")
enum class UpdateStrategy {
/**
* Series marked as always update will be included in the library