Clean up some build warnings (#2929)

* Replace deprecated rememberPlainTooltipPositionProvider

* Remove superfluous when branch

This when is marked as exhaustive.

* Replace deprecated LibrariesContainer call

AboutLibraries now wants us to produce the libraries ourselves.

* Replace deprecated ClipboardManager with Clipboard

Clipboard uses suspend functions, hence the coroutine scope addition.

* Use multi-dollar strs to simplify GraphQL queries

These have been available since Kotlin 2.1.

* Remove various redundant casts & conversions

- WebViewScreenContent: loadingState is in the LoadingState.Loading
  branch, no need to cast at all
- Bangumi: username is not modified, make val
- Kavita: token is already a String
- PagerViewerAdapter: insertPageLastPage is already null-checked
- PagerViewerAdapter: use reified filterIsInstance
- ReaderViewModel: chapter IDs are already Longs
- CloudflareInterceptor: webview is smart-cast to non-null here

* Replace deprecated MenuAnchorType

Literally just a typealias for ExposedDropdownMenuAnchorType anyway.

* OptimizeNonSkippingGroups is enabled by default

* Suppress shadowing warning

This is explicitly intentional according to the KDocs.

* Migrate Context Receivers to Context Parameters

Requires changing the compiler arg, but that is part of the migration:

https://blog.jetbrains.com/kotlin/2025/04/update-on-context-parameters

Apparently, the only visible change is that names are required now.
"_" can be used for anonymous context parameters.

* Fix expression bodies with explicit return

Naming conflict resolved by aliasing.

From 2.4/2.5 onward, these will only be allowed with explicit return
types, or have to be turned into a block body. I opted for the latter
since the function is reasonably dense already.

see: https://youtrack.jetbrains.com/issue/KTLC-288

* Suppress deprecation of non-AutoMirrored icons

We use these arrows for navigation in the Upcoming screen.
I strongly doubt the AutoMirrored versions would make sense for our
use-case.

* Explicitly opt-in to new annotation default rules

affects the following annotated value-parameters:
- Preference.SliderPreference.steps (`@IntRange`)
- ReaderViewModel.State.brightnessOverlayValue (`@IntRange`)
- ReadingMode.iconRes (`@DrawableRes`)
- MigrationListScreenModel.Dialog.Progress.progress (`@FloatRange`)

see: https://youtrack.jetbrains.com/issue/KT-73255
see: https://github.com/Kotlin/KEEP/blob/change-defaulting-rule/proposals/annotation-target-in-properties.md

Warning message was the following:

    This annotation is currently applied to the value parameter only, but in the future it will also be applied to field.
    - To opt in to applying to both value parameter and field, add '-Xannotation-default-target=param-property' to your compiler arguments.
    - To keep applying to the value parameter only, use the '@param:' annotation target.

(cherry picked from commit b543bc089a442c5e93b0fb6c83bc4037740b1eb5)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewerAdapter.kt
#	core/common/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/CloudflareInterceptor.kt
#	core/common/src/main/kotlin/mihon/core/common/archive/ArchiveInputStream.kt
This commit is contained in:
MajorTanya
2026-02-05 06:29:21 +01:00
committed by Jobobby04
parent bb8698b2a6
commit 2034971cc0
22 changed files with 104 additions and 83 deletions
+1
View File
@@ -157,6 +157,7 @@ kotlin {
"-opt-in=kotlinx.coroutines.FlowPreview", "-opt-in=kotlinx.coroutines.FlowPreview",
"-opt-in=kotlinx.coroutines.InternalCoroutinesApi", "-opt-in=kotlinx.coroutines.InternalCoroutinesApi",
"-opt-in=kotlinx.serialization.ExperimentalSerializationApi", "-opt-in=kotlinx.serialization.ExperimentalSerializationApi",
"-Xannotation-default-target=param-property",
) )
} }
} }
@@ -21,8 +21,9 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.PlainTooltip import androidx.compose.material3.PlainTooltip
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextFieldDefaults import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.TooltipAnchorPosition
import androidx.compose.material3.TooltipBox import androidx.compose.material3.TooltipBox
import androidx.compose.material3.TooltipDefaults import androidx.compose.material3.TooltipDefaults.rememberTooltipPositionProvider
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.material3.TopAppBarScrollBehavior
@@ -195,7 +196,7 @@ fun AppBarActions(
actions.filterIsInstance<AppBar.Action>().map { actions.filterIsInstance<AppBar.Action>().map {
TooltipBox( TooltipBox(
positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(), positionProvider = rememberTooltipPositionProvider(TooltipAnchorPosition.Above),
tooltip = { tooltip = {
PlainTooltip { PlainTooltip {
Text(it.title) Text(it.title)
@@ -220,7 +221,7 @@ fun AppBarActions(
val overflowActions = actions.filterIsInstance<AppBar.OverflowAction>() val overflowActions = actions.filterIsInstance<AppBar.OverflowAction>()
if (overflowActions.isNotEmpty()) { if (overflowActions.isNotEmpty()) {
TooltipBox( TooltipBox(
positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(), positionProvider = rememberTooltipPositionProvider(TooltipAnchorPosition.Above),
tooltip = { tooltip = {
PlainTooltip { PlainTooltip {
Text(stringResource(MR.strings.action_menu_overflow_description)) Text(stringResource(MR.strings.action_menu_overflow_description))
@@ -349,7 +350,7 @@ fun SearchToolbar(
// Don't show search action // Don't show search action
} else if (searchQuery == null) { } else if (searchQuery == null) {
TooltipBox( TooltipBox(
positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(), positionProvider = rememberTooltipPositionProvider(TooltipAnchorPosition.Above),
tooltip = { tooltip = {
PlainTooltip { PlainTooltip {
Text(stringResource(MR.strings.action_search)) Text(stringResource(MR.strings.action_search))
@@ -369,7 +370,7 @@ fun SearchToolbar(
} }
} else if (searchQuery.isNotEmpty()) { } else if (searchQuery.isNotEmpty()) {
TooltipBox( TooltipBox(
positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(), positionProvider = rememberTooltipPositionProvider(TooltipAnchorPosition.Above),
tooltip = { tooltip = {
PlainTooltip { PlainTooltip {
Text(stringResource(MR.strings.action_reset)) Text(stringResource(MR.strings.action_reset))
@@ -245,7 +245,6 @@ object AboutScreen : Screen() {
is GetApplicationRelease.Result.OsTooOld -> { is GetApplicationRelease.Result.OsTooOld -> {
context.toast(MR.strings.update_check_eol) context.toast(MR.strings.update_check_eol)
} }
else -> {}
} }
} catch (e: Exception) { } catch (e: Exception) {
context.toast(e.message) context.toast(e.message)
@@ -2,13 +2,16 @@ package eu.kanade.presentation.more.settings.screen.about
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import com.mikepenz.aboutlibraries.ui.compose.android.produceLibraries
import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer
import com.mikepenz.aboutlibraries.ui.compose.util.htmlReadyLicenseContent import com.mikepenz.aboutlibraries.ui.compose.util.htmlReadyLicenseContent
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.util.Screen import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.R
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
@@ -27,7 +30,9 @@ class OpenSourceLicensesScreen : Screen() {
) )
}, },
) { contentPadding -> ) { contentPadding ->
val libraries by produceLibraries(R.raw.aboutlibraries)
LibrariesContainer( LibrariesContainer(
libraries = libraries,
modifier = Modifier modifier = Modifier
.fillMaxSize(), .fillMaxSize(),
contentPadding = contentPadding, contentPadding = contentPadding,
@@ -78,7 +78,7 @@ class DebugInfoScreen : Screen() {
val status by produceState(initialValue = "-") { val status by produceState(initialValue = "-") {
val result = ProfileVerifier.getCompilationStatusAsync().await().profileInstallResultCode val result = ProfileVerifier.getCompilationStatusAsync().await().profileInstallResultCode
value = when (result) { value = when (result) {
ProfileVerifier.CompilationStatus.RESULT_CODE_NO_PROFILE -> "No profile installed" ProfileVerifier.CompilationStatus.RESULT_CODE_NO_PROFILE_INSTALLED -> "No profile installed"
ProfileVerifier.CompilationStatus.RESULT_CODE_COMPILED_WITH_PROFILE -> "Compiled" ProfileVerifier.CompilationStatus.RESULT_CODE_COMPILED_WITH_PROFILE -> "Compiled"
ProfileVerifier.CompilationStatus.RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING -> ProfileVerifier.CompilationStatus.RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING ->
"Compiled non-matching" "Compiled non-matching"
@@ -1,5 +1,6 @@
package eu.kanade.presentation.track package eu.kanade.presentation.track
import android.content.ClipData
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
@@ -47,6 +48,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
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
@@ -55,11 +57,11 @@ import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.platform.ClipboardManager import androidx.compose.ui.platform.Clipboard
import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalClipboard
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.platform.toClipEntry
import androidx.compose.ui.text.capitalize import androidx.compose.ui.text.capitalize
import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.text.intl.Locale
@@ -73,6 +75,7 @@ import eu.kanade.presentation.manga.components.MangaCover
import eu.kanade.presentation.theme.TachiyomiPreviewTheme import eu.kanade.presentation.theme.TachiyomiPreviewTheme
import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.util.system.openInBrowser import eu.kanade.tachiyomi.util.system.openInBrowser
import kotlinx.coroutines.launch
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.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
@@ -240,7 +243,7 @@ private fun SearchResultItem(
onClick: () -> Unit, onClick: () -> Unit,
) { ) {
val context = LocalContext.current val context = LocalContext.current
val clipboardManager: ClipboardManager = LocalClipboardManager.current val clipboard: Clipboard = LocalClipboard.current
val focusManager = LocalFocusManager.current val focusManager = LocalFocusManager.current
val type = trackSearch.publishing_type.toLowerCase(Locale.current).capitalize(Locale.current) val type = trackSearch.publishing_type.toLowerCase(Locale.current).capitalize(Locale.current)
val status = trackSearch.publishing_status.toLowerCase(Locale.current).capitalize(Locale.current) val status = trackSearch.publishing_status.toLowerCase(Locale.current).capitalize(Locale.current)
@@ -248,6 +251,7 @@ private fun SearchResultItem(
val shape = RoundedCornerShape(16.dp) val shape = RoundedCornerShape(16.dp)
val borderColor = if (selected) MaterialTheme.colorScheme.outline else Color.Transparent val borderColor = if (selected) MaterialTheme.colorScheme.outline else Color.Transparent
var dropDownMenuExpanded by remember { mutableStateOf(false) } var dropDownMenuExpanded by remember { mutableStateOf(false) }
val scope = rememberCoroutineScope()
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@@ -295,7 +299,13 @@ private fun SearchResultItem(
expanded = dropDownMenuExpanded, expanded = dropDownMenuExpanded,
onCollapseMenu = { dropDownMenuExpanded = false }, onCollapseMenu = { dropDownMenuExpanded = false },
onCopyName = { onCopyName = {
clipboardManager.setText(AnnotatedString(trackSearch.title)) scope.launch {
val clipEntry = ClipData.newPlainText(
trackSearch.title,
trackSearch.title,
).toClipEntry()
clipboard.setClipEntry(clipEntry)
}
}, },
onOpenInBrowser = { onOpenInBrowser = {
val url = trackSearch.tracking_url val url = trackSearch.tracking_url
@@ -9,21 +9,21 @@ import tachiyomi.domain.source.model.SourceNotInstalledException
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import java.net.UnknownHostException import java.net.UnknownHostException
context(Context) context(context: Context)
val Throwable.formattedMessage: String val Throwable.formattedMessage: String
get() { get() {
when (this) { when (this) {
is HttpException -> return stringResource(MR.strings.exception_http, code) is HttpException -> return context.stringResource(MR.strings.exception_http, code)
is UnknownHostException -> { is UnknownHostException -> {
return if (!isOnline()) { return if (!context.isOnline()) {
stringResource(MR.strings.exception_offline) context.stringResource(MR.strings.exception_offline)
} else { } else {
stringResource(MR.strings.exception_unknown_host, message ?: "") context.stringResource(MR.strings.exception_unknown_host, message ?: "")
} }
} }
is NoResultsException -> return stringResource(MR.strings.no_results_found) is NoResultsException -> return context.stringResource(MR.strings.no_results_found)
is SourceNotInstalledException -> return stringResource(MR.strings.loader_not_implemented_error) is SourceNotInstalledException -> return context.stringResource(MR.strings.loader_not_implemented_error)
} }
return when (val className = this::class.simpleName) { return when (val className = this::class.simpleName) {
"Exception", "IOException" -> message ?: className "Exception", "IOException" -> message ?: className
@@ -4,5 +4,7 @@ import androidx.compose.foundation.lazy.LazyItemScope
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
// https://issuetracker.google.com/352584409 // https://issuetracker.google.com/352584409
context(LazyItemScope) context(itemScope: LazyItemScope)
fun Modifier.animateItemFastScroll() = this.animateItem(fadeInSpec = null, fadeOutSpec = null) fun Modifier.animateItemFastScroll() = with(itemScope) {
this@animateItemFastScroll.animateItem(fadeInSpec = null, fadeOutSpec = null)
}
@@ -80,7 +80,7 @@ fun EhLoginWebViewScreen(
) )
is LoadingState.Loading -> { is LoadingState.Loading -> {
val animatedProgress by animateFloatAsState( val animatedProgress by animateFloatAsState(
(loadingState as? LoadingState.Loading)?.progress ?: 1f, loadingState.progress,
animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec, animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec,
label = "webview_loading", label = "webview_loading",
) )
@@ -273,7 +273,7 @@ fun WebViewScreenContent(
.align(Alignment.BottomCenter), .align(Alignment.BottomCenter),
) )
is LoadingState.Loading -> LinearProgressIndicator( is LoadingState.Loading -> LinearProgressIndicator(
progress = { (loadingState as? LoadingState.Loading)?.progress ?: 1f }, progress = { loadingState.progress },
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.align(Alignment.BottomCenter), .align(Alignment.BottomCenter),
@@ -45,9 +45,9 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
suspend fun addLibManga(track: Track): Track { suspend fun addLibManga(track: Track): Track {
return withIOContext { return withIOContext {
val query = """ val query = $$"""
|mutation AddManga(${'$'}mangaId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus, ${'$'}private: Boolean) { |mutation AddManga($mangaId: Int, $progress: Int, $status: MediaListStatus, $private: Boolean) {
|SaveMediaListEntry (mediaId: ${'$'}mangaId, progress: ${'$'}progress, status: ${'$'}status, private: ${'$'}private) { |SaveMediaListEntry (mediaId: $mangaId, progress: $progress, status: $status, private: $private) {
| id | id
| status | status
|} |}
@@ -82,14 +82,14 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
suspend fun updateLibManga(track: Track): Track { suspend fun updateLibManga(track: Track): Track {
return withIOContext { return withIOContext {
val query = """ val query = $$"""
|mutation UpdateManga( |mutation UpdateManga(
|${'$'}listId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus, ${'$'}private: Boolean, |$listId: Int, $progress: Int, $status: MediaListStatus, $private: Boolean,
|${'$'}score: Int, ${'$'}startedAt: FuzzyDateInput, ${'$'}completedAt: FuzzyDateInput |$score: Int, $startedAt: FuzzyDateInput, $completedAt: FuzzyDateInput
|) { |) {
|SaveMediaListEntry( |SaveMediaListEntry(
|id: ${'$'}listId, progress: ${'$'}progress, status: ${'$'}status, private: ${'$'}private, |id: $listId, progress: $progress, status: $status, private: $private,
|scoreRaw: ${'$'}score, startedAt: ${'$'}startedAt, completedAt: ${'$'}completedAt |scoreRaw: $score, startedAt: $startedAt, completedAt: $completedAt
|) { |) {
|id |id
|status |status
@@ -118,9 +118,9 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
suspend fun deleteLibManga(track: DomainTrack) { suspend fun deleteLibManga(track: DomainTrack) {
withIOContext { withIOContext {
val query = """ val query = $$"""
|mutation DeleteManga(${'$'}listId: Int) { |mutation DeleteManga($listId: Int) {
|DeleteMediaListEntry(id: ${'$'}listId) { |DeleteMediaListEntry(id: $listId) {
|deleted |deleted
|} |}
|} |}
@@ -139,10 +139,10 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
suspend fun search(search: String): List<TrackSearch> { suspend fun search(search: String): List<TrackSearch> {
return withIOContext { return withIOContext {
val query = """ val query = $$"""
|query Search(${'$'}query: String) { |query Search($query: String) {
|Page (perPage: 50) { |Page (perPage: 50) {
|media(search: ${'$'}query, type: MANGA, format_not_in: [NOVEL]) { |media(search: $query, type: MANGA, format_not_in: [NOVEL]) {
|id |id
|staff { |staff {
|edges { |edges {
@@ -201,10 +201,10 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
suspend fun findLibManga(track: Track, userid: Int): Track? { suspend fun findLibManga(track: Track, userid: Int): Track? {
return withIOContext { return withIOContext {
val query = """ val query = $$"""
|query (${'$'}id: Int!, ${'$'}manga_id: Int!) { |query ($id: Int!, $manga_id: Int!) {
|Page { |Page {
|mediaList(userId: ${'$'}id, type: MANGA, mediaId: ${'$'}manga_id) { |mediaList(userId: $id, type: MANGA, mediaId: $manga_id) {
|id |id
|status |status
|scoreRaw: score(format: POINT_100) |scoreRaw: score(format: POINT_100)
@@ -113,7 +113,7 @@ class Bangumi(id: Long) : BaseTracker(id, "Bangumi") {
// Users can set a 'username' (not nickname) once which effectively // Users can set a 'username' (not nickname) once which effectively
// replaces the stringified ID in certain queries. // replaces the stringified ID in certain queries.
// If no username is set, the API returns the user ID as a strings // If no username is set, the API returns the user ID as a strings
var username = api.getUsername() val username = api.getUsername()
saveCredentials(username, oauth.accessToken) saveCredentials(username, oauth.accessToken)
} catch (_: Throwable) { } catch (_: Throwable) {
logout() logout()
@@ -137,7 +137,7 @@ class Kavita(id: Long) : BaseTracker(id, "Kavita"), EnhancedTracker {
} }
authentication.apiUrl = prefApiUrl authentication.apiUrl = prefApiUrl
authentication.jwtToken = token.toString() authentication.jwtToken = token
} }
authentications = oauth authentications = oauth
} }
@@ -37,14 +37,14 @@ class SuwayomiApi(private val trackId: Long) {
public fun sourcePreferences(): SharedPreferences = configurableSource.sourcePreferences() public fun sourcePreferences(): SharedPreferences = configurableSource.sourcePreferences()
suspend fun getTrackSearch(mangaId: Long): TrackSearch = withIOContext { suspend fun getTrackSearch(mangaId: Long): TrackSearch = withIOContext {
val query = """ val query = $$"""
|query GetManga(${'$'}mangaId: Int!) { |query GetManga($mangaId: Int!) {
| manga(id: ${'$'}mangaId) { | manga(id: $mangaId) {
| ...MangaFragment | ...MangaFragment
| } | }
|} |}
| |
|$MangaFragment |$$MangaFragment
""".trimMargin() """.trimMargin()
val payload = buildJsonObject { val payload = buildJsonObject {
put("query", query) put("query", query)
@@ -87,9 +87,9 @@ class SuwayomiApi(private val trackId: Long) {
// TODO: Include a filter on the chapter number here // TODO: Include a filter on the chapter number here
// Below, we only consider older chapters; since v2.1.1985 filtering works properly in the query // Below, we only consider older chapters; since v2.1.1985 filtering works properly in the query
val chaptersQuery = """ val chaptersQuery = $$"""
|query GetMangaUnreadChapters(${'$'}mangaId: Int!) { |query GetMangaUnreadChapters($mangaId: Int!) {
| chapters(condition: {mangaId: ${'$'}mangaId, isRead: false}) { | chapters(condition: {mangaId: $mangaId, isRead: false}) {
| nodes { | nodes {
| id | id
| chapterNumber | chapterNumber
@@ -119,20 +119,20 @@ class SuwayomiApi(private val trackId: Long) {
} }
val markQuery = if (deleteDownloadsOnServer) { val markQuery = if (deleteDownloadsOnServer) {
""" $$"""
|mutation MarkChaptersRead(${'$'}chapters: [Int!]!) { |mutation MarkChaptersRead($chapters: [Int!]!) {
| updateChapters(input: {ids: ${'$'}chapters, patch: {isRead: true}}) { | updateChapters(input: {ids: $chapters, patch: {isRead: true}}) {
| __typename | __typename
| } | }
| deleteDownloadedChapters(input: {ids: ${'$'}chapters}) { | deleteDownloadedChapters(input: {ids: $chapters}) {
| __typename | __typename
| } | }
|} |}
""".trimMargin() """.trimMargin()
} else { } else {
""" $$"""
|mutation MarkChaptersRead(${'$'}chapters: [Int!]!) { |mutation MarkChaptersRead($chapters: [Int!]!) {
| updateChapters(input: {ids: ${'$'}chapters, patch: {isRead: true}}) { | updateChapters(input: {ids: $chapters, patch: {isRead: true}}) {
| __typename | __typename
| } | }
|} |}
@@ -156,9 +156,9 @@ class SuwayomiApi(private val trackId: Long) {
.awaitSuccess() .awaitSuccess()
} }
val trackQuery = """ val trackQuery = $$"""
|mutation TrackManga(${'$'}mangaId: Int!) { |mutation TrackManga($mangaId: Int!) {
| trackProgress(input: {mangaId: ${'$'}mangaId}) { | trackProgress(input: {mangaId: $mangaId}) {
| __typename | __typename
| } | }
|} |}
@@ -655,7 +655,7 @@ class ReaderViewModel @JvmOverloads constructor(
* if setting is enabled and [currentChapter] is queued for download * if setting is enabled and [currentChapter] is queued for download
*/ */
private fun cancelQueuedDownloads(currentChapter: ReaderChapter): Download? { private fun cancelQueuedDownloads(currentChapter: ReaderChapter): Download? {
return downloadManager.getQueuedDownloadOrNull(currentChapter.chapter.id!!.toLong())?.also { return downloadManager.getQueuedDownloadOrNull(currentChapter.chapter.id!!)?.also {
downloadManager.cancelQueuedDownloads(listOf(it)) downloadManager.cancelQueuedDownloads(listOf(it))
} }
} }
@@ -848,7 +848,7 @@ class ReaderViewModel @JvmOverloads constructor(
viewModelScope.launchNonCancellable { viewModelScope.launchNonCancellable {
updateChapter.await( updateChapter.await(
ChapterUpdate( ChapterUpdate(
id = chapter.id!!.toLong(), id = chapter.id!!,
bookmark = bookmarked, bookmark = bookmarked,
), ),
) )
@@ -5,6 +5,7 @@ package androidx.preference
/** /**
* Returns package-private [EditTextPreference.getOnBindEditTextListener] * Returns package-private [EditTextPreference.getOnBindEditTextListener]
*/ */
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
fun EditTextPreference.getOnBindEditTextListener(): EditTextPreference.OnBindEditTextListener? { fun EditTextPreference.getOnBindEditTextListener(): EditTextPreference.OnBindEditTextListener? {
return onBindEditTextListener return onBindEditTextListener
} }
@@ -54,9 +54,11 @@ fun CalenderHeader(
} }
Row { Row {
IconButton(onClick = onPreviousClick) { IconButton(onClick = onPreviousClick) {
@Suppress("DEPRECATION")
Icon(Icons.Default.KeyboardArrowLeft, stringResource(MR.strings.upcoming_calendar_prev)) Icon(Icons.Default.KeyboardArrowLeft, stringResource(MR.strings.upcoming_calendar_prev))
} }
IconButton(onClick = onNextClick) { IconButton(onClick = onNextClick) {
@Suppress("DEPRECATION")
Icon(Icons.Default.KeyboardArrowRight, stringResource(MR.strings.upcoming_calendar_next)) Icon(Icons.Default.KeyboardArrowRight, stringResource(MR.strings.upcoming_calendar_next))
} }
} }
@@ -14,7 +14,6 @@ import org.gradle.kotlin.dsl.provideDelegate
import org.gradle.kotlin.dsl.the import org.gradle.kotlin.dsl.the
import org.gradle.kotlin.dsl.withType import org.gradle.kotlin.dsl.withType
import org.jetbrains.kotlin.compose.compiler.gradle.ComposeCompilerGradlePluginExtension import org.jetbrains.kotlin.compose.compiler.gradle.ComposeCompilerGradlePluginExtension
import org.jetbrains.kotlin.compose.compiler.gradle.ComposeFeatureFlag
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import java.io.File import java.io.File
@@ -42,7 +41,7 @@ internal fun Project.configureAndroid(commonExtension: CommonExtension<*, *, *,
compilerOptions { compilerOptions {
jvmTarget.set(AndroidConfig.JvmTarget) jvmTarget.set(AndroidConfig.JvmTarget)
freeCompilerArgs.addAll( freeCompilerArgs.addAll(
"-Xcontext-receivers", "-Xcontext-parameters",
"-opt-in=kotlin.RequiresOptIn", "-opt-in=kotlin.RequiresOptIn",
) )
@@ -73,8 +72,6 @@ internal fun Project.configureCompose(commonExtension: CommonExtension<*, *, *,
} }
extensions.configure<ComposeCompilerGradlePluginExtension> { extensions.configure<ComposeCompilerGradlePluginExtension> {
featureFlags.set(setOf(ComposeFeatureFlag.OptimizeNonSkippingGroups))
val enableMetrics = project.providers.gradleProperty("enableComposeCompilerMetrics").orNull.toBoolean() val enableMetrics = project.providers.gradleProperty("enableComposeCompilerMetrics").orNull.toBoolean()
val enableReports = project.providers.gradleProperty("enableComposeCompilerReports").orNull.toBoolean() val enableReports = project.providers.gradleProperty("enableComposeCompilerReports").orNull.toBoolean()
@@ -134,18 +134,18 @@ fun OkHttpClient.newCachelessCallWithProgress(request: Request, listener: Progre
return progressClient.newCall(request) return progressClient.newCall(request)
} }
context(Json) context(_: Json)
inline fun <reified T> Response.parseAs(): T { inline fun <reified T> Response.parseAs(): T {
return decodeFromJsonResponse(serializer(), this) return decodeFromJsonResponse(serializer(), this)
} }
context(Json) context(json: Json)
fun <T> decodeFromJsonResponse( fun <T> decodeFromJsonResponse(
deserializer: DeserializationStrategy<T>, deserializer: DeserializationStrategy<T>,
response: Response, response: Response,
): T { ): T {
return response.body.source().use { return response.body.source().use {
decodeFromBufferedSource(deserializer, it) json.decodeFromBufferedSource(deserializer, it)
} }
} }
@@ -73,7 +73,7 @@ class CloudflareInterceptor(
executor.execute { executor.execute {
webview = createWebView(originalRequest) webview = createWebView(originalRequest)
webview?.webViewClient = object : WebViewClientCompat() { webview.webViewClient = object : WebViewClientCompat() {
override fun onPageFinished(view: WebView, url: String) { override fun onPageFinished(view: WebView, url: String) {
fun isCloudFlareBypassed(): Boolean { fun isCloudFlareBypassed(): Boolean {
return cookieManager.get(origRequestUrl.toHttpUrl()) return cookieManager.get(origRequestUrl.toHttpUrl())
@@ -111,7 +111,7 @@ class CloudflareInterceptor(
} }
} }
webview?.loadUrl(origRequestUrl, headers) webview.loadUrl(origRequestUrl, headers)
} }
latch.awaitFor30Seconds() latch.awaitFor30Seconds()
@@ -7,6 +7,7 @@ import me.zhanghai.android.libarchive.ArchiveException
import java.io.InputStream import java.io.InputStream
import java.nio.ByteBuffer import java.nio.ByteBuffer
import kotlin.concurrent.Volatile import kotlin.concurrent.Volatile
import mihon.core.common.archive.ArchiveEntry as MihonArchiveEntry
class ArchiveInputStream( class ArchiveInputStream(
buffer: Long, buffer: Long,
@@ -67,13 +68,14 @@ class ArchiveInputStream(
Archive.readFree(archive) Archive.readFree(archive)
} }
fun getNextEntry() = Archive.readNextHeader(archive).takeUnless { it == 0L }?.let { entry -> fun getNextEntry(): MihonArchiveEntry? {
return Archive.readNextHeader(archive).takeUnless { it == 0L }?.let { entry ->
val name = ArchiveEntry.pathnameUtf8(entry) ?: ArchiveEntry.pathname(entry)?.decodeToString() ?: return null val name = ArchiveEntry.pathnameUtf8(entry) ?: ArchiveEntry.pathname(entry)?.decodeToString() ?: return null
val isFile = ArchiveEntry.filetype(entry) == ArchiveEntry.AE_IFREG val isFile = ArchiveEntry.filetype(entry) == ArchiveEntry.AE_IFREG
// SY --> // SY -->
val isEncrypted = ArchiveEntry.isEncrypted(entry) val isEncrypted = ArchiveEntry.isEncrypted(entry)
// SY <-- // SY <--
ArchiveEntry( MihonArchiveEntry(
name, name,
isFile, isFile,
// SY --> // SY -->
@@ -82,3 +84,4 @@ class ArchiveInputStream(
) )
} }
} }
}
@@ -23,11 +23,11 @@ import androidx.compose.material.icons.rounded.CheckBoxOutlineBlank
import androidx.compose.material.icons.rounded.DisabledByDefault import androidx.compose.material.icons.rounded.DisabledByDefault
import androidx.compose.material3.Checkbox import androidx.compose.material3.Checkbox
import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExposedDropdownMenuAnchorType
import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.MenuAnchorType
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.RadioButton import androidx.compose.material3.RadioButton
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
@@ -293,7 +293,7 @@ fun SelectItem(
) { ) {
OutlinedTextField( OutlinedTextField(
modifier = Modifier modifier = Modifier
.menuAnchor(MenuAnchorType.PrimaryNotEditable) .menuAnchor(ExposedDropdownMenuAnchorType.PrimaryNotEditable)
.fillMaxWidth() .fillMaxWidth()
.padding( .padding(
horizontal = SettingsItemsPaddings.Horizontal, horizontal = SettingsItemsPaddings.Horizontal,