From 015620711d4d6b060a5e416995efd2c6e6b20bbc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 12:56:45 +0600 Subject: [PATCH 001/146] Update gradle/actions action to v3.2.1 (#657) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- .github/workflows/build_push.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index d58311c47..585cd2a7b 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -35,7 +35,7 @@ jobs: distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@e24011a3b5db78bd5ab798036042d9312002f252 # v3.2.0 + uses: gradle/actions/setup-gradle@1168cd3d07c1876a65e1724114de42ccbdfa7b78 # v3.2.1 - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index 878382f17..218778a7b 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -33,7 +33,7 @@ jobs: distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@e24011a3b5db78bd5ab798036042d9312002f252 # v3.2.0 + uses: gradle/actions/setup-gradle@1168cd3d07c1876a65e1724114de42ccbdfa7b78 # v3.2.1 - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest From ea0fe2414e1e30b6e82ddf65144035283b31a5c4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 12:57:10 +0600 Subject: [PATCH 002/146] Update dependency com.android.tools.build:gradle to v8.3.2 (#655) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 2357dfc0b..879f5e846 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -1,5 +1,5 @@ [versions] -agp_version = "8.3.1" +agp_version = "8.3.2" lifecycle_version = "2.7.0" paging_version = "3.2.1" From 4c43a0ef66e9c8a321fb745b860319aaa074c57f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 12:57:35 +0600 Subject: [PATCH 003/146] Update aboutlib.version to v11.1.3 (#654) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7a5e8555b..5f663ad82 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -aboutlib_version = "11.1.1" +aboutlib_version = "11.1.3" leakcanary = "2.13" moko = "0.23.0" okhttp_version = "5.0.0-alpha.12" From c94d212ef471012990bfe02ba43044de476ab9cd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 13:21:30 +0600 Subject: [PATCH 004/146] chore(deps): update gradle/wrapper-validation-action action to v2.1.3 (#658) Update gradle/wrapper-validation-action action to v2.1.3 Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- .github/workflows/build_push.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 585cd2a7b..75864d31f 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@b231772637bb498f11fdbc86052b6e8a8dc9fc92 # v2.1.2 + uses: gradle/wrapper-validation-action@b5418f5a58f5fd2eb486dd7efb368fe7be7eae45 # v2.1.3 - name: Dependency Review uses: actions/dependency-review-action@5bbc3ba658137598168acb2ab73b21c432dd411b # v4.2.5 diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index 218778a7b..d64c3c569 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -20,7 +20,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@b231772637bb498f11fdbc86052b6e8a8dc9fc92 # v2.1.2 + uses: gradle/wrapper-validation-action@b5418f5a58f5fd2eb486dd7efb368fe7be7eae45 # v2.1.3 - name: Setup Android SDK run: | From 5378c4c5d6407064ee7f170a0224a748a6f45c61 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 13 Apr 2024 21:55:56 +0600 Subject: [PATCH 005/146] chore(deps): update gradle/wrapper-validation-action action to v3 (#661) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- .github/workflows/build_push.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 75864d31f..1e680c32d 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@b5418f5a58f5fd2eb486dd7efb368fe7be7eae45 # v2.1.3 + uses: gradle/wrapper-validation-action@460a3ca55fc5d559238a0efc7fa9f7465df8585d # v3.3.0 - name: Dependency Review uses: actions/dependency-review-action@5bbc3ba658137598168acb2ab73b21c432dd411b # v4.2.5 diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index d64c3c569..34c85991b 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -20,7 +20,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@b5418f5a58f5fd2eb486dd7efb368fe7be7eae45 # v2.1.3 + uses: gradle/wrapper-validation-action@460a3ca55fc5d559238a0efc7fa9f7465df8585d # v3.3.0 - name: Setup Android SDK run: | From 8bba9268918138106aa156db4e530b66c54f85d2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 13 Apr 2024 21:56:30 +0600 Subject: [PATCH 006/146] chore(deps): update gradle/actions action to v3.3.0 (#659) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- .github/workflows/build_push.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 1e680c32d..74488ccfd 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -35,7 +35,7 @@ jobs: distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@1168cd3d07c1876a65e1724114de42ccbdfa7b78 # v3.2.1 + uses: gradle/actions/setup-gradle@6cec5d49d4d6d4bb982fbed7047db31ea6d38f11 # v3.3.0 - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index 34c85991b..700c0466c 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -33,7 +33,7 @@ jobs: distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@1168cd3d07c1876a65e1724114de42ccbdfa7b78 # v3.2.1 + uses: gradle/actions/setup-gradle@6cec5d49d4d6d4bb982fbed7047db31ea6d38f11 # v3.3.0 - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest From f080a4937e61d3dde5473876c34db8f16844e30c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 13 Apr 2024 21:57:21 +0600 Subject: [PATCH 007/146] fix(deps): update dependency com.google.firebase:firebase-analytics-ktx to v21.6.2 (#656) Update dependency com.google.firebase:firebase-analytics-ktx to v21.6.2 Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5f663ad82..47446f893 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -73,7 +73,7 @@ moko-gradle = { module = "dev.icerock.moko:resources-generator", version.ref = " logcat = "com.squareup.logcat:logcat:0.1" -firebase-analytics = "com.google.firebase:firebase-analytics-ktx:21.6.1" +firebase-analytics = "com.google.firebase:firebase-analytics-ktx:21.6.2" aboutLibraries-gradle = { module = "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin", version.ref = "aboutlib_version" } aboutLibraries-compose = { module = "com.mikepenz:aboutlibraries-compose-m3", version.ref = "aboutlib_version" } From 843daa5304d0b1a93ba69f8cc69791e446a58596 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Wed, 17 Apr 2024 15:21:09 +0800 Subject: [PATCH 008/146] Update compose bom and fix renovate config for it (#674) --- .github/renovate.json5 | 10 +++++++++- gradle/compose.versions.toml | 3 ++- .../presentation/core/components/AdaptiveSheet.kt | 4 ++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 4cc36b133..751500ef3 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -6,12 +6,20 @@ "schedule": ["every friday"], "labels": ["Dependencies"], "packageRules": [ + { + "groupName": "Compose BOM", + "matchPackageNames": [ + "dev.chrisbanes.compose:compose-bom" + ], + "ignoreUnstable": false + }, { // Compiler plugins are tightly coupled to Kotlin version "groupName": "Kotlin", "matchPackagePrefixes": [ "androidx.compose.compiler", - "org.jetbrains.kotlin", + "org.jetbrains.kotlin.", + "org.jetbrains.kotlin:" ], } ] diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index b37960e0a..cd0e14211 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,6 +1,7 @@ [versions] compiler = "1.5.11" -compose-bom = "2024.02.00-alpha02" +# 2024.04.00-alpha01 has several bugs with the new animateItem() modifier +compose-bom = "2024.03.00-alpha02" accompanist = "0.35.0-alpha" [libraries] diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/AdaptiveSheet.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/AdaptiveSheet.kt index 568332ca7..5ee802edc 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/AdaptiveSheet.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/AdaptiveSheet.kt @@ -122,7 +122,7 @@ fun AdaptiveSheet( ) } val internalOnDismissRequest = { - if (anchoredDraggableState.currentValue == 0) { + if (anchoredDraggableState.settledValue == 0) { scope.launch { anchoredDraggableState.animateTo(1) } } } @@ -192,7 +192,7 @@ fun AdaptiveSheet( LaunchedEffect(anchoredDraggableState) { scope.launch { anchoredDraggableState.animateTo(0) } - snapshotFlow { anchoredDraggableState.currentValue } + snapshotFlow { anchoredDraggableState.settledValue } .drop(1) .filter { it == 1 } .collectLatest { From f27ca3b1b2f92258c213bca6b27d8eff4c7363ad Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Wed, 17 Apr 2024 15:21:24 +0800 Subject: [PATCH 009/146] Use m3 ripple and clean up interactionSource usage (#675) Also remove a leftover of scoped storage adaptation. --- app/build.gradle.kts | 1 - .../presentation/browse/ExtensionDetailsScreen.kt | 5 +---- .../manga/components/ChapterDownloadIndicator.kt | 2 +- .../manga/components/MangaBottomActionMenu.kt | 5 ++--- .../presentation/core/components/AdaptiveSheet.kt | 10 ++++------ .../core/components/material/Surface.kt | 2 +- .../tachiyomi/presentation/core/util/Modifier.kt | 15 ++++++--------- 7 files changed, 15 insertions(+), 25 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a4b166fa1..5a16ab034 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -286,7 +286,6 @@ tasks { "-opt-in=androidx.compose.animation.ExperimentalAnimationApi", "-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi", "-opt-in=coil3.annotation.ExperimentalCoilApi", - "-opt-in=com.google.accompanist.permissions.ExperimentalPermissionsApi", "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", "-opt-in=kotlinx.coroutines.FlowPreview", "-opt-in=kotlinx.coroutines.InternalCoroutinesApi", diff --git a/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt index 807b68b20..dcd5067d3 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt @@ -5,7 +5,6 @@ import android.net.Uri import android.provider.Settings import android.util.DisplayMetrics import androidx.compose.foundation.clickable -import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues @@ -354,10 +353,8 @@ private fun InfoText( primaryTextStyle: TextStyle = MaterialTheme.typography.bodyLarge, onClick: (() -> Unit)? = null, ) { - val interactionSource = remember { MutableInteractionSource() } - val clickableModifier = if (onClick != null) { - Modifier.clickable(interactionSource, indication = null) { onClick() } + Modifier.clickable(interactionSource = null, indication = null, onClick = onClick) } else { Modifier } diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt b/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt index b1a1474ec..518f09d4b 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt @@ -9,13 +9,13 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.CheckCircle import androidx.compose.material.icons.outlined.ArrowDownward import androidx.compose.material.icons.outlined.ErrorOutline -import androidx.compose.material.ripple import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ProgressIndicatorDefaults import androidx.compose.material3.Text +import androidx.compose.material3.ripple import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaBottomActionMenu.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaBottomActionMenu.kt index d67a406e7..a78383590 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaBottomActionMenu.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaBottomActionMenu.kt @@ -8,7 +8,6 @@ import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.shrinkVertically import androidx.compose.foundation.combinedClickable -import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -30,11 +29,11 @@ import androidx.compose.material.icons.outlined.Delete import androidx.compose.material.icons.outlined.DoneAll import androidx.compose.material.icons.outlined.Download import androidx.compose.material.icons.outlined.RemoveDone -import androidx.compose.material.ripple import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text +import androidx.compose.material3.ripple import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateListOf @@ -191,7 +190,7 @@ private fun RowScope.Button( .size(48.dp) .weight(animatedWeight) .combinedClickable( - interactionSource = remember { MutableInteractionSource() }, + interactionSource = null, indication = ripple(bounded = false), onLongClick = onLongClick, onClick = onClick, diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/AdaptiveSheet.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/AdaptiveSheet.kt index 5ee802edc..fa6eb375a 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/AdaptiveSheet.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/AdaptiveSheet.kt @@ -10,7 +10,6 @@ import androidx.compose.foundation.gestures.DraggableAnchors import androidx.compose.foundation.gestures.Orientation 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.fillMaxSize import androidx.compose.foundation.layout.navigationBarsPadding @@ -78,8 +77,7 @@ fun AdaptiveSheet( Box( modifier = Modifier .clickable( - enabled = true, - interactionSource = remember { MutableInteractionSource() }, + interactionSource = null, indication = null, onClick = internalOnDismissRequest, ) @@ -91,7 +89,7 @@ fun AdaptiveSheet( modifier = Modifier .requiredWidthIn(max = 460.dp) .clickable( - interactionSource = remember { MutableInteractionSource() }, + interactionSource = null, indication = null, onClick = {}, ) @@ -129,7 +127,7 @@ fun AdaptiveSheet( Box( modifier = Modifier .clickable( - interactionSource = remember { MutableInteractionSource() }, + interactionSource = null, indication = null, onClick = internalOnDismissRequest, ) @@ -147,7 +145,7 @@ fun AdaptiveSheet( modifier = Modifier .widthIn(max = 460.dp) .clickable( - interactionSource = remember { MutableInteractionSource() }, + interactionSource = null, indication = null, onClick = {}, ) diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Surface.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Surface.kt index e472e5127..0e857ef75 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Surface.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Surface.kt @@ -6,13 +6,13 @@ import androidx.compose.foundation.border import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box -import androidx.compose.material.ripple import androidx.compose.material3.ColorScheme import androidx.compose.material3.LocalAbsoluteTonalElevation import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.contentColorFor import androidx.compose.material3.minimumInteractiveComponentSize +import androidx.compose.material3.ripple import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.NonRestartableComposable diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/util/Modifier.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/util/Modifier.kt index 411fc9983..875cd4583 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/util/Modifier.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/util/Modifier.kt @@ -1,7 +1,6 @@ package tachiyomi.presentation.core.util import androidx.compose.foundation.combinedClickable -import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.isImeVisible @@ -42,14 +41,12 @@ fun Modifier.secondaryItemAlpha(): Modifier = this.alpha(SecondaryItemAlpha) fun Modifier.clickableNoIndication( onLongClick: (() -> Unit)? = null, onClick: () -> Unit, -): Modifier = composed { - Modifier.combinedClickable( - interactionSource = remember { MutableInteractionSource() }, - indication = null, - onLongClick = onLongClick, - onClick = onClick, - ) -} +) = this.combinedClickable( + interactionSource = null, + indication = null, + onLongClick = onLongClick, + onClick = onClick, +) /** * For TextField, the provided [action] will be invoked when From b152e3881bffd9050a8a0ed4030823886e3fe04f Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Sat, 20 Apr 2024 12:52:40 +0800 Subject: [PATCH 010/146] Use Okio instead of `java.io` for image processing (#691) --- app/src/main/java/eu/kanade/tachiyomi/App.kt | 2 + .../data/coil/BufferedSourceFetcher.kt | 38 +++++++++ .../ui/reader/viewer/ReaderPageImageView.kt | 26 +++--- .../ui/reader/viewer/pager/PagerPageHolder.kt | 81 +++++++++---------- .../viewer/webtoon/WebtoonPageHolder.kt | 39 ++++----- .../core/common/util/system/ImageUtil.kt | 73 +++++++---------- 6 files changed, 132 insertions(+), 127 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/coil/BufferedSourceFetcher.kt diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index 12170d8a5..558ad213e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -27,6 +27,7 @@ import eu.kanade.domain.ui.UiPreferences import eu.kanade.domain.ui.model.setAppCompatDelegateThemeMode import eu.kanade.tachiyomi.crash.CrashActivity import eu.kanade.tachiyomi.crash.GlobalExceptionHandler +import eu.kanade.tachiyomi.data.coil.BufferedSourceFetcher import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher import eu.kanade.tachiyomi.data.coil.MangaCoverKeyer import eu.kanade.tachiyomi.data.coil.MangaKeyer @@ -162,6 +163,7 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor add(MangaCoverFetcher.MangaCoverFactory(callFactoryLazy)) add(MangaKeyer()) add(MangaCoverKeyer()) + add(BufferedSourceFetcher.Factory()) } crossfade((300 * this@App.animatorDurationScale).toInt()) allowRgb565(DeviceUtil.isLowRamDevice(this@App)) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/BufferedSourceFetcher.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/BufferedSourceFetcher.kt new file mode 100644 index 000000000..4bee925ed --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/BufferedSourceFetcher.kt @@ -0,0 +1,38 @@ +package eu.kanade.tachiyomi.data.coil + +import coil3.ImageLoader +import coil3.decode.DataSource +import coil3.decode.ImageSource +import coil3.fetch.FetchResult +import coil3.fetch.Fetcher +import coil3.fetch.SourceFetchResult +import coil3.request.Options +import okio.BufferedSource + +class BufferedSourceFetcher( + private val data: BufferedSource, + private val options: Options, +) : Fetcher { + + override suspend fun fetch(): FetchResult { + return SourceFetchResult( + source = ImageSource( + source = data, + fileSystem = options.fileSystem, + ), + mimeType = null, + dataSource = DataSource.MEMORY, + ) + } + + class Factory : Fetcher.Factory { + + override fun create( + data: BufferedSource, + options: Options, + imageLoader: ImageLoader, + ): Fetcher { + return BufferedSourceFetcher(data, options) + } + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt index 2cd0f2840..0c83d97ea 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt @@ -33,8 +33,7 @@ import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonSubsamplingImageView import eu.kanade.tachiyomi.util.system.GLUtil import eu.kanade.tachiyomi.util.system.animatorDurationScale import eu.kanade.tachiyomi.util.view.isVisibleOnScreen -import java.io.InputStream -import java.nio.ByteBuffer +import okio.BufferedSource /** * A wrapper view for showing page image. @@ -146,14 +145,14 @@ open class ReaderPageImageView @JvmOverloads constructor( } } - fun setImage(inputStream: InputStream, isAnimated: Boolean, config: Config) { + fun setImage(source: BufferedSource, isAnimated: Boolean, config: Config) { this.config = config if (isAnimated) { prepareAnimatedImageView() - setAnimatedImage(inputStream, config) + setAnimatedImage(source, config) } else { prepareNonAnimatedImageView() - setNonAnimatedImage(inputStream, config) + setNonAnimatedImage(source, config) } } @@ -262,7 +261,7 @@ open class ReaderPageImageView @JvmOverloads constructor( } private fun setNonAnimatedImage( - image: Any, + data: Any, config: Config, ) = (pageView as? SubsamplingScaleImageView)?.apply { setDoubleTapZoomDuration(config.zoomDuration.getSystemScaledDuration()) @@ -283,10 +282,10 @@ open class ReaderPageImageView @JvmOverloads constructor( }, ) - when (image) { - is BitmapDrawable -> setImage(ImageSource.bitmap(image.bitmap)) - is InputStream -> setImage(ImageSource.inputStream(image)) - else -> throw IllegalArgumentException("Not implemented for class ${image::class.simpleName}") + when (data) { + is BitmapDrawable -> setImage(ImageSource.bitmap(data.bitmap)) + is BufferedSource -> setImage(ImageSource.inputStream(data.inputStream())) + else -> throw IllegalArgumentException("Not implemented for class ${data::class.simpleName}") } isVisible = true } @@ -331,18 +330,13 @@ open class ReaderPageImageView @JvmOverloads constructor( } private fun setAnimatedImage( - image: Any, + data: Any, config: Config, ) = (pageView as? AppCompatImageView)?.apply { if (this is PhotoView) { setZoomTransitionDuration(config.zoomDuration.getSystemScaledDuration()) } - val data = when (image) { - is Drawable -> image - is InputStream -> ByteBuffer.wrap(image.readBytes()) - else -> throw IllegalArgumentException("Not implemented for class ${image::class.simpleName}") - } val request = ImageRequest.Builder(context) .data(data) .memoryCachePolicy(CachePolicy.DISABLED) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt index 7b6d1dd6a..71d499c38 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt @@ -18,14 +18,13 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope import logcat.LogPriority +import okio.Buffer +import okio.BufferedSource import tachiyomi.core.common.util.lang.launchIO import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.lang.withUIContext import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.core.common.util.system.logcat -import java.io.BufferedInputStream -import java.io.ByteArrayInputStream -import java.io.InputStream /** * View of the ViewPager that contains a page of a chapter. @@ -139,38 +138,30 @@ class PagerPageHolder( val streamFn = page.stream ?: return try { - val (bais, isAnimated, background) = withIOContext { - streamFn().buffered(16).use { stream -> - process(item, stream).use { itemStream -> - val bais = ByteArrayInputStream(itemStream.readBytes()) - val isAnimated = ImageUtil.isAnimatedAndSupported(bais) - bais.reset() - val background = if (!isAnimated && viewer.config.automaticBackground) { - ImageUtil.chooseBackground(context, bais) - } else { - null - } - bais.reset() - Triple(bais, isAnimated, background) - } + val (source, isAnimated, background) = withIOContext { + val source = streamFn().use { process(item, Buffer().readFrom(it)) } + val isAnimated = ImageUtil.isAnimatedAndSupported(source) + val background = if (!isAnimated && viewer.config.automaticBackground) { + ImageUtil.chooseBackground(context, source.peek().inputStream()) + } else { + null } + Triple(source, isAnimated, background) } withUIContext { - bais.use { - setImage( - it, - isAnimated, - Config( - zoomDuration = viewer.config.doubleTapAnimDuration, - minimumScaleType = viewer.config.imageScaleType, - cropBorders = viewer.config.imageCropBorders, - zoomStartPosition = viewer.config.imageZoomType, - landscapeZoom = viewer.config.landscapeZoom, - ), - ) - if (!isAnimated) { - pageBackground = background - } + setImage( + source, + isAnimated, + Config( + zoomDuration = viewer.config.doubleTapAnimDuration, + minimumScaleType = viewer.config.imageScaleType, + cropBorders = viewer.config.imageCropBorders, + zoomStartPosition = viewer.config.imageZoomType, + landscapeZoom = viewer.config.landscapeZoom, + ), + ) + if (!isAnimated) { + pageBackground = background } removeErrorLayout() } @@ -182,40 +173,40 @@ class PagerPageHolder( } } - private fun process(page: ReaderPage, imageStream: BufferedInputStream): InputStream { + private fun process(page: ReaderPage, imageSource: BufferedSource): BufferedSource { if (viewer.config.dualPageRotateToFit) { - return rotateDualPage(imageStream) + return rotateDualPage(imageSource) } if (!viewer.config.dualPageSplit) { - return imageStream + return imageSource } if (page is InsertPage) { - return splitInHalf(imageStream) + return splitInHalf(imageSource) } - val isDoublePage = ImageUtil.isWideImage(imageStream) + val isDoublePage = ImageUtil.isWideImage(imageSource) if (!isDoublePage) { - return imageStream + return imageSource } onPageSplit(page) - return splitInHalf(imageStream) + return splitInHalf(imageSource) } - private fun rotateDualPage(imageStream: BufferedInputStream): InputStream { - val isDoublePage = ImageUtil.isWideImage(imageStream) + private fun rotateDualPage(imageSource: BufferedSource): BufferedSource { + val isDoublePage = ImageUtil.isWideImage(imageSource) return if (isDoublePage) { val rotation = if (viewer.config.dualPageRotateToFitInvert) -90f else 90f - ImageUtil.rotateImage(imageStream, rotation) + ImageUtil.rotateImage(imageSource, rotation) } else { - imageStream + imageSource } } - private fun splitInHalf(imageStream: InputStream): InputStream { + private fun splitInHalf(imageSource: BufferedSource): BufferedSource { var side = when { viewer is L2RPagerViewer && page is InsertPage -> ImageUtil.Side.RIGHT viewer !is L2RPagerViewer && page is InsertPage -> ImageUtil.Side.LEFT @@ -231,7 +222,7 @@ class PagerPageHolder( } } - return ImageUtil.splitInHalf(imageStream, side) + return ImageUtil.splitInHalf(imageSource, side) } private fun onPageSplit(page: ReaderPage) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt index b501725f7..488db7bb6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt @@ -22,15 +22,14 @@ import kotlinx.coroutines.MainScope import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope -import kotlinx.coroutines.suspendCancellableCoroutine import logcat.LogPriority +import okio.Buffer +import okio.BufferedSource import tachiyomi.core.common.util.lang.launchIO import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.lang.withUIContext import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.core.common.util.system.logcat -import java.io.BufferedInputStream -import java.io.InputStream /** * Holder of the webtoon reader for a single page of a chapter. @@ -188,16 +187,14 @@ class WebtoonPageHolder( val streamFn = page?.stream ?: return try { - val (openStream, isAnimated) = withIOContext { - val stream = streamFn().buffered(16) - val openStream = process(stream) - - val isAnimated = ImageUtil.isAnimatedAndSupported(stream) - Pair(openStream, isAnimated) + val (source, isAnimated) = withIOContext { + val source = streamFn().use { process(Buffer().readFrom(it)) } + val isAnimated = ImageUtil.isAnimatedAndSupported(source) + Pair(source, isAnimated) } withUIContext { frame.setImage( - openStream, + source, isAnimated, ReaderPageImageView.Config( zoomDuration = viewer.config.doubleTapAnimDuration, @@ -207,10 +204,6 @@ class WebtoonPageHolder( ) removeErrorLayout() } - // Suspend the coroutine to close the input stream only when the WebtoonPageHolder is recycled - suspendCancellableCoroutine { continuation -> - continuation.invokeOnCancellation { openStream.close() } - } } catch (e: Throwable) { logcat(LogPriority.ERROR, e) withUIContext { @@ -219,29 +212,29 @@ class WebtoonPageHolder( } } - private fun process(imageStream: BufferedInputStream): InputStream { + private fun process(imageSource: BufferedSource): BufferedSource { if (viewer.config.dualPageRotateToFit) { - return rotateDualPage(imageStream) + return rotateDualPage(imageSource) } if (viewer.config.dualPageSplit) { - val isDoublePage = ImageUtil.isWideImage(imageStream) + val isDoublePage = ImageUtil.isWideImage(imageSource) if (isDoublePage) { val upperSide = if (viewer.config.dualPageInvert) ImageUtil.Side.LEFT else ImageUtil.Side.RIGHT - return ImageUtil.splitAndMerge(imageStream, upperSide) + return ImageUtil.splitAndMerge(imageSource, upperSide) } } - return imageStream + return imageSource } - private fun rotateDualPage(imageStream: BufferedInputStream): InputStream { - val isDoublePage = ImageUtil.isWideImage(imageStream) + private fun rotateDualPage(imageSource: BufferedSource): BufferedSource { + val isDoublePage = ImageUtil.isWideImage(imageSource) return if (isDoublePage) { val rotation = if (viewer.config.dualPageRotateToFitInvert) -90f else 90f - ImageUtil.rotateImage(imageStream, rotation) + ImageUtil.rotateImage(imageSource, rotation) } else { - imageStream + imageSource } } diff --git a/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt b/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt index 03a2f2d60..f5e9a8098 100644 --- a/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt @@ -24,11 +24,10 @@ import androidx.core.graphics.green import androidx.core.graphics.red import com.hippo.unifile.UniFile import logcat.LogPriority +import okio.Buffer +import okio.BufferedSource import tachiyomi.decoder.Format import tachiyomi.decoder.ImageDecoder -import java.io.BufferedInputStream -import java.io.ByteArrayInputStream -import java.io.ByteArrayOutputStream import java.io.InputStream import java.net.URLConnection import java.util.Locale @@ -76,9 +75,9 @@ object ImageUtil { ?: "jpg" } - fun isAnimatedAndSupported(stream: InputStream): Boolean { + fun isAnimatedAndSupported(source: BufferedSource): Boolean { return try { - val type = getImageType(stream) ?: return false + val type = getImageType(source.peek().inputStream()) ?: return false // https://coil-kt.github.io/coil/getting_started/#supported-image-formats when (type.format) { Format.Gif -> true @@ -125,18 +124,16 @@ object ImageUtil { * * @return true if the width is greater than the height */ - fun isWideImage(imageStream: BufferedInputStream): Boolean { - val options = extractImageOptions(imageStream) + fun isWideImage(imageSource: BufferedSource): Boolean { + val options = extractImageOptions(imageSource) return options.outWidth > options.outHeight } /** - * Extract the 'side' part from imageStream and return it as InputStream. + * Extract the 'side' part from [BufferedSource] and return it as [BufferedSource]. */ - fun splitInHalf(imageStream: InputStream, side: Side): InputStream { - val imageBytes = imageStream.readBytes() - - val imageBitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size) + fun splitInHalf(imageSource: BufferedSource, side: Side): BufferedSource { + val imageBitmap = BitmapFactory.decodeStream(imageSource.inputStream()) val height = imageBitmap.height val width = imageBitmap.width @@ -150,22 +147,20 @@ object ImageUtil { half.applyCanvas { drawBitmap(imageBitmap, part, singlePage, null) } - val output = ByteArrayOutputStream() - half.compress(Bitmap.CompressFormat.JPEG, 100, output) + val output = Buffer() + half.compress(Bitmap.CompressFormat.JPEG, 100, output.outputStream()) - return ByteArrayInputStream(output.toByteArray()) + return output } - fun rotateImage(imageStream: InputStream, degrees: Float): InputStream { - val imageBytes = imageStream.readBytes() - - val imageBitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size) + fun rotateImage(imageSource: BufferedSource, degrees: Float): BufferedSource { + val imageBitmap = BitmapFactory.decodeStream(imageSource.inputStream()) val rotated = rotateBitMap(imageBitmap, degrees) - val output = ByteArrayOutputStream() - rotated.compress(Bitmap.CompressFormat.JPEG, 100, output) + val output = Buffer() + rotated.compress(Bitmap.CompressFormat.JPEG, 100, output.outputStream()) - return ByteArrayInputStream(output.toByteArray()) + return output } private fun rotateBitMap(bitmap: Bitmap, degrees: Float): Bitmap { @@ -176,10 +171,8 @@ object ImageUtil { /** * Split the image into left and right parts, then merge them into a new image. */ - fun splitAndMerge(imageStream: InputStream, upperSide: Side): InputStream { - val imageBytes = imageStream.readBytes() - - val imageBitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size) + fun splitAndMerge(imageSource: BufferedSource, upperSide: Side): BufferedSource { + val imageBitmap = BitmapFactory.decodeStream(imageSource.inputStream()) val height = imageBitmap.height val width = imageBitmap.width @@ -201,9 +194,9 @@ object ImageUtil { drawBitmap(imageBitmap, leftPart, bottomPart, null) } - val output = ByteArrayOutputStream() - result.compress(Bitmap.CompressFormat.JPEG, 100, output) - return ByteArrayInputStream(output.toByteArray()) + val output = Buffer() + result.compress(Bitmap.CompressFormat.JPEG, 100, output.outputStream()) + return output } enum class Side { @@ -216,8 +209,8 @@ object ImageUtil { * * @return true if the height:width ratio is greater than 3. */ - private fun isTallImage(imageStream: InputStream): Boolean { - val options = extractImageOptions(imageStream, resetAfterExtraction = false) + private fun isTallImage(imageSource: BufferedSource): Boolean { + val options = extractImageOptions(imageSource) return (options.outHeight / options.outWidth) > 3 } @@ -225,17 +218,18 @@ object ImageUtil { * Splits tall images to improve performance of reader */ fun splitTallImage(tmpDir: UniFile, imageFile: UniFile, filenamePrefix: String): Boolean { - if (isAnimatedAndSupported(imageFile.openInputStream()) || !isTallImage(imageFile.openInputStream())) { + val imageSource = imageFile.openInputStream().use { Buffer().readFrom(it) } + if (isAnimatedAndSupported(imageSource) || !isTallImage(imageSource)) { return true } - val bitmapRegionDecoder = getBitmapRegionDecoder(imageFile.openInputStream()) + val bitmapRegionDecoder = getBitmapRegionDecoder(imageSource.peek().inputStream()) if (bitmapRegionDecoder == null) { logcat { "Failed to create new instance of BitmapRegionDecoder" } return false } - val options = extractImageOptions(imageFile.openInputStream(), resetAfterExtraction = false).apply { + val options = extractImageOptions(imageSource).apply { inJustDecodeBounds = false } @@ -548,16 +542,9 @@ object ImageUtil { /** * Used to check an image's dimensions without loading it in the memory. */ - private fun extractImageOptions( - imageStream: InputStream, - resetAfterExtraction: Boolean = true, - ): BitmapFactory.Options { - imageStream.mark(Int.MAX_VALUE) - - val imageBytes = imageStream.readBytes() + private fun extractImageOptions(imageSource: BufferedSource): BitmapFactory.Options { val options = BitmapFactory.Options().apply { inJustDecodeBounds = true } - BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size, options) - if (resetAfterExtraction) imageStream.reset() + BitmapFactory.decodeStream(imageSource.peek().inputStream(), null, options) return options } From 7fd8f653529b8e1488dd57c051000abf2a80ed12 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 20 Apr 2024 10:52:56 +0600 Subject: [PATCH 011/146] fix(deps): update dependency androidx.core:core-ktx to v1.13.0 (#690) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 879f5e846..1f455723b 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -10,7 +10,7 @@ annotation = "androidx.annotation:annotation:1.7.1" appcompat = "androidx.appcompat:appcompat:1.6.1" biometricktx = "androidx.biometric:biometric-ktx:1.2.0-alpha05" constraintlayout = "androidx.constraintlayout:constraintlayout:2.1.4" -corektx = "androidx.core:core-ktx:1.12.0" +corektx = "androidx.core:core-ktx:1.13.0" splashscreen = "androidx.core:core-splashscreen:1.0.1" recyclerview = "androidx.recyclerview:recyclerview:1.3.2" viewpager = "androidx.viewpager:viewpager:1.1.0-alpha01" From 2ad462b4d882c4a03359092515aa6b8d3cb4fd5d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 20 Apr 2024 10:53:19 +0600 Subject: [PATCH 012/146] fix(deps): update dependency androidx.activity:activity-compose to v1.9.0 (#689) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/compose.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index cd0e14211..18a102af6 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -7,7 +7,7 @@ accompanist = "0.35.0-alpha" [libraries] compiler = { module = "androidx.compose.compiler:compiler", version.ref = "compiler" } -activity = "androidx.activity:activity-compose:1.8.2" +activity = "androidx.activity:activity-compose:1.9.0" bom = { group = "dev.chrisbanes.compose", name = "compose-bom", version.ref = "compose-bom" } foundation = { module = "androidx.compose.foundation:foundation" } animation = { module = "androidx.compose.animation:animation" } From 935c0c7e2e706c512726b1b603cb84b0c56ba4e3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 20 Apr 2024 10:53:35 +0600 Subject: [PATCH 013/146] chore(deps): update gradle/actions action to v3.3.1 (#682) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- .github/workflows/build_push.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 74488ccfd..2f350a463 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -35,7 +35,7 @@ jobs: distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@6cec5d49d4d6d4bb982fbed7047db31ea6d38f11 # v3.3.0 + uses: gradle/actions/setup-gradle@750cdda3edd6d51b7fdfc069d2e2818cf3c44f4c # v3.3.1 - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index 700c0466c..e56565552 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -33,7 +33,7 @@ jobs: distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@6cec5d49d4d6d4bb982fbed7047db31ea6d38f11 # v3.3.0 + uses: gradle/actions/setup-gradle@750cdda3edd6d51b7fdfc069d2e2818cf3c44f4c # v3.3.1 - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest From e82a2f5f9f79cefed84d4c340a1aa4c0282f7973 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 20 Apr 2024 10:53:55 +0600 Subject: [PATCH 014/146] chore(deps): update gradle/wrapper-validation-action action to v3.3.1 (#683) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- .github/workflows/build_push.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 2f350a463..7cf283dd5 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@460a3ca55fc5d559238a0efc7fa9f7465df8585d # v3.3.0 + uses: gradle/wrapper-validation-action@5188e9b5527a0a094cee21e2fe9a8ca44b4629af # v3.3.1 - name: Dependency Review uses: actions/dependency-review-action@5bbc3ba658137598168acb2ab73b21c432dd411b # v4.2.5 diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index e56565552..5fa0c075f 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -20,7 +20,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@460a3ca55fc5d559238a0efc7fa9f7465df8585d # v3.3.0 + uses: gradle/wrapper-validation-action@5188e9b5527a0a094cee21e2fe9a8ca44b4629af # v3.3.1 - name: Setup Android SDK run: | From 25570147a1ca8ac374a75d7f29cf105bd686954b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 20 Apr 2024 10:54:09 +0600 Subject: [PATCH 015/146] fix(deps): update dependency androidx.benchmark:benchmark-macro-junit4 to v1.2.4 (#684) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 1f455723b..3f78c27c1 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -25,7 +25,7 @@ workmanager = "androidx.work:work-runtime:2.9.0" paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "paging_version" } paging-compose = { module = "androidx.paging:paging-compose", version.ref = "paging_version" } -benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.3" +benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.4" test-ext = "androidx.test.ext:junit-ktx:1.2.0-alpha03" test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-alpha03" test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0" From 213effa169e28e144f3e323290d865b02d0bf94b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 20 Apr 2024 10:54:26 +0600 Subject: [PATCH 016/146] fix(deps): update dependency androidx.compose.compiler:compiler to v1.5.12 (#685) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/compose.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 18a102af6..58c1ed191 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,5 +1,5 @@ [versions] -compiler = "1.5.11" +compiler = "1.5.12" # 2024.04.00-alpha01 has several bugs with the new animateItem() modifier compose-bom = "2024.03.00-alpha02" accompanist = "0.35.0-alpha" From 9a3ffe2ea6cbf7ef2c2966c304a54b715a5fa682 Mon Sep 17 00:00:00 2001 From: MajorTanya <39014446+MajorTanya@users.noreply.github.com> Date: Tue, 23 Apr 2024 07:38:16 +0200 Subject: [PATCH 017/146] Add MyAnimeList issue autoclose (#703) [skip ci] Add MyAnimeList issue autoclose This rule is intended to automatically close issues that report problems with linking MAL that would be solved with the standard solution of updating & changing the default UA. The RegEx might be too general, but there isn't any neat pattern in the previously filed issues. --- .github/workflows/issue_moderator.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/issue_moderator.yml b/.github/workflows/issue_moderator.yml index 70292069d..1a4fbe18f 100644 --- a/.github/workflows/issue_moderator.yml +++ b/.github/workflows/issue_moderator.yml @@ -30,6 +30,12 @@ jobs: "ignoreCase": true, "labels": ["Cloudflare protected"], "message": "Refer to the **Solving Cloudflare issues** section at https://mihon.app/docs/guides/troubleshooting/#cloudflare. If it doesn't work, migrate to other sources or wait until they lower their protection." + }, + { + "type": "both", + "regex": "^.*(myanimelist|mal).*$", + "ignoreCase": true, + "message": "For issues with linking MyAnimeList, please follow these steps:\n1. Update Mihon to version 0.16.4 or newer\n2. Change your default User-Agent (`More → Settings → Advanced → Default user agent string`)\n3. Close and restart Mihon\n4. Attempt to link MyAnimeList again\n\nIf you had MyAnimeList linked before, try to unlink it first before trying these steps." } ] auto-close-ignore-label: do-not-autoclose From e85ce6456a364a82cdde898be01bc62271c788cc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 26 Apr 2024 14:50:15 +0600 Subject: [PATCH 018/146] chore(deps): update actions/checkout action to v4.1.4 (#710) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- .github/workflows/build_push.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 7cf283dd5..885898b18 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Clone repo - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Validate Gradle Wrapper uses: gradle/wrapper-validation-action@5188e9b5527a0a094cee21e2fe9a8ca44b4629af # v3.3.1 diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index 5fa0c075f..aa5c2cc8c 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -17,7 +17,7 @@ jobs: steps: - name: Clone repo - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Validate Gradle Wrapper uses: gradle/wrapper-validation-action@5188e9b5527a0a094cee21e2fe9a8ca44b4629af # v3.3.1 From dd932e136256e7f420a6926aaed1559ec63badc3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 26 Apr 2024 14:50:29 +0600 Subject: [PATCH 019/146] chore(deps): update gradle/actions action to v3.3.2 (#711) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- .github/workflows/build_push.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 885898b18..f3b51a6ee 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -35,7 +35,7 @@ jobs: distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@750cdda3edd6d51b7fdfc069d2e2818cf3c44f4c # v3.3.1 + uses: gradle/actions/setup-gradle@db19848a5fa7950289d3668fb053140cf3028d43 # v3.3.2 - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index aa5c2cc8c..37952e40d 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -33,7 +33,7 @@ jobs: distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@750cdda3edd6d51b7fdfc069d2e2818cf3c44f4c # v3.3.1 + uses: gradle/actions/setup-gradle@db19848a5fa7950289d3668fb053140cf3028d43 # v3.3.2 - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest From 5289830a840c8812ee46ea639e095d849182a53d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 26 Apr 2024 14:50:41 +0600 Subject: [PATCH 020/146] chore(deps): update gradle/wrapper-validation-action action to v3.3.2 (#713) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- .github/workflows/build_push.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index f3b51a6ee..30407dbfc 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@5188e9b5527a0a094cee21e2fe9a8ca44b4629af # v3.3.1 + uses: gradle/wrapper-validation-action@216d1ad2b3710bf005dc39237337b9673fd8fcd5 # v3.3.2 - name: Dependency Review uses: actions/dependency-review-action@5bbc3ba658137598168acb2ab73b21c432dd411b # v4.2.5 diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index 37952e40d..0686e1889 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -20,7 +20,7 @@ jobs: uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@5188e9b5527a0a094cee21e2fe9a8ca44b4629af # v3.3.1 + uses: gradle/wrapper-validation-action@216d1ad2b3710bf005dc39237337b9673fd8fcd5 # v3.3.2 - name: Setup Android SDK run: | From f1ce205d002aa7ee92d69ceaaab910e6b037239a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 26 Apr 2024 14:50:52 +0600 Subject: [PATCH 021/146] fix(deps): update dependency dev.chrisbanes.compose:compose-bom to v2024.04.00-alpha02 (#714) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/compose.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 58c1ed191..0d5ae0aa4 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,7 +1,7 @@ [versions] compiler = "1.5.12" # 2024.04.00-alpha01 has several bugs with the new animateItem() modifier -compose-bom = "2024.03.00-alpha02" +compose-bom = "2024.04.00-alpha02" accompanist = "0.35.0-alpha" [libraries] From 2ad98520aa869cf27a4acdce41a9dc8f5a986419 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Fri, 26 Apr 2024 19:04:49 +0600 Subject: [PATCH 022/146] Revert "fix(deps): update dependency dev.chrisbanes.compose:compose-bom to v2024.04.00-alpha02 (#714)" This reverts commit f1ce205d002aa7ee92d69ceaaab910e6b037239a. --- gradle/compose.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 0d5ae0aa4..58c1ed191 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,7 +1,7 @@ [versions] compiler = "1.5.12" # 2024.04.00-alpha01 has several bugs with the new animateItem() modifier -compose-bom = "2024.04.00-alpha02" +compose-bom = "2024.03.00-alpha02" accompanist = "0.35.0-alpha" [libraries] From c3e7bb12f4cccf42dd3ea169111c771876e640fe Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Wed, 1 May 2024 15:07:30 +0800 Subject: [PATCH 023/146] Use Coil pipeline instead of SSIV for image decode (#692) --- .../data/coil/TachiyomiImageDecoder.kt | 46 +++++++++++++++++-- .../eu/kanade/tachiyomi/data/coil/Utils.kt | 44 ++++++++++++++++++ .../tachiyomi/ui/reader/ReaderActivity.kt | 5 +- .../ui/reader/viewer/ReaderPageImageView.kt | 39 ++++++++++++++-- 4 files changed, 123 insertions(+), 11 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/coil/Utils.kt diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt index 2f0c3df49..2929f4da3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt @@ -1,12 +1,16 @@ package eu.kanade.tachiyomi.data.coil +import android.graphics.Bitmap import coil3.ImageLoader import coil3.asCoilImage import coil3.decode.DecodeResult +import coil3.decode.DecodeUtils import coil3.decode.Decoder import coil3.decode.ImageSource import coil3.fetch.SourceFetchResult import coil3.request.Options +import coil3.request.bitmapConfig +import eu.kanade.tachiyomi.util.system.GLUtil import okio.BufferedSource import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.decoder.ImageDecoder @@ -18,27 +22,55 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti override suspend fun decode(): DecodeResult { val decoder = resources.sourceOrNull()?.use { - ImageDecoder.newInstance(it.inputStream()) + ImageDecoder.newInstance(it.inputStream(), options.cropBorders, displayProfile) } check(decoder != null && decoder.width > 0 && decoder.height > 0) { "Failed to initialize decoder" } - val bitmap = decoder.decode() + val srcWidth = decoder.width + val srcHeight = decoder.height + + val dstWidth = options.size.widthPx(options.scale) { srcWidth } + val dstHeight = options.size.heightPx(options.scale) { srcHeight } + + val sampleSize = DecodeUtils.calculateInSampleSize( + srcWidth = srcWidth, + srcHeight = srcHeight, + dstWidth = dstWidth, + dstHeight = dstHeight, + scale = options.scale, + ) + + var bitmap = decoder.decode(sampleSize = sampleSize) decoder.recycle() check(bitmap != null) { "Failed to decode image" } + if ( + options.bitmapConfig == Bitmap.Config.HARDWARE && + maxOf(bitmap.width, bitmap.height) <= GLUtil.maxTextureSize + ) { + val hwBitmap = bitmap.copy(Bitmap.Config.HARDWARE, false) + if (hwBitmap != null) { + bitmap.recycle() + bitmap = hwBitmap + } + } + return DecodeResult( image = bitmap.asCoilImage(), - isSampled = false, + isSampled = sampleSize > 1, ) } class Factory : Decoder.Factory { override fun create(result: SourceFetchResult, options: Options, imageLoader: ImageLoader): Decoder? { - if (!isApplicable(result.source.source())) return null - return TachiyomiImageDecoder(result.source, options) + return if (options.customDecoder || isApplicable(result.source.source())) { + TachiyomiImageDecoder(result.source, options) + } else { + null + } } private fun isApplicable(source: BufferedSource): Boolean { @@ -55,4 +87,8 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti override fun hashCode() = javaClass.hashCode() } + + companion object { + var displayProfile: ByteArray? = null + } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/Utils.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/Utils.kt new file mode 100644 index 000000000..7a920bf39 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/Utils.kt @@ -0,0 +1,44 @@ +package eu.kanade.tachiyomi.data.coil + +import coil3.Extras +import coil3.getExtra +import coil3.request.ImageRequest +import coil3.request.Options +import coil3.size.Dimension +import coil3.size.Scale +import coil3.size.Size +import coil3.size.isOriginal +import coil3.size.pxOrElse + +internal inline fun Size.widthPx(scale: Scale, original: () -> Int): Int { + return if (isOriginal) original() else width.toPx(scale) +} + +internal inline fun Size.heightPx(scale: Scale, original: () -> Int): Int { + return if (isOriginal) original() else height.toPx(scale) +} + +internal fun Dimension.toPx(scale: Scale): Int = pxOrElse { + when (scale) { + Scale.FILL -> Int.MIN_VALUE + Scale.FIT -> Int.MAX_VALUE + } +} + +fun ImageRequest.Builder.cropBorders(enable: Boolean) = apply { + extras[cropBordersKey] = enable +} + +val Options.cropBorders: Boolean + get() = getExtra(cropBordersKey) + +private val cropBordersKey = Extras.Key(default = false) + +fun ImageRequest.Builder.customDecoder(enable: Boolean) = apply { + extras[customDecoderKey] = enable +} + +val Options.customDecoder: Boolean + get() = getExtra(customDecoderKey) + +private val customDecoderKey = Extras.Key(default = false) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt index 62bf23646..ae471a025 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt @@ -51,6 +51,7 @@ import eu.kanade.presentation.reader.ReadingModeSelectDialog import eu.kanade.presentation.reader.appbars.ReaderAppBars import eu.kanade.presentation.reader.settings.ReaderSettingsDialog import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.coil.TachiyomiImageDecoder import eu.kanade.tachiyomi.data.notification.NotificationReceiver import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.databinding.ReaderActivityBinding @@ -872,7 +873,9 @@ class ReaderActivity : BaseActivity() { input.copyTo(output) } } - SubsamplingScaleImageView.setDisplayProfile(outputStream.toByteArray()) + val data = outputStream.toByteArray() + SubsamplingScaleImageView.setDisplayProfile(data) + TachiyomiImageDecoder.displayProfile = data } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt index 0c83d97ea..17b973a7e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt @@ -18,17 +18,22 @@ import androidx.annotation.StyleRes import androidx.appcompat.widget.AppCompatImageView import androidx.core.os.postDelayed import androidx.core.view.isVisible +import coil3.BitmapImage import coil3.dispose import coil3.imageLoader import coil3.request.CachePolicy import coil3.request.ImageRequest import coil3.request.crossfade +import coil3.size.Precision +import coil3.size.ViewSizeResolver import com.davemorrissey.labs.subscaleview.ImageSource import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.EASE_IN_OUT_QUAD import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.EASE_OUT_QUAD import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE import com.github.chrisbanes.photoview.PhotoView +import eu.kanade.tachiyomi.data.coil.cropBorders +import eu.kanade.tachiyomi.data.coil.customDecoder import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonSubsamplingImageView import eu.kanade.tachiyomi.util.system.GLUtil import eu.kanade.tachiyomi.util.system.animatorDurationScale @@ -282,12 +287,36 @@ open class ReaderPageImageView @JvmOverloads constructor( }, ) - when (data) { - is BitmapDrawable -> setImage(ImageSource.bitmap(data.bitmap)) - is BufferedSource -> setImage(ImageSource.inputStream(data.inputStream())) - else -> throw IllegalArgumentException("Not implemented for class ${data::class.simpleName}") + if (isWebtoon) { + val request = ImageRequest.Builder(context) + .data(data) + .memoryCachePolicy(CachePolicy.DISABLED) + .diskCachePolicy(CachePolicy.DISABLED) + .target( + onSuccess = { result -> + val image = result as BitmapImage + setImage(ImageSource.bitmap(image.bitmap)) + isVisible = true + }, + onError = { + this@ReaderPageImageView.onImageLoadError() + }, + ) + .size(ViewSizeResolver(this@ReaderPageImageView)) + .precision(Precision.INEXACT) + .cropBorders(config.cropBorders) + .customDecoder(true) + .crossfade(false) + .build() + context.imageLoader.enqueue(request) + } else { + when (data) { + is BitmapDrawable -> setImage(ImageSource.bitmap(data.bitmap)) + is BufferedSource -> setImage(ImageSource.inputStream(data.inputStream())) + else -> throw IllegalArgumentException("Not implemented for class ${data::class.simpleName}") + } + isVisible = true } - isVisible = true } private fun prepareAnimatedImageView() { From 80461d883f7d6ca2203ae7455223ff49e8ef96ab Mon Sep 17 00:00:00 2001 From: w Date: Wed, 1 May 2024 00:09:19 -0700 Subject: [PATCH 024/146] Update subsampling-scale-image-view (#687) Update libs.versions.toml --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 47446f893..1e6ab9e7e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -49,7 +49,7 @@ coil-gif = { module = "io.coil-kt.coil3:coil-gif" } coil-compose = { module = "io.coil-kt.coil3:coil-compose" } coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp" } -subsamplingscaleimageview = "com.github.tachiyomiorg:subsampling-scale-image-view:aeaa170036" +subsamplingscaleimageview = "com.github.tachiyomiorg:subsampling-scale-image-view:b8e1b0ed2b" image-decoder = "com.github.tachiyomiorg:image-decoder:e08e9be535" natural-comparator = "com.github.gpanther:java-nat-sort:natural-comparator-1.1" From a3d438e2f5b427eb8b4c391ab9fe10c5a83baf29 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Fri, 3 May 2024 02:59:29 +0800 Subject: [PATCH 025/146] Log app crash exceptions in dumped crash logs (#742) --- app/src/main/java/eu/kanade/presentation/crash/CrashScreen.kt | 2 +- app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/crash/CrashScreen.kt b/app/src/main/java/eu/kanade/presentation/crash/CrashScreen.kt index fd8b27030..5be7a2ca5 100644 --- a/app/src/main/java/eu/kanade/presentation/crash/CrashScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/crash/CrashScreen.kt @@ -37,7 +37,7 @@ fun CrashScreen( acceptText = stringResource(MR.strings.pref_dump_crash_logs), onAcceptClick = { scope.launch { - CrashLogUtil(context).dumpLogs() + CrashLogUtil(context).dumpLogs(exception) } }, rejectText = stringResource(MR.strings.crash_screen_restart_application), diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt b/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt index 56e6c278b..ed27314ef 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt @@ -19,12 +19,13 @@ class CrashLogUtil( private val extensionManager: ExtensionManager = Injekt.get(), ) { - suspend fun dumpLogs() = withNonCancellableContext { + suspend fun dumpLogs(exception: Throwable? = null) = withNonCancellableContext { try { val file = context.createFileInCacheDir("mihon_crash_logs.txt") file.appendText(getDebugInfo() + "\n\n") getExtensionsInfo()?.let { file.appendText("$it\n\n") } + exception?.let { file.appendText("$it\n\n") } Runtime.getRuntime().exec("logcat *:E -d -f ${file.absolutePath}").waitFor() From 6290cf222df922240575e2199459ab7b707d6ae2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 3 May 2024 14:19:59 +0600 Subject: [PATCH 026/146] fix(deps): update aboutlib.version to v11.1.4 (#744) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1e6ab9e7e..3c81f6603 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -aboutlib_version = "11.1.3" +aboutlib_version = "11.1.4" leakcanary = "2.13" moko = "0.23.0" okhttp_version = "5.0.0-alpha.12" From c6a1412f18cb16f89c4ddeadb3448c141a49072e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 3 May 2024 14:20:16 +0600 Subject: [PATCH 027/146] fix(deps): update dependency androidx.test.ext:junit-ktx to v1.2.0-alpha04 (#751) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 3f78c27c1..407c5cec5 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -26,7 +26,7 @@ paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "pag paging-compose = { module = "androidx.paging:paging-compose", version.ref = "paging_version" } benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.4" -test-ext = "androidx.test.ext:junit-ktx:1.2.0-alpha03" +test-ext = "androidx.test.ext:junit-ktx:1.2.0-alpha04" test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-alpha03" test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0" From b720f34267e4111466cdabf3a298d006231e4b55 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 3 May 2024 14:20:46 +0600 Subject: [PATCH 028/146] fix(deps): update dependency androidx.core:core-ktx to v1.13.1 (#748) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 407c5cec5..67e1f970a 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -10,7 +10,7 @@ annotation = "androidx.annotation:annotation:1.7.1" appcompat = "androidx.appcompat:appcompat:1.6.1" biometricktx = "androidx.biometric:biometric-ktx:1.2.0-alpha05" constraintlayout = "androidx.constraintlayout:constraintlayout:2.1.4" -corektx = "androidx.core:core-ktx:1.13.0" +corektx = "androidx.core:core-ktx:1.13.1" splashscreen = "androidx.core:core-splashscreen:1.0.1" recyclerview = "androidx.recyclerview:recyclerview:1.3.2" viewpager = "androidx.viewpager:viewpager:1.1.0-alpha01" From 4e5cbbc96b5c7be1e51fc65c55103733f1eb12aa Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 3 May 2024 14:21:08 +0600 Subject: [PATCH 029/146] fix(deps): update dependency androidx.compose.compiler:compiler to v1.5.13 (#745) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/compose.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 58c1ed191..642d5600c 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,5 +1,5 @@ [versions] -compiler = "1.5.12" +compiler = "1.5.13" # 2024.04.00-alpha01 has several bugs with the new animateItem() modifier compose-bom = "2024.03.00-alpha02" accompanist = "0.35.0-alpha" From a2f7d47a0a65bf88ac609b2227d440a7a2f841bf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 3 May 2024 14:21:52 +0600 Subject: [PATCH 030/146] fix(deps): update dependency androidx.test.espresso:espresso-core to v3.6.0-alpha04 (#749) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 67e1f970a..eac7f8955 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -27,7 +27,7 @@ paging-compose = { module = "androidx.paging:paging-compose", version.ref = "pag benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.4" test-ext = "androidx.test.ext:junit-ktx:1.2.0-alpha04" -test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-alpha03" +test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-alpha04" test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0" [bundles] From 47ee2b45a879b730407e65fcd6599e94a86cf832 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 3 May 2024 14:22:13 +0600 Subject: [PATCH 031/146] chore(deps): update actions/dependency-review-action action to v4.3.2 (#752) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 30407dbfc..be47b2425 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -26,7 +26,7 @@ jobs: uses: gradle/wrapper-validation-action@216d1ad2b3710bf005dc39237337b9673fd8fcd5 # v3.3.2 - name: Dependency Review - uses: actions/dependency-review-action@5bbc3ba658137598168acb2ab73b21c432dd411b # v4.2.5 + uses: actions/dependency-review-action@0c155c5e8556a497adf53f2c18edabf945ed8e70 # v4.3.2 - name: Set up JDK uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 From 8a51d56c594e1f7ae4ebc01fc6a639292dde78bd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 3 May 2024 19:08:36 +0600 Subject: [PATCH 032/146] fix(deps): update dependency com.android.tools.build:gradle to v8.4.0 (#753) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index eac7f8955..247035a9f 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -1,5 +1,5 @@ [versions] -agp_version = "8.3.2" +agp_version = "8.4.0" lifecycle_version = "2.7.0" paging_version = "3.2.1" From fa6dba6cc76f0b08cbc9bf222b0e087f4fb16d76 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 3 May 2024 19:34:33 +0600 Subject: [PATCH 033/146] fix(deps): update leakcanary to v2.14 (#715) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3c81f6603..a2f4303db 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] aboutlib_version = "11.1.4" -leakcanary = "2.13" +leakcanary = "2.14" moko = "0.23.0" okhttp_version = "5.0.0-alpha.12" richtext = "0.20.0" From 134e4648a9353fc91b4b2a7a40f12807eaee1d92 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 4 May 2024 13:33:12 +0600 Subject: [PATCH 034/146] Revert "fix(deps): update dependency androidx.compose.compiler:compiler to v1.5.13 (#745)" This reverts commit 4e5cbbc96b5c7be1e51fc65c55103733f1eb12aa. --- gradle/compose.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 642d5600c..58c1ed191 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,5 +1,5 @@ [versions] -compiler = "1.5.13" +compiler = "1.5.12" # 2024.04.00-alpha01 has several bugs with the new animateItem() modifier compose-bom = "2024.03.00-alpha02" accompanist = "0.35.0-alpha" From 21145144cdf550aa775047603e06e261951ebc42 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 4 May 2024 16:08:38 +0600 Subject: [PATCH 035/146] Fix some extension related issue and cleanups - Extension being marked as not installed instead of untrusted after updating with private installer - Extension update counter not updating due to extension being marked as untrusted - Minimize `Key "extension-XXX-YYY" was already used` crash --- .../interactor/GetExtensionsByType.kt | 6 +- .../tachiyomi/extension/ExtensionManager.kt | 151 ++++++++---------- .../util/ExtensionInstallReceiver.kt | 53 +++--- 3 files changed, 91 insertions(+), 119 deletions(-) diff --git a/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionsByType.kt b/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionsByType.kt index dadbd35f6..489403407 100644 --- a/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionsByType.kt +++ b/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionsByType.kt @@ -20,7 +20,7 @@ class GetExtensionsByType( extensionManager.installedExtensionsFlow, extensionManager.untrustedExtensionsFlow, extensionManager.availableExtensionsFlow, - ) { _activeLanguages, _installed, _untrusted, _available -> + ) { enabledLanguages, _installed, _untrusted, _available -> val (updates, installed) = _installed .filter { (showNsfwSources || !it.isNsfw) } .sortedWith( @@ -40,9 +40,9 @@ class GetExtensionsByType( } .flatMap { ext -> if (ext.sources.isEmpty()) { - return@flatMap if (ext.lang in _activeLanguages) listOf(ext) else emptyList() + return@flatMap if (ext.lang in enabledLanguages) listOf(ext) else emptyList() } - ext.sources.filter { it.lang in _activeLanguages } + ext.sources.filter { it.lang in enabledLanguages } .map { ext.copy( name = it.name, diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt index 15f386d38..5c5e359c2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt @@ -13,14 +13,17 @@ import eu.kanade.tachiyomi.extension.util.ExtensionInstallReceiver import eu.kanade.tachiyomi.extension.util.ExtensionInstaller import eu.kanade.tachiyomi.extension.util.ExtensionLoader import eu.kanade.tachiyomi.util.system.toast -import kotlinx.coroutines.async +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn import logcat.LogPriority -import tachiyomi.core.common.util.lang.launchNow import tachiyomi.core.common.util.lang.withUIContext import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.source.model.StubSource @@ -42,6 +45,8 @@ class ExtensionManager( private val trustExtension: TrustExtension = Injekt.get(), ) { + val scope = CoroutineScope(SupervisorJob()) + private val _isInitialized = MutableStateFlow(false) val isInitialized: StateFlow = _isInitialized.asStateFlow() @@ -57,24 +62,35 @@ class ExtensionManager( private val iconMap = mutableMapOf() - private val _installedExtensionsFlow = MutableStateFlow(emptyList()) - val installedExtensionsFlow = _installedExtensionsFlow.asStateFlow() + private val _installedExtensionsMapFlow = MutableStateFlow(emptyMap()) + val installedExtensionsFlow = _installedExtensionsMapFlow.mapExtensions(scope) + + private val _availableExtensionsMapFlow = MutableStateFlow(emptyMap()) + val availableExtensionsFlow = _availableExtensionsMapFlow.mapExtensions(scope) + + private val _untrustedExtensionsMapFlow = MutableStateFlow(emptyMap()) + val untrustedExtensionsFlow = _untrustedExtensionsMapFlow.mapExtensions(scope) + + init { + initExtensions() + ExtensionInstallReceiver(InstallationListener()).register(context) + } private var subLanguagesEnabledOnFirstRun = preferences.enabledLanguages().isSet() fun getAppIconForSource(sourceId: Long): Drawable? { - val pkgName = _installedExtensionsFlow.value.find { ext -> ext.sources.any { it.id == sourceId } }?.pkgName - if (pkgName != null) { - return iconMap[pkgName] ?: iconMap.getOrPut(pkgName) { - ExtensionLoader.getExtensionPackageInfoFromPkgName(context, pkgName)!!.applicationInfo - .loadIcon(context.packageManager) + val pkgName = _installedExtensionsMapFlow.value.values + .find { ext -> + ext.sources.any { it.id == sourceId } } - } - return null - } + ?.pkgName + ?: return null - private val _availableExtensionsFlow = MutableStateFlow(emptyList()) - val availableExtensionsFlow = _availableExtensionsFlow.asStateFlow() + return iconMap[pkgName] ?: iconMap.getOrPut(pkgName) { + ExtensionLoader.getExtensionPackageInfoFromPkgName(context, pkgName)!!.applicationInfo + .loadIcon(context.packageManager) + } + } private var availableExtensionsSourcesData: Map = emptyMap() @@ -87,33 +103,25 @@ class ExtensionManager( fun getSourceData(id: Long) = availableExtensionsSourcesData[id] - private val _untrustedExtensionsFlow = MutableStateFlow(emptyList()) - val untrustedExtensionsFlow = _untrustedExtensionsFlow.asStateFlow() - - init { - initExtensions() - ExtensionInstallReceiver(InstallationListener()).register(context) - } - /** * Loads and registers the installed extensions. */ private fun initExtensions() { val extensions = ExtensionLoader.loadExtensions(context) - _installedExtensionsFlow.value = extensions + _installedExtensionsMapFlow.value = extensions .filterIsInstance() - .map { it.extension } + .associate { it.extension.pkgName to it.extension } - _untrustedExtensionsFlow.value = extensions + _untrustedExtensionsMapFlow.value = extensions .filterIsInstance() - .map { it.extension } + .associate { it.extension.pkgName to it.extension } _isInitialized.value = true } /** - * Finds the available extensions in the [api] and updates [availableExtensions]. + * Finds the available extensions in the [api] and updates [_availableExtensionsMapFlow]. */ suspend fun findAvailableExtensions() { val extensions: List = try { @@ -126,7 +134,7 @@ class ExtensionManager( enableAdditionalSubLanguages(extensions) - _availableExtensionsFlow.value = extensions + _availableExtensionsMapFlow.value = extensions.associateBy { it.pkgName } updatedInstalledExtensionsStatuses(extensions) setupAvailableExtensionsSourcesDataMap(extensions) } @@ -172,35 +180,31 @@ class ExtensionManager( return } - val mutInstalledExtensions = _installedExtensionsFlow.value.toMutableList() + val installedExtensionsMap = _installedExtensionsMapFlow.value.toMutableMap() var changed = false - - for ((index, installedExt) in mutInstalledExtensions.withIndex()) { - val pkgName = installedExt.pkgName + for ((pkgName, extension) in installedExtensionsMap) { val availableExt = availableExtensions.find { it.pkgName == pkgName } - if (availableExt == null && !installedExt.isObsolete) { - mutInstalledExtensions[index] = installedExt.copy(isObsolete = true) + if (availableExt == null && !extension.isObsolete) { + installedExtensionsMap[pkgName] = extension.copy(isObsolete = true) changed = true } else if (availableExt != null) { - val hasUpdate = installedExt.updateExists(availableExt) - - if (installedExt.hasUpdate != hasUpdate) { - mutInstalledExtensions[index] = installedExt.copy( + val hasUpdate = extension.updateExists(availableExt) + if (extension.hasUpdate != hasUpdate) { + installedExtensionsMap[pkgName] = extension.copy( hasUpdate = hasUpdate, repoUrl = availableExt.repoUrl, ) - changed = true } else { - mutInstalledExtensions[index] = installedExt.copy( + installedExtensionsMap[pkgName] = extension.copy( repoUrl = availableExt.repoUrl, ) - changed = true } + changed = true } } if (changed) { - _installedExtensionsFlow.value = mutInstalledExtensions + _installedExtensionsMapFlow.value = installedExtensionsMap } updatePendingUpdatesCount() } @@ -224,8 +228,7 @@ class ExtensionManager( * @param extension The extension to be updated. */ fun updateExtension(extension: Extension.Installed): Flow { - val availableExt = _availableExtensionsFlow.value.find { it.pkgName == extension.pkgName } - ?: return emptyFlow() + val availableExt = _availableExtensionsMapFlow.value[extension.pkgName] ?: return emptyFlow() return installExtension(availableExt) } @@ -262,23 +265,15 @@ class ExtensionManager( * @param extension the extension to trust */ fun trust(extension: Extension.Untrusted) { - val untrustedPkgNames = _untrustedExtensionsFlow.value.map { it.pkgName }.toSet() - if (extension.pkgName !in untrustedPkgNames) return + _untrustedExtensionsMapFlow.value[extension.pkgName] ?: return trustExtension.trust(extension.pkgName, extension.versionCode, extension.signatureHash) - val nowTrustedExtensions = _untrustedExtensionsFlow.value - .filter { it.pkgName == extension.pkgName && it.versionCode == extension.versionCode } - _untrustedExtensionsFlow.value -= nowTrustedExtensions + _untrustedExtensionsMapFlow.value -= extension.pkgName - launchNow { - nowTrustedExtensions - .map { extension -> - async { ExtensionLoader.loadExtensionFromPkgName(context, extension.pkgName) }.await() - } - .filterIsInstance() - .forEach { registerNewExtension(it.extension) } - } + ExtensionLoader.loadExtensionFromPkgName(context, extension.pkgName) + .let { it as? LoadResult.Success } + ?.let { registerNewExtension(it.extension) } } /** @@ -287,7 +282,7 @@ class ExtensionManager( * @param extension The extension to be registered. */ private fun registerNewExtension(extension: Extension.Installed) { - _installedExtensionsFlow.value += extension + _installedExtensionsMapFlow.value += extension } /** @@ -297,13 +292,7 @@ class ExtensionManager( * @param extension The extension to be registered. */ private fun registerUpdatedExtension(extension: Extension.Installed) { - val mutInstalledExtensions = _installedExtensionsFlow.value.toMutableList() - val oldExtension = mutInstalledExtensions.find { it.pkgName == extension.pkgName } - if (oldExtension != null) { - mutInstalledExtensions -= oldExtension - } - mutInstalledExtensions += extension - _installedExtensionsFlow.value = mutInstalledExtensions + _installedExtensionsMapFlow.value += extension } /** @@ -313,14 +302,8 @@ class ExtensionManager( * @param pkgName The package name of the uninstalled application. */ private fun unregisterExtension(pkgName: String) { - val installedExtension = _installedExtensionsFlow.value.find { it.pkgName == pkgName } - if (installedExtension != null) { - _installedExtensionsFlow.value -= installedExtension - } - val untrustedExtension = _untrustedExtensionsFlow.value.find { it.pkgName == pkgName } - if (untrustedExtension != null) { - _untrustedExtensionsFlow.value -= untrustedExtension - } + _installedExtensionsMapFlow.value -= pkgName + _untrustedExtensionsMapFlow.value -= pkgName } /** @@ -339,14 +322,9 @@ class ExtensionManager( } override fun onExtensionUntrusted(extension: Extension.Untrusted) { - val installedExtension = _installedExtensionsFlow.value - .find { it.pkgName == extension.pkgName } - - if (installedExtension != null) { - _installedExtensionsFlow.value -= installedExtension - } else { - _untrustedExtensionsFlow.value += extension - } + _installedExtensionsMapFlow.value -= extension.pkgName + _untrustedExtensionsMapFlow.value += extension + updatePendingUpdatesCount() } override fun onPackageUninstalled(pkgName: String) { @@ -368,17 +346,24 @@ class ExtensionManager( } private fun Extension.Installed.updateExists(availableExtension: Extension.Available? = null): Boolean { - val availableExt = availableExtension ?: _availableExtensionsFlow.value.find { it.pkgName == pkgName } + val availableExt = availableExtension + ?: _availableExtensionsMapFlow.value[pkgName] ?: return false return (availableExt.versionCode > versionCode || availableExt.libVersion > libVersion) } private fun updatePendingUpdatesCount() { - val pendingUpdateCount = _installedExtensionsFlow.value.count { it.hasUpdate } + val pendingUpdateCount = _installedExtensionsMapFlow.value.values.count { it.hasUpdate } preferences.extensionUpdatesCount().set(pendingUpdateCount) if (pendingUpdateCount == 0) { ExtensionUpdateNotifier(context).dismiss() } } + + private operator fun Map.plus(extension: T) = plus(extension.pkgName to extension) + + private fun StateFlow>.mapExtensions(scope: CoroutineScope): StateFlow> { + return map { it.values.toList() }.stateIn(scope, SharingStarted.Lazily, value.values.toList()) + } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallReceiver.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallReceiver.kt index e0a008e1c..be9a60a75 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallReceiver.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallReceiver.kt @@ -9,12 +9,7 @@ import androidx.core.content.ContextCompat import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.extension.model.Extension import eu.kanade.tachiyomi.extension.model.LoadResult -import kotlinx.coroutines.CoroutineStart -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.async import logcat.LogPriority -import tachiyomi.core.common.util.lang.launchNow import tachiyomi.core.common.util.system.logcat /** @@ -23,8 +18,7 @@ import tachiyomi.core.common.util.system.logcat * * @param listener The listener that should be notified of extension installation events. */ -internal class ExtensionInstallReceiver(private val listener: Listener) : - BroadcastReceiver() { +internal class ExtensionInstallReceiver(private val listener: Listener) : BroadcastReceiver() { /** * Registers this broadcast receiver @@ -36,16 +30,15 @@ internal class ExtensionInstallReceiver(private val listener: Listener) : /** * Returns the intent filter this receiver should subscribe to. */ - private val filter - get() = IntentFilter().apply { - addAction(Intent.ACTION_PACKAGE_ADDED) - addAction(Intent.ACTION_PACKAGE_REPLACED) - addAction(Intent.ACTION_PACKAGE_REMOVED) - addAction(ACTION_EXTENSION_ADDED) - addAction(ACTION_EXTENSION_REPLACED) - addAction(ACTION_EXTENSION_REMOVED) - addDataScheme("package") - } + private val filter = IntentFilter().apply { + addAction(Intent.ACTION_PACKAGE_ADDED) + addAction(Intent.ACTION_PACKAGE_REPLACED) + addAction(Intent.ACTION_PACKAGE_REMOVED) + addAction(ACTION_EXTENSION_ADDED) + addAction(ACTION_EXTENSION_REPLACED) + addAction(ACTION_EXTENSION_REMOVED) + addDataScheme("package") + } /** * Called when one of the events of the [filter] is received. When the package is an extension, @@ -58,21 +51,17 @@ internal class ExtensionInstallReceiver(private val listener: Listener) : Intent.ACTION_PACKAGE_ADDED, ACTION_EXTENSION_ADDED -> { if (isReplacing(intent)) return - launchNow { - when (val result = getExtensionFromIntent(context, intent)) { - is LoadResult.Success -> listener.onExtensionInstalled(result.extension) - is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension) - else -> {} - } + when (val result = getExtensionFromIntent(context, intent)) { + is LoadResult.Success -> listener.onExtensionInstalled(result.extension) + is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension) + else -> {} } } Intent.ACTION_PACKAGE_REPLACED, ACTION_EXTENSION_REPLACED -> { - launchNow { - when (val result = getExtensionFromIntent(context, intent)) { - is LoadResult.Success -> listener.onExtensionUpdated(result.extension) - is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension) - else -> {} - } + when (val result = getExtensionFromIntent(context, intent)) { + is LoadResult.Success -> listener.onExtensionUpdated(result.extension) + is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension) + else -> {} } } Intent.ACTION_PACKAGE_REMOVED, ACTION_EXTENSION_REMOVED -> { @@ -101,15 +90,13 @@ internal class ExtensionInstallReceiver(private val listener: Listener) : * @param context The application context. * @param intent The intent containing the package name of the extension. */ - private suspend fun getExtensionFromIntent(context: Context, intent: Intent?): LoadResult { + private fun getExtensionFromIntent(context: Context, intent: Intent?): LoadResult { val pkgName = getPackageNameFromIntent(intent) if (pkgName == null) { logcat(LogPriority.WARN) { "Package name not found" } return LoadResult.Error } - return GlobalScope.async(Dispatchers.Default, CoroutineStart.DEFAULT) { - ExtensionLoader.loadExtensionFromPkgName(context, pkgName) - }.await() + return ExtensionLoader.loadExtensionFromPkgName(context, pkgName) } /** From 70cd688ac245a70a3146e2ac7374f24b0c3453ab Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 4 May 2024 16:26:45 +0600 Subject: [PATCH 036/146] Trust extension by repo (#570) --- .../java/eu/kanade/domain/DomainModule.kt | 2 +- .../extension/interactor/TrustExtension.kt | 13 ++++---- .../tachiyomi/extension/ExtensionManager.kt | 2 +- .../util/ExtensionInstallReceiver.kt | 33 ++++++++++--------- .../extension/util/ExtensionLoader.kt | 7 ++-- .../browse/extension/ExtensionsScreenModel.kt | 5 ++- 6 files changed, 35 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/eu/kanade/domain/DomainModule.kt b/app/src/main/java/eu/kanade/domain/DomainModule.kt index 48c183a92..4c769f703 100644 --- a/app/src/main/java/eu/kanade/domain/DomainModule.kt +++ b/app/src/main/java/eu/kanade/domain/DomainModule.kt @@ -179,7 +179,7 @@ class DomainModule : InjektModule { addFactory { ToggleLanguage(get()) } addFactory { ToggleSource(get()) } addFactory { ToggleSourcePin(get()) } - addFactory { TrustExtension(get()) } + addFactory { TrustExtension(get(), get()) } addSingletonFactory { ExtensionRepoRepositoryImpl(get()) } addFactory { ExtensionRepoService(get(), get()) } diff --git a/app/src/main/java/eu/kanade/domain/extension/interactor/TrustExtension.kt b/app/src/main/java/eu/kanade/domain/extension/interactor/TrustExtension.kt index c3daec5f8..b4259945d 100644 --- a/app/src/main/java/eu/kanade/domain/extension/interactor/TrustExtension.kt +++ b/app/src/main/java/eu/kanade/domain/extension/interactor/TrustExtension.kt @@ -3,15 +3,18 @@ package eu.kanade.domain.extension.interactor import android.content.pm.PackageInfo import androidx.core.content.pm.PackageInfoCompat import eu.kanade.domain.source.service.SourcePreferences +import mihon.domain.extensionrepo.repository.ExtensionRepoRepository import tachiyomi.core.common.preference.getAndSet class TrustExtension( + private val extensionRepoRepository: ExtensionRepoRepository, private val preferences: SourcePreferences, ) { - fun isTrusted(pkgInfo: PackageInfo, signatureHash: String): Boolean { - val key = "${pkgInfo.packageName}:${PackageInfoCompat.getLongVersionCode(pkgInfo)}:$signatureHash" - return key in preferences.trustedExtensions().get() + suspend fun isTrusted(pkgInfo: PackageInfo, fingerprints: List): Boolean { + val trustedFingerprints = extensionRepoRepository.getAll().map { it.signingKeyFingerprint }.toHashSet() + val key = "${pkgInfo.packageName}:${PackageInfoCompat.getLongVersionCode(pkgInfo)}:${fingerprints.last()}" + return trustedFingerprints.any { fingerprints.contains(it) } || key in preferences.trustedExtensions().get() } fun trust(pkgName: String, versionCode: Long, signatureHash: String) { @@ -19,9 +22,7 @@ class TrustExtension( // Remove previously trusted versions val removed = exts.filterNot { it.startsWith("$pkgName:") }.toMutableSet() - removed.also { - it += "$pkgName:$versionCode:$signatureHash" - } + removed.also { it += "$pkgName:$versionCode:$signatureHash" } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt index 5c5e359c2..968263b5d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt @@ -264,7 +264,7 @@ class ExtensionManager( * * @param extension the extension to trust */ - fun trust(extension: Extension.Untrusted) { + suspend fun trust(extension: Extension.Untrusted) { _untrustedExtensionsMapFlow.value[extension.pkgName] ?: return trustExtension.trust(extension.pkgName, extension.versionCode, extension.signatureHash) diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallReceiver.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallReceiver.kt index be9a60a75..a0ccb23fb 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallReceiver.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallReceiver.kt @@ -9,6 +9,9 @@ import androidx.core.content.ContextCompat import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.extension.model.Extension import eu.kanade.tachiyomi.extension.model.LoadResult +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.launch import logcat.LogPriority import tachiyomi.core.common.util.system.logcat @@ -20,16 +23,12 @@ import tachiyomi.core.common.util.system.logcat */ internal class ExtensionInstallReceiver(private val listener: Listener) : BroadcastReceiver() { - /** - * Registers this broadcast receiver - */ + val scope = CoroutineScope(SupervisorJob()) + fun register(context: Context) { ContextCompat.registerReceiver(context, this, filter, ContextCompat.RECEIVER_NOT_EXPORTED) } - /** - * Returns the intent filter this receiver should subscribe to. - */ private val filter = IntentFilter().apply { addAction(Intent.ACTION_PACKAGE_ADDED) addAction(Intent.ACTION_PACKAGE_REPLACED) @@ -51,17 +50,21 @@ internal class ExtensionInstallReceiver(private val listener: Listener) : Broadc Intent.ACTION_PACKAGE_ADDED, ACTION_EXTENSION_ADDED -> { if (isReplacing(intent)) return - when (val result = getExtensionFromIntent(context, intent)) { - is LoadResult.Success -> listener.onExtensionInstalled(result.extension) - is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension) - else -> {} + scope.launch { + when (val result = getExtensionFromIntent(context, intent)) { + is LoadResult.Success -> listener.onExtensionInstalled(result.extension) + is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension) + else -> {} + } } } Intent.ACTION_PACKAGE_REPLACED, ACTION_EXTENSION_REPLACED -> { - when (val result = getExtensionFromIntent(context, intent)) { - is LoadResult.Success -> listener.onExtensionUpdated(result.extension) - is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension) - else -> {} + scope.launch { + when (val result = getExtensionFromIntent(context, intent)) { + is LoadResult.Success -> listener.onExtensionUpdated(result.extension) + is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension) + else -> {} + } } } Intent.ACTION_PACKAGE_REMOVED, ACTION_EXTENSION_REMOVED -> { @@ -90,7 +93,7 @@ internal class ExtensionInstallReceiver(private val listener: Listener) : Broadc * @param context The application context. * @param intent The intent containing the package name of the extension. */ - private fun getExtensionFromIntent(context: Context, intent: Intent?): LoadResult { + private suspend fun getExtensionFromIntent(context: Context, intent: Intent?): LoadResult { val pkgName = getPackageNameFromIntent(intent) if (pkgName == null) { logcat(LogPriority.WARN) { "Package name not found" } diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt index 0468c45e5..50ab94279 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt @@ -172,7 +172,7 @@ internal object ExtensionLoader { * Attempts to load an extension from the given package name. It checks if the extension * contains the required feature flag before trying to load it. */ - fun loadExtensionFromPkgName(context: Context, pkgName: String): LoadResult { + suspend fun loadExtensionFromPkgName(context: Context, pkgName: String): LoadResult { val extensionPackage = getExtensionInfoFromPkgName(context, pkgName) if (extensionPackage == null) { logcat(LogPriority.ERROR) { "Extension package is not found ($pkgName)" } @@ -223,7 +223,8 @@ internal object ExtensionLoader { * @param context The application context. * @param extensionInfo The extension to load. */ - private fun loadExtension(context: Context, extensionInfo: ExtensionInfo): LoadResult { + @Suppress("LongMethod", "CyclomaticComplexMethod", "ReturnCount") + private suspend fun loadExtension(context: Context, extensionInfo: ExtensionInfo): LoadResult { val pkgManager = context.packageManager val pkgInfo = extensionInfo.packageInfo val appInfo = pkgInfo.applicationInfo @@ -252,7 +253,7 @@ internal object ExtensionLoader { if (signatures.isNullOrEmpty()) { logcat(LogPriority.WARN) { "Package $pkgName isn't signed" } return LoadResult.Error - } else if (!trustExtension.isTrusted(pkgInfo, signatures.last())) { + } else if (!trustExtension.isTrusted(pkgInfo, signatures)) { val extension = Extension.Untrusted( extName, pkgName, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsScreenModel.kt index e71d65997..feb69c5ce 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsScreenModel.kt @@ -27,6 +27,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch import tachiyomi.core.common.util.lang.launchIO import tachiyomi.i18n.MR import uy.kohesive.injekt.Injekt @@ -196,7 +197,9 @@ class ExtensionsScreenModel( } fun trustExtension(extension: Extension.Untrusted) { - extensionManager.trust(extension) + screenModelScope.launch { + extensionManager.trust(extension) + } } @Immutable From 28dca3b7b818ad095008e7cd49ec07a82b0ebcad Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 4 May 2024 16:30:53 +0600 Subject: [PATCH 037/146] Address firebase ktx module deprecation --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a2f4303db..2685b5344 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -73,7 +73,7 @@ moko-gradle = { module = "dev.icerock.moko:resources-generator", version.ref = " logcat = "com.squareup.logcat:logcat:0.1" -firebase-analytics = "com.google.firebase:firebase-analytics-ktx:21.6.2" +firebase-analytics = "com.google.firebase:firebase-analytics:22.0.0" aboutLibraries-gradle = { module = "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin", version.ref = "aboutlib_version" } aboutLibraries-compose = { module = "com.mikepenz:aboutlibraries-compose-m3", version.ref = "aboutlib_version" } From e55e5f6f64f872475d370d6ce0c186e2601776e4 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 4 May 2024 22:26:45 +0600 Subject: [PATCH 038/146] Remove some legacy folder/file name lookup for download Related to #705 --- .../tachiyomi/data/download/DownloadProvider.kt | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt index 001395af1..2c978bed1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt @@ -160,21 +160,12 @@ class DownloadProvider( */ fun getValidChapterDirNames(chapterName: String, chapterScanlator: String?): List { val chapterDirName = getChapterDirName(chapterName, chapterScanlator) - return buildList(4) { + return buildList(2) { // Folder of images add(chapterDirName) // Archived chapters add("$chapterDirName.cbz") - - if (chapterScanlator.isNullOrBlank()) { - // Previously null scanlator fields were converted to "" due to a bug - add("_$chapterDirName") - add("_$chapterDirName.cbz") - } else { - // Legacy chapter directory name used in v0.9.2 and before - add(DiskUtil.buildValidFilename(chapterName)) - } } } } From 7ec2108812fbe0483111dbe996e29e5a621b583a Mon Sep 17 00:00:00 2001 From: Radon Rosborough Date: Sat, 4 May 2024 09:40:41 -0700 Subject: [PATCH 039/146] Massively improve findFile performance (#728) * Massively improve findFile performance * Update libs.versions.toml --------- Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> --- .../eu/kanade/tachiyomi/data/download/DownloadProvider.kt | 8 ++++---- .../java/eu/kanade/tachiyomi/data/download/Downloader.kt | 2 +- gradle/libs.versions.toml | 2 +- .../kotlin/tachiyomi/source/local/LocalSource.kt | 4 ++-- .../tachiyomi/source/local/io/LocalSourceFileSystem.kt | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt index 2c978bed1..2ab5e55d9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt @@ -57,7 +57,7 @@ class DownloadProvider( * @param source the source to query. */ fun findSourceDir(source: Source): UniFile? { - return downloadsDir?.findFile(getSourceDirName(source), true) + return downloadsDir?.findFile(getSourceDirName(source)) } /** @@ -68,7 +68,7 @@ class DownloadProvider( */ fun findMangaDir(mangaTitle: String, source: Source): UniFile? { val sourceDir = findSourceDir(source) - return sourceDir?.findFile(getMangaDirName(mangaTitle), true) + return sourceDir?.findFile(getMangaDirName(mangaTitle)) } /** @@ -82,7 +82,7 @@ class DownloadProvider( fun findChapterDir(chapterName: String, chapterScanlator: String?, mangaTitle: String, source: Source): UniFile? { val mangaDir = findMangaDir(mangaTitle, source) return getValidChapterDirNames(chapterName, chapterScanlator).asSequence() - .mapNotNull { mangaDir?.findFile(it, true) } + .mapNotNull { mangaDir?.findFile(it) } .firstOrNull() } @@ -97,7 +97,7 @@ class DownloadProvider( val mangaDir = findMangaDir(manga.title, source) ?: return null to emptyList() return mangaDir to chapters.mapNotNull { chapter -> getValidChapterDirNames(chapter.name, chapter.scanlator).asSequence() - .mapNotNull { mangaDir.findFile(it, true) } + .mapNotNull { mangaDir.findFile(it) } .firstOrNull() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt index 7c8238d5a..11bfe28d1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt @@ -645,7 +645,7 @@ class Downloader( ) // Remove the old file - dir.findFile(COMIC_INFO_FILE, true)?.delete() + dir.findFile(COMIC_INFO_FILE)?.delete() dir.createFile(COMIC_INFO_FILE)!!.openOutputStream().use { val comicInfoString = xml.encodeToString(ComicInfo.serializer(), comicInfo) it.write(comicInfoString.toByteArray()) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2685b5344..b6776f997 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -31,7 +31,7 @@ quickjs-android = "app.cash.quickjs:quickjs-android:0.9.2" jsoup = "org.jsoup:jsoup:1.17.2" disklrucache = "com.jakewharton:disklrucache:2.0.2" -unifile = "com.github.tachiyomiorg:unifile:7c257e1c64" +unifile = "com.github.tachiyomiorg:unifile:e0def6b3dc" common-compress = "org.apache.commons:commons-compress:1.26.1" junrar = "com.github.junrar:junrar:7.5.5" diff --git a/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt b/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt index 9e2aa8406..06ed4551d 100644 --- a/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt +++ b/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt @@ -300,8 +300,8 @@ actual class LocalSource( try { val (mangaDirName, chapterName) = chapter.url.split('/', limit = 2) return fileSystem.getBaseDirectory() - ?.findFile(mangaDirName, true) - ?.findFile(chapterName, true) + ?.findFile(mangaDirName) + ?.findFile(chapterName) ?.let(Format.Companion::valueOf) ?: throw Exception(context.stringResource(MR.strings.chapter_not_found)) } catch (e: Format.UnknownFormatException) { diff --git a/source-local/src/androidMain/kotlin/tachiyomi/source/local/io/LocalSourceFileSystem.kt b/source-local/src/androidMain/kotlin/tachiyomi/source/local/io/LocalSourceFileSystem.kt index 402df7e00..69faaa2b7 100644 --- a/source-local/src/androidMain/kotlin/tachiyomi/source/local/io/LocalSourceFileSystem.kt +++ b/source-local/src/androidMain/kotlin/tachiyomi/source/local/io/LocalSourceFileSystem.kt @@ -17,7 +17,7 @@ actual class LocalSourceFileSystem( actual fun getMangaDirectory(name: String): UniFile? { return getBaseDirectory() - ?.findFile(name, true) + ?.findFile(name) ?.takeIf { it.isDirectory } } From 263e467cdeb948b8f3679e2ea0282a291cf2f131 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sun, 5 May 2024 01:15:07 +0600 Subject: [PATCH 040/146] Fix badge count getting cut off on tab title Fixes #335 --- .../tachiyomi/presentation/core/components/material/Tabs.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Tabs.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Tabs.kt index 30f6766d8..c0c0dea2b 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Tabs.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Tabs.kt @@ -6,6 +6,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.sp import tachiyomi.presentation.core.components.Pill @@ -21,6 +22,7 @@ fun TabText(text: String, badgeCount: Int? = null) { text = text, maxLines = 1, overflow = TextOverflow.Ellipsis, + modifier = Modifier.weight(1f, fill = false), ) if (badgeCount != null) { Pill( From e473c7f09fc009161145aca94bd70027f042b0bf Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Mon, 8 Apr 2024 15:35:17 +0600 Subject: [PATCH 041/146] Bump compose version Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> --- .../browse/ExtensionDetailsScreen.kt | 2 +- .../browse/ExtensionFilterScreen.kt | 2 +- .../presentation/browse/ExtensionsScreen.kt | 6 +- .../presentation/browse/GlobalSearchScreen.kt | 2 + .../browse/MigrateSourceScreen.kt | 2 +- .../browse/SourcesFilterScreen.kt | 4 +- .../presentation/browse/SourcesScreen.kt | 4 +- .../components/GlobalSearchResultItems.kt | 3 +- .../presentation/category/CategoryScreen.kt | 2 +- .../CategoryFloatingActionButton.kt | 5 +- .../presentation/history/HistoryScreen.kt | 4 +- .../kanade/presentation/manga/MangaScreen.kt | 7 +- .../manga/components/MangaInfoHeader.kt | 4 +- .../manga/components/ScanlatorFilterDialog.kt | 6 +- .../more/onboarding/PermissionStep.kt | 2 +- .../components/ExtensionReposContent.kt | 2 +- .../settings/widget/ListPreferenceWidget.kt | 6 +- .../settings/widget/TriStateListDialog.kt | 14 +- .../track/TrackInfoDialogSelector.kt | 6 +- .../presentation/updates/UpdatesUiItem.kt | 6 +- .../kanade/presentation/util/Permissions.kt | 2 +- gradle/compose.versions.toml | 3 +- .../core/components/AdaptiveSheet.kt | 100 ++++--- .../core/components/material/PullRefresh.kt | 270 ++---------------- .../presentation/core/util/LazyListState.kt | 59 +--- 25 files changed, 112 insertions(+), 411 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt index dcd5067d3..277dc5e65 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt @@ -190,7 +190,7 @@ private fun ExtensionDetails( key = { it.source.id }, ) { source -> SourceSwitchPreference( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), source = source, onClickSourcePreferences = onClickSourcePreferences, onClickSource = onClickSource, diff --git a/app/src/main/java/eu/kanade/presentation/browse/ExtensionFilterScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/ExtensionFilterScreen.kt index c65f0d0b1..7f7e18baa 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/ExtensionFilterScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/ExtensionFilterScreen.kt @@ -58,7 +58,7 @@ private fun ExtensionFilterContent( ) { items(state.languages) { language -> SwitchPreferenceWidget( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), title = LocaleHelper.getSourceDisplayName(language, context), checked = language in state.enabledLanguages, onCheckedChanged = { onClickLang(language) }, diff --git a/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt index d08a2d93b..a3cb94800 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt @@ -187,14 +187,14 @@ private fun ExtensionContent( } ExtensionHeader( textRes = header.textRes, - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), action = action, ) } is ExtensionUiModel.Header.Text -> { ExtensionHeader( text = header.text, - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), ) } } @@ -212,7 +212,7 @@ private fun ExtensionContent( }, ) { item -> ExtensionItem( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), item = item, onClickItem = { when (it) { diff --git a/app/src/main/java/eu/kanade/presentation/browse/GlobalSearchScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/GlobalSearchScreen.kt index 7f79dd3ed..da4db5e98 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/GlobalSearchScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/GlobalSearchScreen.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.Composable import androidx.compose.runtime.State +import androidx.compose.ui.Modifier import eu.kanade.presentation.browse.components.GlobalSearchCardRow import eu.kanade.presentation.browse.components.GlobalSearchErrorResultItem import eu.kanade.presentation.browse.components.GlobalSearchLoadingResultItem @@ -79,6 +80,7 @@ internal fun GlobalSearchContent( } ?: source.name, subtitle = LocaleHelper.getLocalizedDisplayName(source.lang), onClick = { onClickSource(source) }, + modifier = Modifier.animateItem(), ) { when (result) { SearchItemResult.Loading -> { diff --git a/app/src/main/java/eu/kanade/presentation/browse/MigrateSourceScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/MigrateSourceScreen.kt index 404d11476..3f8a67c4c 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/MigrateSourceScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/MigrateSourceScreen.kt @@ -133,7 +133,7 @@ private fun MigrateSourceList( key = { (source, _) -> "migrate-${source.id}" }, ) { (source, count) -> MigrateSourceItem( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), source = source, count = count, onClickItem = { onClickItem(source) }, diff --git a/app/src/main/java/eu/kanade/presentation/browse/SourcesFilterScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/SourcesFilterScreen.kt index e334a451e..9f74c3799 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/SourcesFilterScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/SourcesFilterScreen.kt @@ -68,7 +68,7 @@ private fun SourcesFilterContent( contentType = "source-filter-header", ) { SourcesFilterHeader( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), language = language, enabled = enabled, onClickItem = onClickLanguage, @@ -81,7 +81,7 @@ private fun SourcesFilterContent( contentType = { "source-filter-item" }, ) { source -> SourcesFilterItem( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), source = source, enabled = "${source.id}" !in state.disabledSources, onClickItem = onClickSource, diff --git a/app/src/main/java/eu/kanade/presentation/browse/SourcesScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/SourcesScreen.kt index d0411b7a8..56644b3d8 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/SourcesScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/SourcesScreen.kt @@ -74,12 +74,12 @@ fun SourcesScreen( when (model) { is SourceUiModel.Header -> { SourceHeader( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), language = model.language, ) } is SourceUiModel.Item -> SourceItem( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), source = model.source, onClickItem = onClickItem, onLongClickItem = onLongClickItem, diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchResultItems.kt b/app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchResultItems.kt index 29ef3b97b..5de9a2142 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchResultItems.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchResultItems.kt @@ -32,9 +32,10 @@ fun GlobalSearchResultItem( title: String, subtitle: String, onClick: () -> Unit, + modifier: Modifier = Modifier, content: @Composable () -> Unit, ) { - Column { + Column(modifier = modifier) { Row( modifier = Modifier .padding( diff --git a/app/src/main/java/eu/kanade/presentation/category/CategoryScreen.kt b/app/src/main/java/eu/kanade/presentation/category/CategoryScreen.kt index 34a6a1217..df553a203 100644 --- a/app/src/main/java/eu/kanade/presentation/category/CategoryScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/category/CategoryScreen.kt @@ -107,7 +107,7 @@ private fun CategoryContent( key = { _, category -> "category-${category.id}" }, ) { index, category -> CategoryListItem( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), category = category, canMoveUp = index != 0, canMoveDown = index != categories.lastIndex, diff --git a/app/src/main/java/eu/kanade/presentation/category/components/CategoryFloatingActionButton.kt b/app/src/main/java/eu/kanade/presentation/category/components/CategoryFloatingActionButton.kt index 58d7f163a..a151e9b2f 100644 --- a/app/src/main/java/eu/kanade/presentation/category/components/CategoryFloatingActionButton.kt +++ b/app/src/main/java/eu/kanade/presentation/category/components/CategoryFloatingActionButton.kt @@ -10,8 +10,7 @@ import androidx.compose.ui.Modifier import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton import tachiyomi.presentation.core.i18n.stringResource -import tachiyomi.presentation.core.util.isScrolledToEnd -import tachiyomi.presentation.core.util.isScrollingUp +import tachiyomi.presentation.core.util.shouldExpandFAB @Composable fun CategoryFloatingActionButton( @@ -23,7 +22,7 @@ fun CategoryFloatingActionButton( text = { Text(text = stringResource(MR.strings.action_add)) }, icon = { Icon(imageVector = Icons.Outlined.Add, contentDescription = null) }, onClick = onCreate, - expanded = lazyListState.isScrollingUp() || lazyListState.isScrolledToEnd(), + expanded = lazyListState.shouldExpandFAB(), modifier = modifier, ) } diff --git a/app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt b/app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt index c15604829..dba526b54 100644 --- a/app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt @@ -113,14 +113,14 @@ private fun HistoryScreenContent( when (item) { is HistoryUiModel.Header -> { ListGroupHeader( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), text = relativeDateText(item.date), ) } is HistoryUiModel.Item -> { val value = item.item HistoryItem( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), history = value, onClickCover = { onClickCover(value) }, onClickResume = { onClickResume(value) }, diff --git a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt index 9a6e3f6fd..efe44ceb4 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt @@ -75,8 +75,7 @@ import tachiyomi.presentation.core.components.material.ExtendedFloatingActionBut import tachiyomi.presentation.core.components.material.PullRefresh import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.i18n.stringResource -import tachiyomi.presentation.core.util.isScrolledToEnd -import tachiyomi.presentation.core.util.isScrollingUp +import tachiyomi.presentation.core.util.shouldExpandFAB import tachiyomi.source.local.isLocal import java.time.Instant @@ -346,7 +345,7 @@ private fun MangaScreenSmallImpl( }, icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) }, onClick = onContinueReading, - expanded = chapterListState.isScrollingUp() || chapterListState.isScrolledToEnd(), + expanded = chapterListState.shouldExpandFAB(), ) } }, @@ -594,7 +593,7 @@ fun MangaScreenLargeImpl( }, icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) }, onClick = onContinueReading, - expanded = chapterListState.isScrollingUp() || chapterListState.isScrolledToEnd(), + expanded = chapterListState.shouldExpandFAB(), ) } }, diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt index ac4946c99..f1b660626 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt @@ -42,7 +42,7 @@ import androidx.compose.material.icons.outlined.Sync import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Icon import androidx.compose.material3.LocalContentColor -import androidx.compose.material3.LocalMinimumInteractiveComponentEnforcement +import androidx.compose.material3.LocalMinimumInteractiveComponentSize import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ProvideTextStyle @@ -649,7 +649,7 @@ private fun TagsChip( modifier: Modifier = Modifier, onClick: () -> Unit, ) { - CompositionLocalProvider(LocalMinimumInteractiveComponentEnforcement provides false) { + CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) { SuggestionChip( modifier = modifier, onClick = onClick, diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/ScanlatorFilterDialog.kt b/app/src/main/java/eu/kanade/presentation/manga/components/ScanlatorFilterDialog.kt index d7c46e90d..747ac09e0 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/ScanlatorFilterDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/ScanlatorFilterDialog.kt @@ -31,8 +31,6 @@ import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.TextButton import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.i18n.stringResource -import tachiyomi.presentation.core.util.isScrolledToEnd -import tachiyomi.presentation.core.util.isScrolledToStart @Composable fun ScanlatorFilterDialog( @@ -96,8 +94,8 @@ fun ScanlatorFilterDialog( } } } - if (!state.isScrolledToStart()) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter)) - if (!state.isScrolledToEnd()) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter)) + if (state.canScrollBackward) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter)) + if (state.canScrollForward) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter)) } }, properties = DialogProperties( diff --git a/app/src/main/java/eu/kanade/presentation/more/onboarding/PermissionStep.kt b/app/src/main/java/eu/kanade/presentation/more/onboarding/PermissionStep.kt index 79e45159f..8b3d9c07b 100644 --- a/app/src/main/java/eu/kanade/presentation/more/onboarding/PermissionStep.kt +++ b/app/src/main/java/eu/kanade/presentation/more/onboarding/PermissionStep.kt @@ -28,11 +28,11 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.unit.dp import androidx.core.content.getSystemService import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.compose.LocalLifecycleOwner import eu.kanade.presentation.util.rememberRequestPackageInstallsPermissionState import eu.kanade.tachiyomi.util.system.launchRequestPackageInstallsPermission import tachiyomi.i18n.MR diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposContent.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposContent.kt index 20a924d11..e6b2a227f 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposContent.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposContent.kt @@ -46,7 +46,7 @@ fun ExtensionReposContent( repos.forEach { item { ExtensionRepoListItem( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), repo = it, onOpenWebsite = { onOpenWebsite(it) }, onDelete = { onClickDelete(it.baseUrl) }, diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/widget/ListPreferenceWidget.kt b/app/src/main/java/eu/kanade/presentation/more/settings/widget/ListPreferenceWidget.kt index c8e757491..7daf65168 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/widget/ListPreferenceWidget.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/widget/ListPreferenceWidget.kt @@ -26,8 +26,6 @@ import androidx.compose.ui.unit.dp import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.ScrollbarLazyColumn import tachiyomi.presentation.core.i18n.stringResource -import tachiyomi.presentation.core.util.isScrolledToEnd -import tachiyomi.presentation.core.util.isScrolledToStart @Composable fun ListPreferenceWidget( @@ -69,8 +67,8 @@ fun ListPreferenceWidget( } } } - if (!state.isScrolledToStart()) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter)) - if (!state.isScrolledToEnd()) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter)) + if (state.canScrollBackward) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter)) + if (state.canScrollForward) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter)) } }, confirmButton = { diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/widget/TriStateListDialog.kt b/app/src/main/java/eu/kanade/presentation/more/settings/widget/TriStateListDialog.kt index f6e195e4d..be5029ac3 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/widget/TriStateListDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/widget/TriStateListDialog.kt @@ -30,8 +30,6 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.unit.dp import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource -import tachiyomi.presentation.core.util.isScrolledToEnd -import tachiyomi.presentation.core.util.isScrolledToStart private enum class State { CHECKED, INVERSED, UNCHECKED @@ -115,16 +113,8 @@ fun TriStateListDialog( } } - if (!listState.isScrolledToStart()) { - HorizontalDivider( - modifier = Modifier.align(Alignment.TopCenter), - ) - } - if (!listState.isScrolledToEnd()) { - HorizontalDivider( - modifier = Modifier.align(Alignment.BottomCenter), - ) - } + if (listState.canScrollBackward) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter)) + if (listState.canScrollForward) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter)) } } }, diff --git a/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogSelector.kt b/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogSelector.kt index 053ba7bbc..c6fda5cc5 100644 --- a/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogSelector.kt +++ b/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogSelector.kt @@ -43,8 +43,6 @@ import tachiyomi.presentation.core.components.WheelTextPicker import tachiyomi.presentation.core.components.material.AlertDialogContent import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.i18n.stringResource -import tachiyomi.presentation.core.util.isScrolledToEnd -import tachiyomi.presentation.core.util.isScrolledToStart @Composable fun TrackStatusSelector( @@ -86,8 +84,8 @@ fun TrackStatusSelector( } } } - if (!state.isScrolledToStart()) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter)) - if (!state.isScrolledToEnd()) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter)) + if (state.canScrollBackward) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter)) + if (state.canScrollForward) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter)) }, onConfirm = onConfirm, onDismissRequest = onDismissRequest, diff --git a/app/src/main/java/eu/kanade/presentation/updates/UpdatesUiItem.kt b/app/src/main/java/eu/kanade/presentation/updates/UpdatesUiItem.kt index a69252ffc..9f5c92479 100644 --- a/app/src/main/java/eu/kanade/presentation/updates/UpdatesUiItem.kt +++ b/app/src/main/java/eu/kanade/presentation/updates/UpdatesUiItem.kt @@ -54,7 +54,7 @@ internal fun LazyListScope.updatesLastUpdatedItem( item(key = "updates-lastUpdated") { Box( modifier = Modifier - .animateItemPlacement() + .animateItem() .padding(horizontal = MaterialTheme.padding.medium, vertical = MaterialTheme.padding.small), ) { Text( @@ -91,14 +91,14 @@ internal fun LazyListScope.updatesUiItems( when (item) { is UpdatesUiModel.Header -> { ListGroupHeader( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), text = relativeDateText(item.date), ) } is UpdatesUiModel.Item -> { val updatesItem = item.item UpdatesUiItem( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), update = updatesItem.update, selected = updatesItem.selected, readProgress = updatesItem.update.lastPageRead diff --git a/app/src/main/java/eu/kanade/presentation/util/Permissions.kt b/app/src/main/java/eu/kanade/presentation/util/Permissions.kt index 6bee9203e..80c2f90b9 100644 --- a/app/src/main/java/eu/kanade/presentation/util/Permissions.kt +++ b/app/src/main/java/eu/kanade/presentation/util/Permissions.kt @@ -7,9 +7,9 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.compose.LocalLifecycleOwner @Composable fun rememberRequestPackageInstallsPermissionState(initialValue: Boolean = false): Boolean { diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 58c1ed191..1249e384f 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,7 +1,6 @@ [versions] compiler = "1.5.12" -# 2024.04.00-alpha01 has several bugs with the new animateItem() modifier -compose-bom = "2024.03.00-alpha02" +compose-bom = "2024.05.00-alpha01" accompanist = "0.35.0-alpha" [libraries] diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/AdaptiveSheet.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/AdaptiveSheet.kt index fa6eb375a..54d2ad700 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/AdaptiveSheet.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/AdaptiveSheet.kt @@ -153,7 +153,9 @@ fun AdaptiveSheet( if (enableSwipeDismiss) { Modifier.nestedScroll( remember(anchoredDraggableState) { - anchoredDraggableState.preUpPostDownNestedScrollConnection() + anchoredDraggableState.preUpPostDownNestedScrollConnection( + onFling = { scope.launch { anchoredDraggableState.settle(it) } } + ) }, ) } else { @@ -201,55 +203,51 @@ fun AdaptiveSheet( } } -private fun AnchoredDraggableState.preUpPostDownNestedScrollConnection() = - object : NestedScrollConnection { - override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { - val delta = available.toFloat() - return if (delta < 0 && source == NestedScrollSource.Drag) { - dispatchRawDelta(delta).toOffset() - } else { - Offset.Zero - } +private fun AnchoredDraggableState.preUpPostDownNestedScrollConnection( + onFling: (velocity: Float) -> Unit +) = object : NestedScrollConnection { + override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { + val delta = available.toFloat() + return if (delta < 0 && source == NestedScrollSource.UserInput) { + dispatchRawDelta(delta).toOffset() + } else { + Offset.Zero } - - override fun onPostScroll( - consumed: Offset, - available: Offset, - source: NestedScrollSource, - ): Offset { - return if (source == NestedScrollSource.Drag) { - dispatchRawDelta(available.toFloat()).toOffset() - } else { - Offset.Zero - } - } - - override suspend fun onPreFling(available: Velocity): Velocity { - val toFling = available.toFloat() - return if (toFling < 0 && offset > anchors.minAnchor()) { - settle(toFling) - // since we go to the anchor with tween settling, consume all for the best UX - available - } else { - Velocity.Zero - } - } - - override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity { - val toFling = available.toFloat() - return if (toFling > 0) { - settle(toFling) - available - } else { - Velocity.Zero - } - } - - private fun Float.toOffset(): Offset = Offset(0f, this) - - @JvmName("velocityToFloat") - private fun Velocity.toFloat() = this.y - - @JvmName("offsetToFloat") - private fun Offset.toFloat(): Float = this.y } + + override fun onPostScroll( + consumed: Offset, + available: Offset, + source: NestedScrollSource, + ): Offset { + return if (source == NestedScrollSource.UserInput) { + dispatchRawDelta(available.toFloat()).toOffset() + } else { + Offset.Zero + } + } + + override suspend fun onPreFling(available: Velocity): Velocity { + val toFling = available.toFloat() + return if (toFling < 0 && offset > anchors.minAnchor()) { + onFling(toFling) + // since we go to the anchor with tween settling, consume all for the best UX + available + } else { + Velocity.Zero + } + } + + override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity { + onFling(available.toFloat()) + return available + } + + private fun Float.toOffset(): Offset = Offset(0f, this) + + @JvmName("velocityToFloat") + private fun Velocity.toFloat() = this.y + + @JvmName("offsetToFloat") + private fun Offset.toFloat(): Float = this.y +} diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/PullRefresh.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/PullRefresh.kt index 9642cec71..b1987185b 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/PullRefresh.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/PullRefresh.kt @@ -1,34 +1,16 @@ package tachiyomi.presentation.core.components.material -import androidx.compose.animation.core.animate import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.pulltorefresh.PullToRefreshContainer -import androidx.compose.material3.pulltorefresh.PullToRefreshState +import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults +import androidx.compose.material3.pulltorefresh.pullToRefresh +import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableFloatStateOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.saveable.Saver -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.input.nestedscroll.NestedScrollConnection -import androidx.compose.ui.input.nestedscroll.NestedScrollSource -import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.LayoutDirection -import androidx.compose.ui.unit.Velocity import androidx.compose.ui.unit.dp -import kotlin.math.abs -import kotlin.math.pow /** * @param refreshing Whether the layout is currently refreshing @@ -46,242 +28,26 @@ fun PullRefresh( indicatorPadding: PaddingValues = PaddingValues(0.dp), content: @Composable () -> Unit, ) { - val state = rememberPullToRefreshState( - isRefreshing = refreshing, - extraVerticalOffset = indicatorPadding.calculateTopPadding(), - enabled = enabled, - onRefresh = onRefresh, - ) - - Box(modifier.nestedScroll(state.nestedScrollConnection)) { + val state = rememberPullToRefreshState() + Box( + modifier = modifier + .pullToRefresh( + isRefreshing = refreshing, + state = state, + enabled = enabled, + onRefresh = onRefresh, + ) + ) { content() - val contentPadding = remember(indicatorPadding) { - object : PaddingValues { - override fun calculateLeftPadding(layoutDirection: LayoutDirection): Dp = - indicatorPadding.calculateLeftPadding(layoutDirection) - - override fun calculateTopPadding(): Dp = 0.dp - - override fun calculateRightPadding(layoutDirection: LayoutDirection): Dp = - indicatorPadding.calculateRightPadding(layoutDirection) - - override fun calculateBottomPadding(): Dp = - indicatorPadding.calculateBottomPadding() - } - } - PullToRefreshContainer( - state = state, + PullToRefreshDefaults.Indicator( modifier = Modifier .align(Alignment.TopCenter) - .padding(contentPadding), + .padding(indicatorPadding), + isRefreshing = refreshing, + state = state, containerColor = MaterialTheme.colorScheme.surfaceVariant, - contentColor = MaterialTheme.colorScheme.onSurfaceVariant, + color = MaterialTheme.colorScheme.onSurfaceVariant, ) } } - -@Composable -private fun rememberPullToRefreshState( - isRefreshing: Boolean, - extraVerticalOffset: Dp, - positionalThreshold: Dp = 64.dp, - enabled: () -> Boolean = { true }, - onRefresh: () -> Unit, -): PullToRefreshStateImpl { - val density = LocalDensity.current - val extraVerticalOffsetPx = with(density) { extraVerticalOffset.toPx() } - val positionalThresholdPx = with(density) { positionalThreshold.toPx() } - return rememberSaveable( - extraVerticalOffset, - positionalThresholdPx, - enabled, - onRefresh, - saver = PullToRefreshStateImpl.Saver( - extraVerticalOffset = extraVerticalOffsetPx, - positionalThreshold = positionalThresholdPx, - enabled = enabled, - onRefresh = onRefresh, - ), - ) { - PullToRefreshStateImpl( - initialRefreshing = isRefreshing, - extraVerticalOffset = extraVerticalOffsetPx, - positionalThreshold = positionalThresholdPx, - enabled = enabled, - onRefresh = onRefresh, - ) - }.also { - LaunchedEffect(isRefreshing) { - if (isRefreshing && !it.isRefreshing) { - it.startRefreshAnimated() - } else if (!isRefreshing && it.isRefreshing) { - it.endRefreshAnimated() - } - } - } -} - -/** - * Creates a [PullToRefreshState]. - * - * @param positionalThreshold The positional threshold, in pixels, in which a refresh is triggered - * @param extraVerticalOffset Extra vertical offset, in pixels, for the "refreshing" state - * @param initialRefreshing The initial refreshing value of [PullToRefreshState] - * @param enabled a callback used to determine whether scroll events are to be handled by this - * @param onRefresh a callback to run when pull-to-refresh action is triggered by user - * [PullToRefreshState] - */ -private class PullToRefreshStateImpl( - initialRefreshing: Boolean, - private val extraVerticalOffset: Float, - override val positionalThreshold: Float, - enabled: () -> Boolean, - private val onRefresh: () -> Unit, -) : PullToRefreshState { - - override val progress get() = adjustedDistancePulled / positionalThreshold - override var verticalOffset by mutableFloatStateOf(if (initialRefreshing) refreshingVerticalOffset else 0f) - - override var isRefreshing by mutableStateOf(initialRefreshing) - - private val refreshingVerticalOffset: Float - get() = positionalThreshold + extraVerticalOffset - - override fun startRefresh() { - isRefreshing = true - verticalOffset = refreshingVerticalOffset - } - - suspend fun startRefreshAnimated() { - isRefreshing = true - animateTo(refreshingVerticalOffset) - } - - override fun endRefresh() { - verticalOffset = 0f - isRefreshing = false - } - - suspend fun endRefreshAnimated() { - animateTo(0f) - isRefreshing = false - } - - override var nestedScrollConnection = object : NestedScrollConnection { - override fun onPreScroll( - available: Offset, - source: NestedScrollSource, - ): Offset = when { - !enabled() -> Offset.Zero - // Swiping up - source == NestedScrollSource.Drag && available.y < 0 -> { - consumeAvailableOffset(available) - } - else -> Offset.Zero - } - - override fun onPostScroll( - consumed: Offset, - available: Offset, - source: NestedScrollSource, - ): Offset = when { - !enabled() -> Offset.Zero - // Swiping down - source == NestedScrollSource.Drag && available.y > 0 -> { - consumeAvailableOffset(available) - } - else -> Offset.Zero - } - - override suspend fun onPreFling(available: Velocity): Velocity { - return Velocity(0f, onRelease(available.y)) - } - } - - /** Helper method for nested scroll connection */ - fun consumeAvailableOffset(available: Offset): Offset { - val y = if (isRefreshing) { - 0f - } else { - val newOffset = (distancePulled + available.y).coerceAtLeast(0f) - val dragConsumed = newOffset - distancePulled - distancePulled = newOffset - verticalOffset = calculateVerticalOffset() + (extraVerticalOffset * progress.coerceIn(0f, 1f)) - dragConsumed - } - return Offset(0f, y) - } - - /** Helper method for nested scroll connection. Calls onRefresh callback when triggered */ - suspend fun onRelease(velocity: Float): Float { - if (isRefreshing) return 0f // Already refreshing, do nothing - // Trigger refresh - if (adjustedDistancePulled > positionalThreshold) { - onRefresh() - startRefreshAnimated() - } else { - animateTo(0f) - } - - val consumed = when { - // We are flinging without having dragged the pull refresh (for example a fling inside - // a list) - don't consume - distancePulled == 0f -> 0f - // If the velocity is negative, the fling is upwards, and we don't want to prevent the - // the list from scrolling - velocity < 0f -> 0f - // We are showing the indicator, and the fling is downwards - consume everything - else -> velocity - } - distancePulled = 0f - return consumed - } - - suspend fun animateTo(offset: Float) { - animate(initialValue = verticalOffset, targetValue = offset) { value, _ -> - verticalOffset = value - } - } - - /** Provides custom vertical offset behavior for [PullToRefreshContainer] */ - fun calculateVerticalOffset(): Float = when { - // If drag hasn't gone past the threshold, the position is the adjustedDistancePulled. - adjustedDistancePulled <= positionalThreshold -> adjustedDistancePulled - else -> { - // How far beyond the threshold pull has gone, as a percentage of the threshold. - val overshootPercent = abs(progress) - 1.0f - // Limit the overshoot to 200%. Linear between 0 and 200. - val linearTension = overshootPercent.coerceIn(0f, 2f) - // Non-linear tension. Increases with linearTension, but at a decreasing rate. - val tensionPercent = linearTension - linearTension.pow(2) / 4 - // The additional offset beyond the threshold. - val extraOffset = positionalThreshold * tensionPercent - positionalThreshold + extraOffset - } - } - - companion object { - /** The default [Saver] for [PullToRefreshStateImpl]. */ - fun Saver( - extraVerticalOffset: Float, - positionalThreshold: Float, - enabled: () -> Boolean, - onRefresh: () -> Unit, - ) = Saver( - save = { it.isRefreshing }, - restore = { isRefreshing -> - PullToRefreshStateImpl( - initialRefreshing = isRefreshing, - extraVerticalOffset = extraVerticalOffset, - positionalThreshold = positionalThreshold, - enabled = enabled, - onRefresh = onRefresh, - ) - }, - ) - } - - private var distancePulled by mutableFloatStateOf(0f) - private val adjustedDistancePulled: Float get() = distancePulled * 0.5f -} diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/util/LazyListState.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/util/LazyListState.kt index 1abb3205a..cdabf1b49 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/util/LazyListState.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/util/LazyListState.kt @@ -3,63 +3,16 @@ package tachiyomi.presentation.core.util import androidx.compose.foundation.lazy.LazyListState import androidx.compose.runtime.Composable import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue @Composable -fun LazyListState.isScrolledToStart(): Boolean { +fun LazyListState.shouldExpandFAB(): Boolean { return remember { derivedStateOf { - val firstItem = layoutInfo.visibleItemsInfo.firstOrNull() - firstItem == null || firstItem.offset == layoutInfo.viewportStartOffset + (firstVisibleItemIndex == 0 && firstVisibleItemScrollOffset == 0) || + lastScrolledBackward || + !canScrollForward } - }.value -} - -@Composable -fun LazyListState.isScrolledToEnd(): Boolean { - return remember { - derivedStateOf { - val lastItem = layoutInfo.visibleItemsInfo.lastOrNull() - lastItem == null || lastItem.size + lastItem.offset <= layoutInfo.viewportEndOffset - } - }.value -} - -@Composable -fun LazyListState.isScrollingUp(): Boolean { - var previousIndex by remember { mutableIntStateOf(firstVisibleItemIndex) } - var previousScrollOffset by remember { mutableIntStateOf(firstVisibleItemScrollOffset) } - return remember { - derivedStateOf { - if (previousIndex != firstVisibleItemIndex) { - previousIndex > firstVisibleItemIndex - } else { - previousScrollOffset >= firstVisibleItemScrollOffset - }.also { - previousIndex = firstVisibleItemIndex - previousScrollOffset = firstVisibleItemScrollOffset - } - } - }.value -} - -@Composable -fun LazyListState.isScrollingDown(): Boolean { - var previousIndex by remember { mutableIntStateOf(firstVisibleItemIndex) } - var previousScrollOffset by remember { mutableIntStateOf(firstVisibleItemScrollOffset) } - return remember { - derivedStateOf { - if (previousIndex != firstVisibleItemIndex) { - previousIndex < firstVisibleItemIndex - } else { - previousScrollOffset <= firstVisibleItemScrollOffset - }.also { - previousIndex = firstVisibleItemIndex - previousScrollOffset = firstVisibleItemScrollOffset - } - } - }.value + } + .value } From dbcc4a7d714cf36b48a41d6074ef671f3c039823 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sun, 5 May 2024 03:31:50 +0600 Subject: [PATCH 042/146] Revert "Fix badge count getting cut off on tab title" This reverts commit 263e467cdeb948b8f3679e2ea0282a291cf2f131. --- .../tachiyomi/presentation/core/components/material/Tabs.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Tabs.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Tabs.kt index c0c0dea2b..30f6766d8 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Tabs.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Tabs.kt @@ -6,7 +6,6 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.sp import tachiyomi.presentation.core.components.Pill @@ -22,7 +21,6 @@ fun TabText(text: String, badgeCount: Int? = null) { text = text, maxLines = 1, overflow = TextOverflow.Ellipsis, - modifier = Modifier.weight(1f, fill = false), ) if (badgeCount != null) { Pill( From 550f1197e818c35c7c05fd6184e69c7d29559e9f Mon Sep 17 00:00:00 2001 From: Reagan Date: Sun, 5 May 2024 22:23:09 +1200 Subject: [PATCH 043/146] Change keyboard type in extension repo dialog (#764) --- .../settings/screen/browse/components/ExtensionReposDialogs.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposDialogs.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposDialogs.kt index 5022f44f0..239f917d0 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposDialogs.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposDialogs.kt @@ -1,6 +1,7 @@ package eu.kanade.presentation.more.settings.screen.browse.components import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.AlertDialog import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text @@ -14,6 +15,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.text.input.KeyboardType import kotlinx.collections.immutable.ImmutableSet import kotlinx.coroutines.delay import mihon.domain.extensionrepo.model.ExtensionRepo @@ -74,6 +76,7 @@ fun ExtensionRepoCreateDialog( Text(text = stringResource(msgRes)) }, isError = name.isNotEmpty() && nameAlreadyExists, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Uri), singleLine = true, ) } From ca7391bbf3d49428f6321aa6baa03b0d2b5abff9 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Tue, 7 May 2024 15:03:32 +0600 Subject: [PATCH 044/146] Fix search bar style --- .../main/java/eu/kanade/presentation/components/AppBar.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/components/AppBar.kt b/app/src/main/java/eu/kanade/presentation/components/AppBar.kt index e7338c13e..01dcb575c 100644 --- a/app/src/main/java/eu/kanade/presentation/components/AppBar.kt +++ b/app/src/main/java/eu/kanade/presentation/components/AppBar.kt @@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material.TextFieldDefaults import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.outlined.ArrowBack import androidx.compose.material.icons.outlined.Close @@ -21,6 +20,7 @@ import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.PlainTooltip import androidx.compose.material3.Text +import androidx.compose.material3.TextFieldDefaults import androidx.compose.material3.TooltipBox import androidx.compose.material3.TooltipDefaults import androidx.compose.material3.TopAppBar @@ -312,7 +312,7 @@ fun SearchToolbar( visualTransformation = visualTransformation, interactionSource = interactionSource, decorationBox = { innerTextField -> - TextFieldDefaults.TextFieldDecorationBox( + TextFieldDefaults.DecorationBox( value = searchQuery, innerTextField = innerTextField, enabled = true, @@ -331,6 +331,7 @@ fun SearchToolbar( ), ) }, + container = {}, ) }, ) From 1df87eabf2b301cf6fc60cfa5f9391756984b790 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sun, 5 May 2024 01:53:45 +0600 Subject: [PATCH 045/146] Use new SurfaceContainer color roles Non-dynamic themes need to be updated Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> --- .../eu/kanade/presentation/components/AdaptiveSheet.kt | 6 ------ .../java/eu/kanade/presentation/components/TabbedDialog.kt | 1 + .../presentation/manga/components/MangaBottomActionMenu.kt | 4 ++-- .../more/settings/widget/AppThemePreferenceWidget.kt | 3 +-- .../kanade/presentation/reader/ReaderPageActionsDialog.kt | 4 +--- .../eu/kanade/presentation/track/TrackInfoDialogHome.kt | 2 +- .../ui/browse/source/browse/SourceFilterDialog.kt | 4 +--- .../presentation/core/components/AdaptiveSheet.kt | 6 ++---- .../java/tachiyomi/presentation/core/components/Pill.kt | 7 ++----- .../presentation/core/components/material/Button.kt | 2 -- 10 files changed, 11 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/components/AdaptiveSheet.kt b/app/src/main/java/eu/kanade/presentation/components/AdaptiveSheet.kt index 2712d9a27..15d05a6ec 100644 --- a/app/src/main/java/eu/kanade/presentation/components/AdaptiveSheet.kt +++ b/app/src/main/java/eu/kanade/presentation/components/AdaptiveSheet.kt @@ -7,8 +7,6 @@ import androidx.compose.animation.fadeOut import androidx.compose.animation.togetherWith import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties import cafe.adriel.voyager.core.annotation.InternalVoyagerApi @@ -23,7 +21,6 @@ import tachiyomi.presentation.core.components.AdaptiveSheet as AdaptiveSheetImpl @Composable fun NavigatorAdaptiveSheet( screen: Screen, - tonalElevation: Dp = 1.dp, enableSwipeDismiss: (Navigator) -> Boolean = { true }, onDismissRequest: () -> Unit, ) { @@ -31,7 +28,6 @@ fun NavigatorAdaptiveSheet( screen = screen, content = { sheetNavigator -> AdaptiveSheet( - tonalElevation = tonalElevation, enableSwipeDismiss = enableSwipeDismiss(sheetNavigator), onDismissRequest = onDismissRequest, ) { @@ -73,7 +69,6 @@ fun NavigatorAdaptiveSheet( fun AdaptiveSheet( onDismissRequest: () -> Unit, modifier: Modifier = Modifier, - tonalElevation: Dp = 1.dp, enableSwipeDismiss: Boolean = true, content: @Composable () -> Unit, ) { @@ -86,7 +81,6 @@ fun AdaptiveSheet( AdaptiveSheetImpl( modifier = modifier, isTabletUi = isTabletUi, - tonalElevation = tonalElevation, enableSwipeDismiss = enableSwipeDismiss, onDismissRequest = onDismissRequest, ) { diff --git a/app/src/main/java/eu/kanade/presentation/components/TabbedDialog.kt b/app/src/main/java/eu/kanade/presentation/components/TabbedDialog.kt index 4ef2e9771..b651060f7 100644 --- a/app/src/main/java/eu/kanade/presentation/components/TabbedDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/components/TabbedDialog.kt @@ -58,6 +58,7 @@ fun TabbedDialog( PrimaryTabRow( modifier = Modifier.weight(1f), selectedTabIndex = pagerState.currentPage, + containerColor = MaterialTheme.colorScheme.surfaceContainerHigh, divider = {}, ) { tabTitles.fastForEachIndexed { index, tab -> diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaBottomActionMenu.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaBottomActionMenu.kt index a78383590..93b8c1843 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaBottomActionMenu.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaBottomActionMenu.kt @@ -81,7 +81,7 @@ fun MangaBottomActionMenu( Surface( modifier = modifier, shape = MaterialTheme.shapes.large.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize), - tonalElevation = 3.dp, + color = MaterialTheme.colorScheme.surfaceContainerHigh, ) { val haptic = LocalHapticFeedback.current val confirm = remember { mutableStateListOf(false, false, false, false, false, false, false) } @@ -237,7 +237,7 @@ fun LibraryBottomActionMenu( Surface( modifier = modifier, shape = MaterialTheme.shapes.large.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize), - tonalElevation = 3.dp, + color = MaterialTheme.colorScheme.surfaceContainerHigh, ) { val haptic = LocalHapticFeedback.current val confirm = remember { mutableStateListOf(false, false, false, false, false) } diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemePreferenceWidget.kt b/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemePreferenceWidget.kt index db0842d08..5e3f76efe 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemePreferenceWidget.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemePreferenceWidget.kt @@ -223,13 +223,12 @@ fun AppThemePreviewItem( contentAlignment = Alignment.BottomCenter, ) { Surface( - tonalElevation = 3.dp, + color = MaterialTheme.colorScheme.surfaceContainer, ) { Row( modifier = Modifier .height(32.dp) .fillMaxWidth() - .background(MaterialTheme.colorScheme.surfaceVariant) .padding(horizontal = 8.dp), verticalAlignment = Alignment.CenterVertically, ) { diff --git a/app/src/main/java/eu/kanade/presentation/reader/ReaderPageActionsDialog.kt b/app/src/main/java/eu/kanade/presentation/reader/ReaderPageActionsDialog.kt index 70cc58f20..b83e8d6ad 100644 --- a/app/src/main/java/eu/kanade/presentation/reader/ReaderPageActionsDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/reader/ReaderPageActionsDialog.kt @@ -33,9 +33,7 @@ fun ReaderPageActionsDialog( ) { var showSetCoverDialog by remember { mutableStateOf(false) } - AdaptiveSheet( - onDismissRequest = onDismissRequest, - ) { + AdaptiveSheet(onDismissRequest = onDismissRequest) { Row( modifier = Modifier.padding(vertical = 16.dp), horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small), diff --git a/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogHome.kt b/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogHome.kt index 993532180..7ed6b3cc0 100644 --- a/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogHome.kt +++ b/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogHome.kt @@ -186,7 +186,7 @@ private fun TrackInfoItem( modifier = Modifier .padding(top = 12.dp) .clip(MaterialTheme.shapes.medium) - .background(MaterialTheme.colorScheme.surface) + .background(MaterialTheme.colorScheme.surfaceContainerHighest) .padding(8.dp) .clip(RoundedCornerShape(6.dp)), ) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterDialog.kt index 017af7479..42355c504 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterDialog.kt @@ -40,9 +40,7 @@ fun SourceFilterDialog( ) { val updateFilters = { onUpdate(filters) } - AdaptiveSheet( - onDismissRequest = onDismissRequest, - ) { + AdaptiveSheet(onDismissRequest = onDismissRequest) { LazyColumn { stickyHeader { Row( diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/AdaptiveSheet.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/AdaptiveSheet.kt index 54d2ad700..bb9ed8e85 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/AdaptiveSheet.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/AdaptiveSheet.kt @@ -38,7 +38,6 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.Velocity import androidx.compose.ui.unit.dp @@ -53,7 +52,6 @@ private val sheetAnimationSpec = tween(durationMillis = 350) @Composable fun AdaptiveSheet( isTabletUi: Boolean, - tonalElevation: Dp, enableSwipeDismiss: Boolean, onDismissRequest: () -> Unit, modifier: Modifier = Modifier, @@ -97,7 +95,7 @@ fun AdaptiveSheet( .padding(vertical = 16.dp) .then(modifier), shape = MaterialTheme.shapes.extraLarge, - tonalElevation = tonalElevation, + color = MaterialTheme.colorScheme.surfaceContainerHigh, content = { BackHandler(enabled = alpha > 0f, onBack = internalOnDismissRequest) content() @@ -180,7 +178,7 @@ fun AdaptiveSheet( .navigationBarsPadding() .statusBarsPadding(), shape = MaterialTheme.shapes.extraLarge, - tonalElevation = tonalElevation, + color = MaterialTheme.colorScheme.surfaceContainerHigh, content = { BackHandler( enabled = anchoredDraggableState.targetValue == 0, diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/Pill.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/Pill.kt index 4389fb482..1d8cf2d5f 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/Pill.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/Pill.kt @@ -10,7 +10,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.dp @@ -18,9 +17,8 @@ import androidx.compose.ui.unit.dp fun Pill( text: String, modifier: Modifier = Modifier, - color: Color = MaterialTheme.colorScheme.background, - contentColor: Color = MaterialTheme.colorScheme.onBackground, - elevation: Dp = 1.dp, + color: Color = MaterialTheme.colorScheme.surfaceContainerHigh, + contentColor: Color = MaterialTheme.colorScheme.onSurface, fontSize: TextUnit = LocalTextStyle.current.fontSize, ) { Surface( @@ -29,7 +27,6 @@ fun Pill( shape = MaterialTheme.shapes.extraLarge, color = color, contentColor = contentColor, - tonalElevation = elevation, ) { Box( modifier = Modifier diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Button.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Button.kt index 6a645ed6a..5e65c89a2 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Button.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Button.kt @@ -100,7 +100,6 @@ fun Button( val containerColor = colors.containerColor(enabled).value val contentColor = colors.contentColor(enabled).value val shadowElevation = elevation?.shadowElevation(enabled, interactionSource)?.value ?: 0.dp - val tonalElevation = elevation?.tonalElevation(enabled, interactionSource)?.value ?: 0.dp Surface( onClick = onClick, @@ -109,7 +108,6 @@ fun Button( shape = shape, color = containerColor, contentColor = contentColor, - tonalElevation = tonalElevation, shadowElevation = shadowElevation, border = border, interactionSource = interactionSource, From 8e9396a9cfe5f5e87e4e5f2093421a3fa24d43db Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Tue, 7 May 2024 17:13:43 +0800 Subject: [PATCH 046/146] Fix tap control area shifting after zooming out (#767) --- .../tachiyomi/ui/reader/viewer/webtoon/WebtoonRecyclerView.kt | 3 ++- .../kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonRecyclerView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonRecyclerView.kt index 20c18c622..fe232fe87 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonRecyclerView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonRecyclerView.kt @@ -28,7 +28,8 @@ class WebtoonRecyclerView @JvmOverloads constructor( private var atFirstPosition = false private var halfWidth = 0 private var halfHeight = 0 - private var originalHeight = 0 + var originalHeight = 0 + private set private var heightSet = false private var firstVisibleItemPosition = 0 private var lastVisibleItemPosition = 0 diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt index 1e370a8c7..a965631fc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt @@ -117,7 +117,7 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr recycler.getLocationInWindow(viewPositionRelativeToWindow) val pos = PointF( (event.rawX - viewPosition[0] + viewPositionRelativeToWindow[0]) / recycler.width, - (event.rawY - viewPosition[1] + viewPositionRelativeToWindow[1]) / recycler.height, + (event.rawY - viewPosition[1] + viewPositionRelativeToWindow[1]) / recycler.originalHeight, ) when (config.navigator.getAction(pos)) { NavigationRegion.MENU -> activity.toggleMenu() From fb9423028eb017c110cb805f2d0601e5b02e50f9 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Tue, 7 May 2024 15:53:26 +0600 Subject: [PATCH 047/146] Remove dependency on compose material 2 components --- app/build.gradle.kts | 1 - gradle/compose.versions.toml | 3 --- presentation-core/build.gradle.kts | 1 - .../presentation/core/components/SettingsItems.kt | 7 ++++--- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5a16ab034..f7f9a78a3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -151,7 +151,6 @@ dependencies { implementation(compose.activity) implementation(compose.foundation) implementation(compose.material3.core) - implementation(compose.material.core) implementation(compose.material.icons) implementation(compose.animation) implementation(compose.animation.graphics) diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 1249e384f..77a87adf1 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -18,9 +18,6 @@ ui-util = { module = "androidx.compose.ui:ui-util" } material3-core = { module = "androidx.compose.material3:material3" } material-icons = { module = "androidx.compose.material:material-icons-extended" } -# Some components aren't available in Material3 -material-core = { module = "androidx.compose.material:material" } - glance = "androidx.glance:glance-appwidget:1.0.0" accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanist" } diff --git a/presentation-core/build.gradle.kts b/presentation-core/build.gradle.kts index c7b64ad42..8fca92019 100644 --- a/presentation-core/build.gradle.kts +++ b/presentation-core/build.gradle.kts @@ -21,7 +21,6 @@ dependencies { implementation(compose.activity) implementation(compose.foundation) implementation(compose.material3.core) - implementation(compose.material.core) implementation(compose.material.icons) implementation(compose.animation) implementation(compose.animation.graphics) diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/SettingsItems.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/SettingsItems.kt index abdf61c9f..4fd4b7571 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/SettingsItems.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/SettingsItems.kt @@ -14,7 +14,6 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyGridScope import androidx.compose.foundation.lazy.grid.LazyVerticalGrid -import androidx.compose.material.ContentAlpha import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowDownward import androidx.compose.material.icons.filled.ArrowUpward @@ -56,6 +55,8 @@ object SettingsItemsPaddings { val Vertical = 10.dp } +private const val DisabledContentAlpha = 0.38f + @Composable fun HeadingItem(labelRes: StringResource) { HeadingItem(stringResource(labelRes)) @@ -278,7 +279,7 @@ fun TriStateItem( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.large), ) { - val stateAlpha = if (enabled && onClick != null) 1f else ContentAlpha.disabled + val stateAlpha = if (enabled && onClick != null) 1f else DisabledContentAlpha Icon( imageVector = when (state) { @@ -291,7 +292,7 @@ fun TriStateItem( MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = stateAlpha) } else { when (onClick) { - null -> MaterialTheme.colorScheme.onSurface.copy(alpha = ContentAlpha.disabled) + null -> MaterialTheme.colorScheme.onSurface.copy(alpha = DisabledContentAlpha) else -> MaterialTheme.colorScheme.primary } }, From 5955c9c3116c4d40e448c708a32057c6fd697fc4 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Wed, 8 May 2024 23:06:35 +0600 Subject: [PATCH 048/146] Update project icon --- .idea/icon.png | Bin 26887 -> 63677 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/.idea/icon.png b/.idea/icon.png index cd287a6c46643f4747b4039cd86e0a551bd78860..465a5c55e8aa80f63d0fa72742100a91a9db72f0 100644 GIT binary patch literal 63677 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 RA_u@c>dHDw)e5%Z{|7!{J!}8~ literal 26887 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 Date: Fri, 10 May 2024 12:52:05 +0600 Subject: [PATCH 049/146] chore(deps): update actions/checkout action to v4.1.5 (#771) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- .github/workflows/build_push.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index be47b2425..d9942cf0a 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Clone repo - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 - name: Validate Gradle Wrapper uses: gradle/wrapper-validation-action@216d1ad2b3710bf005dc39237337b9673fd8fcd5 # v3.3.2 diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index 0686e1889..e2ee5a7aa 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -17,7 +17,7 @@ jobs: steps: - name: Clone repo - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 - name: Validate Gradle Wrapper uses: gradle/wrapper-validation-action@216d1ad2b3710bf005dc39237337b9673fd8fcd5 # v3.3.2 From aebb86794aa6391335cc8a4ab61bce2b9141ac1e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 10 May 2024 12:52:18 +0600 Subject: [PATCH 050/146] chore(deps): update softprops/action-gh-release action to v2.0.5 (#773) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_push.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index e2ee5a7aa..246f6e37c 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -83,7 +83,7 @@ jobs: - name: Create Release if: startsWith(github.ref, 'refs/tags/') && github.repository == 'mihonapp/mihon' - uses: softprops/action-gh-release@9d7c94cfd0a1f3ed45544c887983e9fa900f0564 # v2.0.4 + uses: softprops/action-gh-release@69320dbe05506a9a39fc8ae11030b214ec2d1f87 # v2.0.5 with: tag_name: ${{ env.VERSION_TAG }} name: Mihon ${{ env.VERSION_TAG }} From d3306e8cfe9e3e373a9fd688bdcd0d91fc5f4277 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 10 May 2024 12:52:40 +0600 Subject: [PATCH 051/146] fix(deps): update dependency io.kotest:kotest-assertions-core to v5.9.0 (#774) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b6776f997..1c287986e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -91,7 +91,7 @@ sqldelight-dialects-sql = { module = "app.cash.sqldelight:sqlite-3-38-dialect", sqldelight-gradle = { module = "app.cash.sqldelight:gradle-plugin", version.ref = "sqldelight" } junit = "org.junit.jupiter:junit-jupiter:5.10.2" -kotest-assertions = "io.kotest:kotest-assertions-core:5.8.1" +kotest-assertions = "io.kotest:kotest-assertions-core:5.9.0" mockk = "io.mockk:mockk:1.13.10" voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" } From ab546e08843bc71ec059c5dd44031116fdac781f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 10 May 2024 20:25:59 +0600 Subject: [PATCH 052/146] fix(deps): update dependency org.jetbrains.kotlinx:kotlinx-coroutines-bom to v1.8.1 (#778) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/kotlinx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/kotlinx.versions.toml b/gradle/kotlinx.versions.toml index 4c8871cd9..f86682ec5 100644 --- a/gradle/kotlinx.versions.toml +++ b/gradle/kotlinx.versions.toml @@ -9,7 +9,7 @@ gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = " immutables = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version = "0.3.7" } -coroutines-bom = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-bom", version = "1.8.0" } +coroutines-bom = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-bom", version = "1.8.1" } coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core" } coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android" } coroutines-guava = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-guava" } From f603db3f3fac72954997bfa0c2cd77a0b92cd7f0 Mon Sep 17 00:00:00 2001 From: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com> Date: Sat, 11 May 2024 14:15:02 +0500 Subject: [PATCH 053/146] update r8 rules for `MultipartBody.Builder` in extensions (#783) --- app/proguard-rules.pro | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index c6471bea7..f4451efda 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -44,6 +44,10 @@ -dontnote rx.internal.util.PlatformDependent ##---------------End: proguard configuration for RxJava 1.x ---------- +##---------------Begin: proguard configuration for okhttp ---------- +-keepclasseswithmembers class okhttp3.MultipartBody$Builder { *; } +##---------------End: proguard configuration for okhttp ---------- + ##---------------Begin: proguard configuration for kotlinx.serialization ---------- -keepattributes *Annotation*, InnerClasses -dontnote kotlinx.serialization.** # core serialization annotations From 16392adcbba4027cbed0a44e2fc62df330af6385 Mon Sep 17 00:00:00 2001 From: CrepeTF <70870719+CrepeTF@users.noreply.github.com> Date: Mon, 13 May 2024 21:44:40 +0100 Subject: [PATCH 054/146] Update themes to follow new compose update changes (#766) * Update Green Apple theme * Add some Green Apple theme comments * Update Lavender theme * Update Midnight Dusk theme * Update Nord theme * Update Strawberry Daiquiri theme * Update Tako theme * Update Teal & Turquoise theme * Update Lavender secondaryContainer and onSecondaryContainer colour * Update M.Dusk secondaryContainer and onSecondaryContainer colour * Update Tako secondaryContainer and onSecondaryContainer colour * Comments * Update Tidal Wave theme * Update Yin Yang theme * Update Yotsuba theme * Fix navbar tinted background on pure black * Add surfaceContainer levels to Lavender theme * Resolve detekt issues * Add surfaceContainer levels to Midnight Dusk theme * Add surfaceContainer levels to Nord theme * Add surfaceContainer levels to Tako theme * Add surfaceContainer levels to Teal & Turquoise theme * Add surfaceContainer levels to Tidal Wave theme * Add surfaceContainer levels to Yin Yang theme * Add surfaceContainer levels to Yotsuba theme * Add dark theme surfaceContainer levels to Yotsuba theme * surfaceContainer tweaks to Yotsuba theme * surfaceContainer tweaks to Strawberry Daiquiri theme * surfaceContainer tweaks to Nord theme * surfaceContainer tweaks to Lavender theme * Update Tachiyomi theme * Update Pure Black theme * Resolve detekt issues * Oopsie --- .../theme/colorscheme/BaseColorScheme.kt | 12 ++ .../colorscheme/GreenAppleColorScheme.kt | 110 +++++++++++------- .../theme/colorscheme/LavenderColorScheme.kt | 102 +++++++++------- .../colorscheme/MidnightDuskColorScheme.kt | 38 +++--- .../theme/colorscheme/NordColorScheme.kt | 40 ++++--- .../colorscheme/StrawberryColorScheme.kt | 110 +++++++++++------- .../theme/colorscheme/TachiyomiColorScheme.kt | 38 +++--- .../theme/colorscheme/TakoColorScheme.kt | 38 +++--- .../colorscheme/TealTurqoiseColorScheme.kt | 38 +++--- .../theme/colorscheme/TidalWaveColorScheme.kt | 38 +++--- .../theme/colorscheme/YinYangColorScheme.kt | 38 +++--- .../theme/colorscheme/YotsubaColorScheme.kt | 38 +++--- 12 files changed, 402 insertions(+), 238 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/BaseColorScheme.kt b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/BaseColorScheme.kt index 97455fd9e..22dd9a0a7 100644 --- a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/BaseColorScheme.kt +++ b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/BaseColorScheme.kt @@ -8,6 +8,12 @@ internal abstract class BaseColorScheme { abstract val darkScheme: ColorScheme abstract val lightScheme: ColorScheme + // Cannot be pure black as there's content scrolling behind it + // https://m3.material.io/components/navigation-bar/guidelines#90615a71-607e-485e-9e09-778bfc080563 + private val surfaceContainer = Color(0xFF0C0C0C) + private val surfaceContainerHigh = Color(0xFF131313) + private val surfaceContainerHighest = Color(0xFF1B1B1B) + fun getColorScheme(isDark: Boolean, isAmoled: Boolean): ColorScheme { if (!isDark) return lightScheme @@ -18,6 +24,12 @@ internal abstract class BaseColorScheme { onBackground = Color.White, surface = Color.Black, onSurface = Color.White, + surfaceVariant = surfaceContainer, // Navigation bar background (ThemePrefWidget) + surfaceContainerLowest = surfaceContainer, + surfaceContainerLow = surfaceContainer, + surfaceContainer = surfaceContainer, // Navigation bar background + surfaceContainerHigh = surfaceContainerHigh, + surfaceContainerHighest = surfaceContainerHighest, ) } } diff --git a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/GreenAppleColorScheme.kt b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/GreenAppleColorScheme.kt index 354faed5f..566cce91b 100644 --- a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/GreenAppleColorScheme.kt +++ b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/GreenAppleColorScheme.kt @@ -19,53 +19,77 @@ internal object GreenAppleColorScheme : BaseColorScheme() { override val darkScheme = darkColorScheme( primary = Color(0xFF7ADB8F), - onPrimary = Color(0xFF003915), - primaryContainer = Color(0xFF005322), - onPrimaryContainer = Color(0xFF96F8A9), - inversePrimary = Color(0xFF006D2F), - secondary = Color(0xFF7ADB8F), - onSecondary = Color(0xFF003915), - secondaryContainer = Color(0xFF005322), - onSecondaryContainer = Color(0xFF96F8A9), - tertiary = Color(0xFFFFB3AA), - onTertiary = Color(0xFF680006), - tertiaryContainer = Color(0xFF93000D), - onTertiaryContainer = Color(0xFFFFDAD5), - background = Color(0xFF1A1C19), - onBackground = Color(0xFFE1E3DD), - surface = Color(0xFF1A1C19), - onSurface = Color(0xFFE1E3DD), - surfaceVariant = Color(0xFF414941), - onSurfaceVariant = Color(0xFFC1C8BE), - surfaceTint = Color(0xFF7ADB8F), - inverseSurface = Color(0xFFE1E3DD), - inverseOnSurface = Color(0xFF1A1C19), - outline = Color(0xFF8B9389), + onPrimary = Color(0xFF003917), + primaryContainer = Color(0xFF017737), + onPrimaryContainer = Color(0xFFFFFFFF), + secondary = Color(0xFF7ADB8F), // Unread badge + onSecondary = Color(0xFF003917), // Unread badge text + secondaryContainer = Color(0xFF017737), // Navigation bar selector pill & progress indicator (remaining) + onSecondaryContainer = Color(0xFFFFFFFF), // Navigation bar selected icon + tertiary = Color(0xFFFFB3AC), // Downloaded badge + onTertiary = Color(0xFF680008), // Downloaded badge text + tertiaryContainer = Color(0xFFC7282A), + onTertiaryContainer = Color(0xFFFFFFFF), + error = Color(0xFFFFB4AB), + onError = Color(0xFF690005), + errorContainer = Color(0xFF93000A), + onErrorContainer = Color(0xFFFFDAD6), + background = Color(0xFF0F1510), + onBackground = Color(0xFFDFE4DB), + surface = Color(0xFF0F1510), + onSurface = Color(0xFFDFE4DB), + surfaceVariant = Color(0xFF3F493F), // Navigation bar background (ThemePrefWidget) + onSurfaceVariant = Color(0xFFBECABC), + outline = Color(0xFF889487), + outlineVariant = Color(0xFF3F493F), + scrim = Color(0xFF000000), + inverseSurface = Color(0xFFDFE4DB), + inverseOnSurface = Color(0xFF2C322C), + inversePrimary = Color(0xFF006D32), + surfaceDim = Color(0xFF0F1510), + surfaceBright = Color(0xFF353B35), + surfaceContainerLowest = Color(0xFF0A0F0B), + surfaceContainerLow = Color(0xFF181D18), + surfaceContainer = Color(0xFF1C211C), // Navigation bar background + surfaceContainerHigh = Color(0xFF262B26), + surfaceContainerHighest = Color(0xFF313630), ) override val lightScheme = lightColorScheme( - primary = Color(0xFF006D2F), + primary = Color(0xFF005927), onPrimary = Color(0xFFFFFFFF), - primaryContainer = Color(0xFF96F8A9), - onPrimaryContainer = Color(0xFF002109), + primaryContainer = Color(0xFF188140), + onPrimaryContainer = Color(0xFFFFFFFF), + secondary = Color(0xFF005927), // Unread badge + onSecondary = Color(0xFFFFFFFF), // Unread badge text + secondaryContainer = Color(0xFF97f7a9), // Navigation bar selector pill & progress indicator (remaining) + onSecondaryContainer = Color(0xFF000000), // Navigation bar selected icon + tertiary = Color(0xFF9D0012), // Downloaded badge + onTertiary = Color(0xFFFFFFFF), // Downloaded badge text + tertiaryContainer = Color(0xFFD33131), + onTertiaryContainer = Color(0xFFFFFFFF), + error = Color(0xFFBA1A1A), + onError = Color(0xFFFFFFFF), + errorContainer = Color(0xFFFFDAD6), + onErrorContainer = Color(0xFF410002), + background = Color(0xFFF6FBF2), + onBackground = Color(0xFF181D18), + surface = Color(0xFFF6FBF2), + onSurface = Color(0xFF181D18), + surfaceVariant = Color(0xFFDAE6D7), // Navigation bar background (ThemePrefWidget) + onSurfaceVariant = Color(0xFF3F493F), + outline = Color(0xFF6F7A6E), + outlineVariant = Color(0xFFBECABC), + scrim = Color(0xFF000000), + inverseSurface = Color(0xFF2C322C), + inverseOnSurface = Color(0xFFEDF2E9), inversePrimary = Color(0xFF7ADB8F), - secondary = Color(0xFF006D2F), - onSecondary = Color(0xFFFFFFFF), - secondaryContainer = Color(0xFF96F8A9), - onSecondaryContainer = Color(0xFF002109), - tertiary = Color(0xFFB91D22), - onTertiary = Color(0xFFFFFFFF), - tertiaryContainer = Color(0xFFFFDAD5), - onTertiaryContainer = Color(0xFF410003), - background = Color(0xFFFBFDF7), - onBackground = Color(0xFF1A1C19), - surface = Color(0xFFFBFDF7), - onSurface = Color(0xFF1A1C19), - surfaceVariant = Color(0xFFDDE5DA), - onSurfaceVariant = Color(0xFF414941), - surfaceTint = Color(0xFF006D2F), - inverseSurface = Color(0xFF2F312E), - inverseOnSurface = Color(0xFFF0F2EC), - outline = Color(0xFF717970), + surfaceDim = Color(0xFFD6DCD3), + surfaceBright = Color(0xFFF6FBF2), + surfaceContainerLowest = Color(0xFFFFFFFF), + surfaceContainerLow = Color(0xFFF0F5EC), + surfaceContainer = Color(0xFFEAEFE6), // Navigation bar background + surfaceContainerHigh = Color(0xFFE4EAE1), + surfaceContainerHighest = Color(0xFFDFE4DB), ) } diff --git a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/LavenderColorScheme.kt b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/LavenderColorScheme.kt index 70a9bd196..e1bb6b3ee 100644 --- a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/LavenderColorScheme.kt +++ b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/LavenderColorScheme.kt @@ -18,53 +18,77 @@ internal object LavenderColorScheme : BaseColorScheme() { override val darkScheme = darkColorScheme( primary = Color(0xFFA177FF), - onPrimary = Color(0xFF111129), + onPrimary = Color(0xFF3D0090), primaryContainer = Color(0xFFA177FF), - onPrimaryContainer = Color(0xFF111129), - inversePrimary = Color(0xFF006D2F), - secondary = Color(0xFFA177FF), - onSecondary = Color(0xFF111129), - secondaryContainer = Color(0xFFA177FF), - onSecondaryContainer = Color(0xFF111129), - tertiary = Color(0xFF5E25E1), - onTertiary = Color(0xFFE8E8E8), - tertiaryContainer = Color(0xFF111129), - onTertiaryContainer = Color(0xFFDEE8FF), + onPrimaryContainer = Color(0xFFFFFFFF), + secondary = Color(0xFFA177FF), // Unread badge + onSecondary = Color(0xFFFFFFFF), // Unread badge text + secondaryContainer = Color(0xFF423271), // Navigation bar selector pill & progress indicator (remaining) + onSecondaryContainer = Color(0xFFA177FF), // Navigation bar selected icon + tertiary = Color(0xFFCDBDFF), // Downloaded badge + onTertiary = Color(0xFF360096), // Downloaded badge text + tertiaryContainer = Color(0xFF5512D8), + onTertiaryContainer = Color(0xFFEFE6FF), + error = Color(0xFFFFB4AB), + onError = Color(0xFF690005), + errorContainer = Color(0xFF93000A), + onErrorContainer = Color(0xFFFFDAD6), background = Color(0xFF111129), - onBackground = Color(0xFFDEE8FF), + onBackground = Color(0xFFE7E0EC), surface = Color(0xFF111129), - onSurface = Color(0xFFDEE8FF), - surfaceVariant = Color(0x2CB6B6B6), - onSurfaceVariant = Color(0xFFE8E8E8), - surfaceTint = Color(0xFFA177FF), - inverseSurface = Color(0xFF221247), - inverseOnSurface = Color(0xFFDEE8FF), - outline = Color(0xA8905FFF), + onSurface = Color(0xFFE7E0EC), + surfaceVariant = Color(0xFF3D2F6B), // Navigation bar background (ThemePrefWidget) + onSurfaceVariant = Color(0xFFCBC3D6), + outline = Color(0xFF958E9F), + outlineVariant = Color(0xFF4A4453), + scrim = Color(0xFF000000), + inverseSurface = Color(0xFFE7E0EC), + inverseOnSurface = Color(0xFF322F38), + inversePrimary = Color(0xFF6D41C8), + surfaceDim = Color(0xFF111129), + surfaceBright = Color(0xFF3B3841), + surfaceContainerLowest = Color(0xFF15132d), + surfaceContainerLow = Color(0xFF171531), + surfaceContainer = Color(0xFF1D193B), // Navigation bar background + surfaceContainerHigh = Color(0xFF241f41), + surfaceContainerHighest = Color(0xFF282446), ) override val lightScheme = lightColorScheme( - primary = Color(0xFF7B46AF), - onPrimary = Color(0xFFEDE2FF), + primary = Color(0xFF6D41C8), + onPrimary = Color(0xFFFFFFFF), primaryContainer = Color(0xFF7B46AF), - onPrimaryContainer = Color(0xFFEDE2FF), - inversePrimary = Color(0xFFD6BAFF), - secondary = Color(0xFF7B46AF), - onSecondary = Color(0xFFEDE2FF), - secondaryContainer = Color(0xFF7B46AF), - onSecondaryContainer = Color(0xFFEDE2FF), - tertiary = Color(0xFFEDE2FF), - onTertiary = Color(0xFF7B46AF), - tertiaryContainer = Color(0xFFEDE2FF), - onTertiaryContainer = Color(0xFF7B46AF), + onPrimaryContainer = Color(0xFF130038), + secondary = Color(0xFF7B46AF), // Unread badge + onSecondary = Color(0xFFEDE2FF), // Unread badge text + secondaryContainer = Color(0xFFC9B0E6), // Navigation bar selector pill & progress indicator (remaining) + onSecondaryContainer = Color(0xFF7B46AF), // Navigation bar selector icon + tertiary = Color(0xFFEDE2FF), // Downloaded badge + onTertiary = Color(0xFF7B46AF), // Downloaded badge text + tertiaryContainer = Color(0xFF6D3BF0), + onTertiaryContainer = Color(0xFFFFFFFF), + error = Color(0xFFBA1A1A), + onError = Color(0xFFFFFFFF), + errorContainer = Color(0xFFFFDAD6), + onErrorContainer = Color(0xFF410002), background = Color(0xFFEDE2FF), - onBackground = Color(0xFF1B1B22), + onBackground = Color(0xFF1D1A22), surface = Color(0xFFEDE2FF), - onSurface = Color(0xFF1B1B22), - surfaceVariant = Color(0xFFB9B0CC), - onSurfaceVariant = Color(0xD849454E), - surfaceTint = Color(0xFF7B46AF), - inverseSurface = Color(0xFF313033), - inverseOnSurface = Color(0xFFF3EFF4), - outline = Color(0xFF7B46AF), + onSurface = Color(0xFF1D1A22), + surfaceVariant = Color(0xFFE4D5F8), // Navigation bar background (ThemePrefWidget) + onSurfaceVariant = Color(0xFF4A4453), + outline = Color(0xFF7B7485), + outlineVariant = Color(0xFFCBC3D6), + scrim = Color(0xFF000000), + inverseSurface = Color(0xFF322F38), + inverseOnSurface = Color(0xFFF5EEFA), + inversePrimary = Color(0xFFA177FF), + surfaceDim = Color(0xFFDED7E3), + surfaceBright = Color(0xFFEDE2FF), + surfaceContainerLowest = Color(0xFFDACCEC), + surfaceContainerLow = Color(0xFFDED0F1), + surfaceContainer = Color(0xFFE4D5F8), // Navigation bar background + surfaceContainerHigh = Color(0xFFEADCFD), + surfaceContainerHighest = Color(0xFFEEE2FF), ) } diff --git a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/MidnightDuskColorScheme.kt b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/MidnightDuskColorScheme.kt index 7feaae333..5ae86aa34 100644 --- a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/MidnightDuskColorScheme.kt +++ b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/MidnightDuskColorScheme.kt @@ -23,24 +23,29 @@ internal object MidnightDuskColorScheme : BaseColorScheme() { primaryContainer = Color(0xFFBD1C5C), onPrimaryContainer = Color(0xFFFFFFFF), inversePrimary = Color(0xFFF02475), - secondary = Color(0xFFF02475), - onSecondary = Color(0xFFFFFFFF), - secondaryContainer = Color(0xFFF02475), - onSecondaryContainer = Color(0xFFFFFFFF), - tertiary = Color(0xFF55971C), - onTertiary = Color(0xFFFFFFFF), + secondary = Color(0xFFF02475), // Unread badge + onSecondary = Color(0xFF16151D), // Unread badge text + secondaryContainer = Color(0xFF66183C), // Navigation bar selector pill & progress indicator (remaining) + onSecondaryContainer = Color(0xFFF02475), // Navigation bar selector icon + tertiary = Color(0xFF55971C), // Downloaded badge + onTertiary = Color(0xFF16151D), // Downloaded badge text tertiaryContainer = Color(0xFF386412), onTertiaryContainer = Color(0xFFE5E1E5), background = Color(0xFF16151D), onBackground = Color(0xFFE5E1E5), surface = Color(0xFF16151D), onSurface = Color(0xFFE5E1E5), - surfaceVariant = Color(0xFF524346), + surfaceVariant = Color(0xFF281624), // Navigation bar background (ThemePrefWidget) onSurfaceVariant = Color(0xFFD6C1C4), surfaceTint = Color(0xFFF02475), inverseSurface = Color(0xFF333043), inverseOnSurface = Color(0xFFFFFFFF), outline = Color(0xFF9F8C8F), + surfaceContainerLowest = Color(0xFF221320), + surfaceContainerLow = Color(0xFF251522), + surfaceContainer = Color(0xFF281624), // Navigation bar background + surfaceContainerHigh = Color(0xFF2D1C2A), + surfaceContainerHighest = Color(0xFF2F1F2C), ) override val lightScheme = lightColorScheme( @@ -49,23 +54,28 @@ internal object MidnightDuskColorScheme : BaseColorScheme() { primaryContainer = Color(0xFFFFD9E1), onPrimaryContainer = Color(0xFF3F0017), inversePrimary = Color(0xFFFFB1C4), - secondary = Color(0xFFBB0054), - onSecondary = Color(0xFFFFFFFF), - secondaryContainer = Color(0xFFFFD9E1), - onSecondaryContainer = Color(0xFF3F0017), - tertiary = Color(0xFF006638), - onTertiary = Color(0xFFFFFFFF), + secondary = Color(0xFFBB0054), // Unread badge + onSecondary = Color(0xFFFFFFFF), // Unread badge text + secondaryContainer = Color(0xFFEFBAD4), // Navigation bar selector pill & progress indicator (remaining) + onSecondaryContainer = Color(0xFFD1377C), // Navigation bar selector icon + tertiary = Color(0xFF006638), // Downloaded badge + onTertiary = Color(0xFFFFFFFF), // Downloaded badge text tertiaryContainer = Color(0xFF00894b), onTertiaryContainer = Color(0xFF2D1600), background = Color(0xFFFFFBFF), onBackground = Color(0xFF1C1B1F), surface = Color(0xFFFFFBFF), onSurface = Color(0xFF1C1B1F), - surfaceVariant = Color(0xFFF3DDE0), + surfaceVariant = Color(0xFFF9E6F1), // Navigation bar background (ThemePrefWidget) onSurfaceVariant = Color(0xFF524346), surfaceTint = Color(0xFFBB0054), inverseSurface = Color(0xFF313033), inverseOnSurface = Color(0xFFF4F0F4), outline = Color(0xFF847376), + surfaceContainerLowest = Color(0xFFDAC0CD), + surfaceContainerLow = Color(0xFFE8D1DD), + surfaceContainer = Color(0xFFF9E6F1), // Navigation bar background + surfaceContainerHigh = Color(0xFFFCF3F8), + surfaceContainerHighest = Color(0xFFFEF9FC), ) } diff --git a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/NordColorScheme.kt b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/NordColorScheme.kt index d493e2d62..2e89b9ea4 100644 --- a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/NordColorScheme.kt +++ b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/NordColorScheme.kt @@ -17,19 +17,19 @@ internal object NordColorScheme : BaseColorScheme() { primaryContainer = Color(0xFF88C0D0), onPrimaryContainer = Color(0xFF2E3440), inversePrimary = Color(0xFF397E91), - secondary = Color(0xFF81A1C1), - onSecondary = Color(0xFF2E3440), - secondaryContainer = Color(0xFF81A1C1), - onSecondaryContainer = Color(0xFF2E3440), - tertiary = Color(0xFF5E81AC), - onTertiary = Color(0xFF000000), + secondary = Color(0xFF81A1C1), // Unread badge + onSecondary = Color(0xFF2E3440), // Unread badge text + secondaryContainer = Color(0xFF81A1C1), // Navigation bar selector pill & progress indicator (remaining) + onSecondaryContainer = Color(0xFF2E3440), // Navigation bar selector icon + tertiary = Color(0xFF5E81AC), // Downloaded badge + onTertiary = Color(0xFF000000), // Downloaded badge text tertiaryContainer = Color(0xFF5E81AC), onTertiaryContainer = Color(0xFF000000), background = Color(0xFF2E3440), onBackground = Color(0xFFECEFF4), - surface = Color(0xFF3B4252), + surface = Color(0xFF2E3440), onSurface = Color(0xFFECEFF4), - surfaceVariant = Color(0xFF2E3440), + surfaceVariant = Color(0xFF414C5C), // Navigation bar background (ThemePrefWidget) onSurfaceVariant = Color(0xFFECEFF4), surfaceTint = Color(0xFF88C0D0), inverseSurface = Color(0xFFD8DEE9), @@ -39,6 +39,11 @@ internal object NordColorScheme : BaseColorScheme() { onError = Color(0xFF2E3440), errorContainer = Color(0xFFBF616A), onErrorContainer = Color(0xFF000000), + surfaceContainerLowest = Color(0xFF373F4D), + surfaceContainerLow = Color(0xFF3E4756), + surfaceContainer = Color(0xFF414C5C), + surfaceContainerHigh = Color(0xFF4E5766), + surfaceContainerHighest = Color(0xFF505968), // Navigation bar background ) override val lightScheme = lightColorScheme( @@ -47,19 +52,19 @@ internal object NordColorScheme : BaseColorScheme() { primaryContainer = Color(0xFF5E81AC), onPrimaryContainer = Color(0xFF000000), inversePrimary = Color(0xFF8CA8CD), - secondary = Color(0xFF81A1C1), - onSecondary = Color(0xFF2E3440), - secondaryContainer = Color(0xFF81A1C1), - onSecondaryContainer = Color(0xFF2E3440), - tertiary = Color(0xFF88C0D0), - onTertiary = Color(0xFF2E3440), + secondary = Color(0xFF81A1C1), // Unread badge + onSecondary = Color(0xFF2E3440), // Unread badge text + secondaryContainer = Color(0xFF81A1C1), // Navigation bar selector pill & progress indicator (remaining) + onSecondaryContainer = Color(0xFF2E3440), // Navigation bar selector icon + tertiary = Color(0xFF88C0D0), // Downloaded badge + onTertiary = Color(0xFF2E3440), // Downloaded badge text tertiaryContainer = Color(0xFF88C0D0), onTertiaryContainer = Color(0xFF2E3440), background = Color(0xFFECEFF4), onBackground = Color(0xFF2E3440), surface = Color(0xFFE5E9F0), onSurface = Color(0xFF2E3440), - surfaceVariant = Color(0xFFffffff), + surfaceVariant = Color(0xFFDAE0EA), // Navigation bar background (ThemePrefWidget) onSurfaceVariant = Color(0xFF2E3440), surfaceTint = Color(0xFF5E81AC), inverseSurface = Color(0xFF3B4252), @@ -68,5 +73,10 @@ internal object NordColorScheme : BaseColorScheme() { onError = Color(0xFFECEFF4), errorContainer = Color(0xFFBF616A), onErrorContainer = Color(0xFF000000), + surfaceContainerLowest = Color(0xFFD1D7E0), + surfaceContainerLow = Color(0xFFD6DCE6), + surfaceContainer = Color(0xFFDAE0EA), // Navigation bar background + surfaceContainerHigh = Color(0xFFE9EDF3), + surfaceContainerHighest = Color(0xFFF2F4F8), ) } diff --git a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/StrawberryColorScheme.kt b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/StrawberryColorScheme.kt index 98417e336..e1b096e25 100644 --- a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/StrawberryColorScheme.kt +++ b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/StrawberryColorScheme.kt @@ -18,54 +18,78 @@ import androidx.compose.ui.graphics.Color internal object StrawberryColorScheme : BaseColorScheme() { override val darkScheme = darkColorScheme( - primary = Color(0xFFFFB2B9), - onPrimary = Color(0xFF67001B), - primaryContainer = Color(0xFF91002A), - onPrimaryContainer = Color(0xFFFFDADD), - inversePrimary = Color(0xFFB61E40), - secondary = Color(0xFFFFB2B9), - onSecondary = Color(0xFF67001B), - secondaryContainer = Color(0xFF91002A), - onSecondaryContainer = Color(0xFFFFDADD), - tertiary = Color(0xFFE8C08E), - onTertiary = Color(0xFF432C06), - tertiaryContainer = Color(0xFF5D421B), - onTertiaryContainer = Color(0xFFFFDDB1), + primary = Color(0xFFFFB2B8), + onPrimary = Color(0xFF67001D), + primaryContainer = Color(0xFFD53855), + onPrimaryContainer = Color(0xFFFFFFFF), + secondary = Color(0xFFED4A65), // Unread badge + onSecondary = Color(0xFF201A1A), // Unread badge text + secondaryContainer = Color(0xFF91002A), // Navigation bar selector pill & progress indicator (remaining) + onSecondaryContainer = Color(0xFFFFFFFF), // Navigation bar selector icon + tertiary = Color(0xFFE8C08E), // Downloaded badge + onTertiary = Color(0xFF201A1A), // Downloaded badge text + tertiaryContainer = Color(0xFF775930), + onTertiaryContainer = Color(0xFFFFF7F1), + error = Color(0xFFFFB4AB), + onError = Color(0xFF690005), + errorContainer = Color(0xFF93000A), + onErrorContainer = Color(0xFFFFDAD6), background = Color(0xFF201A1A), - onBackground = Color(0xFFECDFDF), + onBackground = Color(0xFFF7DCDD), surface = Color(0xFF201A1A), - onSurface = Color(0xFFECDFDF), - surfaceVariant = Color(0xFF534344), - onSurfaceVariant = Color(0xFFD7C1C2), - surfaceTint = Color(0xFFFFB2B9), - inverseSurface = Color(0xFFECDFDF), - inverseOnSurface = Color(0xFF201A1A), - outline = Color(0xFFA08C8D), + onSurface = Color(0xFFF7DCDD), + surfaceVariant = Color(0xFF322727), // Navigation bar background (ThemePrefWidget) + onSurfaceVariant = Color(0xFFE1BEC0), + outline = Color(0xFFA9898B), + outlineVariant = Color(0xFF594042), + scrim = Color(0xFF000000), + inverseSurface = Color(0xFFF7DCDD), + inverseOnSurface = Color(0xFF3D2C2D), + inversePrimary = Color(0xFFB61F40), + surfaceDim = Color(0xFF1D1011), + surfaceBright = Color(0xFF463536), + surfaceContainerLowest = Color(0xFF2C2222), + surfaceContainerLow = Color(0xFF302525), + surfaceContainer = Color(0xFF322727), // Navigation bar background + surfaceContainerHigh = Color(0xFF3C2F2F), + surfaceContainerHighest = Color(0xFF463737), ) override val lightScheme = lightColorScheme( - primary = Color(0xFFB61E40), + primary = Color(0xFFA10833), onPrimary = Color(0xFFFFFFFF), - primaryContainer = Color(0xFFFFDADD), - onPrimaryContainer = Color(0xFF40000D), - inversePrimary = Color(0xFFFFB2B9), - secondary = Color(0xFFB61E40), - onSecondary = Color(0xFFFFFFFF), - secondaryContainer = Color(0xFFFFDADD), - onSecondaryContainer = Color(0xFF40000D), - tertiary = Color(0xFF775930), - onTertiary = Color(0xFFFFFFFF), - tertiaryContainer = Color(0xFFFFDDB1), - onTertiaryContainer = Color(0xFF2A1800), - background = Color(0xFFFCFCFC), - onBackground = Color(0xFF201A1A), - surface = Color(0xFFFCFCFC), - onSurface = Color(0xFF201A1A), - surfaceVariant = Color(0xFFF4DDDD), - onSurfaceVariant = Color(0xFF534344), - surfaceTint = Color(0xFFB61E40), - inverseSurface = Color(0xFF362F2F), - inverseOnSurface = Color(0xFFFBEDED), - outline = Color(0xFF857374), + primaryContainer = Color(0xFFD53855), + onPrimaryContainer = Color(0xFFFFFFFF), + secondary = Color(0xFFA10833), // Unread badge + onSecondary = Color(0xFFFFFFFF), // Unread badge text + secondaryContainer = Color(0xFFD53855), // Navigation bar selector pill & progress indicator (remaining) + onSecondaryContainer = Color(0xFFF6EAED), // Navigation bar selector icon + tertiary = Color(0xFF5F441D), // Downloaded badge + onTertiary = Color(0xFFFFFFFF), // Downloaded badge text + tertiaryContainer = Color(0xFF87683D), + onTertiaryContainer = Color(0xFFFFFFFF), + error = Color(0xFFBA1A1A), + onError = Color(0xFFFFFFFF), + errorContainer = Color(0xFFFFDAD6), + onErrorContainer = Color(0xFF410002), + background = Color(0xFFFAFAFA), + onBackground = Color(0xFF261819), + surface = Color(0xFFFAFAFA), + onSurface = Color(0xFF261819), + surfaceVariant = Color(0xFFF6EAED), // Navigation bar background (ThemePrefWidget) + onSurfaceVariant = Color(0xFF594042), + outline = Color(0xFF8D7071), + outlineVariant = Color(0xFFE1BEC0), + scrim = Color(0xFF000000), + inverseSurface = Color(0xFF3D2C2D), + inverseOnSurface = Color(0xFFFFECED), + inversePrimary = Color(0xFFFFB2B8), + surfaceDim = Color(0xFFEED4D5), + surfaceBright = Color(0xFFFFF8F7), + surfaceContainerLowest = Color(0xFFF7DCDD), + surfaceContainerLow = Color(0xFFFDE2E3), + surfaceContainer = Color(0xFFF6EAED), // Navigation bar background + surfaceContainerHigh = Color(0xFFFFF0F0), + surfaceContainerHighest = Color(0xFFFFFFFF), ) } diff --git a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TachiyomiColorScheme.kt b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TachiyomiColorScheme.kt index 974d5f22d..5faeb7df0 100644 --- a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TachiyomiColorScheme.kt +++ b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TachiyomiColorScheme.kt @@ -22,19 +22,19 @@ internal object TachiyomiColorScheme : BaseColorScheme() { primaryContainer = Color(0xFF00429B), onPrimaryContainer = Color(0xFFD9E2FF), inversePrimary = Color(0xFF0058CA), - secondary = Color(0xFFB0C6FF), - onSecondary = Color(0xFF002D6E), - secondaryContainer = Color(0xFF00429B), - onSecondaryContainer = Color(0xFFD9E2FF), - tertiary = Color(0xFF7ADC77), - onTertiary = Color(0xFF003909), + secondary = Color(0xFFB0C6FF), // Unread badge + onSecondary = Color(0xFF002D6E), // Unread badge text + secondaryContainer = Color(0xFF00429B), // Navigation bar selector pill & pro + onSecondaryContainer = Color(0xFFD9E2FF), // Navigation bar selector icon + tertiary = Color(0xFF7ADC77), // Downloaded badge + onTertiary = Color(0xFF003909), // Downloaded badge text tertiaryContainer = Color(0xFF005312), onTertiaryContainer = Color(0xFF95F990), background = Color(0xFF1B1B1F), onBackground = Color(0xFFE3E2E6), surface = Color(0xFF1B1B1F), onSurface = Color(0xFFE3E2E6), - surfaceVariant = Color(0xFF44464F), + surfaceVariant = Color(0xFF211F26), // Navigation bar background (ThemePrefWidget) onSurfaceVariant = Color(0xFFC5C6D0), surfaceTint = Color(0xFFB0C6FF), inverseSurface = Color(0xFFE3E2E6), @@ -45,6 +45,11 @@ internal object TachiyomiColorScheme : BaseColorScheme() { onErrorContainer = Color(0xFFFFDAD6), outline = Color(0xFF8F9099), outlineVariant = Color(0xFF44464F), + surfaceContainerLowest = Color(0xFF1A181D), + surfaceContainerLow = Color(0xFF1E1C22), + surfaceContainer = Color(0xFF211F26), // Navigation bar background + surfaceContainerHigh = Color(0xFF292730), + surfaceContainerHighest = Color(0xFF302E38), ) override val lightScheme = lightColorScheme( @@ -53,19 +58,19 @@ internal object TachiyomiColorScheme : BaseColorScheme() { primaryContainer = Color(0xFFD9E2FF), onPrimaryContainer = Color(0xFF001945), inversePrimary = Color(0xFFB0C6FF), - secondary = Color(0xFF0058CA), - onSecondary = Color(0xFFFFFFFF), - secondaryContainer = Color(0xFFD9E2FF), - onSecondaryContainer = Color(0xFF001945), - tertiary = Color(0xFF006E1B), - onTertiary = Color(0xFFFFFFFF), + secondary = Color(0xFF0058CA), // Unread badge + onSecondary = Color(0xFFFFFFFF), // Unread badge text + secondaryContainer = Color(0xFFD9E2FF), // Navigation bar selector pill & progress indicator (remaining) + onSecondaryContainer = Color(0xFF001945), // Navigation bar selector icon + tertiary = Color(0xFF006E1B), // Downloaded badge + onTertiary = Color(0xFFFFFFFF), // Downloaded badge text tertiaryContainer = Color(0xFF95F990), onTertiaryContainer = Color(0xFF002203), background = Color(0xFFFEFBFF), onBackground = Color(0xFF1B1B1F), surface = Color(0xFFFEFBFF), onSurface = Color(0xFF1B1B1F), - surfaceVariant = Color(0xFFE1E2EC), + surfaceVariant = Color(0xFFF3EDF7), // Navigation bar background (ThemePrefWidget) onSurfaceVariant = Color(0xFF44464F), surfaceTint = Color(0xFF0058CA), inverseSurface = Color(0xFF303034), @@ -76,5 +81,10 @@ internal object TachiyomiColorScheme : BaseColorScheme() { onErrorContainer = Color(0xFF410002), outline = Color(0xFF757780), outlineVariant = Color(0xFFC5C6D0), + surfaceContainerLowest = Color(0xFFF5F1F8), + surfaceContainerLow = Color(0xFFF7F2FA), + surfaceContainer = Color(0xFFF3EDF7), // Navigation bar background + surfaceContainerHigh = Color(0xFFFCF7FF), + surfaceContainerHighest = Color(0xFFFCF7FF), ) } diff --git a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TakoColorScheme.kt b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TakoColorScheme.kt index 244e769d4..798812963 100644 --- a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TakoColorScheme.kt +++ b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TakoColorScheme.kt @@ -23,24 +23,29 @@ internal object TakoColorScheme : BaseColorScheme() { primaryContainer = Color(0xFFF3B375), onPrimaryContainer = Color(0xFF38294E), inversePrimary = Color(0xFF84531E), - secondary = Color(0xFFF3B375), - onSecondary = Color(0xFF38294E), - secondaryContainer = Color(0xFFF3B375), - onSecondaryContainer = Color(0xFF38294E), - tertiary = Color(0xFF66577E), - onTertiary = Color(0xFFF3B375), + secondary = Color(0xFFF3B375), // Unread badge + onSecondary = Color(0xFF38294E), // Unread badge text + secondaryContainer = Color(0xFF5C4D4B), // Navigation bar selector pill & progress indicator (remaining) + onSecondaryContainer = Color(0xFFF3B375), // Navigation bar selector icon + tertiary = Color(0xFF66577E), // Downloaded badge + onTertiary = Color(0xFFF3B375), // Downloaded badge text tertiaryContainer = Color(0xFF4E4065), onTertiaryContainer = Color(0xFFEDDCFF), background = Color(0xFF21212E), onBackground = Color(0xFFE3E0F2), surface = Color(0xFF21212E), onSurface = Color(0xFFE3E0F2), - surfaceVariant = Color(0xFF49454E), + surfaceVariant = Color(0xFF2A2A3C), // Navigation bar background (ThemePrefWidget) onSurfaceVariant = Color(0xFFCBC4CE), surfaceTint = Color(0xFF66577E), inverseSurface = Color(0xFFE5E1E6), inverseOnSurface = Color(0xFF1B1B1E), outline = Color(0xFF958F99), + surfaceContainerLowest = Color(0xFF20202E), + surfaceContainerLow = Color(0xFF262636), + surfaceContainer = Color(0xFF2A2A3C), // Navigation bar background + surfaceContainerHigh = Color(0xFF303044), + surfaceContainerHighest = Color(0xFF36364D), ) override val lightScheme = lightColorScheme( @@ -49,23 +54,28 @@ internal object TakoColorScheme : BaseColorScheme() { primaryContainer = Color(0xFF66577E), onPrimaryContainer = Color(0xFFF3B375), inversePrimary = Color(0xFFD6BAFF), - secondary = Color(0xFF66577E), - onSecondary = Color(0xFFF3B375), - secondaryContainer = Color(0xFF66577E), - onSecondaryContainer = Color(0xFFF3B375), - tertiary = Color(0xFFF3B375), - onTertiary = Color(0xFF574360), + secondary = Color(0xFF66577E), // Unread badge + onSecondary = Color(0xFFF3B375), // Unread badge text + secondaryContainer = Color(0xFFC8BED0), // Navigation bar selector pill & progress indicator (remaining) + onSecondaryContainer = Color(0xFF66577E), // Navigation bar selector icon + tertiary = Color(0xFFF3B375), // Downloaded badge + onTertiary = Color(0xFF574360), // Downloaded badge text tertiaryContainer = Color(0xFFFDD6B0), onTertiaryContainer = Color(0xFF221437), background = Color(0xFFF7F5FF), onBackground = Color(0xFF1B1B22), surface = Color(0xFFF7F5FF), onSurface = Color(0xFF1B1B22), - surfaceVariant = Color(0xFFE8E0EB), + surfaceVariant = Color(0xFFE8E0EB), // Navigation bar background (ThemePrefWidget) onSurfaceVariant = Color(0xFF49454E), surfaceTint = Color(0xFF66577E), inverseSurface = Color(0xFF313033), inverseOnSurface = Color(0xFFF3EFF4), outline = Color(0xFF7A757E), + surfaceContainerLowest = Color(0xFFD7D0DA), + surfaceContainerLow = Color(0xFFDFD8E2), + surfaceContainer = Color(0xFFE8E0EB), // Navigation bar background + surfaceContainerHigh = Color(0xFFEEE6F1), + surfaceContainerHighest = Color(0xFFF7EEFA), ) } diff --git a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TealTurqoiseColorScheme.kt b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TealTurqoiseColorScheme.kt index e914b49fc..28811fa6a 100644 --- a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TealTurqoiseColorScheme.kt +++ b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TealTurqoiseColorScheme.kt @@ -15,24 +15,29 @@ internal object TealTurqoiseColorScheme : BaseColorScheme() { primaryContainer = Color(0xFF40E0D0), onPrimaryContainer = Color(0xFF000000), inversePrimary = Color(0xFF008080), - secondary = Color(0xFF40E0D0), - onSecondary = Color(0xFF000000), - secondaryContainer = Color(0xFF18544E), - onSecondaryContainer = Color(0xFF40E0D0), - tertiary = Color(0xFFBF1F2F), - onTertiary = Color(0xFFFFFFFF), + secondary = Color(0xFF40E0D0), // Unread badge + onSecondary = Color(0xFF000000), // Unread badge text + secondaryContainer = Color(0xFF18544E), // Navigation bar selector pill & progress indicator (remaining) + onSecondaryContainer = Color(0xFF40E0D0), // Navigation bar selector icon + tertiary = Color(0xFFBF1F2F), // Downloaded badge + onTertiary = Color(0xFFFFFFFF), // Downloaded badge text tertiaryContainer = Color(0xFF200508), onTertiaryContainer = Color(0xFFBF1F2F), background = Color(0xFF202125), onBackground = Color(0xFFDFDEDA), surface = Color(0xFF202125), onSurface = Color(0xFFDFDEDA), - surfaceVariant = Color(0xFF3F4947), + surfaceVariant = Color(0xFF233133), // Navigation bar background (ThemePrefWidget) onSurfaceVariant = Color(0xFFDFDEDA), surfaceTint = Color(0xFF40E0D0), inverseSurface = Color(0xFFDFDEDA), inverseOnSurface = Color(0xFF202125), outline = Color(0xFF899391), + surfaceContainerLowest = Color(0xFF202C2E), + surfaceContainerLow = Color(0xFF222F31), + surfaceContainer = Color(0xFF233133), // Navigation bar background + surfaceContainerHigh = Color(0xFF28383A), + surfaceContainerHighest = Color(0xFF2F4244), ) override val lightScheme = lightColorScheme( @@ -41,23 +46,28 @@ internal object TealTurqoiseColorScheme : BaseColorScheme() { primaryContainer = Color(0xFF008080), onPrimaryContainer = Color(0xFFFFFFFF), inversePrimary = Color(0xFF40E0D0), - secondary = Color(0xFF008080), - onSecondary = Color(0xFFFFFFFF), - secondaryContainer = Color(0xFFBFDFDF), - onSecondaryContainer = Color(0xFF008080), - tertiary = Color(0xFFFF7F7F), - onTertiary = Color(0xFF000000), + secondary = Color(0xFF008080), // Unread badge text + onSecondary = Color(0xFFFFFFFF), // Unread badge text + secondaryContainer = Color(0xFFCFE5E4), // Navigation bar selector pill & progress indicator (remaining) + onSecondaryContainer = Color(0xFF008080), // Navigation bar selector icon + tertiary = Color(0xFFFF7F7F), // Downloaded badge + onTertiary = Color(0xFF000000), // Downloaded badge text tertiaryContainer = Color(0xFF2A1616), onTertiaryContainer = Color(0xFFFF7F7F), background = Color(0xFFFAFAFA), onBackground = Color(0xFF050505), surface = Color(0xFFFAFAFA), onSurface = Color(0xFF050505), - surfaceVariant = Color(0xFFDAE5E2), + surfaceVariant = Color(0xFFEBF3F1), // Navigation bar background (ThemePrefWidget) onSurfaceVariant = Color(0xFF050505), surfaceTint = Color(0xFFBFDFDF), inverseSurface = Color(0xFF050505), inverseOnSurface = Color(0xFFFAFAFA), outline = Color(0xFF6F7977), + surfaceContainerLowest = Color(0xFFE1E9E7), + surfaceContainerLow = Color(0xFFE6EEEC), + surfaceContainer = Color(0xFFEBF3F1), // Navigation bar background + surfaceContainerHigh = Color(0xFFF0F8F6), + surfaceContainerHighest = Color(0xFFF7FFFD), ) } diff --git a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TidalWaveColorScheme.kt b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TidalWaveColorScheme.kt index c56a1fa57..09dc248c0 100644 --- a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TidalWaveColorScheme.kt +++ b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/TidalWaveColorScheme.kt @@ -22,24 +22,29 @@ internal object TidalWaveColorScheme : BaseColorScheme() { primaryContainer = Color(0xFF004d61), onPrimaryContainer = Color(0xFFb8eaff), inversePrimary = Color(0xFFa12b03), - secondary = Color(0xFF5ed4fc), - onSecondary = Color(0xFF003544), - secondaryContainer = Color(0xFF004d61), - onSecondaryContainer = Color(0xFFb8eaff), - tertiary = Color(0xFF92f7bc), - onTertiary = Color(0xFF001c3b), + secondary = Color(0xFF5ed4fc), // Unread badge + onSecondary = Color(0xFF003544), // Unread badge text + secondaryContainer = Color(0xFF004d61), // Navigation bar selector pill & progress indicator (remaining) + onSecondaryContainer = Color(0xFFb8eaff), // Navigation bar selector icon + tertiary = Color(0xFF92f7bc), // Downloaded badge + onTertiary = Color(0xFF001c3b), // Downloaded badge text tertiaryContainer = Color(0xFFc3fada), onTertiaryContainer = Color(0xFF78ffd6), background = Color(0xFF001c3b), onBackground = Color(0xFFd5e3ff), surface = Color(0xFF001c3b), onSurface = Color(0xFFd5e3ff), - surfaceVariant = Color(0xFF40484c), + surfaceVariant = Color(0xFF082b4b), // Navigation bar background (ThemePrefWidget) onSurfaceVariant = Color(0xFFbfc8cc), surfaceTint = Color(0xFF5ed4fc), inverseSurface = Color(0xFFffe3c4), inverseOnSurface = Color(0xFF001c3b), outline = Color(0xFF8a9296), + surfaceContainerLowest = Color(0xFF072642), + surfaceContainerLow = Color(0xFF072947), + surfaceContainer = Color(0xFF082b4b), // Navigation bar background + surfaceContainerHigh = Color(0xFF093257), + surfaceContainerHighest = Color(0xFF0A3861), ) override val lightScheme = lightColorScheme( @@ -48,23 +53,28 @@ internal object TidalWaveColorScheme : BaseColorScheme() { primaryContainer = Color(0xFFB4D4DF), onPrimaryContainer = Color(0xFF001f28), inversePrimary = Color(0xFFff987f), - secondary = Color(0xFF006780), - onSecondary = Color(0xFFffffff), - secondaryContainer = Color(0xFFb8eaff), - onSecondaryContainer = Color(0xFF001f28), - tertiary = Color(0xFF92f7bc), - onTertiary = Color(0xFF001c3b), + secondary = Color(0xFF006780), // Unread badge + onSecondary = Color(0xFFffffff), // Unread badge text + secondaryContainer = Color(0xFF9AE1FF), // Navigation bar selector pill & progress indicator (remaining) + onSecondaryContainer = Color(0xFF001f28), // Navigation bar selector icon + tertiary = Color(0xFF92f7bc), // Downloaded badge + onTertiary = Color(0xFF001c3b), // Downloaded badge text tertiaryContainer = Color(0xFFc3fada), onTertiaryContainer = Color(0xFF78ffd6), background = Color(0xFFfdfbff), onBackground = Color(0xFF001c3b), surface = Color(0xFFfdfbff), onSurface = Color(0xFF001c3b), - surfaceVariant = Color(0xFFdce4e8), + surfaceVariant = Color(0xFFe8eff5), // Navigation bar background (ThemePrefWidget) onSurfaceVariant = Color(0xFF40484c), surfaceTint = Color(0xFF006780), inverseSurface = Color(0xFF020400), inverseOnSurface = Color(0xFFffe3c4), outline = Color(0xFF70787c), + surfaceContainerLowest = Color(0xFFe2e8ec), + surfaceContainerLow = Color(0xFFe5ecf1), + surfaceContainer = Color(0xFFe8eff5), // Navigation bar background + surfaceContainerHigh = Color(0xFFedf4fA), + surfaceContainerHighest = Color(0xFFf5faff), ) } diff --git a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/YinYangColorScheme.kt b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/YinYangColorScheme.kt index da9dee424..1e9b12978 100644 --- a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/YinYangColorScheme.kt +++ b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/YinYangColorScheme.kt @@ -17,24 +17,29 @@ internal object YinYangColorScheme : BaseColorScheme() { primaryContainer = Color(0xFFFFFFFF), onPrimaryContainer = Color(0xFF000000), inversePrimary = Color(0xFFCECECE), - secondary = Color(0xFFFFFFFF), - onSecondary = Color(0xFF5A5A5A), - secondaryContainer = Color(0xFF717171), - onSecondaryContainer = Color(0xFFE4E4E4), - tertiary = Color(0xFF000000), - onTertiary = Color(0xFFFFFFFF), + secondary = Color(0xFFFFFFFF), // Unread badge + onSecondary = Color(0xFF5A5A5A), // Unread badge text + secondaryContainer = Color(0xFF717171), // Navigation bar selector pill & progress indicator (remaining) + onSecondaryContainer = Color(0xFFE4E4E4), // Navigation bar selector icon + tertiary = Color(0xFF000000), // Downloaded badge + onTertiary = Color(0xFFFFFFFF), // Downloaded badge text tertiaryContainer = Color(0xFF00419E), onTertiaryContainer = Color(0xFFD8E2FF), background = Color(0xFF1E1E1E), onBackground = Color(0xFFE6E6E6), surface = Color(0xFF1E1E1E), onSurface = Color(0xFFE6E6E6), - surfaceVariant = Color(0xFF4E4E4E), + surfaceVariant = Color(0xFF313131), // Navigation bar background (ThemePrefWidget) onSurfaceVariant = Color(0xFFD1D1D1), surfaceTint = Color(0xFFFFFFFF), inverseSurface = Color(0xFFE6E6E6), inverseOnSurface = Color(0xFF1E1E1E), outline = Color(0xFF999999), + surfaceContainerLowest = Color(0xFF2A2A2A), + surfaceContainerLow = Color(0xFF2D2D2D), + surfaceContainer = Color(0xFF313131), // Navigation bar background + surfaceContainerHigh = Color(0xFF383838), + surfaceContainerHighest = Color(0xFF3F3F3F), ) override val lightScheme = lightColorScheme( @@ -43,23 +48,28 @@ internal object YinYangColorScheme : BaseColorScheme() { primaryContainer = Color(0xFF000000), onPrimaryContainer = Color(0xFFFFFFFF), inversePrimary = Color(0xFFA6A6A6), - secondary = Color(0xFF000000), - onSecondary = Color(0xFFFFFFFF), - secondaryContainer = Color(0xFFDDDDDD), - onSecondaryContainer = Color(0xFF0C0C0C), - tertiary = Color(0xFFFFFFFF), - onTertiary = Color(0xFF000000), + secondary = Color(0xFF000000), // Unread badge + onSecondary = Color(0xFFFFFFFF), // Unread badge text + secondaryContainer = Color(0xFFDDDDDD), // Navigation bar selector pill & progress indicator (remaining) + onSecondaryContainer = Color(0xFF0C0C0C), // Navigation bar selector icon + tertiary = Color(0xFFFFFFFF), // Downloaded badge + onTertiary = Color(0xFF000000), // Downloaded badge text tertiaryContainer = Color(0xFFD8E2FF), onTertiaryContainer = Color(0xFF001947), background = Color(0xFFFDFDFD), onBackground = Color(0xFF222222), surface = Color(0xFFFDFDFD), onSurface = Color(0xFF222222), - surfaceVariant = Color(0xFFEDEDED), + surfaceVariant = Color(0xFFE8E8E8), // Navigation bar background (ThemePrefWidget) onSurfaceVariant = Color(0xFF515151), surfaceTint = Color(0xFF000000), inverseSurface = Color(0xFF333333), inverseOnSurface = Color(0xFFF4F4F4), outline = Color(0xFF838383), + surfaceContainerLowest = Color(0xFFCFCFCF), + surfaceContainerLow = Color(0xFFDADADA), + surfaceContainer = Color(0xFFE8E8E8), // Navigation bar background + surfaceContainerHigh = Color(0xFFECECEC), + surfaceContainerHighest = Color(0xFFEFEFEF), ) } diff --git a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/YotsubaColorScheme.kt b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/YotsubaColorScheme.kt index fdda6b7dc..58007a680 100644 --- a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/YotsubaColorScheme.kt +++ b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/YotsubaColorScheme.kt @@ -23,24 +23,29 @@ internal object YotsubaColorScheme : BaseColorScheme() { primaryContainer = Color(0xFF862200), onPrimaryContainer = Color(0xFFFFDBCF), inversePrimary = Color(0xFFAE3200), - secondary = Color(0xFFFFB59D), - onSecondary = Color(0xFF5F1600), - secondaryContainer = Color(0xFF862200), - onSecondaryContainer = Color(0xFFFFDBCF), - tertiary = Color(0xFFD7C68D), - onTertiary = Color(0xFF3A2F05), + secondary = Color(0xFFFFB59D), // Unread badge + onSecondary = Color(0xFF5F1600), // Unread badge text + secondaryContainer = Color(0xFF862200), // Navigation bar selector pill & progress indicator (remaining) + onSecondaryContainer = Color(0xFFFFDBCF), // Navigation bar selector icon + tertiary = Color(0xFFD7C68D), // Downloaded badge + onTertiary = Color(0xFF3A2F05), // Downloaded badge text tertiaryContainer = Color(0xFF524619), onTertiaryContainer = Color(0xFFF5E2A7), background = Color(0xFF211A18), onBackground = Color(0xFFEDE0DD), surface = Color(0xFF211A18), onSurface = Color(0xFFEDE0DD), - surfaceVariant = Color(0xFF53433F), + surfaceVariant = Color(0xFF332723), // Navigation bar background (ThemePrefWidget) onSurfaceVariant = Color(0xFFD8C2BC), surfaceTint = Color(0xFFFFB59D), inverseSurface = Color(0xFFEDE0DD), inverseOnSurface = Color(0xFF211A18), outline = Color(0xFFA08C87), + surfaceContainerLowest = Color(0xFF2E221F), + surfaceContainerLow = Color(0xFF312521), + surfaceContainer = Color(0xFF332723), // Navigation bar background + surfaceContainerHigh = Color(0xFF413531), + surfaceContainerHighest = Color(0xFF4C403D), ) override val lightScheme = lightColorScheme( @@ -49,23 +54,28 @@ internal object YotsubaColorScheme : BaseColorScheme() { primaryContainer = Color(0xFFFFDBCF), onPrimaryContainer = Color(0xFF3B0A00), inversePrimary = Color(0xFFFFB59D), - secondary = Color(0xFFAE3200), - onSecondary = Color(0xFFFFFFFF), - secondaryContainer = Color(0xFFFFDBCF), - onSecondaryContainer = Color(0xFF3B0A00), - tertiary = Color(0xFF6B5E2F), - onTertiary = Color(0xFFFFFFFF), + secondary = Color(0xFFAE3200), // Unread badge + onSecondary = Color(0xFFFFFFFF), // Unread badge text + secondaryContainer = Color(0xFFEBCDC2), // Navigation bar selector pill & progress indicator (remaining) + onSecondaryContainer = Color(0xFF3B0A00), // Navigation bar selector icon + tertiary = Color(0xFF6B5E2F), // Downloaded badge + onTertiary = Color(0xFFFFFFFF), // Downloaded badge text tertiaryContainer = Color(0xFFF5E2A7), onTertiaryContainer = Color(0xFF231B00), background = Color(0xFFFCFCFC), onBackground = Color(0xFF211A18), surface = Color(0xFFFCFCFC), onSurface = Color(0xFF211A18), - surfaceVariant = Color(0xFFF5DED8), + surfaceVariant = Color(0xFFF6EBE7), // Navigation bar background (ThemePrefWidget) onSurfaceVariant = Color(0xFF53433F), surfaceTint = Color(0xFFAE3200), inverseSurface = Color(0xFF362F2D), inverseOnSurface = Color(0xFFFBEEEB), outline = Color(0xFF85736E), + surfaceContainerLowest = Color(0xFFECE3E0), + surfaceContainerLow = Color(0xFFF1E7E4), + surfaceContainer = Color(0xFFF6EBE7), // Navigation bar background + surfaceContainerHigh = Color(0xFFFAF4F2), + surfaceContainerHighest = Color(0xFFFBF6F4), ) } From 0392ef0d1878be01633b8119cbc8d1b263737307 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Fri, 17 May 2024 16:06:23 +0600 Subject: [PATCH 055/146] Update renovate config --- .github/renovate.json5 | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 751500ef3..0c0785f22 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -1,25 +1,20 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:base" - ], - "schedule": ["every friday"], + "extends": ["config:base"], "labels": ["Dependencies"], "packageRules": [ { - "groupName": "Compose BOM", + "groupName": "Compose BOM (Alpha)", "matchPackageNames": [ "dev.chrisbanes.compose:compose-bom" ], "ignoreUnstable": false }, { - // Compiler plugins are tightly coupled to Kotlin version - "groupName": "Kotlin", + "groupName": "Kotlin and Compose Compiler", "matchPackagePrefixes": [ "androidx.compose.compiler", "org.jetbrains.kotlin.", - "org.jetbrains.kotlin:" ], } ] From 84ea5166dee476db771e8b5b38839a34a80de976 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 17 May 2024 16:09:21 +0600 Subject: [PATCH 056/146] chore(deps): update actions/checkout action to v4.1.6 (#796) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- .github/workflows/build_push.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index d9942cf0a..06dd2e7e8 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Clone repo - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: Validate Gradle Wrapper uses: gradle/wrapper-validation-action@216d1ad2b3710bf005dc39237337b9673fd8fcd5 # v3.3.2 diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index 246f6e37c..90eee2c09 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -17,7 +17,7 @@ jobs: steps: - name: Clone repo - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: Validate Gradle Wrapper uses: gradle/wrapper-validation-action@216d1ad2b3710bf005dc39237337b9673fd8fcd5 # v3.3.2 From ce497003e38d838e846b6e4ce88aafe4610d522b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 17 May 2024 16:09:36 +0600 Subject: [PATCH 057/146] fix(deps): update dependency androidx.test.espresso:espresso-core to v3.6.0-beta01 (#797) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 247035a9f..8341cfdaf 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -27,7 +27,7 @@ paging-compose = { module = "androidx.paging:paging-compose", version.ref = "pag benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.4" test-ext = "androidx.test.ext:junit-ktx:1.2.0-alpha04" -test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-alpha04" +test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-beta01" test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0" [bundles] From fffa6a462d9c2c8473d4fdabad775f0c5849775b Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 18 May 2024 23:00:17 +0600 Subject: [PATCH 058/146] Fix renovate config --- .github/renovate.json5 | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 0c0785f22..cc1468ce2 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -15,6 +15,7 @@ "matchPackagePrefixes": [ "androidx.compose.compiler", "org.jetbrains.kotlin.", + "org.jetbrains.kotlin:", ], } ] From 1dca9363a46aa310623d057113c68de89c31ee43 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 18 May 2024 23:02:46 +0600 Subject: [PATCH 059/146] fix(deps): update dependency dev.chrisbanes.compose:compose-bom to v2024.05.00-alpha02 (#802) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/compose.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 77a87adf1..cc57c27ff 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,6 +1,6 @@ [versions] compiler = "1.5.12" -compose-bom = "2024.05.00-alpha01" +compose-bom = "2024.05.00-alpha02" accompanist = "0.35.0-alpha" [libraries] From 653d5d3e252ff2508b100b0f291fdcfaf2a98097 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 18 May 2024 23:03:05 +0600 Subject: [PATCH 060/146] fix(deps): update dependency com.google.android.material:material to v1.12.0 (#754) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1c287986e..87e1f3e03 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -57,7 +57,7 @@ natural-comparator = "com.github.gpanther:java-nat-sort:natural-comparator-1.1" richtext-commonmark = { module = "com.halilibo.compose-richtext:richtext-commonmark", version.ref = "richtext" } richtext-m3 = { module = "com.halilibo.compose-richtext:richtext-ui-material3", version.ref = "richtext" } -material = "com.google.android.material:material:1.11.0" +material = "com.google.android.material:material:1.12.0" flexible-adapter-core = "com.github.arkon.FlexibleAdapter:flexible-adapter:c8013533" photoview = "com.github.chrisbanes:PhotoView:2.3.0" directionalviewpager = "com.github.tachiyomiorg:DirectionalViewPager:1.0.0" From 8d187f786594356dd47899dad660d602b52099ef Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 18 May 2024 23:03:38 +0600 Subject: [PATCH 061/146] fix(deps): update dependency androidx.test.ext:junit-ktx to v1.2.0-beta01 (#801) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 8341cfdaf..1c901b069 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -26,7 +26,7 @@ paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "pag paging-compose = { module = "androidx.paging:paging-compose", version.ref = "paging_version" } benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.4" -test-ext = "androidx.test.ext:junit-ktx:1.2.0-alpha04" +test-ext = "androidx.test.ext:junit-ktx:1.2.0-beta01" test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-beta01" test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0" From 99b550ae0d9eaf530d644f7755162d9705c302f4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 18 May 2024 23:03:52 +0600 Subject: [PATCH 062/146] fix(deps): update dependency io.mockk:mockk to v1.13.11 (#803) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 87e1f3e03..d4b58651d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -92,7 +92,7 @@ sqldelight-gradle = { module = "app.cash.sqldelight:gradle-plugin", version.ref junit = "org.junit.jupiter:junit-jupiter:5.10.2" kotest-assertions = "io.kotest:kotest-assertions-core:5.9.0" -mockk = "io.mockk:mockk:1.13.10" +mockk = "io.mockk:mockk:1.13.11" voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" } voyager-screenmodel = { module = "cafe.adriel.voyager:voyager-screenmodel", version.ref = "voyager" } From 5912d6b08f3624a385f820582e49f1dde1b55864 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 18 May 2024 23:12:16 +0600 Subject: [PATCH 063/146] fix(deps): update dependency androidx.annotation:annotation to v1.8.0 (#808) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 1c901b069..eb6cdc61e 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -6,7 +6,7 @@ paging_version = "3.2.1" [libraries] gradle = { module = "com.android.tools.build:gradle", version.ref = "agp_version" } -annotation = "androidx.annotation:annotation:1.7.1" +annotation = "androidx.annotation:annotation:1.8.0" appcompat = "androidx.appcompat:appcompat:1.6.1" biometricktx = "androidx.biometric:biometric-ktx:1.2.0-alpha05" constraintlayout = "androidx.constraintlayout:constraintlayout:2.1.4" From d2e5c78074829ca7c7bace3e5bfee25ea430e44c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 18 May 2024 23:12:27 +0600 Subject: [PATCH 064/146] fix(deps): update lifecycle.version to v2.8.0 (#809) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index eb6cdc61e..70768b424 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -1,6 +1,6 @@ [versions] agp_version = "8.4.0" -lifecycle_version = "2.7.0" +lifecycle_version = "2.8.0" paging_version = "3.2.1" [libraries] From 2f243fae11e48799437b1672ec1a8571552caacb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 18 May 2024 23:14:12 +0600 Subject: [PATCH 065/146] chore(deps): update kotlin and compose compiler (#800) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/compose.versions.toml | 2 +- gradle/kotlinx.versions.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index cc57c27ff..3f1d253e1 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,5 +1,5 @@ [versions] -compiler = "1.5.12" +compiler = "1.5.14" compose-bom = "2024.05.00-alpha02" accompanist = "0.35.0-alpha" diff --git a/gradle/kotlinx.versions.toml b/gradle/kotlinx.versions.toml index f86682ec5..66e69752a 100644 --- a/gradle/kotlinx.versions.toml +++ b/gradle/kotlinx.versions.toml @@ -1,5 +1,5 @@ [versions] -kotlin_version = "1.9.23" +kotlin_version = "1.9.24" serialization_version = "1.6.3" xml_serialization_version = "0.86.3" From 0cb1794a445c8c7f4e9c163bd850f1681cdbef6a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 22 May 2024 13:45:53 +0600 Subject: [PATCH 066/146] fix(deps): update dependency com.android.tools.build:gradle to v8.4.1 (#818) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 70768b424..94cb08052 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -1,5 +1,5 @@ [versions] -agp_version = "8.4.0" +agp_version = "8.4.1" lifecycle_version = "2.8.0" paging_version = "3.2.1" From 9b944092c75046b68ac38e2d9d3a5e49033d7588 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 27 May 2024 02:05:26 +0600 Subject: [PATCH 067/146] fix(deps): update aboutlib.version to v11.2.0 (#823) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d4b58651d..0dc2137fd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -aboutlib_version = "11.1.4" +aboutlib_version = "11.2.0" leakcanary = "2.14" moko = "0.23.0" okhttp_version = "5.0.0-alpha.12" From de5a64aa73c8a2229fa87e9baba9981197474f51 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 27 May 2024 02:05:40 +0600 Subject: [PATCH 068/146] fix(deps): update dependency org.apache.commons:commons-compress to v1.26.2 (#826) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0dc2137fd..8cb3ec05f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -32,7 +32,7 @@ jsoup = "org.jsoup:jsoup:1.17.2" disklrucache = "com.jakewharton:disklrucache:2.0.2" unifile = "com.github.tachiyomiorg:unifile:e0def6b3dc" -common-compress = "org.apache.commons:commons-compress:1.26.1" +common-compress = "org.apache.commons:commons-compress:1.26.2" junrar = "com.github.junrar:junrar:7.5.5" sqlite-framework = { module = "androidx.sqlite:sqlite-framework", version.ref = "sqlite" } From af8696cb9060e652f41220ea4a6074568df8bc3d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 27 May 2024 02:05:53 +0600 Subject: [PATCH 069/146] fix(deps): update paging.version to v3.3.0 (#810) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 94cb08052..87533a374 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -1,7 +1,7 @@ [versions] agp_version = "8.4.1" lifecycle_version = "2.8.0" -paging_version = "3.2.1" +paging_version = "3.3.0" [libraries] gradle = { module = "com.android.tools.build:gradle", version.ref = "agp_version" } From b9da98b527e3d9ac56e263f7b595768239fa718e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 May 2024 05:07:08 +0600 Subject: [PATCH 070/146] fix(deps): update lifecycle.version to v2.8.1 (#844) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 87533a374..fd2c6c767 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -1,6 +1,6 @@ [versions] agp_version = "8.4.1" -lifecycle_version = "2.8.0" +lifecycle_version = "2.8.1" paging_version = "3.3.0" [libraries] From 095da924b9a1d724e6a228cb4abcc4dfe948db7d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 May 2024 05:07:22 +0600 Subject: [PATCH 071/146] fix(deps): update dependency androidx.appcompat:appcompat to v1.7.0 (#845) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index fd2c6c767..6154d7ed1 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -7,7 +7,7 @@ paging_version = "3.3.0" gradle = { module = "com.android.tools.build:gradle", version.ref = "agp_version" } annotation = "androidx.annotation:annotation:1.8.0" -appcompat = "androidx.appcompat:appcompat:1.6.1" +appcompat = "androidx.appcompat:appcompat:1.7.0" biometricktx = "androidx.biometric:biometric-ktx:1.2.0-alpha05" constraintlayout = "androidx.constraintlayout:constraintlayout:2.1.4" corektx = "androidx.core:core-ktx:1.13.1" From e567250b17c231abee151541fea1f97b269b516a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 1 Jun 2024 22:02:54 +0600 Subject: [PATCH 072/146] fix(deps): update dependency androidx.test.espresso:espresso-core to v3.6.0-rc01 (#851) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 6154d7ed1..30322329f 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -27,7 +27,7 @@ paging-compose = { module = "androidx.paging:paging-compose", version.ref = "pag benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.4" test-ext = "androidx.test.ext:junit-ktx:1.2.0-beta01" -test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-beta01" +test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-rc01" test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0" [bundles] From 7ab7f5ac37e8c102ca259144f136616b59172e6d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 1 Jun 2024 22:03:03 +0600 Subject: [PATCH 073/146] fix(deps): update dependency com.google.gms:google-services to v4.4.2 (#849) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8cb3ec05f..4c4fe977a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,7 +14,7 @@ detektCompose = "0.3.12" [libraries] desugar = "com.android.tools:desugar_jdk_libs:2.0.4" android-shortcut-gradle = "com.github.zellius:android-shortcut-gradle-plugin:0.1.2" -google-services-gradle = "com.google.gms:google-services:4.4.1" +google-services-gradle = "com.google.gms:google-services:4.4.2" rxjava = "io.reactivex:rxjava:1.3.8" From 1f286f1a357a6c0f8cf2a7274b11b175315de4ea Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 1 Jun 2024 22:03:10 +0600 Subject: [PATCH 074/146] fix(deps): update dependency com.google.firebase:firebase-analytics to v22.0.1 (#848) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4c4fe977a..2962a56ba 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -73,7 +73,7 @@ moko-gradle = { module = "dev.icerock.moko:resources-generator", version.ref = " logcat = "com.squareup.logcat:logcat:0.1" -firebase-analytics = "com.google.firebase:firebase-analytics:22.0.0" +firebase-analytics = "com.google.firebase:firebase-analytics:22.0.1" aboutLibraries-gradle = { module = "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin", version.ref = "aboutlib_version" } aboutLibraries-compose = { module = "com.mikepenz:aboutlibraries-compose-m3", version.ref = "aboutlib_version" } From 9f5db70572cee99243c850d2da55fe0a10d52809 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 1 Jun 2024 22:03:20 +0600 Subject: [PATCH 075/146] fix(deps): update aboutlib.version to v11.2.1 (#846) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2962a56ba..2fe0f076b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -aboutlib_version = "11.2.0" +aboutlib_version = "11.2.1" leakcanary = "2.14" moko = "0.23.0" okhttp_version = "5.0.0-alpha.12" From 098f925519502a99aef9eb33c9b88b1280351a73 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 2 Jun 2024 01:23:31 +0600 Subject: [PATCH 076/146] fix(deps): update dependency androidx.test.ext:junit-ktx to v1.2.0-rc01 (#855) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 30322329f..ff0416cb7 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -26,7 +26,7 @@ paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "pag paging-compose = { module = "androidx.paging:paging-compose", version.ref = "paging_version" } benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.4" -test-ext = "androidx.test.ext:junit-ktx:1.2.0-beta01" +test-ext = "androidx.test.ext:junit-ktx:1.2.0-rc01" test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-rc01" test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0" From 116579d38c08c203fe2c4996419e277c7bf9c165 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 2 Jun 2024 01:23:39 +0600 Subject: [PATCH 077/146] chore(deps): update dependency gradle to v8.8 (#856) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b82aa23a4..a4413138c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a426..b740cf133 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. From 8632ba85ee1ed080d7baa70050d460807c8edfcf Mon Sep 17 00:00:00 2001 From: Sven Date: Sat, 1 Jun 2024 21:25:58 +0200 Subject: [PATCH 078/146] fix: storage permission request for non-conforming devices (#726) * fix: storage permission request for non-conforming devices * fix: catch more specific exception * chore: add toast message to indicate missing persistent permissions * chore: correct newly introduced translaction string * Change error toast message --------- Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> --- .../more/settings/screen/SettingsDataScreen.kt | 12 +++++++++++- i18n/src/commonMain/resources/MR/base/strings.xml | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt index 91de2993f..ecfd2ec75 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt @@ -111,7 +111,17 @@ object SettingsDataScreen : SearchableSettings { val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION - context.contentResolver.takePersistableUriPermission(uri, flags) + // For some reason InkBook devices do not implement the SAF properly. Persistable URI grants do not + // work. However, simply retrieving the URI and using it works fine for these devices. Access is not + // revoked after the app is closed or the device is restarted. + // This also holds for some Samsung devices. Thus, we simply execute inside of a try-catch block and + // ignore the exception if it is thrown. + try { + context.contentResolver.takePersistableUriPermission(uri, flags) + } catch (e: SecurityException) { + logcat(LogPriority.ERROR, e) + context.toast(MR.strings.file_picker_uri_permission_unsupported) + } UniFile.fromUri(context, uri)?.let { storageDirPref.set(it.uri.toString()) diff --git a/i18n/src/commonMain/resources/MR/base/strings.xml b/i18n/src/commonMain/resources/MR/base/strings.xml index bd30e2900..b0a397b1d 100644 --- a/i18n/src/commonMain/resources/MR/base/strings.xml +++ b/i18n/src/commonMain/resources/MR/base/strings.xml @@ -869,6 +869,7 @@ Select cover image Select backup file No file picker app found + Failed to acquire persistent folder access. The app may behave unexpectedly. No file selected From 0870cffba121c0cc7db9c79f83f770a43d9c32e7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 2 Jun 2024 15:20:56 +0600 Subject: [PATCH 079/146] fix(deps): update dependency io.github.fornewid:material-motion-compose-core to v1.2.1 (#858) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2fe0f076b..085533d04 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -62,7 +62,7 @@ flexible-adapter-core = "com.github.arkon.FlexibleAdapter:flexible-adapter:c8013 photoview = "com.github.chrisbanes:PhotoView:2.3.0" directionalviewpager = "com.github.tachiyomiorg:DirectionalViewPager:1.0.0" insetter = "dev.chrisbanes.insetter:insetter:0.6.1" -compose-materialmotion = "io.github.fornewid:material-motion-compose-core:1.2.0" +compose-materialmotion = "io.github.fornewid:material-motion-compose-core:1.2.1" compose-webview = "io.github.kevinnzou:compose-webview:0.33.6" compose-grid = "io.woong.compose.grid:grid:1.2.2" From da62c7a21a81f513988fa64df6253376f85228ef Mon Sep 17 00:00:00 2001 From: "Cuong M. Tran" Date: Fri, 7 Jun 2024 04:35:26 +0700 Subject: [PATCH 080/146] Fix MigratorTest after update to io.mockk v1.13.11 (#814) * Fix MigratorTest after update to io.mockk v1.13.11 Causing error: io.mockk.MockKException: was not can only be called on a mocked object * remove import --- app/src/test/java/mihon/core/migration/MigratorTest.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/src/test/java/mihon/core/migration/MigratorTest.kt b/app/src/test/java/mihon/core/migration/MigratorTest.kt index e0feff3fd..47c4bc7c2 100644 --- a/app/src/test/java/mihon/core/migration/MigratorTest.kt +++ b/app/src/test/java/mihon/core/migration/MigratorTest.kt @@ -1,6 +1,5 @@ package mihon.core.migration -import io.mockk.Called import io.mockk.slot import io.mockk.spyk import io.mockk.verify @@ -59,7 +58,7 @@ class MigratorTest { val result = execute.await() assertFalse(result) - verify { migrationJobFactory.create(any()) wasNot Called } + verify(exactly = 0) { migrationJobFactory.create(any()) } } @Test @@ -72,7 +71,7 @@ class MigratorTest { val result = execute.await() assertFalse(result) - verify { migrationJobFactory.create(any()) wasNot Called } + verify(exactly = 0) { migrationJobFactory.create(any()) } } @Test From 1f7574bd4fc0471b7f974cffdf166c2551b2749b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 7 Jun 2024 03:35:38 +0600 Subject: [PATCH 081/146] fix(deps): update dependency io.kotest:kotest-assertions-core to v5.9.1 (#869) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 085533d04..ad58e9e7b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -91,7 +91,7 @@ sqldelight-dialects-sql = { module = "app.cash.sqldelight:sqlite-3-38-dialect", sqldelight-gradle = { module = "app.cash.sqldelight:gradle-plugin", version.ref = "sqldelight" } junit = "org.junit.jupiter:junit-jupiter:5.10.2" -kotest-assertions = "io.kotest:kotest-assertions-core:5.9.0" +kotest-assertions = "io.kotest:kotest-assertions-core:5.9.1" mockk = "io.mockk:mockk:1.13.11" voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" } From e8fdfaad648ac70ed296e470cac2d07150c239b1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 7 Jun 2024 03:35:50 +0600 Subject: [PATCH 082/146] chore(deps): update actions/dependency-review-action action to v4.3.3 (#867) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 06dd2e7e8..ff697921b 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -26,7 +26,7 @@ jobs: uses: gradle/wrapper-validation-action@216d1ad2b3710bf005dc39237337b9673fd8fcd5 # v3.3.2 - name: Dependency Review - uses: actions/dependency-review-action@0c155c5e8556a497adf53f2c18edabf945ed8e70 # v4.3.2 + uses: actions/dependency-review-action@72eb03d02c7872a771aacd928f3123ac62ad6d3a # v4.3.3 - name: Set up JDK uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 From 46003ec25139319079abc9fde89b3afd344a1a11 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 7 Jun 2024 03:48:35 +0600 Subject: [PATCH 083/146] chore(deps): update kotlin and compose compiler to v2 (major) (#819) * chore(deps): update kotlin and compose compiler to v2 * Update .gitignore * Fix build --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> --- .github/renovate.json5 | 8 --- .gitignore | 1 + app/build.gradle.kts | 2 +- buildSrc/build.gradle.kts | 10 +-- ...hon.android.application.compose.gradle.kts | 1 - .../kotlin/mihon/buildlogic/AndroidConfig.kt | 4 ++ .../mihon/buildlogic/ProjectExtensions.kt | 70 +++++++++---------- data/build.gradle.kts | 2 +- domain/build.gradle.kts | 2 +- gradle/compose.versions.toml | 3 - gradle/kotlinx.versions.toml | 4 +- i18n/build.gradle.kts | 2 +- presentation-core/build.gradle.kts | 2 +- source-api/build.gradle.kts | 2 +- source-local/build.gradle.kts | 2 +- .../tachiyomi/source/local/LocalSource.kt | 13 ++-- 16 files changed, 61 insertions(+), 67 deletions(-) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index cc1468ce2..c01f918f5 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -9,14 +9,6 @@ "dev.chrisbanes.compose:compose-bom" ], "ignoreUnstable": false - }, - { - "groupName": "Kotlin and Compose Compiler", - "matchPackagePrefixes": [ - "androidx.compose.compiler", - "org.jetbrains.kotlin.", - "org.jetbrains.kotlin:", - ], } ] } diff --git a/.gitignore b/.gitignore index 6abb1d29e..70e152f0a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .gradle +.kotlin /local.properties /.idea/workspace.xml .DS_Store diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f7f9a78a3..622715259 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -274,7 +274,7 @@ androidComponents { tasks { // See https://kotlinlang.org/docs/reference/experimental.html#experimental-status-of-experimental-api(-markers) withType { - kotlinOptions.freeCompilerArgs += listOf( + compilerOptions.freeCompilerArgs.addAll( "-Xcontext-receivers", "-opt-in=androidx.compose.foundation.layout.ExperimentalLayoutApi", "-opt-in=androidx.compose.material.ExperimentalMaterialApi", diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 0ce5d68de..b72b8a98b 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -3,14 +3,16 @@ plugins { } dependencies { + implementation(androidx.gradle) + implementation(kotlinx.gradle) + implementation(kotlinx.compose.compiler.gradle) + implementation(libs.detekt.gradlePlugin) + implementation(gradleApi()) + implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location)) implementation(files(androidx.javaClass.superclass.protectionDomain.codeSource.location)) implementation(files(compose.javaClass.superclass.protectionDomain.codeSource.location)) implementation(files(kotlinx.javaClass.superclass.protectionDomain.codeSource.location)) - implementation(androidx.gradle) - implementation(kotlinx.gradle) - implementation(libs.detekt.gradlePlugin) - implementation(gradleApi()) } repositories { diff --git a/buildSrc/src/main/kotlin/mihon.android.application.compose.gradle.kts b/buildSrc/src/main/kotlin/mihon.android.application.compose.gradle.kts index eda5fb22e..6b330b1d9 100644 --- a/buildSrc/src/main/kotlin/mihon.android.application.compose.gradle.kts +++ b/buildSrc/src/main/kotlin/mihon.android.application.compose.gradle.kts @@ -1,4 +1,3 @@ -import mihon.buildlogic.AndroidConfig import mihon.buildlogic.configureCompose plugins { diff --git a/buildSrc/src/main/kotlin/mihon/buildlogic/AndroidConfig.kt b/buildSrc/src/main/kotlin/mihon/buildlogic/AndroidConfig.kt index afea120de..23f0d2b9a 100644 --- a/buildSrc/src/main/kotlin/mihon/buildlogic/AndroidConfig.kt +++ b/buildSrc/src/main/kotlin/mihon/buildlogic/AndroidConfig.kt @@ -1,11 +1,15 @@ package mihon.buildlogic import org.gradle.api.JavaVersion as GradleJavaVersion +import org.jetbrains.kotlin.gradle.dsl.JvmTarget as KotlinJvmTarget object AndroidConfig { const val COMPILE_SDK = 34 const val TARGET_SDK = 34 const val MIN_SDK = 26 const val NDK = "26.1.10909125" + + // https://youtrack.jetbrains.com/issue/KT-66995/JvmTarget-and-JavaVersion-compatibility-for-easier-JVM-version-setup val JavaVersion = GradleJavaVersion.VERSION_17 + val JvmTarget = KotlinJvmTarget.JVM_17 } diff --git a/buildSrc/src/main/kotlin/mihon/buildlogic/ProjectExtensions.kt b/buildSrc/src/main/kotlin/mihon/buildlogic/ProjectExtensions.kt index 874e556a2..4b9e762b9 100644 --- a/buildSrc/src/main/kotlin/mihon/buildlogic/ProjectExtensions.kt +++ b/buildSrc/src/main/kotlin/mihon/buildlogic/ProjectExtensions.kt @@ -8,9 +8,12 @@ import org.gradle.accessors.dm.LibrariesForLibs import org.gradle.api.Project import org.gradle.api.tasks.testing.Test import org.gradle.api.tasks.testing.logging.TestLogEvent +import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.dependencies +import org.gradle.kotlin.dsl.provideDelegate import org.gradle.kotlin.dsl.the import org.gradle.kotlin.dsl.withType +import org.jetbrains.kotlin.compose.compiler.gradle.ComposeCompilerGradlePluginExtension import org.jetbrains.kotlin.gradle.tasks.KotlinCompile val Project.androidx get() = the() @@ -37,15 +40,18 @@ internal fun Project.configureAndroid(commonExtension: CommonExtension<*, *, *, } tasks.withType().configureEach { - kotlinOptions { - jvmTarget = AndroidConfig.JavaVersion.toString() - // freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn" - // freeCompilerArgs += "-Xcontext-receivers" + compilerOptions { + jvmTarget.set(AndroidConfig.JvmTarget) + freeCompilerArgs.addAll( + "-opt-in=kotlin.RequiresOptIn", + "-Xcontext-receivers", + ) // Treat all Kotlin warnings as errors (disabled by default) // Override by setting warningsAsErrors=true in your ~/.gradle/gradle.properties - // val warningsAsErrors: String? by project - // allWarningsAsErrors = warningsAsErrors.toBoolean() + val warningsAsErrors: String? by project + allWarningsAsErrors.set(warningsAsErrors.toBoolean()) + } } @@ -55,51 +61,41 @@ internal fun Project.configureAndroid(commonExtension: CommonExtension<*, *, *, } internal fun Project.configureCompose(commonExtension: CommonExtension<*, *, *, *, *, *>) { + pluginManager.apply(kotlinx.plugins.compose.compiler.get().pluginId) + commonExtension.apply { buildFeatures { compose = true } - composeOptions { - kotlinCompilerExtensionVersion = compose.versions.compiler.get() - } - dependencies { "implementation"(platform(compose.bom)) } } - tasks.withType().configureEach { - kotlinOptions { - freeCompilerArgs += buildComposeMetricsParameters() + extensions.configure { + // Enable strong skipping mode + enableStrongSkippingMode.set(true) - // Enable experimental compiler opts - // https://developer.android.com/jetpack/androidx/releases/compose-compiler#1.5.9 - freeCompilerArgs += listOf( - "-P", - "plugin:androidx.compose.compiler.plugins.kotlin:nonSkippingGroupOptimization=true", - ) + // Enable experimental compiler opts + // https://developer.android.com/jetpack/androidx/releases/compose-compiler#1.5.9 + enableNonSkippingGroupOptimization.set(true) + + val enableMetrics = project.providers.gradleProperty("enableComposeCompilerMetrics").orNull.toBoolean() + val enableReports = project.providers.gradleProperty("enableComposeCompilerReports").orNull.toBoolean() + + val rootProjectDir = rootProject.layout.buildDirectory.asFile.get() + val relativePath = projectDir.relativeTo(rootDir) + if (enableMetrics) { + val buildDirPath = rootProjectDir.resolve("compose-metrics").resolve(relativePath) + metricsDestination.set(buildDirPath) + } + if (enableReports) { + val buildDirPath = rootProjectDir.resolve("compose-reports").resolve(relativePath) + reportsDestination.set(buildDirPath) } } -} -private fun Project.buildComposeMetricsParameters(): List { - val rootProjectDir = rootProject.layout.buildDirectory.asFile.get() - val relativePath = projectDir.relativeTo(rootDir) - - val enableMetrics = project.providers.gradleProperty("enableComposeCompilerMetrics").orNull.toBoolean() - val enableReports = project.providers.gradleProperty("enableComposeCompilerReports").orNull.toBoolean() - - return listOfNotNull( - ("metricsDestination" to "compose-metrics").takeIf { enableMetrics }, - ("reportsDestination" to "compose-reports").takeIf { enableReports }, - ).flatMap { (flag, dirName) -> - val buildDirPath = rootProjectDir.resolve(dirName).resolve(relativePath).absolutePath - listOf( - "-P", - "plugin:androidx.compose.compiler.plugins.kotlin:$flag=$buildDirPath" - ) - } } internal fun Project.configureTest() { diff --git a/data/build.gradle.kts b/data/build.gradle.kts index e3f9d9004..2fded2043 100644 --- a/data/build.gradle.kts +++ b/data/build.gradle.kts @@ -33,7 +33,7 @@ dependencies { tasks { withType { - kotlinOptions.freeCompilerArgs += listOf( + compilerOptions.freeCompilerArgs.addAll( "-Xcontext-receivers", "-opt-in=kotlinx.serialization.ExperimentalSerializationApi", ) diff --git a/domain/build.gradle.kts b/domain/build.gradle.kts index c49c5bc51..5e2357181 100644 --- a/domain/build.gradle.kts +++ b/domain/build.gradle.kts @@ -31,7 +31,7 @@ dependencies { tasks { withType { - kotlinOptions.freeCompilerArgs += listOf( + compilerOptions.freeCompilerArgs.addAll( "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", "-Xcontext-receivers", ) diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 3f1d253e1..ea04b45ff 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,11 +1,8 @@ [versions] -compiler = "1.5.14" compose-bom = "2024.05.00-alpha02" accompanist = "0.35.0-alpha" [libraries] -compiler = { module = "androidx.compose.compiler:compiler", version.ref = "compiler" } - activity = "androidx.activity:activity-compose:1.9.0" bom = { group = "dev.chrisbanes.compose", name = "compose-bom", version.ref = "compose-bom" } foundation = { module = "androidx.compose.foundation:foundation" } diff --git a/gradle/kotlinx.versions.toml b/gradle/kotlinx.versions.toml index 66e69752a..6cfb1bdd9 100644 --- a/gradle/kotlinx.versions.toml +++ b/gradle/kotlinx.versions.toml @@ -1,11 +1,12 @@ [versions] -kotlin_version = "1.9.24" +kotlin_version = "2.0.0" serialization_version = "1.6.3" xml_serialization_version = "0.86.3" [libraries] reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin_version" } gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin_version" } +compose-compiler-gradle = { module = "org.jetbrains.kotlin:compose-compiler-gradle-plugin", version.ref = "kotlin_version" } immutables = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version = "0.3.7" } @@ -28,4 +29,5 @@ serialization = ["serialization-json", "serialization-json-okio", "serialization [plugins] android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin_version" } +compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin_version" } serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin_version" } \ No newline at end of file diff --git a/i18n/build.gradle.kts b/i18n/build.gradle.kts index c782eac0f..bdfe09b97 100644 --- a/i18n/build.gradle.kts +++ b/i18n/build.gradle.kts @@ -50,7 +50,7 @@ tasks { } withType { - kotlinOptions.freeCompilerArgs += listOf( + compilerOptions.freeCompilerArgs.addAll( "-Xexpect-actual-classes", ) } diff --git a/presentation-core/build.gradle.kts b/presentation-core/build.gradle.kts index 8fca92019..c6e5c0b65 100644 --- a/presentation-core/build.gradle.kts +++ b/presentation-core/build.gradle.kts @@ -34,7 +34,7 @@ dependencies { tasks { // See https://kotlinlang.org/docs/reference/experimental.html#experimental-status-of-experimental-api(-markers) withType { - kotlinOptions.freeCompilerArgs += listOf( + compilerOptions.freeCompilerArgs.addAll( "-opt-in=androidx.compose.foundation.layout.ExperimentalLayoutApi", "-opt-in=androidx.compose.material.ExperimentalMaterialApi", "-opt-in=androidx.compose.material3.ExperimentalMaterial3Api", diff --git a/source-api/build.gradle.kts b/source-api/build.gradle.kts index ae28caa14..7ac4d6e2c 100644 --- a/source-api/build.gradle.kts +++ b/source-api/build.gradle.kts @@ -38,7 +38,7 @@ android { tasks { withType { - kotlinOptions.freeCompilerArgs += listOf( + compilerOptions.freeCompilerArgs.addAll( "-Xexpect-actual-classes", ) } diff --git a/source-local/build.gradle.kts b/source-local/build.gradle.kts index b63ae8fab..b0a720b97 100644 --- a/source-local/build.gradle.kts +++ b/source-local/build.gradle.kts @@ -40,7 +40,7 @@ android { tasks { withType { - kotlinOptions.freeCompilerArgs += listOf( + compilerOptions.freeCompilerArgs.addAll( "-Xexpect-actual-classes", "-opt-in=kotlinx.serialization.ExperimentalSerializationApi", ) diff --git a/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt b/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt index 06ed4551d..8efea5fd2 100644 --- a/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt +++ b/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt @@ -7,6 +7,7 @@ import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.UnmeteredSource import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.MangasPage +import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder @@ -20,17 +21,17 @@ import mihon.core.common.extensions.toZipFile import nl.adaptivity.xmlutil.AndroidXmlReader import nl.adaptivity.xmlutil.serialization.XML import tachiyomi.core.common.i18n.stringResource -import tachiyomi.core.metadata.comicinfo.COMIC_INFO_FILE -import tachiyomi.core.metadata.comicinfo.ComicInfo -import tachiyomi.core.metadata.comicinfo.copyFromComicInfo -import tachiyomi.core.metadata.comicinfo.getComicInfo -import tachiyomi.core.metadata.tachiyomi.MangaDetails import tachiyomi.core.common.storage.extension import tachiyomi.core.common.storage.nameWithoutExtension import tachiyomi.core.common.storage.openReadOnlyChannel import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.core.common.util.system.logcat +import tachiyomi.core.metadata.comicinfo.COMIC_INFO_FILE +import tachiyomi.core.metadata.comicinfo.ComicInfo +import tachiyomi.core.metadata.comicinfo.copyFromComicInfo +import tachiyomi.core.metadata.comicinfo.getComicInfo +import tachiyomi.core.metadata.tachiyomi.MangaDetails import tachiyomi.domain.chapter.service.ChapterRecognition import tachiyomi.domain.manga.model.Manga import tachiyomi.i18n.MR @@ -294,7 +295,7 @@ actual class LocalSource( override fun getFilterList() = FilterList(OrderBy.Popular(context)) // Unused stuff - override suspend fun getPageList(chapter: SChapter) = throw UnsupportedOperationException("Unused") + override suspend fun getPageList(chapter: SChapter): List = throw UnsupportedOperationException("Unused") fun getFormat(chapter: SChapter): Format { try { From 71b558cb34c4e2da435877f391e57b6d49c4ef4f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 7 Jun 2024 03:58:55 +0600 Subject: [PATCH 084/146] fix(deps): update serialization.version to v1.7.0 (#870) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/kotlinx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/kotlinx.versions.toml b/gradle/kotlinx.versions.toml index 6cfb1bdd9..ee3c15715 100644 --- a/gradle/kotlinx.versions.toml +++ b/gradle/kotlinx.versions.toml @@ -1,6 +1,6 @@ [versions] kotlin_version = "2.0.0" -serialization_version = "1.6.3" +serialization_version = "1.7.0" xml_serialization_version = "0.86.3" [libraries] From 777a071f4a2f32efc3447d118afd8b48006b3919 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 7 Jun 2024 04:23:27 +0600 Subject: [PATCH 085/146] fix(deps): update dependency dev.chrisbanes.compose:compose-bom to v2024.05.00-alpha03 (#843) * fix(deps): update dependency dev.chrisbanes.compose:compose-bom to v2024.05.00-alpha03 * Fix build --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> --- .../java/eu/kanade/presentation/browse/ExtensionsScreen.kt | 2 +- app/src/main/java/eu/kanade/presentation/components/AppBar.kt | 2 +- .../kanade/presentation/library/components/LibraryContent.kt | 2 +- app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt | 4 ++-- .../main/java/eu/kanade/presentation/updates/UpdatesScreen.kt | 2 +- gradle/compose.versions.toml | 4 ++-- .../presentation/core/components/material/PullRefresh.kt | 4 ++-- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt index a3cb94800..5849e8c9e 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt @@ -90,7 +90,7 @@ fun ExtensionScreen( PullRefresh( refreshing = state.isRefreshing, onRefresh = onRefresh, - enabled = { !state.isLoading }, + enabled = !state.isLoading, ) { when { state.isLoading -> LoadingScreen(Modifier.padding(contentPadding)) diff --git a/app/src/main/java/eu/kanade/presentation/components/AppBar.kt b/app/src/main/java/eu/kanade/presentation/components/AppBar.kt index 01dcb575c..2315e0050 100644 --- a/app/src/main/java/eu/kanade/presentation/components/AppBar.kt +++ b/app/src/main/java/eu/kanade/presentation/components/AppBar.kt @@ -179,7 +179,7 @@ fun AppBarTitle( maxLines = 1, overflow = TextOverflow.Ellipsis, modifier = Modifier.basicMarquee( - delayMillis = 2_000, + repeatDelayMillis = 2_000, ), ) } diff --git a/app/src/main/java/eu/kanade/presentation/library/components/LibraryContent.kt b/app/src/main/java/eu/kanade/presentation/library/components/LibraryContent.kt index f0a63f597..61da10345 100644 --- a/app/src/main/java/eu/kanade/presentation/library/components/LibraryContent.kt +++ b/app/src/main/java/eu/kanade/presentation/library/components/LibraryContent.kt @@ -93,7 +93,7 @@ fun LibraryContent( isRefreshing = false } }, - enabled = { notSelectionMode }, + enabled = notSelectionMode, ) { LibraryPager( state = pagerState, diff --git a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt index efe44ceb4..91546f57f 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt @@ -355,7 +355,7 @@ private fun MangaScreenSmallImpl( PullRefresh( refreshing = state.isRefreshingData, onRefresh = onRefresh, - enabled = { !isAnySelected }, + enabled = !isAnySelected, indicatorPadding = PaddingValues(top = topPadding), ) { val layoutDirection = LocalLayoutDirection.current @@ -601,7 +601,7 @@ fun MangaScreenLargeImpl( PullRefresh( refreshing = state.isRefreshingData, onRefresh = onRefresh, - enabled = { !isAnySelected }, + enabled = !isAnySelected, indicatorPadding = PaddingValues( start = insetPadding.calculateStartPadding(layoutDirection), top = with(density) { topBarHeight.toDp() }, diff --git a/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt b/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt index fa2bd53fe..5693185c0 100644 --- a/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt @@ -104,7 +104,7 @@ fun UpdateScreen( isRefreshing = false } }, - enabled = { !state.selectionMode }, + enabled = !state.selectionMode, indicatorPadding = contentPadding, ) { FastScrollLazyColumn( diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index ea04b45ff..85091b5d3 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,6 +1,6 @@ [versions] -compose-bom = "2024.05.00-alpha02" -accompanist = "0.35.0-alpha" +compose-bom = "2024.05.00-alpha03" +accompanist = "0.35.1-alpha" [libraries] activity = "androidx.activity:activity-compose:1.9.0" diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/PullRefresh.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/PullRefresh.kt index b1987185b..08b10cdaa 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/PullRefresh.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/PullRefresh.kt @@ -22,7 +22,7 @@ import androidx.compose.ui.unit.dp @Composable fun PullRefresh( refreshing: Boolean, - enabled: () -> Boolean, + enabled: Boolean, onRefresh: () -> Unit, modifier: Modifier = Modifier, indicatorPadding: PaddingValues = PaddingValues(0.dp), @@ -36,7 +36,7 @@ fun PullRefresh( state = state, enabled = enabled, onRefresh = onRefresh, - ) + ), ) { content() From 1edd55c981aa72faf49c06173f33bf0c2f99fe60 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 7 Jun 2024 04:26:25 +0600 Subject: [PATCH 086/146] fix(deps): update okhttp monorepo to v5.0.0-alpha.14 (#688) * fix(deps): update okhttp monorepo to v5.0.0-alpha.14 * Fix build --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> --- .../java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt | 3 ++- gradle/libs.versions.toml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt index 11ff6a277..65a142099 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt @@ -21,7 +21,6 @@ import okhttp3.CacheControl import okhttp3.Call import okhttp3.Request import okhttp3.Response -import okhttp3.internal.http.HTTP_NOT_MODIFIED import okio.FileSystem import okio.Path.Companion.toOkioPath import okio.Source @@ -348,5 +347,7 @@ class MangaCoverFetcher( private val CACHE_CONTROL_NO_STORE = CacheControl.Builder().noStore().build() private val CACHE_CONTROL_NO_NETWORK_NO_CACHE = CacheControl.Builder().noCache().onlyIfCached().build() + + private const val HTTP_NOT_MODIFIED = 304 } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ad58e9e7b..b2f303917 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ aboutlib_version = "11.2.1" leakcanary = "2.14" moko = "0.23.0" -okhttp_version = "5.0.0-alpha.12" +okhttp_version = "5.0.0-alpha.14" richtext = "0.20.0" shizuku_version = "12.2.0" sqldelight = "2.0.2" From 15d999229fcce865001d5fa77d0163e6e80e38db Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Fri, 7 Jun 2024 04:30:17 +0600 Subject: [PATCH 087/146] MangaChapterListItem: Don't use alpha modifier Possibly fixes #822 Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> --- .../manga/components/MangaChapterListItem.kt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt index 12720957e..6f323993c 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt @@ -30,7 +30,6 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector @@ -66,9 +65,6 @@ fun MangaChapterListItem( onChapterSwipe: (LibraryPreferences.ChapterSwipeAction) -> Unit, modifier: Modifier = Modifier, ) { - val textAlpha = if (read) ReadItemAlpha else 1f - val textSubtitleAlpha = if (read) ReadItemAlpha else SecondaryItemAlpha - val start = getSwipeAction( action = chapterSwipeStartAction, read = read, @@ -133,15 +129,20 @@ fun MangaChapterListItem( Text( text = title, style = MaterialTheme.typography.bodyMedium, - color = LocalContentColor.current.copy(alpha = textAlpha), maxLines = 1, overflow = TextOverflow.Ellipsis, onTextLayout = { textHeight = it.size.height }, + color = LocalContentColor.current.copy(alpha = if (read) ReadItemAlpha else 1f), ) } - Row(modifier = Modifier.alpha(textSubtitleAlpha)) { - ProvideTextStyle(value = MaterialTheme.typography.bodySmall) { + Row { + val subtitleStyle = MaterialTheme.typography.bodySmall + .merge( + color = LocalContentColor.current + .copy(alpha = if (read) ReadItemAlpha else SecondaryItemAlpha) + ) + ProvideTextStyle(value = subtitleStyle) { if (date != null) { Text( text = date, From bdce3c39f1475dc77dad300a0bf3702e85d32916 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 7 Jun 2024 14:58:19 +0600 Subject: [PATCH 088/146] fix(deps): update dependency io.github.fornewid:material-motion-compose-core to v2 (#873) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b2f303917..bdb58f6f4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -62,7 +62,7 @@ flexible-adapter-core = "com.github.arkon.FlexibleAdapter:flexible-adapter:c8013 photoview = "com.github.chrisbanes:PhotoView:2.3.0" directionalviewpager = "com.github.tachiyomiorg:DirectionalViewPager:1.0.0" insetter = "dev.chrisbanes.insetter:insetter:0.6.1" -compose-materialmotion = "io.github.fornewid:material-motion-compose-core:1.2.1" +compose-materialmotion = "io.github.fornewid:material-motion-compose-core:2.0.0" compose-webview = "io.github.kevinnzou:compose-webview:0.33.6" compose-grid = "io.woong.compose.grid:grid:1.2.2" From 87fe64468ca08466af5b9fcc7f9e17e9a23021e6 Mon Sep 17 00:00:00 2001 From: "Weblate (bot)" Date: Fri, 7 Jun 2024 15:30:00 +0200 Subject: [PATCH 089/146] Translations update from Hosted Weblate (#611) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Translated using Weblate (Malayalam) Currently translated at 12.9% (104 of 803 strings) Translated using Weblate (Malayalam) Currently translated at 94.4% (17 of 18 strings) Translated using Weblate (Malayalam) Currently translated at 11.8% (95 of 803 strings) Added translation using Weblate (Malayalam) Added translation using Weblate (Malayalam) Co-authored-by: Akhil Raj Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ml/ Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ml/ Translation: Mihon/Mihon Translation: Mihon/Mihon Plurals * Translated using Weblate (Italian) Currently translated at 99.6% (800 of 803 strings) Co-authored-by: Federico Pierantoni Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/it/ Translation: Mihon/Mihon * Translated using Weblate (Hungarian) Currently translated at 100.0% (803 of 803 strings) Translated using Weblate (Hungarian) Currently translated at 100.0% (803 of 803 strings) Co-authored-by: B4LiN7 Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/ Translation: Mihon/Mihon * Translated using Weblate (Javanese) Currently translated at 38.7% (311 of 803 strings) Translated using Weblate (Japanese) Currently translated at 100.0% (803 of 803 strings) Translated using Weblate (Indonesian) Currently translated at 98.7% (793 of 803 strings) Co-authored-by: TheKingTermux Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/id/ Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/ Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/jv/ Translation: Mihon/Mihon * Translated using Weblate (Greek) Currently translated at 100.0% (803 of 803 strings) Co-authored-by: Pitpe11 Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/el/ Translation: Mihon/Mihon * Translated using Weblate (Serbian) Currently translated at 99.2% (797 of 803 strings) Co-authored-by: Rikishaaa Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sr/ Translation: Mihon/Mihon * Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (803 of 803 strings) Co-authored-by: Blackiezin Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pt_BR/ Translation: Mihon/Mihon * Translated using Weblate (French) Currently translated at 100.0% (18 of 18 strings) Translated using Weblate (French) Currently translated at 99.0% (795 of 803 strings) Co-authored-by: LaQuiche426 Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/fr/ Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fr/ Translation: Mihon/Mihon Translation: Mihon/Mihon Plurals * Translated using Weblate (Portuguese) Currently translated at 99.8% (802 of 803 strings) Co-authored-by: ssantos Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pt/ Translation: Mihon/Mihon * Translated using Weblate (Vietnamese) Currently translated at 100.0% (18 of 18 strings) Translated using Weblate (Vietnamese) Currently translated at 96.8% (778 of 803 strings) Co-authored-by: Karuto Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/vi/ Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/vi/ Translation: Mihon/Mihon Translation: Mihon/Mihon Plurals * Translated using Weblate (Croatian) Currently translated at 99.5% (799 of 803 strings) Co-authored-by: Milo Ivir Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hr/ Translation: Mihon/Mihon * Translated using Weblate (Indonesian) Currently translated at 100.0% (803 of 803 strings) Co-authored-by: Eji-san Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/id/ Translation: Mihon/Mihon * Translated using Weblate (Galician) Currently translated at 100.0% (803 of 803 strings) Co-authored-by: kevans Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/gl/ Translation: Mihon/Mihon * Translated using Weblate (Ukrainian) Currently translated at 99.8% (802 of 803 strings) Co-authored-by: Kodekiro Kodekihara Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/uk/ Translation: Mihon/Mihon * Translated using Weblate (Malay) Currently translated at 98.6% (792 of 803 strings) Co-authored-by: Farith Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ms/ Translation: Mihon/Mihon * Translated using Weblate (Nepali) Currently translated at 100.0% (18 of 18 strings) Translated using Weblate (Nepali) Currently translated at 100.0% (803 of 803 strings) Co-authored-by: FateXBlood Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ne/ Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ne/ Translation: Mihon/Mihon Translation: Mihon/Mihon Plurals * Translated using Weblate (Vietnamese) Currently translated at 100.0% (803 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/vi/ * Translated using Weblate (Croatian) Currently translated at 100.0% (803 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hr/ * Translated using Weblate (Spanish) Currently translated at 100.0% (18 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/es/ * Translated using Weblate (Romanian) Currently translated at 99.6% (800 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ro/ * Translated using Weblate (Romanian) Currently translated at 100.0% (18 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ro/ * Translated using Weblate (Italian) Currently translated at 100.0% (803 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/it/ * Translated using Weblate (Polish) Currently translated at 99.5% (799 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pl/ * Translated using Weblate (Spanish) Currently translated at 100.0% (803 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/ * Translated using Weblate (German) Currently translated at 100.0% (804 of 804 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/de/ * Translated using Weblate (Russian) Currently translated at 100.0% (804 of 804 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ru/ * Translated using Weblate (French) Currently translated at 99.5% (800 of 804 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fr/ * Translated using Weblate (Filipino) Currently translated at 99.8% (803 of 804 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fil/ * Translated using Weblate (Nepali) Currently translated at 100.0% (804 of 804 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ne/ * Translated using Weblate (Catalan) Currently translated at 100.0% (804 of 804 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ca/ * Translated using Weblate (Spanish) Currently translated at 100.0% (804 of 804 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/ * Translated using Weblate (Catalan) Currently translated at 100.0% (18 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ca/ --------- Co-authored-by: Akhil Raj Co-authored-by: Federico Pierantoni Co-authored-by: B4LiN7 Co-authored-by: TheKingTermux Co-authored-by: Pitpe11 Co-authored-by: Rikishaaa Co-authored-by: Blackiezin Co-authored-by: LaQuiche426 Co-authored-by: ssantos Co-authored-by: Karuto Co-authored-by: Milo Ivir Co-authored-by: Eji-san Co-authored-by: kevans Co-authored-by: Kodekiro Kodekihara Co-authored-by: Farith Co-authored-by: FateXBlood Co-authored-by: Nguyễn Trung Đức Co-authored-by: Chrono Lux Co-authored-by: Saft Octavian Co-authored-by: Giorgio Sanna Co-authored-by: sebastians17 Co-authored-by: Tim Schneeberger Co-authored-by: Dexroneum Co-authored-by: Naga Co-authored-by: Infy's Tagalog Translations Co-authored-by: Eduard Ereza Martínez Co-authored-by: gallegonovato --- .../commonMain/resources/MR/ca/plurals.xml | 5 + .../commonMain/resources/MR/ca/strings.xml | 14 +++ .../commonMain/resources/MR/de/strings.xml | 1 + .../commonMain/resources/MR/el/strings.xml | 12 +++ .../commonMain/resources/MR/es/plurals.xml | 12 +-- .../commonMain/resources/MR/es/strings.xml | 17 +-- .../commonMain/resources/MR/fil/strings.xml | 1 + .../commonMain/resources/MR/fr/plurals.xml | 5 + .../commonMain/resources/MR/fr/strings.xml | 10 ++ .../commonMain/resources/MR/gl/strings.xml | 45 +++++++- .../commonMain/resources/MR/hr/strings.xml | 11 ++ .../commonMain/resources/MR/hu/strings.xml | 14 +-- .../commonMain/resources/MR/in/strings.xml | 12 +++ .../commonMain/resources/MR/it/strings.xml | 5 + .../commonMain/resources/MR/ja/strings.xml | 6 +- .../commonMain/resources/MR/jv/strings.xml | 3 + .../commonMain/resources/MR/ml/plurals.xml | 71 ++++++++++++ .../commonMain/resources/MR/ml/strings.xml | 102 ++++++++++++++++++ .../commonMain/resources/MR/ms/strings.xml | 1 + .../commonMain/resources/MR/ne/plurals.xml | 4 + .../commonMain/resources/MR/ne/strings.xml | 18 ++-- .../commonMain/resources/MR/pl/strings.xml | 7 ++ .../resources/MR/pt-rBR/strings.xml | 12 +++ .../commonMain/resources/MR/pt/strings.xml | 95 ++++++++++++++++ .../commonMain/resources/MR/ro/plurals.xml | 5 + .../commonMain/resources/MR/ro/strings.xml | 28 +++-- .../commonMain/resources/MR/ru/strings.xml | 1 + .../commonMain/resources/MR/sr/strings.xml | 5 + .../commonMain/resources/MR/uk/strings.xml | 14 ++- .../commonMain/resources/MR/vi/plurals.xml | 6 ++ .../commonMain/resources/MR/vi/strings.xml | 30 ++++++ 31 files changed, 529 insertions(+), 43 deletions(-) create mode 100644 i18n/src/commonMain/resources/MR/ml/plurals.xml create mode 100644 i18n/src/commonMain/resources/MR/ml/strings.xml diff --git a/i18n/src/commonMain/resources/MR/ca/plurals.xml b/i18n/src/commonMain/resources/MR/ca/plurals.xml index 89fd5c350..78d4572c3 100644 --- a/i18n/src/commonMain/resources/MR/ca/plurals.xml +++ b/i18n/src/commonMain/resources/MR/ca/plurals.xml @@ -85,4 +85,9 @@ %d repositoris %d repositoris + + Demà + D’aquí a %1$d dies + D’aquí a %1$d dies + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ca/strings.xml b/i18n/src/commonMain/resources/MR/ca/strings.xml index bc63d1260..d54f800aa 100644 --- a/i18n/src/commonMain/resources/MR/ca/strings.xml +++ b/i18n/src/commonMain/resources/MR/ca/strings.xml @@ -796,4 +796,18 @@ Aviat Freqüència d’actualització personalitzada: Obre el repositori d’origen + No s’ha pogut obtenir accés persistent a la carpeta. És possible que l’aplicació es comporti de manera inesperada. + Pròxim + Guia de pròximes actualitzacions + Mes següent + Afegeix igualment + Migra l’element existent + Desactiva l’allunyament + Substitueix + L’empremta digital de la clau de xifratge ja existeix + La clau de xifratge del repositori %1$s té la mateixa empremta digital que %2$s. +\nSi espereu que sigui així, se substituirà %2$s. En cas contrari, contacteu amb el mantenidor del repositori. + Perfil de visualització personalitzat + Mostra les pròximes actualitzacions + Mes anterior \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/de/strings.xml b/i18n/src/commonMain/resources/MR/de/strings.xml index 7978b5a8c..25893556d 100644 --- a/i18n/src/commonMain/resources/MR/de/strings.xml +++ b/i18n/src/commonMain/resources/MR/de/strings.xml @@ -809,4 +809,5 @@ Bevorstehend Bevorstehende Aktualisierungen ansehen Leitfaden für Bevorstehendes + Dauerhafter Ordnerzugriff konnte nicht erlangt werden. Die App kann sich unerwartet verhalten. \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/el/strings.xml b/i18n/src/commonMain/resources/MR/el/strings.xml index 00fb56e96..54691d40c 100644 --- a/i18n/src/commonMain/resources/MR/el/strings.xml +++ b/i18n/src/commonMain/resources/MR/el/strings.xml @@ -797,4 +797,16 @@ Ανάκληση αξιόπιστων άγνωστων επεκτάσεων Αποθετήριο ανοικτού κώδικα Απενεργοποίηση σμίκρυνσης εικόνας + Προσθέστε ούτως ή άλλως + Το αποθετήριο %1$s έχει το ίδιο δακτυλικό αποτύπωμα κλειδιού υπογραφής με το %2$s. +\nΕάν αυτό είναι αναμενόμενο, το %2$s θα αντικατασταθεί, διαφορετικά επικοινωνήστε με τον συντηρητή του αποθετηρίου σας. + Ανερχόμενο + Μεταφορά υπάρχουσας καταχώρησης + Προσαρμοσμένο προφίλ εμφάνισης + Δείτε τις επερχόμενες ενημερώσεις + Αντικατάσταση + Το δακτυλικό αποτύπωμα κλειδιού υπογραφής υπάρχει ήδη + Οδηγός ανερχόμενων + Επόμενο μήνα + Προηγούμενο Μήνα \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/es/plurals.xml b/i18n/src/commonMain/resources/MR/es/plurals.xml index 08f01feea..21cff773b 100644 --- a/i18n/src/commonMain/resources/MR/es/plurals.xml +++ b/i18n/src/commonMain/resources/MR/es/plurals.xml @@ -1,7 +1,7 @@ - Tras %1$s minuto + Tras %1$s minutos Tras %1$s minutos Tras %1$s minutos @@ -26,9 +26,9 @@ %d actualizaciones de extensiones disponibles - Queda %1$s - Quedan %1$s - Quedan %1$s + Restante %1$s + Restantes %1$s + Restantes %1$s %d categoría @@ -61,8 +61,8 @@ Hace %1$d días - El siguiente capítulo sin leer - Los siguientes %d capítulos sin leer + Siguiente capítulo sin leer + Siguientes %d capítulos sin leer Los siguientes %d capítulos sin leer diff --git a/i18n/src/commonMain/resources/MR/es/strings.xml b/i18n/src/commonMain/resources/MR/es/strings.xml index 7c5a558c1..4e6501b9b 100644 --- a/i18n/src/commonMain/resources/MR/es/strings.xml +++ b/i18n/src/commonMain/resources/MR/es/strings.xml @@ -158,7 +158,7 @@ No se ha podido descargar el capítulo debido a un error inesperado No estás conectado a ninguna red Wi-Fi Categorías - Títulos en la biblioteca + Entradas de biblioteca Seguimiento Historial Favoritos @@ -508,8 +508,8 @@ Servicios de seguimiento mejorados Guía de seguimiento Automático - No - + Apagado + Encendido Establecer el tipo de orden para cada categoría Actividad en segundo plano Seguir @@ -679,7 +679,7 @@ Ver número de capítulos por leer en el icono de actualizaciones Se ha copiado al portapapeles Saltarse los capítulos repetidos - Están disponibles, pero las fuentes todavía no se han instalado: %s + Disponible, pero fuente no instalada: %s Ya tienes un elemento en la biblioteca con este mismo nombre. \n \n¿Seguro que quieres continuar? @@ -710,7 +710,7 @@ ¿Quieres desvincular %s? Esto eliminará el seguimiento localmente. Quitar también de %s - Borrar los ya descargados + Eliminar descargados Con resultados La biblioteca se ha sincronizado correctamente Sincronizando la biblioteca @@ -740,15 +740,15 @@ Almacenamiento utilizado Puntuación del rastreador Aplicar - Restablecer vista + Restablecer valor predeterminado Crear No se ha encontrado ningún equipo de traducción Equipo de traducción Excluir equipo de traducción Seleccionado - Sin seleccionar + No seleccionado Más opciones - Subir un nivel + Navegar hacia arriba Ubicación del almacenamiento Se utiliza para las copias de seguridad automáticas, poder descargar capítulos y abrir los que ya tengas en tu dispositivo. Elige una carpeta @@ -809,4 +809,5 @@ Próxima guía Próximo mes Mes anterior + No se ha podido obtener acceso a la carpeta persistente. La aplicación puede comportarse de forma inesperada. \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/fil/strings.xml b/i18n/src/commonMain/resources/MR/fil/strings.xml index 58c86182b..df8b126ad 100644 --- a/i18n/src/commonMain/resources/MR/fil/strings.xml +++ b/i18n/src/commonMain/resources/MR/fil/strings.xml @@ -809,4 +809,5 @@ Susunod na Buwan Nakaraang Buwan Tingnan ang mga Paparating na Update + Nabigong makakuha ng patuloy na pag-access ng folder. Ang app ay magkaroon ng di-inaasahang pagkilos. \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/fr/plurals.xml b/i18n/src/commonMain/resources/MR/fr/plurals.xml index 21e4e5eab..8d007cf27 100644 --- a/i18n/src/commonMain/resources/MR/fr/plurals.xml +++ b/i18n/src/commonMain/resources/MR/fr/plurals.xml @@ -85,4 +85,9 @@ %d dépôts %d dépôts + + Demain + Dans %1$d jours + Dans %1$d jours + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/fr/strings.xml b/i18n/src/commonMain/resources/MR/fr/strings.xml index 06b70876d..2764a75ef 100644 --- a/i18n/src/commonMain/resources/MR/fr/strings.xml +++ b/i18n/src/commonMain/resources/MR/fr/strings.xml @@ -796,4 +796,14 @@ Impossible de créer un fichier de sauvegarde Dernière sauvegarde automatique : %s Paramètres sources + Ajouter tout de même + Migrer l’entrée existante + À venir + Remplacer + Profil d\'affichage personnalisé + Désactiver le zoom arrière + Afficher les mises à jour à venir + Guide à venir + Le mois prochain + Le mois précédent \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/gl/strings.xml b/i18n/src/commonMain/resources/MR/gl/strings.xml index 3c0ee8dfe..81f69686e 100644 --- a/i18n/src/commonMain/resources/MR/gl/strings.xml +++ b/i18n/src/commonMain/resources/MR/gl/strings.xml @@ -78,9 +78,9 @@ As fontes desta extensión poden ter contido NSFW (+18) +18 Esta extensión xa non está dispoñible. Pode que non funcione ben e cause problemas na aplicación. Recoméndase desinstalala. - Unha extensión maliciosa podería ler calquera credencial de inicio de sesión gardado en Mihon ou executar código arbitrario. + Unha extensión maliciosa podería ler calquera credencial de inicio de sesión gardada en Mihon ou executar calquera tipo de código. \n -\nConfiando neste certificado aceptas estes riscos. +\nSe confías nesta extensión, aceptas estes riscos. Extensión non confiable Non confiable Desinstalar @@ -754,7 +754,7 @@ «%1$s» en lugar de «%2$s» Predicir cando sae o seguinte capítulo Preme aquí para conceder os permisos para instalar extensións. - Revogar as extensións de confianza de orixe descoñecido + Revogar as extensións de confianza de orixe descoñecida Repositorios de extensións Aínda non engadiches ningún repositorio. Dirección URL do repositorio @@ -770,4 +770,43 @@ Axustes da fonte Crear Erro completo: + Non se puido crear unha copia de seguridade + Almacenamento utilizado + Eliminar tamén de %s + Invalidouse o índice de descargas + Ten resultados + Engadir de todos modos + Migrar a entrada existente + Dispoñible: %1$s / Total: %2$s + Sincronizando a biblioteca + Nunca + Proximamente + Substituír + A impresión dixital da chave da firma xa existe + O repositorio %1$s ten a misma impresión dixital da chave da firma que %2$s. +\nSe isto é o comportamento agardado, %2$s substituirase. Se no é así, contacta con quen mantén o repositorio. + Perfil de visualización personalizado + Incluír configuracións sensibles, como as chaves de inicio de sesión en plataformas de seguemento + Navegar arriba + A biblioteca sincronizouse correctamente + Intervalos + Estimar cada + Prevese que o seguinte capítulo saia en %1$s, compróbase cada %2$s. + Frecuencia de actualización personalizada: + Queres deixar de seguir %s? + Actualizando a biblioteca… (%s) + Omitiuse porque hoxe non se esperaba ningunha publicación + Non se elixiu ningún arquivo + Última copia de seguridade automática: %s + Licenciado: sen capítulos que mostrar + Actualizar cada + Pronto + Excluír equipos de tradución + Non se atopou ningún equipo de tradución + Esto eliminará o seguimento local. + Ver as próximas actualizacións + Guía dos próximos lanzamenos + Mes seguinte + Mes anterior + Preme aquí para obter axuda con Cloudflare \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/hr/strings.xml b/i18n/src/commonMain/resources/MR/hr/strings.xml index ea77daeec..48336a888 100644 --- a/i18n/src/commonMain/resources/MR/hr/strings.xml +++ b/i18n/src/commonMain/resources/MR/hr/strings.xml @@ -798,4 +798,15 @@ Dostupno: %1$s / Ukupno: %2$s Deaktiviraj smanjivanje zumiranja Prilagođeni profil prikaza + Predstojeći + Migriraj postojeći unos + Svejedno dodaj + Prethodni mjesec + Zamijeni + Sljedeći mjesec + Pogledaj nadolazeća aktualizirane verzije + Vodič za nadolazeće verzije + Digitalni otisak prsta za potpisivanje već postoji + Repozitorij %1$s ima isti digitalni otisak ključa za potpisivanje kao %2$s. +\nAko se to očekuje, %2$s će se zamijeniti, u suprotnom se obrati svom održavatelju repozitorija. \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/hu/strings.xml b/i18n/src/commonMain/resources/MR/hu/strings.xml index 370d7bb77..51c00c3eb 100644 --- a/i18n/src/commonMain/resources/MR/hu/strings.xml +++ b/i18n/src/commonMain/resources/MR/hu/strings.xml @@ -69,7 +69,7 @@ Olvasatlan Szűrők eltávolítása Betűrendben - Összes fejezet + Fejezetek száma Legutóbb olvasott Globális keresés Olvasottnak jelölés @@ -431,11 +431,11 @@ Mit tartalmazzon a biztonsági mentés? Biztonsági mentés/helyreállítás nem biztos,hogy működik ha a MIUI Optimalizáció ki van kapcsolva. Helyreállítás folyamatban van - Felhasznált gyorsítótár: %1$s + Felhasznált: %1$s Adatbázis törlése Nem könyvtári elemek előzményeinek törlése %1$d nem könyvtári manga az adatbázisban - Fejezet gyorsítótár törlése kilépéskor + Fejezet gyorsítótárának törlése kilépéskor Helytelen fejezet formátum Hálózat Nem lehetett a fejezeteket letölteni. Próbálja újra a letöltések menüpontban @@ -458,7 +458,7 @@ Hibaüzenetetek törlése Sorozat beállításainak visszaállítása Néhány gyártónak extra korlátozása van arra, hogy kikapcsolja a háttér folyamatokat. Ezen a web oldalon több információt találsz, hogy hogyan oldható meg. - Olvasási előzmények megállítása + Olvasási előzmények rögzítésének szüneteltetése Fedlap Szüneteltetve Kategóriák törölve @@ -663,7 +663,7 @@ Biztos vagy benne? Kategória üres Ez eltávolítja az eddig kiválasztott befejezési dátumot a(z) %s szolgáltatásból - Olvasási idő + Olvasási időtartam Nem található elem ebben a kategóriában A/az \"%s\"-t elfogod távolítani a könyvtáradból Éppen most @@ -674,7 +674,7 @@ %s hibába ütközött. A hiba üzenetet kérünk oszd meg velünk a Discord szerverünkön. Dátum eltávolítása? Applikáció újrainditása - Globális frissités + Globális frissítésben *kötelező Fejezet csúsztatás Balra csúsztatási cselekmény @@ -721,7 +721,7 @@ Könyvtár frissítése ... (%s) Elemek Felnavigálás - Adat és tárolás + Adatok és tárolás Szeretnéd a kategóriákat betűrendbe rendezni? Átlépve, mert ma nem várható kiadás Nincs fájl kiválasztva diff --git a/i18n/src/commonMain/resources/MR/in/strings.xml b/i18n/src/commonMain/resources/MR/in/strings.xml index 286fe0752..fa8ba612c 100644 --- a/i18n/src/commonMain/resources/MR/in/strings.xml +++ b/i18n/src/commonMain/resources/MR/in/strings.xml @@ -797,4 +797,16 @@ Cabut izin ekstensi tidak dikenal yang tepercaya Repo sumber terbuka Menonaktifkan zoom out + Yang akan datang + Migrasikan entri yang ada + Repositori %1$s memiliki Signing Key Fingerprint yang sama dengan %2$s. +\nJika hal ini diharapkan, %2$s akan diganti, jika tidak, hubungi pengelola repo Anda. + Tambahkan saja + Profil tampilan khusus + Ganti + Signing Key Fingerprint sudah ada + Lihat Pembaruan Mendatang + Panduan Mendatang + Bulan Depan + Bulan Kemarin \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/it/strings.xml b/i18n/src/commonMain/resources/MR/it/strings.xml index b147c58b4..32b150fa5 100644 --- a/i18n/src/commonMain/resources/MR/it/strings.xml +++ b/i18n/src/commonMain/resources/MR/it/strings.xml @@ -806,4 +806,9 @@ \nSe è quello che si desidera, %2$s verrà sostituita, altrimenti contatta il manutentore della repository. Sostituisci Disattiva zoom indietro + Prossimamente + Controlla i Prossimi Aggiornamenti + Guida in arrivo + Mese prossimo + Mese scorso \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ja/strings.xml b/i18n/src/commonMain/resources/MR/ja/strings.xml index 9add17edd..09d5268ca 100644 --- a/i18n/src/commonMain/resources/MR/ja/strings.xml +++ b/i18n/src/commonMain/resources/MR/ja/strings.xml @@ -799,7 +799,7 @@ ズームアウトを無効にする 追加 移行 - 置き換える + 交換 カスタムディスプレイプロファイル 前月 翌月 @@ -807,6 +807,6 @@ 今後のアップデート 署名キーのフィンガープリントはすでに存在します 今後のご案内 - リポジトリ %1$s は %2$s と同じ署名キーフィンガープリントを持っています。 -\nこれが予想される場合は %2$s が置き換えされますが、そうでない場合はリポジトリのメンテナーに連絡してください。 + リポジトリ %1$s は %2$s と同じ署名キー指紋を持っています。 +\nこれが予想される場合は %2$s が置換されますが、そうでない場合はリポジトリのメンテナーに連絡してください。 \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/jv/strings.xml b/i18n/src/commonMain/resources/MR/jv/strings.xml index 852bedcf8..5eadb71b9 100644 --- a/i18n/src/commonMain/resources/MR/jv/strings.xml +++ b/i18n/src/commonMain/resources/MR/jv/strings.xml @@ -307,4 +307,7 @@ Ngehapus downloadan Dipilih Gak dipilih + Pilihan liane + Data lan penyimpanan + Bakal muncul \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ml/plurals.xml b/i18n/src/commonMain/resources/MR/ml/plurals.xml new file mode 100644 index 000000000..a3e67ba1c --- /dev/null +++ b/i18n/src/commonMain/resources/MR/ml/plurals.xml @@ -0,0 +1,71 @@ + + + + ഇന്നലെ + %1$d ദിവസം മുമ്പ് + + + നാളെ + %1$d ദിവസത്തിനുള്ളിൽ + + + %2$s പിശകോടെ %1$s-നുള്ളിൽ ചെയ്തു + %2$s പിശകുകളോടെ %1$s-നുള്ളിൽ ചെയ്തു + + + %d അധ്യായം ഒഴിവാക്കുന്നു, ഒന്നുകിൽ ഉറവിടം അത് കാണുന്നില്ല അല്ലെങ്കിൽ അത് ഫിൽട്ടർ ചെയ്തിരിക്കുന്നു + %d അധ്യായങ്ങൾ ഒഴിവാക്കുന്നു, ഒന്നുകിൽ ഉറവിടം കാണുന്നില്ല അല്ലെങ്കിൽ അവ ഫിൽട്ടർ ചെയ്തിരിക്കുന്നു + + + വിപുലീകരണ അപ്ഡേറ്റ് ലഭ്യമാണ് + %d വിപുലീകരണ അപ്‌ഡേറ്റുകൾ ലഭ്യമാണ് + + + അടുത്ത അധ്യായം + അടുത്ത %d അധ്യായങ്ങൾ + + + %1$s മിനിറ്റിന് ശേഷം + %1$s മിനിറ്റിന് ശേഷം + + + %d വിഭാഗം + %d വിഭാഗങ്ങൾ + + + അടുത്ത വായിക്കാത്ത അധ്യായം + അടുത്ത %d വായിക്കാത്ത അധ്യായങ്ങൾ + + + %1$s ശേഷിക്കുന്നു + %1$s ശേഷിക്കുന്നു + + + 1 ദിവസം + %d ദിവസം + + + %1$s അധ്യായം കാണുന്നില്ല + %1$s അധ്യായങ്ങൾ കാണുന്നില്ല + + + %1$s അധ്യായം + %1$s അധ്യായങ്ങൾ + + + %d ട്രാക്കർ + %d ട്രാക്കറുകൾ + + + %1$d പുതിയ അധ്യായം + %1$d പുതിയ അധ്യായങ്ങൾ + + + അധ്യായങ്ങൾ %1$s-ഉം 1-ഉം + %1$s-ഉം %2$d-ഉം അധ്യായങ്ങൾ + + + %d റിപ്പോ + %d റിപ്പോകൾ + + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ml/strings.xml b/i18n/src/commonMain/resources/MR/ml/strings.xml new file mode 100644 index 000000000..7a6be0bfb --- /dev/null +++ b/i18n/src/commonMain/resources/MR/ml/strings.xml @@ -0,0 +1,102 @@ + + + ഡാറ്റാ ആന്റ് സ്റ്റോറേജ് + ചരിത്രം + തിരയുക + ആഗോള തിരയൽ + ബുക്ക്മാർക്ക് അധ്യായം + എല്ലാം പ്രവർത്തനക്ഷമമാക്കുക + വായിച്ചിട്ടില്ലെന്ന് അടയാളപ്പെടുത്തുക + എല്ലാം പ്രവർത്തനരഹിതമാക്കുക + തിരുത്തുക + ചേർക്കുക + വിഭാഗം അപ്ഡേറ്റ് ചെയ്യുക + ക്രമരഹിതമായ എൻട്രി തുറക്കുക + വിഭാഗം ചേർക്കുക + വിഭാഗങ്ങൾ അക്ഷരമാലാക്രമത്തിൽ അടുക്കാൻ നിങ്ങൾ ആഗ്രഹിക്കുന്നുണ്ടോ? + കവർ എഡിറ്റ് ചെയ്യുക + അധ്യായങ്ങൾ കാണുക + വിരാമം + വീണ്ടും ശ്രമിക്കുക + മുൻ അധ്യായം + അടുത്ത അധ്യായം + ഏറ്റവും പുതിയ അധ്യായം + ഓഫ് + തിരഞ്ഞെടുക്കപ്പെട്ട + മുകളിലേക്ക് നയിക്കുക + പേര് + വിഭാഗങ്ങൾ + അദ്ധ്യായങ്ങൾ + ട്രാക്കിംഗ് + ഡൗൺലോഡ് ചെയ്‌തത് ഇല്ലാതാക്കുക + ഇനിയും + ഡൗൺലോഡ് ക്യൂ + ലൈബ്രറി + അപ്ഡേറ്റുകൾ + അടുത്തതായി + ചരിത്രം + ഉൽഭവം + ബാക്കപ്പും റിസ്റ്റോറും + ലൈബ്രറി എൻട്രികൾ + സ്ഥിതിവിവരക്കണക്കുകൾ + മൈഗ്രേറ്റ് ചെയ്യുക + വിപുലീകരണങ്ങൾ + വിപുലീകരണ വിവരം + സഹായം + സ്ഥിരസ്ഥിതി + മുന്നറിയിപ്പ് + ആരംഭിച്ചു + പ്രാദേശികമായ + ഡൗൺലോഡ് ചെയ്തു + %s അൺലോക്ക് ചെയ്യുക + മാറ്റം സ്ഥിരീകരിക്കാൻ പ്രാമാണീകരിക്കുക + ക്രമീകരണങ്ങൾ + പട്ടിക + ഫിൽട്ടർ ചെയ്യുക + ഇടവേള സജ്ജമാക്കുക + ബുക്ക്‌മാർക്ക് ചെയ്തു + ട്രാക്ക് ചെയ്തു + വായിക്കാത്തത് + ഇഷ്‌ടാനുസൃതമാക്കിയ അപ്‌ഡേറ്റ് ആവൃത്തി + ഫിൽട്ടർ നീക്കം ചെയ്യുക + അക്ഷരമാലാക്രമത്തിൽ + ആകെ എൻട്രികൾ + ആകെ അധ്യായങ്ങൾ + അവസാനം വായിച്ചത് + അവസാന അപ്ഡേറ്റ് പരിശോധന + വായിക്കാത്ത എണ്ണം + അടുത്തതായി പ്രതീക്ഷിക്കുന്ന അപ്ഡേറ്റ് + അധ്യായം ലഭിച്ച തീയതി + തീയതി ചേർത്തു + ട്രാക്കർ സ്കോർ + തിരയുക… + തിരയൽ ക്രമീകരണങ്ങൾ + എല്ലാം തിരഞ്ഞെടുക്കുക + വിപരീതം തിരഞ്ഞെടുക്കുക + വായിച്ചതായി അടയാളപ്പെടുത്തുക + മുമ്പത്തേത് വായിച്ചതായി അടയാളപ്പെടുത്തുക + ഡൗൺലോഡ് + അൺബുക്ക്മാർക്ക് അധ്യായം + ഇല്ലാതാക്കുക + ലൈബ്രറി അപ്ഡേറ്റ് ചെയ്യുക + വിഭാഗങ്ങൾ എഡിറ്റ് ചെയ്യുക + വിഭാഗത്തിൻ്റെ പേരുമാറ്റുക + വിഭാഗങ്ങൾ സജ്ജമാക്കുക + \"%s\" എന്ന വിഭാഗം ഇല്ലാതാക്കാൻ നിങ്ങൾ ആഗ്രഹിക്കുന്നുണ്ടോ? + വിഭാഗം ഇല്ലാതാക്കുക + വിഭാഗങ്ങൾ അടുക്കുക + ഓൺ + കൂടുതൽ ഓപ്ഷനുകൾ + തിരഞ്ഞെടുത്തില്ല + സ്കാൻലേറ്റർ + ക്രമീകരണങ്ങൾ + നീക്കം ചെയ്യുക + എല്ലാം നീക്കം ചെയ്യുക + ആരംഭിക്കുക + പുനരാരംഭിക്കുക + ബ്രൗസറിൽ തുറക്കുക + ക്ലിപ്പ്ബോർഡിലേയ്ക്ക് പകർത്തുക + വെബ്‌വ്യൂവിൽ തുറക്കുക + മൈഗ്രേറ്റ് ചെയ്യുക + ഡിസ്പ്ലേ മോഡ് + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ms/strings.xml b/i18n/src/commonMain/resources/MR/ms/strings.xml index eb4ffe5be..539d2d449 100644 --- a/i18n/src/commonMain/resources/MR/ms/strings.xml +++ b/i18n/src/commonMain/resources/MR/ms/strings.xml @@ -796,4 +796,5 @@ Mengurangkan kesan \'ghosting\' pada skrin e-ink Sandaran automatik terakhir:%s Mengemaskini pustaka… (%s) + Ganti \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ne/plurals.xml b/i18n/src/commonMain/resources/MR/ne/plurals.xml index dfe3035be..3f499cb5c 100644 --- a/i18n/src/commonMain/resources/MR/ne/plurals.xml +++ b/i18n/src/commonMain/resources/MR/ne/plurals.xml @@ -68,4 +68,8 @@ %d रेपो %d रेपोहरु + + भोलि + %1$d दिनमा + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ne/strings.xml b/i18n/src/commonMain/resources/MR/ne/strings.xml index 7b110f49c..62f4f896e 100644 --- a/i18n/src/commonMain/resources/MR/ne/strings.xml +++ b/i18n/src/commonMain/resources/MR/ne/strings.xml @@ -92,7 +92,7 @@ तपाईंको पुस्तकालय खाली छ हालै केहि पढेको छैन हालैका कुनै अपडेट छैन - डाउनलोड लाम + डाउनलोड सूची कुनै डाउनलोडहरू छैन मद्दत एक्सटेन्शनको जानकारी @@ -351,7 +351,7 @@ कुनै Wi-Fi जडान उपलब्ध छैन डाउनलोडहरू रोकियो कुनै नेटवर्क जडान उपलब्ध छैन - बाह्य ट्र्याकर सेवाहरूमा अध्याय प्रगति अपडेट गर्न एक-तर्फी सिङ्क। तिनीहरूको ट्र्याकिङ बटनबाट व्यक्तिगत इन्ट्रीहरूको लागि ट्र्याकिङ सेट अप गर्नुहोस्। + बाह्य ट्र्याकर सेवाहरूमा अध्याय प्रगति अपडेट गर्न एकतर्फी सिंक। तिनीहरूको ट्र्याकिङ बटनबाट व्यक्तिगत इन्ट्रीहरूको लागि ट्र्याकिङ सेट अप गर्नुहोस्। ट्र्याकरहरूमा लगइन छैनन्: छुटेको स्रोतहरू: अध्याय क्यास खाली गर्नुहोस् @@ -540,7 +540,7 @@ डाउनलोड अघिल्लो अध्याय छैन थोरै जम्मा गर्ने ठाउँ भएको कारणले अध्याय डाउनलोड हुन सकिँदएन - सावधान: ठूलो हिस्सामा डाउनलोड गर्नाले स्रोत ढिलो चल्न अनि/वा ताचियोमीलाई अवरुद्घ गर्न सक्नेछ। थप जान्न ट्याप गर्नुहोस्। + सावधान: ठूलो हिस्सामा डाउनलोड गर्नाले स्रोत ढिलो चल्न अनि/वा Mihon लाई अवरुद्घ गर्न सक्नेछ। थप जान्न ट्याप गर्नुहोस्। ट्र्याकिङ ट्र्याक गर्नुहोस् पढ्दै @@ -616,7 +616,7 @@ %s एक अप्रत्याशित त्रुटिमा पर्यो। समर्थन को लागि हामी तपाईंलाई हाम्रो Discord को #support च्यानलमा क्र्यास लगहरू साझेदारी गर्न सुझाव दिन्छौं। GitHub मा खोल्नुहोस् डाउनलोड गरिएका अध्यायहरू पुन: जाँच गर्न एपलाई फोर्स गर्नुहोस् - एकतर्फी प्रगति सिङ्क, परिष्कृत सिङ्क + एकतर्फी प्रगति सिंक, परिष्कृत सिंक क्र्यास लग डम्प, ब्याट्री अप्टिमाइजेसन तस्वीर बचत गर्न त्रुटि भयो भर्खरै @@ -716,8 +716,8 @@ ठिक छ डाउनलोड गरिएको हटाउनुहोस् परिणामहरू भएको - पुस्तकालय सिङ्क सम्पन्न भयो - पुस्तकालय सिङ्क गर्दै + पुस्तकालय सिंक सम्पन्न भयो + पुस्तकालय सिंक गर्दै डाउनलोड इन्डेक्स अवैध भयो Cloudflare सम्बन्धित मद्दतको लागि यहाँ ट्याप गर्नुहोस् ट्र्याकर लगइन @@ -808,4 +808,10 @@ साइन गर्ने की फिंगरप्रिन्ट पहिले नै अवस्थित छ रिपो %1$s सँग %2$s जस्तै साइन गर्ने की फिंगरप्रिन्ट छ। \nयदि यो अपेक्षित छ भने, %2$s प्रतिस्थापन गरिनेछ, अन्यथा आफ्नो रिपो प्रबन्धकलाई सम्पर्क गर्नुहोस्। + आगामी + आगामी अपडेटहरू हेर्नुहोस् + आगामी गाइड + अर्को महिना + अघिल्लो महिना + लगातार फोल्डर पहुँच प्राप्त गर्न असफल। एपले अप्रत्याशित रूपमा व्यवहार गर्न सक्छ। \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/pl/strings.xml b/i18n/src/commonMain/resources/MR/pl/strings.xml index 214d732c6..8fdff8642 100644 --- a/i18n/src/commonMain/resources/MR/pl/strings.xml +++ b/i18n/src/commonMain/resources/MR/pl/strings.xml @@ -801,4 +801,11 @@ Pominięto, ponieważ nie spodziewano się dzisiaj żadnej publikacji Wyklucz skanlatorów Wyłącz oddalenie + Dodaj mimo to + Zmigruj istniejący wpis + Zamień + Pokaż nadchodzące aktualizacje + Następny miesiąc + Poprzedni miesiąc + Nadchodzący \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/pt-rBR/strings.xml b/i18n/src/commonMain/resources/MR/pt-rBR/strings.xml index 4a03ce56d..5a3ea0d86 100644 --- a/i18n/src/commonMain/resources/MR/pt-rBR/strings.xml +++ b/i18n/src/commonMain/resources/MR/pt-rBR/strings.xml @@ -797,4 +797,16 @@ Revogar a confiabilidade de extensões desconhecidas Abrir repositório da fonte Desativar redução de zoom + Adicionar de qualquer maneira + Substituir + Próximo + Migrar entrada existente + A impressão digital da chave de assinatura já existe + O repositório %1$s tem a mesma impressão digital da chave de assinatura que %2$s. +\nSe isso for esperado, %2$s será substituído, caso contrário, entre em contato com o mantenedor do repositório. + Perfil de exibição personalizado + Exibir as próximas atualizações + Guia de próximos lançamentos + Próximo mês + Mês anterior \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/pt/strings.xml b/i18n/src/commonMain/resources/MR/pt/strings.xml index e447f8a33..70ddb44b2 100644 --- a/i18n/src/commonMain/resources/MR/pt/strings.xml +++ b/i18n/src/commonMain/resources/MR/pt/strings.xml @@ -717,4 +717,99 @@ Navegar para cima Desbloquear %s Dados e armazenamento + Uso de armazenamento + Disponível: %1$s / Total: %2$s + Índice de descargas invalidado + Não foi possível resolver %s + Frequência de atualização personalizada: + Remover o monitoramento do %s? + Nenhum ficheiro selecionado + Toque aqui para obter ajuda com o Cloudflare + Deletar repositório + Adicionar de qualquer maneira + Migrar entrada existente + Seja notificado para atualizações da biblioteca e mais. + A reinstalar o %s? + Datas relativas + Permissões são necessárias para instalar extensões. Toque aqui para conceder. + Não tem repositórios definidos. + Adicionar repositório + URL do repositório + Perfil de exibição personalizado + Sem conexão de internet + Substituir + A impressão digital da chave de assinatura já existe + Flash ao mudar de página + Local de armazenamento + Criar + Configurações da app + A sincronizar a biblioteca + A atualizar a biblioteca… (%s) + HTTP %d, verifique o site na WebView + Deseja ordenar as categorias alfabeticamente? + Avaliação no monitorador + Aplicar + OK + Reverter para o padrão + Bem-vindo(a)! + Pular + Selecionar uma pasta + Uma pasta deve ser selecionada + A atualizar de uma versão anterior e não tem certeza do que selecionar? Consulte o guia de armazenamento para mais informações. + Guia de armazenamento + Permissão de instalação de apps + Para instalar extensões de fontes. + Permissão de notificação + Uso de pilha em plano de fundo + Evite interrupções para tarefas longas como atualizações da biblioteca, descargas e restauração de backups. + Conceder + Novo no %s? Recomendamos dar uma olhada no guia de introdução. + Nord + \"%1$s\" ao invés de \"%2$s\" + Adiciona repositórios adicionais ao Mihon. Deve ser uma URL que termine com \"index.min.json\". + URL do repositório inválido + Deseja deletar o repositório \"%s\"? + Abrir repositório da fonte + O repositório %1$s tem a mesma impressão digital da chave de assinatura que %2$s. +\nSe isto for esperado, %2$s será substituído, caso contrário, entre em contato com o mantenedor do repositório. + Reduz o efeito fantasma em ecrãs de e-ink + Erro completo: + Configurações das fontes + Incluir configurações sensíveis (tokens de login dos monitoradores, por exemplo) + Não foi possível criar o ficheiro do backup + Licenciado - Nenhum capítulo para mostrar + Há resultados + Em breve + Nunca + Guia de próximos lançamentos + Próximo mês + Mês anterior + Também remover do %s + Vamos definir algumas coisas primeiro. Sempre pode fazer alterações nas configurações depois também. + Próximo + Começar + Escolha uma pasta onde o %1$s irá armazenar as descargas de capítulos, backups e mais. +\n +\nUma pasta dedicada é recomendada. +\n +\nPasta selecionada: %2$s + Apagar scanlators + Nenhum scanlator encontrado + Ordenar as categorias + Mover série para o final + Guia de introdução + Atualização inteligente + Revogar a confiabilidade de extensões desconhecidas + Repositórios de extensões + Este repositório já existe! + Local de armazenamento não definido + Login do monitorador + Usado para backups automáticos, descargas de capítulos e na fonte local. + Último backup automático feito em: %s + Sincronização da biblioteca finalizada + Novos capítulos com previsão de serem lançados em torno de %1$s, verificando em torno de cada %2$s. + Isto irá remover o monitoramento localmente. + Desativar redução de zoom + Exibir as próximas atualizações + A seguir \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ro/plurals.xml b/i18n/src/commonMain/resources/MR/ro/plurals.xml index 17e794c1c..2ba7c8dbe 100644 --- a/i18n/src/commonMain/resources/MR/ro/plurals.xml +++ b/i18n/src/commonMain/resources/MR/ro/plurals.xml @@ -85,4 +85,9 @@ %d repozitorii %d de repozitorii + + Mâine + În %1$d zile + În %1$d de zile + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ro/strings.xml b/i18n/src/commonMain/resources/MR/ro/strings.xml index b4b2fdebf..d43caf5c5 100644 --- a/i18n/src/commonMain/resources/MR/ro/strings.xml +++ b/i18n/src/commonMain/resources/MR/ro/strings.xml @@ -95,11 +95,9 @@ Nesigură Dezinstalează Extensie nesigură - Extensia a fost semnată cu un certificat nesigur și nu a fost activată. + Extensiile rău intenționate pot citi orice credențiale de autentificare stocate sau pot executa cod arbitrar. \n -\nO extensie periculoasă ar putea citi orice date de logare stocate în Mihon sau executa cod periculos. -\n -\nPrin acordarea încrederii acestui certificat acceptați riscurile menționate. +\nAcordând încredere acestei extensii, acceptați aceste riscuri. Ecran complet Animați tranzițiile de pagini Capitole descărcate @@ -119,7 +117,7 @@ Mod de citire implicit De la stânga la dreapta De la dreapta la stânga - Vertical + Paginat (vertical) Benzi desenate web Setări pagini Tip de scalare @@ -376,7 +374,7 @@ Grilă confortabilă Migrează Date - Fișierul de rezerva este invalid + Fișier de rezervă invalid: Nu s-au găsit pagini Tab-uri Afișează filele categoriei @@ -460,9 +458,7 @@ În formă de L Data obținerii capitolului Urmărit - Datele din fișierul de rezervă vor fi restaurate. -\n -\nVa trebui să instalați toate extensiile lipsă și să vă conectați ulterior la serviciile de urmărire pentru a le utiliza. + Este posibil să fie necesar să instalați extensiile lipsă și să vă conectați ulterior la serviciile de urmărire pentru a le utiliza. Nu vor fi descărcate înscrierile din categoriile excluse, chiar dacă acestea se află și în categoriile incluse. Descărcare automată Dreapta @@ -797,4 +793,18 @@ Acest repozitoriu există deja! Șterge repozitoriu URL repozitoriu invalid + Viitoare + Nord + Repozitoriul %1$s are acceeași amprenta digitală a cheii de semnare ca și %2$s +\nÎn cazul în care asta este la ce vă așteptați, %2$s va fi înlocuit, altfel contatati întreținătorul repozitoriului. + Profil de afișare personalizat + Adăugați oricum + Migrați intrare existentă + Înlocuiți + Amprenta digitală a cheii de semnare există deja + Dezactivați funcția de zoom-out + Vizualizați actualizările viitoare + Ghidul viitor + Luna următoare + Luna anterioară \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ru/strings.xml b/i18n/src/commonMain/resources/MR/ru/strings.xml index bf0909033..1337da8d9 100644 --- a/i18n/src/commonMain/resources/MR/ru/strings.xml +++ b/i18n/src/commonMain/resources/MR/ru/strings.xml @@ -809,4 +809,5 @@ Предстоящее Предстоящее руководство Следующий месяц + Не удалось получить постоянный доступ к папке. Приложение может работать некорректно. \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/sr/strings.xml b/i18n/src/commonMain/resources/MR/sr/strings.xml index 36eb985a0..aab9d56c0 100644 --- a/i18n/src/commonMain/resources/MR/sr/strings.xml +++ b/i18n/src/commonMain/resources/MR/sr/strings.xml @@ -801,4 +801,9 @@ Ни једна датотека није одабрана Примени Врати на подразумевано + Dodati svejedno + Predstojeći + Migrirajte postojeći unos + Zameniti + Otisak prsta za otključavanje već postoji \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/uk/strings.xml b/i18n/src/commonMain/resources/MR/uk/strings.xml index d03c85884..276fd442d 100644 --- a/i18n/src/commonMain/resources/MR/uk/strings.xml +++ b/i18n/src/commonMain/resources/MR/uk/strings.xml @@ -78,7 +78,7 @@ Кожні 6 годин Кожні 12 годин Щодня - Кожні 2 дні + Що 2 дні Щонеділі Всі Обмеження пристрою для автоматичних оновлень @@ -796,4 +796,16 @@ Почнемо Давайте для початку налаштуємо дещо. Ви завжди зможете змінити ці налаштування потім. Гайд новачка + Додати у будь-якому разі + Відбиток пальця для підпису ключа вже існує + Репозиторій %1$s має такий самий відбиток ключа підпису, як і %2$s. +\nЯкщо це очікувано, %2$s буде замінено, в іншому випадку зверніться до вашого обслуговуючого репозиторію. + Переглянути майбутні оновлення + Попередній місяць + Перенести існуючий запис + Замінити + Власний профіль відображення + Вимкнути зменшення масштабу + Незабаром + Наступний місяць \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/vi/plurals.xml b/i18n/src/commonMain/resources/MR/vi/plurals.xml index df9978d19..013eada7a 100644 --- a/i18n/src/commonMain/resources/MR/vi/plurals.xml +++ b/i18n/src/commonMain/resources/MR/vi/plurals.xml @@ -48,4 +48,10 @@ %d ngày + + %d kho + + + Trong %1$d ngày + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/vi/strings.xml b/i18n/src/commonMain/resources/MR/vi/strings.xml index ca4675d6e..31a3761cc 100644 --- a/i18n/src/commonMain/resources/MR/vi/strings.xml +++ b/i18n/src/commonMain/resources/MR/vi/strings.xml @@ -781,4 +781,34 @@ Mã lỗi đầy đủ: Cần phải cấp phép để cài đặt tiện ích mở rộng. Bấm đây để cấp. Bao gồm những cài đặt nhạy cảm (ví dụ như token đăng nhập tracker) + Luôn luôn thêm + Phương bắc + Cập nhật thông minh + Sắp tới + Chuyển vào các mục hiện có + Cập nhật từ phiên bản cũ hơn và không biết nên chọn gì? Tham khảo hướng dẫn để biết thêm thông tin. + Thêm nguồn bổ sung vào Mihon. Địa chỉ URL này cần phải kết thúc với \"index.min.json\". + Kho lưu trữ %1$s có Dấu vân tay Khóa ký giống hệt như %2$s. +\nNếu điều này là dự kiến, %2$s sẽ bị thay thế. Nếu không, vui lòng liên hệ với người quản lý kho lưu trữ của bạn. + Bạn chưa có nguồn nào. + Thu hồi quyền truy cập của tiện ích không xác định + Thêm nguồn + Nguồn đã có rồi! + Xóa nguồn + Hướng dẫn + Quản lý nguồn + URL nguồn không hợp lệ + URL nguồn + Bạn có muốn xóa nguồn \"%s\"? + Mở nguồn + Sớm + Tần suất cập nhật tùy chỉnh: + Tùy chỉnh hồ sơ màn hình + Tắt thu nhỏ + Xem các cập nhật sắp tới + Hướng dẫn sắp tới + Tháng tiếp theo + Tháng trước + Thay thế + Dấu vân tay cho khóa ký đã được tạo trước đó. \ No newline at end of file From 119bcbf8ed2415664922ea77fadf0da1165d1732 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 8 Jun 2024 06:38:35 +0600 Subject: [PATCH 090/146] Check category order before restoring from backup Closes #632 Co-authored-by: Cologler <10906962+Cologler@users.noreply.github.com> --- .../restore/restorers/CategoriesRestorer.kt | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/CategoriesRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/CategoriesRestorer.kt index f98af1045..23a2d47fa 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/CategoriesRestorer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/CategoriesRestorer.kt @@ -17,14 +17,20 @@ class CategoriesRestorer( if (backupCategories.isNotEmpty()) { val dbCategories = getCategories.await() val dbCategoriesByName = dbCategories.associateBy { it.name } + var nextOrder = dbCategories.maxOfOrNull { it.order }?.plus(1) ?: 0 - val categories = backupCategories.map { - dbCategoriesByName[it.name] - ?: handler.awaitOneExecutable { - categoriesQueries.insert(it.name, it.order, it.flags) + val categories = backupCategories + .sortedBy { it.order } + .map { + val dbCategory = dbCategoriesByName[it.name] + if (dbCategory != null) return@map dbCategory + val order = nextOrder++ + handler.awaitOneExecutable { + categoriesQueries.insert(it.name, order, it.flags) categoriesQueries.selectLastInsertedRowId() - }.let { id -> it.toCategory(id) } - } + } + .let { id -> it.toCategory(id).copy(order = order) } + } libraryPreferences.categorizedDisplaySettings().set( (dbCategories + categories) From 6a80305d6c572da6c08c0c69f5c25ff26ecf7383 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 8 Jun 2024 07:07:47 +0600 Subject: [PATCH 091/146] Fix chapter number parsing when number is after unwanted tag Fixes #554 Co-authored-by: Naputt1 <94742489+Naputt1@users.noreply.github.com> --- .../chapter/service/ChapterRecognition.kt | 42 +++++++++++-------- .../chapter/service/ChapterRecognitionTest.kt | 11 +++++ 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/domain/src/main/java/tachiyomi/domain/chapter/service/ChapterRecognition.kt b/domain/src/main/java/tachiyomi/domain/chapter/service/ChapterRecognition.kt index b2c3f6b44..3190e0456 100644 --- a/domain/src/main/java/tachiyomi/domain/chapter/service/ChapterRecognition.kt +++ b/domain/src/main/java/tachiyomi/domain/chapter/service/ChapterRecognition.kt @@ -41,27 +41,35 @@ object ChapterRecognition { } // Get chapter title with lower case - var name = chapterName.lowercase() + val cleanChapterName = chapterName.lowercase() + // Remove manga title from chapter title. + .replace(mangaTitle.lowercase(), "").trim() + // Remove comma's or hyphens. + .replace(',', '.') + .replace('-', '.') + // Remove unwanted white spaces. + .replace(unwantedWhiteSpace, "") - // Remove manga title from chapter title. - name = name.replace(mangaTitle.lowercase(), "").trim() + val numberMatch = number.findAll(cleanChapterName) - // Remove comma's or hyphens. - name = name.replace(',', '.').replace('-', '.') + when { + numberMatch.none() -> { + return chapterNumber ?: -1.0 + } + numberMatch.count() > 1 -> { + // Remove unwanted tags. + unwanted.replace(cleanChapterName, "").let { name -> + // Check base case ch.xx + basic.find(name)?.let { return getChapterNumberFromMatch(it) } - // Remove unwanted white spaces. - name = unwantedWhiteSpace.replace(name, "") + // need to find again first number might already removed + number.find(name)?.let { return getChapterNumberFromMatch(it) } + } + } + } - // Remove unwanted tags. - name = unwanted.replace(name, "") - - // Check base case ch.xx - basic.find(name)?.let { return getChapterNumberFromMatch(it) } - - // Take the first number encountered. - number.find(name)?.let { return getChapterNumberFromMatch(it) } - - return chapterNumber ?: -1.0 + // return the first number encountered + return getChapterNumberFromMatch(numberMatch.first()) } /** diff --git a/domain/src/test/java/tachiyomi/domain/chapter/service/ChapterRecognitionTest.kt b/domain/src/test/java/tachiyomi/domain/chapter/service/ChapterRecognitionTest.kt index 90ee3a486..8369bfa5c 100644 --- a/domain/src/test/java/tachiyomi/domain/chapter/service/ChapterRecognitionTest.kt +++ b/domain/src/test/java/tachiyomi/domain/chapter/service/ChapterRecognitionTest.kt @@ -171,6 +171,17 @@ class ChapterRecognitionTest { assertChapter(mangaTitle, "Tokyo ESP 027: Part 002: Chapter 001", 027.0) } + /** + * Case where the chapter title contains the unwanted tag + * But follow by chapter number. + */ + @Test + fun `Number after unwanted tag`() { + val mangaTitle = "One-punch Man" + + assertChapter(mangaTitle, "Mag Version 195.5", 195.5) + } + @Test fun `Unparseable chapter`() { val mangaTitle = "random" From e9d69a83febccf8840dad03597e3ac2a6aa3f972 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 11 Jun 2024 14:51:28 +0600 Subject: [PATCH 092/146] fix(deps): update dependency com.android.tools.build:gradle to v8.4.2 (#883) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index ff0416cb7..74c1e268c 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -1,5 +1,5 @@ [versions] -agp_version = "8.4.1" +agp_version = "8.4.2" lifecycle_version = "2.8.1" paging_version = "3.3.0" From 8e8ee69bbacb2260d0ae52808c02684e567119b9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 13 Jun 2024 02:21:02 +0600 Subject: [PATCH 093/146] fix(deps): update lifecycle.version to v2.8.2 (#889) fix(deps): update dependency androidx.lifecycle:lifecycle-runtime-ktx to v2.8.2 Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 74c1e268c..71b16058e 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -1,6 +1,6 @@ [versions] agp_version = "8.4.2" -lifecycle_version = "2.8.1" +lifecycle_version = "2.8.2" paging_version = "3.3.0" [libraries] From af57e124f2113f78028771f1579a356884d7ead7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 13 Jun 2024 02:21:15 +0600 Subject: [PATCH 094/146] fix(deps): update dependency androidx.glance:glance-appwidget to v1.1.0 (#890) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/compose.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 85091b5d3..b5e306abe 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -15,6 +15,6 @@ ui-util = { module = "androidx.compose.ui:ui-util" } material3-core = { module = "androidx.compose.material3:material3" } material-icons = { module = "androidx.compose.material:material-icons-extended" } -glance = "androidx.glance:glance-appwidget:1.0.0" +glance = "androidx.glance:glance-appwidget:1.1.0" accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanist" } From 6d8cfd5f30753025434b203840a8a1b49b94ed95 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 13 Jun 2024 02:31:28 +0600 Subject: [PATCH 095/146] chore(deps): update actions/checkout action to v4.1.7 (#891) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- .github/workflows/build_push.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index ff697921b..28b914927 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Clone repo - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Validate Gradle Wrapper uses: gradle/wrapper-validation-action@216d1ad2b3710bf005dc39237337b9673fd8fcd5 # v3.3.2 diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index 90eee2c09..98e3a7013 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -17,7 +17,7 @@ jobs: steps: - name: Clone repo - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Validate Gradle Wrapper uses: gradle/wrapper-validation-action@216d1ad2b3710bf005dc39237337b9673fd8fcd5 # v3.3.2 From 9fa22f0b378084478d6e5fa1040a7fd8da62356a Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Thu, 13 Jun 2024 02:45:55 +0600 Subject: [PATCH 096/146] Migrate to `gradle/actions/wrapper-validation` (#892) --- .github/workflows/build_pull_request.yml | 2 +- .github/workflows/build_push.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 28b914927..a767948ca 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@216d1ad2b3710bf005dc39237337b9673fd8fcd5 # v3.3.2 + uses: gradle/actions/wrapper-validation@db19848a5fa7950289d3668fb053140cf3028d43 # v3.3.2 - name: Dependency Review uses: actions/dependency-review-action@72eb03d02c7872a771aacd928f3123ac62ad6d3a # v4.3.3 diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index 98e3a7013..f1e10ee4d 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -20,7 +20,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@216d1ad2b3710bf005dc39237337b9673fd8fcd5 # v3.3.2 + uses: gradle/actions/wrapper-validation@db19848a5fa7950289d3668fb053140cf3028d43 # v3.3.2 - name: Setup Android SDK run: | From f696f209c6b3efb3148e1d587af9e42c71d8dc6f Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Thu, 13 Jun 2024 03:13:57 +0600 Subject: [PATCH 097/146] Fix issue with creating and restoring backup Fixes #881 --- .../java/eu/kanade/tachiyomi/data/backup/BackupDecoder.kt | 3 +-- .../eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt | 3 +-- .../java/eu/kanade/tachiyomi/data/backup/models/Backup.kt | 4 ---- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupDecoder.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupDecoder.kt index e33572caf..34f862548 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupDecoder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupDecoder.kt @@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.data.backup import android.content.Context import android.net.Uri import eu.kanade.tachiyomi.data.backup.models.Backup -import eu.kanade.tachiyomi.data.backup.models.BackupSerializer import kotlinx.serialization.protobuf.ProtoBuf import okio.buffer import okio.gzip @@ -33,7 +32,7 @@ class BackupDecoder( source }.use { it.readByteArray() } - parser.decodeFromByteArray(BackupSerializer, backupString) + parser.decodeFromByteArray(Backup.serializer(), backupString) } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt index 14d05e239..42c97ca9c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt @@ -13,7 +13,6 @@ import eu.kanade.tachiyomi.data.backup.models.Backup import eu.kanade.tachiyomi.data.backup.models.BackupCategory import eu.kanade.tachiyomi.data.backup.models.BackupManga import eu.kanade.tachiyomi.data.backup.models.BackupPreference -import eu.kanade.tachiyomi.data.backup.models.BackupSerializer import eu.kanade.tachiyomi.data.backup.models.BackupSource import eu.kanade.tachiyomi.data.backup.models.BackupSourcePreferences import kotlinx.serialization.protobuf.ProtoBuf @@ -84,7 +83,7 @@ class BackupCreator( backupSourcePreferences = backupSourcePreferences(options), ) - val byteArray = parser.encodeToByteArray(BackupSerializer, backup) + val byteArray = parser.encodeToByteArray(Backup.serializer(), backup) if (byteArray.isEmpty()) { throw IllegalStateException(context.stringResource(MR.strings.empty_backup_error)) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/Backup.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/Backup.kt index cdc5c4ad2..dcf3d1174 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/Backup.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/Backup.kt @@ -1,12 +1,8 @@ package eu.kanade.tachiyomi.data.backup.models import kotlinx.serialization.Serializable -import kotlinx.serialization.Serializer import kotlinx.serialization.protobuf.ProtoNumber -@Serializer(forClass = Backup::class) -object BackupSerializer - @Serializable data class Backup( @ProtoNumber(1) val backupManga: List, From aa1714b2acf0e5b16558ea703220f60d4ecd23e9 Mon Sep 17 00:00:00 2001 From: "Weblate (bot)" Date: Wed, 12 Jun 2024 23:34:04 +0200 Subject: [PATCH 098/146] Translations update from Hosted Weblate (#878) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (804 of 804 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/ * Translated using Weblate (Croatian) Currently translated at 100.0% (804 of 804 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hr/ * Translated using Weblate (Malayalam) Currently translated at 15.5% (125 of 804 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ml/ * Translated using Weblate (Malayalam) Currently translated at 15.5% (125 of 804 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ml/ * Translated using Weblate (Malayalam) Currently translated at 94.4% (17 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ml/ --------- Co-authored-by: ɴᴇᴋᴏ Co-authored-by: Milo Ivir Co-authored-by: Akhil Raj Co-authored-by: Animeboynz --- .../commonMain/resources/MR/hr/strings.xml | 1 + .../commonMain/resources/MR/ml/plurals.xml | 2 +- .../commonMain/resources/MR/ml/strings.xml | 21 +++++++++++++++++++ .../resources/MR/zh-rTW/strings.xml | 1 + 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/i18n/src/commonMain/resources/MR/hr/strings.xml b/i18n/src/commonMain/resources/MR/hr/strings.xml index 48336a888..aafcf3da6 100644 --- a/i18n/src/commonMain/resources/MR/hr/strings.xml +++ b/i18n/src/commonMain/resources/MR/hr/strings.xml @@ -809,4 +809,5 @@ Digitalni otisak prsta za potpisivanje već postoji Repozitorij %1$s ima isti digitalni otisak ključa za potpisivanje kao %2$s. \nAko se to očekuje, %2$s će se zamijeniti, u suprotnom se obrati svom održavatelju repozitorija. + Neuspjelo dobivanje trajnog pristupa mapi. Aplikacija se može ponašati neočekivano. \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ml/plurals.xml b/i18n/src/commonMain/resources/MR/ml/plurals.xml index a3e67ba1c..589be4b61 100644 --- a/i18n/src/commonMain/resources/MR/ml/plurals.xml +++ b/i18n/src/commonMain/resources/MR/ml/plurals.xml @@ -61,7 +61,7 @@ %1$d പുതിയ അധ്യായങ്ങൾ - അധ്യായങ്ങൾ %1$s-ഉം 1-ഉം + അധ്യായങ്ങൾ %1$s-ഉം പിന്നെ 1-ഉം %1$s-ഉം %2$d-ഉം അധ്യായങ്ങൾ diff --git a/i18n/src/commonMain/resources/MR/ml/strings.xml b/i18n/src/commonMain/resources/MR/ml/strings.xml index 7a6be0bfb..b9c9409dd 100644 --- a/i18n/src/commonMain/resources/MR/ml/strings.xml +++ b/i18n/src/commonMain/resources/MR/ml/strings.xml @@ -99,4 +99,25 @@ വെബ്‌വ്യൂവിൽ തുറക്കുക മൈഗ്രേറ്റ് ചെയ്യുക ഡിസ്പ്ലേ മോഡ് + ഏറ്റവും പഴയത് + റദ്ദാക്കുക + വായന തുടരുക ബട്ടൺ + ഭാഷ + എല്ലാം റദ്ദാക്കുക + ശരി + ഈ സീരീസിനായി എല്ലാം റദ്ദാക്കുക + മുകളിലേക്ക് നീങ്ങുക + അധ്യായ നമ്പർ പ്രകാരം + ഏറ്റവും പുതിയത് + എൻട്രി കാണിക്കുക + കോംപാക്റ്റ് ഗ്രിഡ് + സുഖപ്രദമായ ഗ്രിഡ് + ഡൌൺലോഡ് ചെയ്ത അധ്യായങ്ങൾ + ലോക്കൽ സോഴ്സ് + ഡിസേബിൾ + പിൻ ചെയ്യുക + അപ്ലൈ + സോർട് + ലിസ്റ്റ് + ചിത്രം മാത്രമുള്ള ഗ്രിഡ് \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml b/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml index d2ec23ffe..e4dfbc1f6 100644 --- a/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml +++ b/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml @@ -809,4 +809,5 @@ 上個月 預告 檢視新刊預告 + 無法取得永久性資料夾存取權,應用程式可能會表現異常。 \ No newline at end of file From a5838387b1bb661ff95a51dd961f07550ee6954e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 14 Jun 2024 05:05:57 +0600 Subject: [PATCH 099/146] chore(deps): update gradle/actions action to v3.4.0 (#902) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 4 ++-- .github/workflows/build_push.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index a767948ca..0d6f228e1 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Validate Gradle Wrapper - uses: gradle/actions/wrapper-validation@db19848a5fa7950289d3668fb053140cf3028d43 # v3.3.2 + uses: gradle/actions/wrapper-validation@d9336dac04dea2507a617466bc058a3def92b18b # v3.4.0 - name: Dependency Review uses: actions/dependency-review-action@72eb03d02c7872a771aacd928f3123ac62ad6d3a # v4.3.3 @@ -35,7 +35,7 @@ jobs: distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@db19848a5fa7950289d3668fb053140cf3028d43 # v3.3.2 + uses: gradle/actions/setup-gradle@d9336dac04dea2507a617466bc058a3def92b18b # v3.4.0 - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index f1e10ee4d..8d20c072d 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -20,7 +20,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Validate Gradle Wrapper - uses: gradle/actions/wrapper-validation@db19848a5fa7950289d3668fb053140cf3028d43 # v3.3.2 + uses: gradle/actions/wrapper-validation@d9336dac04dea2507a617466bc058a3def92b18b # v3.4.0 - name: Setup Android SDK run: | @@ -33,7 +33,7 @@ jobs: distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@db19848a5fa7950289d3668fb053140cf3028d43 # v3.3.2 + uses: gradle/actions/setup-gradle@d9336dac04dea2507a617466bc058a3def92b18b # v3.4.0 - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest From 2e78bceb30908aca8e585f91942849a6e4e7cb15 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 14 Jun 2024 05:06:11 +0600 Subject: [PATCH 100/146] fix(deps): update dependency com.android.tools.build:gradle to v8.5.0 (#901) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 71b16058e..94c1c15b1 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -1,5 +1,5 @@ [versions] -agp_version = "8.4.2" +agp_version = "8.5.0" lifecycle_version = "2.8.2" paging_version = "3.3.0" From 30a6e3a6a11bf164a8bc7c9943368f7dcdf6230c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 16 Jun 2024 04:47:39 +0600 Subject: [PATCH 101/146] chore(deps): update gradle/actions action to v3.4.1 (#905) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 4 ++-- .github/workflows/build_push.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 0d6f228e1..537dee9ee 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Validate Gradle Wrapper - uses: gradle/actions/wrapper-validation@d9336dac04dea2507a617466bc058a3def92b18b # v3.4.0 + uses: gradle/actions/wrapper-validation@31ae3562f68c96d481c31bc1a8a55cc1be162f83 # v3.4.1 - name: Dependency Review uses: actions/dependency-review-action@72eb03d02c7872a771aacd928f3123ac62ad6d3a # v4.3.3 @@ -35,7 +35,7 @@ jobs: distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@d9336dac04dea2507a617466bc058a3def92b18b # v3.4.0 + uses: gradle/actions/setup-gradle@31ae3562f68c96d481c31bc1a8a55cc1be162f83 # v3.4.1 - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index 8d20c072d..ddd4cff6d 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -20,7 +20,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Validate Gradle Wrapper - uses: gradle/actions/wrapper-validation@d9336dac04dea2507a617466bc058a3def92b18b # v3.4.0 + uses: gradle/actions/wrapper-validation@31ae3562f68c96d481c31bc1a8a55cc1be162f83 # v3.4.1 - name: Setup Android SDK run: | @@ -33,7 +33,7 @@ jobs: distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@d9336dac04dea2507a617466bc058a3def92b18b # v3.4.0 + uses: gradle/actions/setup-gradle@31ae3562f68c96d481c31bc1a8a55cc1be162f83 # v3.4.1 - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest From f3226fb278cab87422255e04e647c50095b61529 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Sun, 16 Jun 2024 16:48:02 +0800 Subject: [PATCH 102/146] Update R8 to fix `NoSuchMethodError` crash (#914) --- build.gradle.kts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index 93146c0a6..c27e7d91d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,11 @@ buildscript { + // https://issuetracker.google.com/344363457 + // TODO: Remove when AGP's bundled R8 is updated + repositories { + maven("https://storage.googleapis.com/r8-releases/raw") + } dependencies { + classpath("com.android.tools:r8:8.5.21") classpath(libs.android.shortcut.gradle) classpath(libs.google.services.gradle) classpath(libs.aboutLibraries.gradle) From 4182ae89a036525c5575961a68371df249ce384f Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Mon, 17 Jun 2024 06:53:02 +0800 Subject: [PATCH 103/146] Fix R8 version configuration not working (#916) This reverts commit f3226fb278cab87422255e04e647c50095b61529. --- build.gradle.kts | 6 ------ settings.gradle.kts | 10 ++++++++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index c27e7d91d..93146c0a6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,11 +1,5 @@ buildscript { - // https://issuetracker.google.com/344363457 - // TODO: Remove when AGP's bundled R8 is updated - repositories { - maven("https://storage.googleapis.com/r8-releases/raw") - } dependencies { - classpath("com.android.tools:r8:8.5.21") classpath(libs.android.shortcut.gradle) classpath(libs.google.services.gradle) classpath(libs.aboutLibraries.gradle) diff --git a/settings.gradle.kts b/settings.gradle.kts index b1a2a2dd5..76b88ed6e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,6 +13,16 @@ pluginManagement { mavenCentral() maven(url = "https://www.jitpack.io") } + // https://issuetracker.google.com/344363457 + // TODO: Remove when AGP's bundled R8 is updated + buildscript { + repositories { + maven("https://storage.googleapis.com/r8-releases/raw") + } + dependencies { + classpath("com.android.tools:r8:8.5.21") + } + } } dependencyResolutionManagement { From 3b8ed3059ad1e54aef597155b40f2e3fe1b25c4f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 18 Jun 2024 05:08:07 +0600 Subject: [PATCH 104/146] chore(deps): update gradle/actions action to v3.4.2 (#924) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 4 ++-- .github/workflows/build_push.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 537dee9ee..ef49e0414 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Validate Gradle Wrapper - uses: gradle/actions/wrapper-validation@31ae3562f68c96d481c31bc1a8a55cc1be162f83 # v3.4.1 + uses: gradle/actions/wrapper-validation@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2 - name: Dependency Review uses: actions/dependency-review-action@72eb03d02c7872a771aacd928f3123ac62ad6d3a # v4.3.3 @@ -35,7 +35,7 @@ jobs: distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@31ae3562f68c96d481c31bc1a8a55cc1be162f83 # v3.4.1 + uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2 - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index ddd4cff6d..fd6862b3f 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -20,7 +20,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Validate Gradle Wrapper - uses: gradle/actions/wrapper-validation@31ae3562f68c96d481c31bc1a8a55cc1be162f83 # v3.4.1 + uses: gradle/actions/wrapper-validation@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2 - name: Setup Android SDK run: | @@ -33,7 +33,7 @@ jobs: distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@31ae3562f68c96d481c31bc1a8a55cc1be162f83 # v3.4.1 + uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2 - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest From 5e2a3ee92757f0ccf7f2b6edea814b92ad059996 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 21 Jun 2024 01:41:26 +0600 Subject: [PATCH 105/146] chore(deps): update softprops/action-gh-release action to v2.0.6 (#929) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_push.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index fd6862b3f..e85411cd2 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -83,7 +83,7 @@ jobs: - name: Create Release if: startsWith(github.ref, 'refs/tags/') && github.repository == 'mihonapp/mihon' - uses: softprops/action-gh-release@69320dbe05506a9a39fc8ae11030b214ec2d1f87 # v2.0.5 + uses: softprops/action-gh-release@a74c6b72af54cfa997e81df42d94703d6313a2d0 # v2.0.6 with: tag_name: ${{ env.VERSION_TAG }} name: Mihon ${{ env.VERSION_TAG }} From cf02119da55c431d0fb4c42ecfec3681d466ae43 Mon Sep 17 00:00:00 2001 From: "Weblate (bot)" Date: Thu, 20 Jun 2024 22:24:22 +0200 Subject: [PATCH 106/146] Translations update from Hosted Weblate (#904) * Translated using Weblate (Malayalam) Currently translated at 16.9% (136 of 804 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ml/ * Translated using Weblate (Swedish) Currently translated at 99.1% (797 of 804 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sv/ * Translated using Weblate (Arabic) Currently translated at 99.5% (800 of 804 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ar/ * Translated using Weblate (Swedish) Currently translated at 100.0% (804 of 804 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sv/ * Translated using Weblate (Swedish) Currently translated at 100.0% (18 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/sv/ --------- Co-authored-by: Akhil Raj Co-authored-by: Norsze Co-authored-by: Duh051 Co-authored-by: bittin1ddc447d824349b2 --- i18n/src/commonMain/resources/MR/ar/strings.xml | 9 +++++++++ i18n/src/commonMain/resources/MR/ml/strings.xml | 11 +++++++++++ i18n/src/commonMain/resources/MR/sv/plurals.xml | 4 ++++ i18n/src/commonMain/resources/MR/sv/strings.xml | 14 ++++++++++++++ 4 files changed, 38 insertions(+) diff --git a/i18n/src/commonMain/resources/MR/ar/strings.xml b/i18n/src/commonMain/resources/MR/ar/strings.xml index 3f5ca8fd2..7ebedaeee 100644 --- a/i18n/src/commonMain/resources/MR/ar/strings.xml +++ b/i18n/src/commonMain/resources/MR/ar/strings.xml @@ -796,4 +796,13 @@ إضافة مستودع من المتوقع أن يتم إصدار فصول جديدة في حوالي 1%1$s، والتحقق من كل 2%2$s . متاح: %1$s / الكل: %2$s + الشهر السابق + الدليل القادم + الشهر القادم + القادم + عرض الثحديثات القادمة + أضف على أي حال + استبدل + بصمة مفتاح الانخراط موجودة من قبل + تعطيل التصغير \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ml/strings.xml b/i18n/src/commonMain/resources/MR/ml/strings.xml index b9c9409dd..7e6ad53a5 100644 --- a/i18n/src/commonMain/resources/MR/ml/strings.xml +++ b/i18n/src/commonMain/resources/MR/ml/strings.xml @@ -120,4 +120,15 @@ സോർട് ലിസ്റ്റ് ചിത്രം മാത്രമുള്ള ഗ്രിഡ് + പുനഃസജ്ജമാക്കുക + പരമ്പര മുകളിലേക്ക് നീക്കുക + വിഭാഗം ടാബുകൾ കാണിക്കുക + അൺപിൻ ചെയ്യുക + അപ്‌ലോഡ് തീയതി പ്രകാരം + ആരോഹണം + അവരോഹണം + ഇൻസ്റ്റാൾ + താഴേക്ക് നീക്കുക + പങ്കിടുക + പ്രദർശിപ്പിക്കുക \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/sv/plurals.xml b/i18n/src/commonMain/resources/MR/sv/plurals.xml index 976b04004..83e463a38 100644 --- a/i18n/src/commonMain/resources/MR/sv/plurals.xml +++ b/i18n/src/commonMain/resources/MR/sv/plurals.xml @@ -68,4 +68,8 @@ %d förråd %d flera förråd + + Imorgon + Om %1$d dagar + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/sv/strings.xml b/i18n/src/commonMain/resources/MR/sv/strings.xml index 5231c7374..bce82c14c 100644 --- a/i18n/src/commonMain/resources/MR/sv/strings.xml +++ b/i18n/src/commonMain/resources/MR/sv/strings.xml @@ -796,4 +796,18 @@ Öppenkällkods förråd Snart Anpassad uppdateringsfrekvens: + Inaktivera utzoomning + Lägg till ändå + Ersätt + Anpassad skärmprofil + Kommande + Föregående månad + Nästa månad + Migrera existerande post + Signeringsnyckel-fingeravtryck finns redan + Förrådet %1$s har samma Signering av nyckelfingeravtryck som %2$s. +\nOm detta förväntas kommer %2$s att ersättas, annars kontakta den förråds-ansvarige. + Visa kommande uppdateringar + Kommande guide + Misslyckades med att skaffa beständig mappåtkomst. Appen kan bete sig oväntat. \ No newline at end of file From f58a05e91828a69c01d49d629e5bfa9ec7ae3ffc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 21 Jun 2024 02:43:48 +0600 Subject: [PATCH 107/146] fix(deps): update moko to v0.24.1 (#933) * fix(deps): update moko to v0.24.1 * Fix build --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> --- .../kotlin/mihon/buildlogic/tasks/LocalesConfigPlugin.kt | 2 +- gradle/libs.versions.toml | 2 +- i18n/build.gradle.kts | 8 ++------ .../{resources/MR => moko-resources}/am/plurals.xml | 0 .../{resources/MR => moko-resources}/am/strings.xml | 0 .../{resources/MR => moko-resources}/ar/plurals.xml | 0 .../{resources/MR => moko-resources}/ar/strings.xml | 0 .../{resources/MR => moko-resources}/base/plurals.xml | 0 .../{resources/MR => moko-resources}/base/strings.xml | 0 .../{resources/MR => moko-resources}/be/plurals.xml | 0 .../{resources/MR => moko-resources}/be/strings.xml | 0 .../{resources/MR => moko-resources}/bg/plurals.xml | 0 .../{resources/MR => moko-resources}/bg/strings.xml | 0 .../{resources/MR => moko-resources}/bn/plurals.xml | 0 .../{resources/MR => moko-resources}/bn/strings.xml | 0 .../{resources/MR => moko-resources}/ca/plurals.xml | 0 .../{resources/MR => moko-resources}/ca/strings.xml | 0 .../{resources/MR => moko-resources}/ceb/plurals.xml | 0 .../{resources/MR => moko-resources}/ceb/strings.xml | 0 .../{resources/MR => moko-resources}/cs/plurals.xml | 0 .../{resources/MR => moko-resources}/cs/strings.xml | 0 .../{resources/MR => moko-resources}/cv/plurals.xml | 0 .../{resources/MR => moko-resources}/cv/strings.xml | 0 .../{resources/MR => moko-resources}/da/plurals.xml | 0 .../{resources/MR => moko-resources}/da/strings.xml | 0 .../{resources/MR => moko-resources}/de/plurals.xml | 0 .../{resources/MR => moko-resources}/de/strings.xml | 0 .../{resources/MR => moko-resources}/el/plurals.xml | 0 .../{resources/MR => moko-resources}/el/strings.xml | 0 .../{resources/MR => moko-resources}/eo/plurals.xml | 0 .../{resources/MR => moko-resources}/eo/strings.xml | 0 .../{resources/MR => moko-resources}/es/plurals.xml | 0 .../{resources/MR => moko-resources}/es/strings.xml | 0 .../{resources/MR => moko-resources}/eu/plurals.xml | 0 .../{resources/MR => moko-resources}/eu/strings.xml | 0 .../{resources/MR => moko-resources}/fa/plurals.xml | 0 .../{resources/MR => moko-resources}/fa/strings.xml | 0 .../{resources/MR => moko-resources}/fi/plurals.xml | 0 .../{resources/MR => moko-resources}/fi/strings.xml | 0 .../{resources/MR => moko-resources}/fil/plurals.xml | 0 .../{resources/MR => moko-resources}/fil/strings.xml | 0 .../{resources/MR => moko-resources}/fr/plurals.xml | 0 .../{resources/MR => moko-resources}/fr/strings.xml | 0 .../{resources/MR => moko-resources}/gl/plurals.xml | 0 .../{resources/MR => moko-resources}/gl/strings.xml | 0 .../{resources/MR => moko-resources}/he/plurals.xml | 0 .../{resources/MR => moko-resources}/he/strings.xml | 0 .../{resources/MR => moko-resources}/hi/plurals.xml | 0 .../{resources/MR => moko-resources}/hi/strings.xml | 0 .../{resources/MR => moko-resources}/hr/plurals.xml | 0 .../{resources/MR => moko-resources}/hr/strings.xml | 0 .../{resources/MR => moko-resources}/hu/plurals.xml | 0 .../{resources/MR => moko-resources}/hu/strings.xml | 0 .../{resources/MR => moko-resources}/in/plurals.xml | 0 .../{resources/MR => moko-resources}/in/strings.xml | 0 .../{resources/MR => moko-resources}/it/plurals.xml | 0 .../{resources/MR => moko-resources}/it/strings.xml | 0 .../{resources/MR => moko-resources}/ja/plurals.xml | 0 .../{resources/MR => moko-resources}/ja/strings.xml | 0 .../{resources/MR => moko-resources}/jv/plurals.xml | 0 .../{resources/MR => moko-resources}/jv/strings.xml | 0 .../{resources/MR => moko-resources}/ka-rGE/plurals.xml | 0 .../{resources/MR => moko-resources}/ka-rGE/strings.xml | 0 .../{resources/MR => moko-resources}/kk/plurals.xml | 0 .../{resources/MR => moko-resources}/kk/strings.xml | 0 .../{resources/MR => moko-resources}/km/plurals.xml | 0 .../{resources/MR => moko-resources}/km/strings.xml | 0 .../{resources/MR => moko-resources}/kn/plurals.xml | 0 .../{resources/MR => moko-resources}/kn/strings.xml | 0 .../{resources/MR => moko-resources}/ko/plurals.xml | 0 .../{resources/MR => moko-resources}/ko/strings.xml | 0 .../{resources/MR => moko-resources}/lt/plurals.xml | 0 .../{resources/MR => moko-resources}/lt/strings.xml | 0 .../{resources/MR => moko-resources}/lv/plurals.xml | 0 .../{resources/MR => moko-resources}/lv/strings.xml | 0 .../{resources/MR => moko-resources}/ml/plurals.xml | 0 .../{resources/MR => moko-resources}/ml/strings.xml | 0 .../{resources/MR => moko-resources}/mr/plurals.xml | 0 .../{resources/MR => moko-resources}/mr/strings.xml | 0 .../{resources/MR => moko-resources}/ms/plurals.xml | 0 .../{resources/MR => moko-resources}/ms/strings.xml | 0 .../{resources/MR => moko-resources}/nb-rNO/plurals.xml | 0 .../{resources/MR => moko-resources}/nb-rNO/strings.xml | 0 .../{resources/MR => moko-resources}/ne/plurals.xml | 0 .../{resources/MR => moko-resources}/ne/strings.xml | 0 .../{resources/MR => moko-resources}/nl/plurals.xml | 0 .../{resources/MR => moko-resources}/nl/strings.xml | 0 .../{resources/MR => moko-resources}/nn/plurals.xml | 0 .../{resources/MR => moko-resources}/nn/strings.xml | 0 .../{resources/MR => moko-resources}/pl/plurals.xml | 0 .../{resources/MR => moko-resources}/pl/strings.xml | 0 .../{resources/MR => moko-resources}/pt-rBR/plurals.xml | 0 .../{resources/MR => moko-resources}/pt-rBR/strings.xml | 0 .../{resources/MR => moko-resources}/pt/plurals.xml | 0 .../{resources/MR => moko-resources}/pt/strings.xml | 0 .../{resources/MR => moko-resources}/ro/plurals.xml | 0 .../{resources/MR => moko-resources}/ro/strings.xml | 0 .../{resources/MR => moko-resources}/ru/plurals.xml | 0 .../{resources/MR => moko-resources}/ru/strings.xml | 0 .../{resources/MR => moko-resources}/sa/plurals.xml | 0 .../{resources/MR => moko-resources}/sa/strings.xml | 0 .../{resources/MR => moko-resources}/sah/plurals.xml | 0 .../{resources/MR => moko-resources}/sah/strings.xml | 0 .../{resources/MR => moko-resources}/sc/plurals.xml | 0 .../{resources/MR => moko-resources}/sc/strings.xml | 0 .../{resources/MR => moko-resources}/sdh/plurals.xml | 0 .../{resources/MR => moko-resources}/sdh/strings.xml | 0 .../{resources/MR => moko-resources}/sk/plurals.xml | 0 .../{resources/MR => moko-resources}/sk/strings.xml | 0 .../{resources/MR => moko-resources}/sq/plurals.xml | 0 .../{resources/MR => moko-resources}/sq/strings.xml | 0 .../{resources/MR => moko-resources}/sr/plurals.xml | 0 .../{resources/MR => moko-resources}/sr/strings.xml | 0 .../{resources/MR => moko-resources}/sv/plurals.xml | 0 .../{resources/MR => moko-resources}/sv/strings.xml | 0 .../{resources/MR => moko-resources}/te/plurals.xml | 0 .../{resources/MR => moko-resources}/te/strings.xml | 0 .../{resources/MR => moko-resources}/th/plurals.xml | 0 .../{resources/MR => moko-resources}/th/strings.xml | 0 .../{resources/MR => moko-resources}/tr/plurals.xml | 0 .../{resources/MR => moko-resources}/tr/strings.xml | 0 .../{resources/MR => moko-resources}/uk/plurals.xml | 0 .../{resources/MR => moko-resources}/uk/strings.xml | 0 .../{resources/MR => moko-resources}/uz/plurals.xml | 0 .../{resources/MR => moko-resources}/uz/strings.xml | 0 .../{resources/MR => moko-resources}/vi/plurals.xml | 0 .../{resources/MR => moko-resources}/vi/strings.xml | 0 .../{resources/MR => moko-resources}/zh-rCN/plurals.xml | 0 .../{resources/MR => moko-resources}/zh-rCN/strings.xml | 0 .../{resources/MR => moko-resources}/zh-rTW/plurals.xml | 0 .../{resources/MR => moko-resources}/zh-rTW/strings.xml | 0 131 files changed, 4 insertions(+), 8 deletions(-) rename i18n/src/commonMain/{resources/MR => moko-resources}/am/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/am/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/ar/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/ar/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/base/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/base/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/be/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/be/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/bg/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/bg/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/bn/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/bn/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/ca/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/ca/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/ceb/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/ceb/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/cs/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/cs/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/cv/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/cv/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/da/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/da/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/de/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/de/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/el/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/el/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/eo/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/eo/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/es/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/es/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/eu/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/eu/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/fa/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/fa/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/fi/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/fi/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/fil/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/fil/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/fr/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/fr/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/gl/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/gl/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/he/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/he/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/hi/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/hi/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/hr/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/hr/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/hu/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/hu/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/in/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/in/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/it/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/it/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/ja/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/ja/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/jv/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/jv/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/ka-rGE/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/ka-rGE/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/kk/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/kk/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/km/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/km/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/kn/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/kn/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/ko/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/ko/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/lt/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/lt/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/lv/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/lv/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/ml/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/ml/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/mr/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/mr/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/ms/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/ms/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/nb-rNO/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/nb-rNO/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/ne/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/ne/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/nl/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/nl/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/nn/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/nn/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/pl/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/pl/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/pt-rBR/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/pt-rBR/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/pt/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/pt/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/ro/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/ro/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/ru/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/ru/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/sa/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/sa/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/sah/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/sah/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/sc/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/sc/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/sdh/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/sdh/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/sk/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/sk/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/sq/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/sq/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/sr/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/sr/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/sv/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/sv/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/te/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/te/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/th/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/th/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/tr/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/tr/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/uk/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/uk/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/uz/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/uz/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/vi/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/vi/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/zh-rCN/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/zh-rCN/strings.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/zh-rTW/plurals.xml (100%) rename i18n/src/commonMain/{resources/MR => moko-resources}/zh-rTW/strings.xml (100%) diff --git a/buildSrc/src/main/kotlin/mihon/buildlogic/tasks/LocalesConfigPlugin.kt b/buildSrc/src/main/kotlin/mihon/buildlogic/tasks/LocalesConfigPlugin.kt index 961a3b751..d84d2cb64 100644 --- a/buildSrc/src/main/kotlin/mihon/buildlogic/tasks/LocalesConfigPlugin.kt +++ b/buildSrc/src/main/kotlin/mihon/buildlogic/tasks/LocalesConfigPlugin.kt @@ -8,7 +8,7 @@ private val emptyResourcesElement = "\\s*|".t fun Project.getLocalesConfigTask(): TaskProvider { return tasks.register("generateLocalesConfig") { - val locales = fileTree("$projectDir/src/commonMain/resources/MR/") + val locales = fileTree("$projectDir/src/commonMain/moko-resources/") .matching { include("**/strings.xml") } .filterNot { it.readText().contains(emptyResourcesElement) } .map { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bdb58f6f4..a3bfcec8f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] aboutlib_version = "11.2.1" leakcanary = "2.14" -moko = "0.23.0" +moko = "0.24.1" okhttp_version = "5.0.0-alpha.14" richtext = "0.20.0" shizuku_version = "12.2.0" diff --git a/i18n/build.gradle.kts b/i18n/build.gradle.kts index bdfe09b97..7c7256acc 100644 --- a/i18n/build.gradle.kts +++ b/i18n/build.gradle.kts @@ -13,15 +13,11 @@ kotlin { applyDefaultHierarchyTemplate() sourceSets { - val commonMain by getting { + commonMain { dependencies { api(libs.moko.core) } } - - androidMain { - dependsOn(commonMain) // https://github.com/icerockdev/moko-resources/issues/562 - } } } @@ -40,7 +36,7 @@ android { } multiplatformResources { - multiplatformResourcesPackage = "tachiyomi.i18n" + resourcesPackage.set("tachiyomi.i18n") } tasks { diff --git a/i18n/src/commonMain/resources/MR/am/plurals.xml b/i18n/src/commonMain/moko-resources/am/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/am/plurals.xml rename to i18n/src/commonMain/moko-resources/am/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/am/strings.xml b/i18n/src/commonMain/moko-resources/am/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/am/strings.xml rename to i18n/src/commonMain/moko-resources/am/strings.xml diff --git a/i18n/src/commonMain/resources/MR/ar/plurals.xml b/i18n/src/commonMain/moko-resources/ar/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/ar/plurals.xml rename to i18n/src/commonMain/moko-resources/ar/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/ar/strings.xml b/i18n/src/commonMain/moko-resources/ar/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/ar/strings.xml rename to i18n/src/commonMain/moko-resources/ar/strings.xml diff --git a/i18n/src/commonMain/resources/MR/base/plurals.xml b/i18n/src/commonMain/moko-resources/base/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/base/plurals.xml rename to i18n/src/commonMain/moko-resources/base/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/base/strings.xml b/i18n/src/commonMain/moko-resources/base/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/base/strings.xml rename to i18n/src/commonMain/moko-resources/base/strings.xml diff --git a/i18n/src/commonMain/resources/MR/be/plurals.xml b/i18n/src/commonMain/moko-resources/be/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/be/plurals.xml rename to i18n/src/commonMain/moko-resources/be/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/be/strings.xml b/i18n/src/commonMain/moko-resources/be/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/be/strings.xml rename to i18n/src/commonMain/moko-resources/be/strings.xml diff --git a/i18n/src/commonMain/resources/MR/bg/plurals.xml b/i18n/src/commonMain/moko-resources/bg/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/bg/plurals.xml rename to i18n/src/commonMain/moko-resources/bg/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/bg/strings.xml b/i18n/src/commonMain/moko-resources/bg/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/bg/strings.xml rename to i18n/src/commonMain/moko-resources/bg/strings.xml diff --git a/i18n/src/commonMain/resources/MR/bn/plurals.xml b/i18n/src/commonMain/moko-resources/bn/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/bn/plurals.xml rename to i18n/src/commonMain/moko-resources/bn/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/bn/strings.xml b/i18n/src/commonMain/moko-resources/bn/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/bn/strings.xml rename to i18n/src/commonMain/moko-resources/bn/strings.xml diff --git a/i18n/src/commonMain/resources/MR/ca/plurals.xml b/i18n/src/commonMain/moko-resources/ca/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/ca/plurals.xml rename to i18n/src/commonMain/moko-resources/ca/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/ca/strings.xml b/i18n/src/commonMain/moko-resources/ca/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/ca/strings.xml rename to i18n/src/commonMain/moko-resources/ca/strings.xml diff --git a/i18n/src/commonMain/resources/MR/ceb/plurals.xml b/i18n/src/commonMain/moko-resources/ceb/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/ceb/plurals.xml rename to i18n/src/commonMain/moko-resources/ceb/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/ceb/strings.xml b/i18n/src/commonMain/moko-resources/ceb/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/ceb/strings.xml rename to i18n/src/commonMain/moko-resources/ceb/strings.xml diff --git a/i18n/src/commonMain/resources/MR/cs/plurals.xml b/i18n/src/commonMain/moko-resources/cs/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/cs/plurals.xml rename to i18n/src/commonMain/moko-resources/cs/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/cs/strings.xml b/i18n/src/commonMain/moko-resources/cs/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/cs/strings.xml rename to i18n/src/commonMain/moko-resources/cs/strings.xml diff --git a/i18n/src/commonMain/resources/MR/cv/plurals.xml b/i18n/src/commonMain/moko-resources/cv/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/cv/plurals.xml rename to i18n/src/commonMain/moko-resources/cv/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/cv/strings.xml b/i18n/src/commonMain/moko-resources/cv/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/cv/strings.xml rename to i18n/src/commonMain/moko-resources/cv/strings.xml diff --git a/i18n/src/commonMain/resources/MR/da/plurals.xml b/i18n/src/commonMain/moko-resources/da/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/da/plurals.xml rename to i18n/src/commonMain/moko-resources/da/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/da/strings.xml b/i18n/src/commonMain/moko-resources/da/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/da/strings.xml rename to i18n/src/commonMain/moko-resources/da/strings.xml diff --git a/i18n/src/commonMain/resources/MR/de/plurals.xml b/i18n/src/commonMain/moko-resources/de/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/de/plurals.xml rename to i18n/src/commonMain/moko-resources/de/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/de/strings.xml b/i18n/src/commonMain/moko-resources/de/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/de/strings.xml rename to i18n/src/commonMain/moko-resources/de/strings.xml diff --git a/i18n/src/commonMain/resources/MR/el/plurals.xml b/i18n/src/commonMain/moko-resources/el/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/el/plurals.xml rename to i18n/src/commonMain/moko-resources/el/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/el/strings.xml b/i18n/src/commonMain/moko-resources/el/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/el/strings.xml rename to i18n/src/commonMain/moko-resources/el/strings.xml diff --git a/i18n/src/commonMain/resources/MR/eo/plurals.xml b/i18n/src/commonMain/moko-resources/eo/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/eo/plurals.xml rename to i18n/src/commonMain/moko-resources/eo/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/eo/strings.xml b/i18n/src/commonMain/moko-resources/eo/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/eo/strings.xml rename to i18n/src/commonMain/moko-resources/eo/strings.xml diff --git a/i18n/src/commonMain/resources/MR/es/plurals.xml b/i18n/src/commonMain/moko-resources/es/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/es/plurals.xml rename to i18n/src/commonMain/moko-resources/es/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/es/strings.xml b/i18n/src/commonMain/moko-resources/es/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/es/strings.xml rename to i18n/src/commonMain/moko-resources/es/strings.xml diff --git a/i18n/src/commonMain/resources/MR/eu/plurals.xml b/i18n/src/commonMain/moko-resources/eu/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/eu/plurals.xml rename to i18n/src/commonMain/moko-resources/eu/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/eu/strings.xml b/i18n/src/commonMain/moko-resources/eu/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/eu/strings.xml rename to i18n/src/commonMain/moko-resources/eu/strings.xml diff --git a/i18n/src/commonMain/resources/MR/fa/plurals.xml b/i18n/src/commonMain/moko-resources/fa/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/fa/plurals.xml rename to i18n/src/commonMain/moko-resources/fa/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/fa/strings.xml b/i18n/src/commonMain/moko-resources/fa/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/fa/strings.xml rename to i18n/src/commonMain/moko-resources/fa/strings.xml diff --git a/i18n/src/commonMain/resources/MR/fi/plurals.xml b/i18n/src/commonMain/moko-resources/fi/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/fi/plurals.xml rename to i18n/src/commonMain/moko-resources/fi/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/fi/strings.xml b/i18n/src/commonMain/moko-resources/fi/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/fi/strings.xml rename to i18n/src/commonMain/moko-resources/fi/strings.xml diff --git a/i18n/src/commonMain/resources/MR/fil/plurals.xml b/i18n/src/commonMain/moko-resources/fil/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/fil/plurals.xml rename to i18n/src/commonMain/moko-resources/fil/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/fil/strings.xml b/i18n/src/commonMain/moko-resources/fil/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/fil/strings.xml rename to i18n/src/commonMain/moko-resources/fil/strings.xml diff --git a/i18n/src/commonMain/resources/MR/fr/plurals.xml b/i18n/src/commonMain/moko-resources/fr/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/fr/plurals.xml rename to i18n/src/commonMain/moko-resources/fr/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/fr/strings.xml b/i18n/src/commonMain/moko-resources/fr/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/fr/strings.xml rename to i18n/src/commonMain/moko-resources/fr/strings.xml diff --git a/i18n/src/commonMain/resources/MR/gl/plurals.xml b/i18n/src/commonMain/moko-resources/gl/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/gl/plurals.xml rename to i18n/src/commonMain/moko-resources/gl/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/gl/strings.xml b/i18n/src/commonMain/moko-resources/gl/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/gl/strings.xml rename to i18n/src/commonMain/moko-resources/gl/strings.xml diff --git a/i18n/src/commonMain/resources/MR/he/plurals.xml b/i18n/src/commonMain/moko-resources/he/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/he/plurals.xml rename to i18n/src/commonMain/moko-resources/he/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/he/strings.xml b/i18n/src/commonMain/moko-resources/he/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/he/strings.xml rename to i18n/src/commonMain/moko-resources/he/strings.xml diff --git a/i18n/src/commonMain/resources/MR/hi/plurals.xml b/i18n/src/commonMain/moko-resources/hi/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/hi/plurals.xml rename to i18n/src/commonMain/moko-resources/hi/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/hi/strings.xml b/i18n/src/commonMain/moko-resources/hi/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/hi/strings.xml rename to i18n/src/commonMain/moko-resources/hi/strings.xml diff --git a/i18n/src/commonMain/resources/MR/hr/plurals.xml b/i18n/src/commonMain/moko-resources/hr/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/hr/plurals.xml rename to i18n/src/commonMain/moko-resources/hr/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/hr/strings.xml b/i18n/src/commonMain/moko-resources/hr/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/hr/strings.xml rename to i18n/src/commonMain/moko-resources/hr/strings.xml diff --git a/i18n/src/commonMain/resources/MR/hu/plurals.xml b/i18n/src/commonMain/moko-resources/hu/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/hu/plurals.xml rename to i18n/src/commonMain/moko-resources/hu/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/hu/strings.xml b/i18n/src/commonMain/moko-resources/hu/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/hu/strings.xml rename to i18n/src/commonMain/moko-resources/hu/strings.xml diff --git a/i18n/src/commonMain/resources/MR/in/plurals.xml b/i18n/src/commonMain/moko-resources/in/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/in/plurals.xml rename to i18n/src/commonMain/moko-resources/in/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/in/strings.xml b/i18n/src/commonMain/moko-resources/in/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/in/strings.xml rename to i18n/src/commonMain/moko-resources/in/strings.xml diff --git a/i18n/src/commonMain/resources/MR/it/plurals.xml b/i18n/src/commonMain/moko-resources/it/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/it/plurals.xml rename to i18n/src/commonMain/moko-resources/it/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/it/strings.xml b/i18n/src/commonMain/moko-resources/it/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/it/strings.xml rename to i18n/src/commonMain/moko-resources/it/strings.xml diff --git a/i18n/src/commonMain/resources/MR/ja/plurals.xml b/i18n/src/commonMain/moko-resources/ja/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/ja/plurals.xml rename to i18n/src/commonMain/moko-resources/ja/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/ja/strings.xml b/i18n/src/commonMain/moko-resources/ja/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/ja/strings.xml rename to i18n/src/commonMain/moko-resources/ja/strings.xml diff --git a/i18n/src/commonMain/resources/MR/jv/plurals.xml b/i18n/src/commonMain/moko-resources/jv/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/jv/plurals.xml rename to i18n/src/commonMain/moko-resources/jv/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/jv/strings.xml b/i18n/src/commonMain/moko-resources/jv/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/jv/strings.xml rename to i18n/src/commonMain/moko-resources/jv/strings.xml diff --git a/i18n/src/commonMain/resources/MR/ka-rGE/plurals.xml b/i18n/src/commonMain/moko-resources/ka-rGE/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/ka-rGE/plurals.xml rename to i18n/src/commonMain/moko-resources/ka-rGE/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/ka-rGE/strings.xml b/i18n/src/commonMain/moko-resources/ka-rGE/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/ka-rGE/strings.xml rename to i18n/src/commonMain/moko-resources/ka-rGE/strings.xml diff --git a/i18n/src/commonMain/resources/MR/kk/plurals.xml b/i18n/src/commonMain/moko-resources/kk/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/kk/plurals.xml rename to i18n/src/commonMain/moko-resources/kk/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/kk/strings.xml b/i18n/src/commonMain/moko-resources/kk/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/kk/strings.xml rename to i18n/src/commonMain/moko-resources/kk/strings.xml diff --git a/i18n/src/commonMain/resources/MR/km/plurals.xml b/i18n/src/commonMain/moko-resources/km/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/km/plurals.xml rename to i18n/src/commonMain/moko-resources/km/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/km/strings.xml b/i18n/src/commonMain/moko-resources/km/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/km/strings.xml rename to i18n/src/commonMain/moko-resources/km/strings.xml diff --git a/i18n/src/commonMain/resources/MR/kn/plurals.xml b/i18n/src/commonMain/moko-resources/kn/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/kn/plurals.xml rename to i18n/src/commonMain/moko-resources/kn/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/kn/strings.xml b/i18n/src/commonMain/moko-resources/kn/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/kn/strings.xml rename to i18n/src/commonMain/moko-resources/kn/strings.xml diff --git a/i18n/src/commonMain/resources/MR/ko/plurals.xml b/i18n/src/commonMain/moko-resources/ko/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/ko/plurals.xml rename to i18n/src/commonMain/moko-resources/ko/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/ko/strings.xml b/i18n/src/commonMain/moko-resources/ko/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/ko/strings.xml rename to i18n/src/commonMain/moko-resources/ko/strings.xml diff --git a/i18n/src/commonMain/resources/MR/lt/plurals.xml b/i18n/src/commonMain/moko-resources/lt/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/lt/plurals.xml rename to i18n/src/commonMain/moko-resources/lt/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/lt/strings.xml b/i18n/src/commonMain/moko-resources/lt/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/lt/strings.xml rename to i18n/src/commonMain/moko-resources/lt/strings.xml diff --git a/i18n/src/commonMain/resources/MR/lv/plurals.xml b/i18n/src/commonMain/moko-resources/lv/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/lv/plurals.xml rename to i18n/src/commonMain/moko-resources/lv/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/lv/strings.xml b/i18n/src/commonMain/moko-resources/lv/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/lv/strings.xml rename to i18n/src/commonMain/moko-resources/lv/strings.xml diff --git a/i18n/src/commonMain/resources/MR/ml/plurals.xml b/i18n/src/commonMain/moko-resources/ml/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/ml/plurals.xml rename to i18n/src/commonMain/moko-resources/ml/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/ml/strings.xml b/i18n/src/commonMain/moko-resources/ml/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/ml/strings.xml rename to i18n/src/commonMain/moko-resources/ml/strings.xml diff --git a/i18n/src/commonMain/resources/MR/mr/plurals.xml b/i18n/src/commonMain/moko-resources/mr/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/mr/plurals.xml rename to i18n/src/commonMain/moko-resources/mr/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/mr/strings.xml b/i18n/src/commonMain/moko-resources/mr/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/mr/strings.xml rename to i18n/src/commonMain/moko-resources/mr/strings.xml diff --git a/i18n/src/commonMain/resources/MR/ms/plurals.xml b/i18n/src/commonMain/moko-resources/ms/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/ms/plurals.xml rename to i18n/src/commonMain/moko-resources/ms/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/ms/strings.xml b/i18n/src/commonMain/moko-resources/ms/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/ms/strings.xml rename to i18n/src/commonMain/moko-resources/ms/strings.xml diff --git a/i18n/src/commonMain/resources/MR/nb-rNO/plurals.xml b/i18n/src/commonMain/moko-resources/nb-rNO/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/nb-rNO/plurals.xml rename to i18n/src/commonMain/moko-resources/nb-rNO/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/nb-rNO/strings.xml b/i18n/src/commonMain/moko-resources/nb-rNO/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/nb-rNO/strings.xml rename to i18n/src/commonMain/moko-resources/nb-rNO/strings.xml diff --git a/i18n/src/commonMain/resources/MR/ne/plurals.xml b/i18n/src/commonMain/moko-resources/ne/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/ne/plurals.xml rename to i18n/src/commonMain/moko-resources/ne/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/ne/strings.xml b/i18n/src/commonMain/moko-resources/ne/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/ne/strings.xml rename to i18n/src/commonMain/moko-resources/ne/strings.xml diff --git a/i18n/src/commonMain/resources/MR/nl/plurals.xml b/i18n/src/commonMain/moko-resources/nl/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/nl/plurals.xml rename to i18n/src/commonMain/moko-resources/nl/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/nl/strings.xml b/i18n/src/commonMain/moko-resources/nl/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/nl/strings.xml rename to i18n/src/commonMain/moko-resources/nl/strings.xml diff --git a/i18n/src/commonMain/resources/MR/nn/plurals.xml b/i18n/src/commonMain/moko-resources/nn/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/nn/plurals.xml rename to i18n/src/commonMain/moko-resources/nn/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/nn/strings.xml b/i18n/src/commonMain/moko-resources/nn/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/nn/strings.xml rename to i18n/src/commonMain/moko-resources/nn/strings.xml diff --git a/i18n/src/commonMain/resources/MR/pl/plurals.xml b/i18n/src/commonMain/moko-resources/pl/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/pl/plurals.xml rename to i18n/src/commonMain/moko-resources/pl/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/pl/strings.xml b/i18n/src/commonMain/moko-resources/pl/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/pl/strings.xml rename to i18n/src/commonMain/moko-resources/pl/strings.xml diff --git a/i18n/src/commonMain/resources/MR/pt-rBR/plurals.xml b/i18n/src/commonMain/moko-resources/pt-rBR/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/pt-rBR/plurals.xml rename to i18n/src/commonMain/moko-resources/pt-rBR/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/pt-rBR/strings.xml b/i18n/src/commonMain/moko-resources/pt-rBR/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/pt-rBR/strings.xml rename to i18n/src/commonMain/moko-resources/pt-rBR/strings.xml diff --git a/i18n/src/commonMain/resources/MR/pt/plurals.xml b/i18n/src/commonMain/moko-resources/pt/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/pt/plurals.xml rename to i18n/src/commonMain/moko-resources/pt/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/pt/strings.xml b/i18n/src/commonMain/moko-resources/pt/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/pt/strings.xml rename to i18n/src/commonMain/moko-resources/pt/strings.xml diff --git a/i18n/src/commonMain/resources/MR/ro/plurals.xml b/i18n/src/commonMain/moko-resources/ro/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/ro/plurals.xml rename to i18n/src/commonMain/moko-resources/ro/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/ro/strings.xml b/i18n/src/commonMain/moko-resources/ro/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/ro/strings.xml rename to i18n/src/commonMain/moko-resources/ro/strings.xml diff --git a/i18n/src/commonMain/resources/MR/ru/plurals.xml b/i18n/src/commonMain/moko-resources/ru/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/ru/plurals.xml rename to i18n/src/commonMain/moko-resources/ru/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/ru/strings.xml b/i18n/src/commonMain/moko-resources/ru/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/ru/strings.xml rename to i18n/src/commonMain/moko-resources/ru/strings.xml diff --git a/i18n/src/commonMain/resources/MR/sa/plurals.xml b/i18n/src/commonMain/moko-resources/sa/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/sa/plurals.xml rename to i18n/src/commonMain/moko-resources/sa/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/sa/strings.xml b/i18n/src/commonMain/moko-resources/sa/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/sa/strings.xml rename to i18n/src/commonMain/moko-resources/sa/strings.xml diff --git a/i18n/src/commonMain/resources/MR/sah/plurals.xml b/i18n/src/commonMain/moko-resources/sah/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/sah/plurals.xml rename to i18n/src/commonMain/moko-resources/sah/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/sah/strings.xml b/i18n/src/commonMain/moko-resources/sah/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/sah/strings.xml rename to i18n/src/commonMain/moko-resources/sah/strings.xml diff --git a/i18n/src/commonMain/resources/MR/sc/plurals.xml b/i18n/src/commonMain/moko-resources/sc/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/sc/plurals.xml rename to i18n/src/commonMain/moko-resources/sc/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/sc/strings.xml b/i18n/src/commonMain/moko-resources/sc/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/sc/strings.xml rename to i18n/src/commonMain/moko-resources/sc/strings.xml diff --git a/i18n/src/commonMain/resources/MR/sdh/plurals.xml b/i18n/src/commonMain/moko-resources/sdh/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/sdh/plurals.xml rename to i18n/src/commonMain/moko-resources/sdh/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/sdh/strings.xml b/i18n/src/commonMain/moko-resources/sdh/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/sdh/strings.xml rename to i18n/src/commonMain/moko-resources/sdh/strings.xml diff --git a/i18n/src/commonMain/resources/MR/sk/plurals.xml b/i18n/src/commonMain/moko-resources/sk/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/sk/plurals.xml rename to i18n/src/commonMain/moko-resources/sk/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/sk/strings.xml b/i18n/src/commonMain/moko-resources/sk/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/sk/strings.xml rename to i18n/src/commonMain/moko-resources/sk/strings.xml diff --git a/i18n/src/commonMain/resources/MR/sq/plurals.xml b/i18n/src/commonMain/moko-resources/sq/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/sq/plurals.xml rename to i18n/src/commonMain/moko-resources/sq/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/sq/strings.xml b/i18n/src/commonMain/moko-resources/sq/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/sq/strings.xml rename to i18n/src/commonMain/moko-resources/sq/strings.xml diff --git a/i18n/src/commonMain/resources/MR/sr/plurals.xml b/i18n/src/commonMain/moko-resources/sr/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/sr/plurals.xml rename to i18n/src/commonMain/moko-resources/sr/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/sr/strings.xml b/i18n/src/commonMain/moko-resources/sr/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/sr/strings.xml rename to i18n/src/commonMain/moko-resources/sr/strings.xml diff --git a/i18n/src/commonMain/resources/MR/sv/plurals.xml b/i18n/src/commonMain/moko-resources/sv/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/sv/plurals.xml rename to i18n/src/commonMain/moko-resources/sv/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/sv/strings.xml b/i18n/src/commonMain/moko-resources/sv/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/sv/strings.xml rename to i18n/src/commonMain/moko-resources/sv/strings.xml diff --git a/i18n/src/commonMain/resources/MR/te/plurals.xml b/i18n/src/commonMain/moko-resources/te/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/te/plurals.xml rename to i18n/src/commonMain/moko-resources/te/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/te/strings.xml b/i18n/src/commonMain/moko-resources/te/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/te/strings.xml rename to i18n/src/commonMain/moko-resources/te/strings.xml diff --git a/i18n/src/commonMain/resources/MR/th/plurals.xml b/i18n/src/commonMain/moko-resources/th/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/th/plurals.xml rename to i18n/src/commonMain/moko-resources/th/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/th/strings.xml b/i18n/src/commonMain/moko-resources/th/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/th/strings.xml rename to i18n/src/commonMain/moko-resources/th/strings.xml diff --git a/i18n/src/commonMain/resources/MR/tr/plurals.xml b/i18n/src/commonMain/moko-resources/tr/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/tr/plurals.xml rename to i18n/src/commonMain/moko-resources/tr/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/tr/strings.xml b/i18n/src/commonMain/moko-resources/tr/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/tr/strings.xml rename to i18n/src/commonMain/moko-resources/tr/strings.xml diff --git a/i18n/src/commonMain/resources/MR/uk/plurals.xml b/i18n/src/commonMain/moko-resources/uk/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/uk/plurals.xml rename to i18n/src/commonMain/moko-resources/uk/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/uk/strings.xml b/i18n/src/commonMain/moko-resources/uk/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/uk/strings.xml rename to i18n/src/commonMain/moko-resources/uk/strings.xml diff --git a/i18n/src/commonMain/resources/MR/uz/plurals.xml b/i18n/src/commonMain/moko-resources/uz/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/uz/plurals.xml rename to i18n/src/commonMain/moko-resources/uz/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/uz/strings.xml b/i18n/src/commonMain/moko-resources/uz/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/uz/strings.xml rename to i18n/src/commonMain/moko-resources/uz/strings.xml diff --git a/i18n/src/commonMain/resources/MR/vi/plurals.xml b/i18n/src/commonMain/moko-resources/vi/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/vi/plurals.xml rename to i18n/src/commonMain/moko-resources/vi/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/vi/strings.xml b/i18n/src/commonMain/moko-resources/vi/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/vi/strings.xml rename to i18n/src/commonMain/moko-resources/vi/strings.xml diff --git a/i18n/src/commonMain/resources/MR/zh-rCN/plurals.xml b/i18n/src/commonMain/moko-resources/zh-rCN/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/zh-rCN/plurals.xml rename to i18n/src/commonMain/moko-resources/zh-rCN/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/zh-rCN/strings.xml b/i18n/src/commonMain/moko-resources/zh-rCN/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/zh-rCN/strings.xml rename to i18n/src/commonMain/moko-resources/zh-rCN/strings.xml diff --git a/i18n/src/commonMain/resources/MR/zh-rTW/plurals.xml b/i18n/src/commonMain/moko-resources/zh-rTW/plurals.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/zh-rTW/plurals.xml rename to i18n/src/commonMain/moko-resources/zh-rTW/plurals.xml diff --git a/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml b/i18n/src/commonMain/moko-resources/zh-rTW/strings.xml similarity index 100% rename from i18n/src/commonMain/resources/MR/zh-rTW/strings.xml rename to i18n/src/commonMain/moko-resources/zh-rTW/strings.xml From b37357f9097730edb1d72f1297461e580286856c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 21 Jun 2024 03:21:27 +0600 Subject: [PATCH 108/146] fix(deps): update dependency com.google.firebase:firebase-analytics to v22.0.2 (#936) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a3bfcec8f..d02410c83 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -73,7 +73,7 @@ moko-gradle = { module = "dev.icerock.moko:resources-generator", version.ref = " logcat = "com.squareup.logcat:logcat:0.1" -firebase-analytics = "com.google.firebase:firebase-analytics:22.0.1" +firebase-analytics = "com.google.firebase:firebase-analytics:22.0.2" aboutLibraries-gradle = { module = "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin", version.ref = "aboutlib_version" } aboutLibraries-compose = { module = "com.mikepenz:aboutlibraries-compose-m3", version.ref = "aboutlib_version" } From f6ec53cdde32a17bc394e55b697a3b59bfd76e58 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 23 Jun 2024 03:35:12 +0600 Subject: [PATCH 109/146] fix(deps): update dependency io.github.fornewid:material-motion-compose-core to v2.0.1 (#945) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d02410c83..3942f468f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -62,7 +62,7 @@ flexible-adapter-core = "com.github.arkon.FlexibleAdapter:flexible-adapter:c8013 photoview = "com.github.chrisbanes:PhotoView:2.3.0" directionalviewpager = "com.github.tachiyomiorg:DirectionalViewPager:1.0.0" insetter = "dev.chrisbanes.insetter:insetter:0.6.1" -compose-materialmotion = "io.github.fornewid:material-motion-compose-core:2.0.0" +compose-materialmotion = "io.github.fornewid:material-motion-compose-core:2.0.1" compose-webview = "io.github.kevinnzou:compose-webview:0.33.6" compose-grid = "io.woong.compose.grid:grid:1.2.2" From 4ed2062cabde8127cfa822cfff193e02a34d8ddb Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sun, 23 Jun 2024 03:40:17 +0600 Subject: [PATCH 110/146] Update `build_pull_request.yml` `paths-ignore` --- .github/workflows/build_pull_request.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index ef49e0414..2a031c279 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -3,8 +3,8 @@ on: pull_request: paths-ignore: - '**.md' - - 'i18n/src/commonMain/resources/**/strings.xml' - - 'i18n/src/commonMain/resources/**/plurals.xml' + - 'i18n/src/commonMain/moko-resources/**/strings.xml' + - 'i18n/src/commonMain/moko-resources/**/plurals.xml' concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number }} From 0ce1cf22cdbb7d82df3db1a901253b4973ab027f Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Sun, 23 Jun 2024 05:53:49 +0800 Subject: [PATCH 111/146] Fix unexpected skips in strong skipping mode (#940) --- gradle/compose.versions.toml | 1 + source-api/build.gradle.kts | 3 +++ .../kotlin/eu/kanade/tachiyomi/source/model/FilterList.kt | 3 +++ 3 files changed, 7 insertions(+) diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index b5e306abe..c36eb21cb 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -8,6 +8,7 @@ bom = { group = "dev.chrisbanes.compose", name = "compose-bom", version.ref = "c foundation = { module = "androidx.compose.foundation:foundation" } animation = { module = "androidx.compose.animation:animation" } animation-graphics = { module = "androidx.compose.animation:animation-graphics" } +runtime = { module = "androidx.compose.runtime:runtime" } ui-tooling = { module = "androidx.compose.ui:ui-tooling" } ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" } ui-util = { module = "androidx.compose.ui:ui-util" } diff --git a/source-api/build.gradle.kts b/source-api/build.gradle.kts index 7ac4d6e2c..ad562dcd5 100644 --- a/source-api/build.gradle.kts +++ b/source-api/build.gradle.kts @@ -13,6 +13,9 @@ kotlin { api(libs.injekt.core) api(libs.rxjava) api(libs.jsoup) + + implementation(project.dependencies.platform(compose.bom)) + implementation(compose.runtime) } } val androidMain by getting { diff --git a/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/model/FilterList.kt b/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/model/FilterList.kt index 77f339b9d..6c9935266 100644 --- a/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/model/FilterList.kt +++ b/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/model/FilterList.kt @@ -1,5 +1,8 @@ package eu.kanade.tachiyomi.source.model +import androidx.compose.runtime.Stable + +@Stable data class FilterList(val list: List>) : List> by list { constructor(vararg fs: Filter<*>) : this(if (fs.isNotEmpty()) fs.asList() else emptyList()) From e57638a49c759d36d25b92f26633df5bdfb0d2b3 Mon Sep 17 00:00:00 2001 From: "Tran M. Cuong" Date: Sun, 23 Jun 2024 05:05:44 +0700 Subject: [PATCH 112/146] Fix Migrator test and also add the test to build script (#896) * Fix MigratorTest after update to Kotlin 2.0.0 * add main module's test to build script --- .github/workflows/build_pull_request.yml | 2 +- .github/workflows/build_push.yml | 2 +- app/src/test/java/mihon/core/migration/MigratorTest.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 2a031c279..334ce464a 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -38,4 +38,4 @@ jobs: uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2 - name: Build app and run unit tests - run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest + run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest testStandardReleaseUnitTest diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index e85411cd2..47e6fe275 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -36,7 +36,7 @@ jobs: uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2 - name: Build app and run unit tests - run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest + run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest testStandardReleaseUnitTest # Sign APK and create release for tags diff --git a/app/src/test/java/mihon/core/migration/MigratorTest.kt b/app/src/test/java/mihon/core/migration/MigratorTest.kt index 47c4bc7c2..a805b5630 100644 --- a/app/src/test/java/mihon/core/migration/MigratorTest.kt +++ b/app/src/test/java/mihon/core/migration/MigratorTest.kt @@ -29,7 +29,7 @@ class MigratorTest { fun initilize() { migrationContext = MigrationContext(false) migrationJobFactory = spyk(MigrationJobFactory(migrationContext, CoroutineScope(Dispatchers.Main + Job()))) - migrationCompletedListener = spyk<() -> Unit>({}) + migrationCompletedListener = spyk(block = {}) migrationStrategyFactory = spyk(MigrationStrategyFactory(migrationJobFactory, migrationCompletedListener)) } From e17f70f7226ea031fc1f962c9dfea3e404ba53ad Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sun, 23 Jun 2024 04:34:49 +0600 Subject: [PATCH 113/146] Cleanup in `CommonMangaItem.kt` Closes #19 Co-authored-by: Roshan Varughese <40583749+Animeboynz@users.noreply.github.com> --- .../library/components/CommonMangaItem.kt | 46 +++++++++++++------ 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/library/components/CommonMangaItem.kt b/app/src/main/java/eu/kanade/presentation/library/components/CommonMangaItem.kt index 92957384a..b4a4c2cc0 100644 --- a/app/src/main/java/eu/kanade/presentation/library/components/CommonMangaItem.kt +++ b/app/src/main/java/eu/kanade/presentation/library/components/CommonMangaItem.kt @@ -35,6 +35,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shadow import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import eu.kanade.presentation.manga.components.MangaCover @@ -42,15 +43,22 @@ import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.BadgeGroup import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.util.selectedBackground +import tachiyomi.domain.manga.model.MangaCover as MangaCoverModel object CommonMangaItemDefaults { val GridHorizontalSpacer = 4.dp val GridVerticalSpacer = 4.dp + @Suppress("ConstPropertyName") const val BrowseFavoriteCoverAlpha = 0.34f } -private val ContinueReadingButtonSize = 28.dp +private val ContinueReadingButtonSizeSmall = 28.dp +private val ContinueReadingButtonSizeLarge = 32.dp + +private val ContinueReadingButtonIconSizeSmall = 16.dp +private val ContinueReadingButtonIconSizeLarge = 20.dp + private val ContinueReadingButtonGridPadding = 6.dp private val ContinueReadingButtonListSpacing = 8.dp @@ -62,7 +70,7 @@ private const val GridSelectedCoverAlpha = 0.76f */ @Composable fun MangaCompactGridItem( - coverData: tachiyomi.domain.manga.model.MangaCover, + coverData: MangaCoverModel, onClick: () -> Unit, onLongClick: () -> Unit, isSelected: Boolean = false, @@ -96,10 +104,12 @@ fun MangaCompactGridItem( ) } else if (onClickContinueReading != null) { ContinueReadingButton( + size = ContinueReadingButtonSizeLarge, + iconSize = ContinueReadingButtonIconSizeLarge, + onClick = onClickContinueReading, modifier = Modifier .padding(ContinueReadingButtonGridPadding) .align(Alignment.BottomEnd), - onClickContinueReading = onClickContinueReading, ) } }, @@ -148,11 +158,13 @@ private fun BoxScope.CoverTextOverlay( ) if (onClickContinueReading != null) { ContinueReadingButton( + size = ContinueReadingButtonSizeSmall, + iconSize = ContinueReadingButtonIconSizeSmall, + onClick = onClickContinueReading, modifier = Modifier.padding( end = ContinueReadingButtonGridPadding, bottom = ContinueReadingButtonGridPadding, ), - onClickContinueReading = onClickContinueReading, ) } } @@ -163,7 +175,7 @@ private fun BoxScope.CoverTextOverlay( */ @Composable fun MangaComfortableGridItem( - coverData: tachiyomi.domain.manga.model.MangaCover, + coverData: MangaCoverModel, title: String, onClick: () -> Unit, onLongClick: () -> Unit, @@ -194,10 +206,12 @@ fun MangaComfortableGridItem( content = { if (onClickContinueReading != null) { ContinueReadingButton( + size = ContinueReadingButtonSizeLarge, + iconSize = ContinueReadingButtonIconSizeLarge, + onClick = onClickContinueReading, modifier = Modifier .padding(ContinueReadingButtonGridPadding) .align(Alignment.BottomEnd), - onClickContinueReading = onClickContinueReading, ) } }, @@ -309,14 +323,14 @@ private fun GridItemSelectable( private fun Modifier.selectedOutline( isSelected: Boolean, color: Color, -) = this then drawBehind { if (isSelected) drawRect(color = color) } +) = drawBehind { if (isSelected) drawRect(color = color) } /** * Layout of list item. */ @Composable fun MangaListItem( - coverData: tachiyomi.domain.manga.model.MangaCover, + coverData: MangaCoverModel, title: String, onClick: () -> Unit, onLongClick: () -> Unit, @@ -354,8 +368,10 @@ fun MangaListItem( BadgeGroup(content = badge) if (onClickContinueReading != null) { ContinueReadingButton( - modifier = Modifier.padding(start = ContinueReadingButtonListSpacing), - onClickContinueReading = onClickContinueReading, + size = ContinueReadingButtonSizeSmall, + iconSize = ContinueReadingButtonIconSizeSmall, + onClick = onClickContinueReading, + modifier = Modifier.padding(start = ContinueReadingButtonListSpacing) ) } } @@ -363,23 +379,25 @@ fun MangaListItem( @Composable private fun ContinueReadingButton( + size: Dp, + iconSize: Dp, + onClick: () -> Unit, modifier: Modifier = Modifier, - onClickContinueReading: () -> Unit, ) { Box(modifier = modifier) { FilledIconButton( - onClick = onClickContinueReading, - modifier = Modifier.size(ContinueReadingButtonSize), + onClick = onClick, shape = MaterialTheme.shapes.small, colors = IconButtonDefaults.filledIconButtonColors( containerColor = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.9f), contentColor = contentColorFor(MaterialTheme.colorScheme.primaryContainer), ), + modifier = Modifier.size(size) ) { Icon( imageVector = Icons.Filled.PlayArrow, contentDescription = stringResource(MR.strings.action_resume), - modifier = Modifier.size(16.dp), + modifier = Modifier.size(iconSize), ) } } From 5c249dd79078c7531c8582fd53a79378200153f0 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Sun, 23 Jun 2024 10:04:30 +0800 Subject: [PATCH 114/146] Upload build artifacts (#941) To decode obfuscated stack traces and help debugging R8 issues. --- .github/workflows/build_pull_request.yml | 12 ++++++++++++ .github/workflows/build_push.yml | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 334ce464a..f12ac59d0 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -39,3 +39,15 @@ jobs: - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest testStandardReleaseUnitTest + + - name: Upload APK + uses: actions/upload-artifact@v4 + with: + name: arm64-v8a-${{ github.sha }} + path: app/build/outputs/apk/standard/release/app-standard-arm64-v8a-release-unsigned.apk + + - name: Upload mapping + uses: actions/upload-artifact@v4 + with: + name: mapping-${{ github.sha }} + path: app/build/outputs/mapping/standardRelease diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index 47e6fe275..bb91f0a4f 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -38,6 +38,18 @@ jobs: - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest testStandardReleaseUnitTest + - name: Upload APK + uses: actions/upload-artifact@v4 + with: + name: arm64-v8a-${{ github.sha }} + path: app/build/outputs/apk/standard/release/app-standard-arm64-v8a-release-unsigned.apk + + - name: Upload mapping + uses: actions/upload-artifact@v4 + with: + name: mapping-${{ github.sha }} + path: app/build/outputs/mapping/standardRelease + # Sign APK and create release for tags - name: Get tag name From a41ea8a61d0630e6c26074569ca9c000c3952f1e Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Mon, 24 Jun 2024 19:28:25 +0600 Subject: [PATCH 115/146] [skip ci] remove unused github workflow --- .github/mergify.yml | 10 ------- .github/workflows/issue_moderator.yml | 41 --------------------------- 2 files changed, 51 deletions(-) delete mode 100644 .github/mergify.yml delete mode 100644 .github/workflows/issue_moderator.yml diff --git a/.github/mergify.yml b/.github/mergify.yml deleted file mode 100644 index c9ddf7602..000000000 --- a/.github/mergify.yml +++ /dev/null @@ -1,10 +0,0 @@ -#pull_request_rules: -# - name: Automatically merge translations -# conditions: -# - "author = weblate" -# - "-conflict" -# - "current-day-of-week = Sat" -# - "created-at < 1 day ago" -# actions: -# merge: -# method: squash \ No newline at end of file diff --git a/.github/workflows/issue_moderator.yml b/.github/workflows/issue_moderator.yml deleted file mode 100644 index 1a4fbe18f..000000000 --- a/.github/workflows/issue_moderator.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Issue moderator - -on: - issues: - types: [opened, edited, reopened] - issue_comment: - types: [created] - -jobs: - moderate: - runs-on: ubuntu-latest - steps: - - name: Moderate issues - uses: keiyoushi/issue-moderator-action@a017be83547db6e107431ce7575f53c1dfa3296a - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - duplicate-label: Duplicate - - auto-close-rules: | - [ - { - "type": "both", - "regex": "^(?!.*myanimelist.*).*(aniyomi|anime).*$", - "ignoreCase": true, - "message": "Mihon does not support anime, and has no plans to support anime. In addition Mihon is not affiliated with Aniyomi https://github.com/jmir1/aniyomi" - }, - { - "type": "both", - "regex": ".*(?:fail(?:ed|ure|s)?|can\\s*(?:no|')?t|(?:not|un).*able|(? Date: Tue, 25 Jun 2024 07:05:44 +0600 Subject: [PATCH 116/146] fix(deps): update dependency androidx.test.espresso:espresso-core to v3.6.0 (#947) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 94c1c15b1..96d9553c4 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -27,7 +27,7 @@ paging-compose = { module = "androidx.paging:paging-compose", version.ref = "pag benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.4" test-ext = "androidx.test.ext:junit-ktx:1.2.0-rc01" -test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-rc01" +test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0" test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0" [bundles] From 36e40c099772d2cb53d4ec87b2b00f97fe455c98 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 25 Jun 2024 07:49:12 +0600 Subject: [PATCH 117/146] fix(deps): update dependency androidx.test.ext:junit-ktx to v1.2.0 (#948) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 96d9553c4..762d2a3d4 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -26,7 +26,7 @@ paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "pag paging-compose = { module = "androidx.paging:paging-compose", version.ref = "paging_version" } benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.4" -test-ext = "androidx.test.ext:junit-ktx:1.2.0-rc01" +test-ext = "androidx.test.ext:junit-ktx:1.2.0" test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0" test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0" From 239c38982c4fd55d4d86b37fd9c3c51c3b47d098 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Wed, 26 Jun 2024 22:54:25 +0800 Subject: [PATCH 118/146] Refactor archive support with libarchive (#949) * Refactor archive support with libarchive * Revert string resource changs * Only mark archive formats as supported Comic book archives should not be compressed. * Fixup * Remove epub from archive format list * Move to mihon package * Format * Cleanup --- app/build.gradle.kts | 1 - app/proguard-rules.pro | 3 - .../tachiyomi/data/download/Downloader.kt | 27 +------ ...{ZipPageLoader.kt => ArchivePageLoader.kt} | 20 ++--- .../ui/reader/loader/ChapterLoader.kt | 12 +-- .../ui/reader/loader/DownloadPageLoader.kt | 10 +-- .../ui/reader/loader/EpubPageLoader.kt | 8 +- .../ui/reader/loader/RarPageLoader.kt | 67 ----------------- core/common/build.gradle.kts | 2 +- .../kanade/tachiyomi/util/storage/EpubFile.kt | 42 +++-------- .../mihon/core/common/archive/ArchiveEntry.kt | 6 ++ .../core/common/archive/ArchiveInputStream.kt | 52 +++++++++++++ .../core/common/archive/ArchiveReader.kt | 42 +++++++++++ .../mihon/core/common/archive/ZipWriter.kt | 74 +++++++++++++++++++ .../common/extensions/SeekableByteChannel.kt | 8 -- .../core/common/storage/UniFileExtensions.kt | 6 +- gradle/libs.versions.toml | 4 +- .../moko-resources/base/strings.xml | 1 - source-local/build.gradle.kts | 1 - .../tachiyomi/source/local/LocalSource.kt | 65 +++++----------- .../tachiyomi/source/local/io/Archive.kt | 4 +- .../tachiyomi/source/local/io/Format.kt | 17 ++--- 22 files changed, 239 insertions(+), 233 deletions(-) rename app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/{ZipPageLoader.kt => ArchivePageLoader.kt} (57%) delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt create mode 100644 core/common/src/main/kotlin/mihon/core/common/archive/ArchiveEntry.kt create mode 100644 core/common/src/main/kotlin/mihon/core/common/archive/ArchiveInputStream.kt create mode 100644 core/common/src/main/kotlin/mihon/core/common/archive/ArchiveReader.kt create mode 100644 core/common/src/main/kotlin/mihon/core/common/archive/ZipWriter.kt delete mode 100644 core/common/src/main/kotlin/mihon/core/common/extensions/SeekableByteChannel.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 622715259..9ea8f6ccf 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -203,7 +203,6 @@ dependencies { // Disk implementation(libs.disklrucache) implementation(libs.unifile) - implementation(libs.bundles.archive) // Preferences implementation(libs.preferencektx) diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index f4451efda..b2971f1b7 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -77,9 +77,6 @@ # XmlUtil -keep public enum nl.adaptivity.xmlutil.EventType { *; } -# Apache Commons Compress --keep class * extends org.apache.commons.compress.archivers.zip.ZipExtraField { (); } - # Firebase -keep class com.google.firebase.installations.** { *; } -keep interface com.google.firebase.installations.** { *; } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt index 11bfe28d1..ebf9df65e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt @@ -38,6 +38,7 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope import logcat.LogPriority +import mihon.core.common.archive.ZipWriter import nl.adaptivity.xmlutil.serialization.XML import okhttp3.Response import tachiyomi.core.common.i18n.stringResource @@ -58,12 +59,8 @@ import tachiyomi.domain.track.interactor.GetTracks import tachiyomi.i18n.MR import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.io.BufferedOutputStream import java.io.File import java.util.Locale -import java.util.zip.CRC32 -import java.util.zip.ZipEntry -import java.util.zip.ZipOutputStream /** * This class is the one in charge of downloading chapters. @@ -594,25 +591,9 @@ class Downloader( tmpDir: UniFile, ) { val zip = mangaDir.createFile("$dirname.cbz$TMP_DIR_SUFFIX")!! - ZipOutputStream(BufferedOutputStream(zip.openOutputStream())).use { zipOut -> - zipOut.setMethod(ZipEntry.STORED) - - tmpDir.listFiles()?.forEach { img -> - img.openInputStream().use { input -> - val data = input.readBytes() - val size = img.length() - val entry = ZipEntry(img.name).apply { - val crc = CRC32().apply { - update(data) - } - setCrc(crc.value) - - compressedSize = size - setSize(size) - } - zipOut.putNextEntry(entry) - zipOut.write(data) - } + ZipWriter(context, zip).use { writer -> + tmpDir.listFiles()?.forEach { file -> + writer.write(file) } } zip.renameTo("$dirname.cbz") diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ArchivePageLoader.kt similarity index 57% rename from app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ArchivePageLoader.kt index 89856bf22..397ac51bc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ArchivePageLoader.kt @@ -3,26 +3,22 @@ package eu.kanade.tachiyomi.ui.reader.loader import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder -import mihon.core.common.extensions.toZipFile +import mihon.core.common.archive.ArchiveReader import tachiyomi.core.common.util.system.ImageUtil -import java.nio.channels.SeekableByteChannel /** - * Loader used to load a chapter from a .zip or .cbz file. + * Loader used to load a chapter from an archive file. */ -internal class ZipPageLoader(channel: SeekableByteChannel) : PageLoader() { - - private val zip = channel.toZipFile() - +internal class ArchivePageLoader(private val reader: ArchiveReader) : PageLoader() { override var isLocal: Boolean = true - override suspend fun getPages(): List { - return zip.entries.asSequence() - .filter { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } } + override suspend fun getPages(): List = reader.useEntries { entries -> + entries + .filter { it.isFile && ImageUtil.isImage(it.name) { reader.getInputStream(it.name)!! } } .sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) } .mapIndexed { i, entry -> ReaderPage(i).apply { - stream = { zip.getInputStream(entry) } + stream = { reader.getInputStream(entry.name)!! } status = Page.State.READY } } @@ -35,6 +31,6 @@ internal class ZipPageLoader(channel: SeekableByteChannel) : PageLoader() { override fun recycle() { super.recycle() - zip.close() + reader.close() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt index 1cd18bceb..3c1b34d6c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt @@ -1,14 +1,13 @@ package eu.kanade.tachiyomi.ui.reader.loader import android.content.Context -import com.github.junrar.exception.UnsupportedRarV5Exception import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.DownloadProvider import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter +import mihon.core.common.archive.archiveReader import tachiyomi.core.common.i18n.stringResource -import tachiyomi.core.common.storage.openReadOnlyChannel import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.manga.model.Manga @@ -95,13 +94,8 @@ class ChapterLoader( source is LocalSource -> source.getFormat(chapter.chapter).let { format -> when (format) { is Format.Directory -> DirectoryPageLoader(format.file) - is Format.Zip -> ZipPageLoader(format.file.openReadOnlyChannel(context)) - is Format.Rar -> try { - RarPageLoader(format.file.openInputStream()) - } catch (e: UnsupportedRarV5Exception) { - error(context.stringResource(MR.strings.loader_rar5_error)) - } - is Format.Epub -> EpubPageLoader(format.file.openReadOnlyChannel(context)) + is Format.Archive -> ArchivePageLoader(format.file.archiveReader(context)) + is Format.Epub -> EpubPageLoader(format.file.archiveReader(context)) } } source is HttpSource -> HttpPageLoader(chapter, source) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt index abef28540..7b0f5c36c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt @@ -10,7 +10,7 @@ import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter import eu.kanade.tachiyomi.ui.reader.model.ReaderPage -import tachiyomi.core.common.storage.openReadOnlyChannel +import mihon.core.common.archive.archiveReader import tachiyomi.domain.manga.model.Manga import uy.kohesive.injekt.injectLazy @@ -27,7 +27,7 @@ internal class DownloadPageLoader( private val context: Application by injectLazy() - private var zipPageLoader: ZipPageLoader? = null + private var archivePageLoader: ArchivePageLoader? = null override var isLocal: Boolean = true @@ -43,11 +43,11 @@ internal class DownloadPageLoader( override fun recycle() { super.recycle() - zipPageLoader?.recycle() + archivePageLoader?.recycle() } private suspend fun getPagesFromArchive(file: UniFile): List { - val loader = ZipPageLoader(file.openReadOnlyChannel(context)).also { zipPageLoader = it } + val loader = ArchivePageLoader(file.archiveReader(context)).also { archivePageLoader = it } return loader.getPages() } @@ -63,6 +63,6 @@ internal class DownloadPageLoader( } override suspend fun loadPage(page: ReaderPage) { - zipPageLoader?.loadPage(page) + archivePageLoader?.loadPage(page) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/EpubPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/EpubPageLoader.kt index baf65324b..8ace2fdee 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/EpubPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/EpubPageLoader.kt @@ -3,21 +3,21 @@ package eu.kanade.tachiyomi.ui.reader.loader import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.util.storage.EpubFile -import java.nio.channels.SeekableByteChannel +import mihon.core.common.archive.ArchiveReader /** * Loader used to load a chapter from a .epub file. */ -internal class EpubPageLoader(channel: SeekableByteChannel) : PageLoader() { +internal class EpubPageLoader(reader: ArchiveReader) : PageLoader() { - private val epub = EpubFile(channel) + private val epub = EpubFile(reader) override var isLocal: Boolean = true override suspend fun getPages(): List { return epub.getImagesFromPages() .mapIndexed { i, path -> - val streamFn = { epub.getInputStream(epub.getEntry(path)!!) } + val streamFn = { epub.getInputStream(path)!! } ReaderPage(i).apply { stream = streamFn status = Page.State.READY diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt deleted file mode 100644 index b1db3300d..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt +++ /dev/null @@ -1,67 +0,0 @@ -package eu.kanade.tachiyomi.ui.reader.loader - -import com.github.junrar.Archive -import com.github.junrar.rarfile.FileHeader -import eu.kanade.tachiyomi.source.model.Page -import eu.kanade.tachiyomi.ui.reader.model.ReaderPage -import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder -import tachiyomi.core.common.util.system.ImageUtil -import java.io.InputStream -import java.io.PipedInputStream -import java.io.PipedOutputStream -import java.util.concurrent.Executors - -/** - * Loader used to load a chapter from a .rar or .cbr file. - */ -internal class RarPageLoader(inputStream: InputStream) : PageLoader() { - - private val rar = Archive(inputStream) - - override var isLocal: Boolean = true - - /** - * Pool for copying compressed files to an input stream. - */ - private val pool = Executors.newFixedThreadPool(1) - - override suspend fun getPages(): List { - return rar.fileHeaders.asSequence() - .filter { !it.isDirectory && ImageUtil.isImage(it.fileName) { rar.getInputStream(it) } } - .sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) } - .mapIndexed { i, header -> - ReaderPage(i).apply { - stream = { getStream(header) } - status = Page.State.READY - } - } - .toList() - } - - override suspend fun loadPage(page: ReaderPage) { - check(!isRecycled) - } - - override fun recycle() { - super.recycle() - rar.close() - pool.shutdown() - } - - /** - * Returns an input stream for the given [header]. - */ - private fun getStream(header: FileHeader): InputStream { - val pipeIn = PipedInputStream() - val pipeOut = PipedOutputStream(pipeIn) - pool.execute { - try { - pipeOut.use { - rar.extractFile(header, it) - } - } catch (e: Exception) { - } - } - return pipeIn - } -} diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts index e31015dc4..d00fec682 100644 --- a/core/common/build.gradle.kts +++ b/core/common/build.gradle.kts @@ -32,7 +32,7 @@ dependencies { implementation(libs.image.decoder) implementation(libs.unifile) - implementation(libs.bundles.archive) + implementation(libs.libarchive) api(kotlinx.coroutines.core) api(kotlinx.serialization.json) diff --git a/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/storage/EpubFile.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/storage/EpubFile.kt index 29cea5824..b194c5ee3 100644 --- a/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/storage/EpubFile.kt +++ b/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/storage/EpubFile.kt @@ -1,48 +1,27 @@ package eu.kanade.tachiyomi.util.storage -import mihon.core.common.extensions.toZipFile -import org.apache.commons.compress.archivers.zip.ZipArchiveEntry +import mihon.core.common.archive.ArchiveReader import org.jsoup.Jsoup import org.jsoup.nodes.Document import java.io.Closeable import java.io.File import java.io.InputStream -import java.nio.channels.SeekableByteChannel /** * Wrapper over ZipFile to load files in epub format. */ -class EpubFile(channel: SeekableByteChannel) : Closeable { - - /** - * Zip file of this epub. - */ - private val zip = channel.toZipFile() +class EpubFile(private val reader: ArchiveReader) : Closeable by reader { /** * Path separator used by this epub. */ private val pathSeparator = getPathSeparator() - /** - * Closes the underlying zip file. - */ - override fun close() { - zip.close() - } - /** * Returns an input stream for reading the contents of the specified zip file entry. */ - fun getInputStream(entry: ZipArchiveEntry): InputStream { - return zip.getInputStream(entry) - } - - /** - * Returns the zip file entry for the specified name, or null if not found. - */ - fun getEntry(name: String): ZipArchiveEntry? { - return zip.getEntry(name) + fun getInputStream(entryName: String): InputStream? { + return reader.getInputStream(entryName) } /** @@ -59,9 +38,9 @@ class EpubFile(channel: SeekableByteChannel) : Closeable { * Returns the path to the package document. */ fun getPackageHref(): String { - val meta = zip.getEntry(resolveZipPath("META-INF", "container.xml")) + val meta = getInputStream(resolveZipPath("META-INF", "container.xml")) if (meta != null) { - val metaDoc = zip.getInputStream(meta).use { Jsoup.parse(it, null, "") } + val metaDoc = meta.use { Jsoup.parse(it, null, "") } val path = metaDoc.getElementsByTag("rootfile").first()?.attr("full-path") if (path != null) { return path @@ -74,8 +53,7 @@ class EpubFile(channel: SeekableByteChannel) : Closeable { * Returns the package document where all the files are listed. */ fun getPackageDocument(ref: String): Document { - val entry = zip.getEntry(ref) - return zip.getInputStream(entry).use { Jsoup.parse(it, null, "") } + return getInputStream(ref)!!.use { Jsoup.parse(it, null, "") } } /** @@ -98,8 +76,7 @@ class EpubFile(channel: SeekableByteChannel) : Closeable { val basePath = getParentDirectory(packageHref) pages.forEach { page -> val entryPath = resolveZipPath(basePath, page) - val entry = zip.getEntry(entryPath) - val document = zip.getInputStream(entry).use { Jsoup.parse(it, null, "") } + val document = getInputStream(entryPath)!!.use { Jsoup.parse(it, null, "") } val imageBasePath = getParentDirectory(entryPath) document.allElements.forEach { @@ -117,8 +94,9 @@ class EpubFile(channel: SeekableByteChannel) : Closeable { * Returns the path separator used by the epub file. */ private fun getPathSeparator(): String { - val meta = zip.getEntry("META-INF\\container.xml") + val meta = getInputStream("META-INF\\container.xml") return if (meta != null) { + meta.close() "\\" } else { "/" diff --git a/core/common/src/main/kotlin/mihon/core/common/archive/ArchiveEntry.kt b/core/common/src/main/kotlin/mihon/core/common/archive/ArchiveEntry.kt new file mode 100644 index 000000000..26240de00 --- /dev/null +++ b/core/common/src/main/kotlin/mihon/core/common/archive/ArchiveEntry.kt @@ -0,0 +1,6 @@ +package mihon.core.common.archive + +class ArchiveEntry( + val name: String, + val isFile: Boolean, +) diff --git a/core/common/src/main/kotlin/mihon/core/common/archive/ArchiveInputStream.kt b/core/common/src/main/kotlin/mihon/core/common/archive/ArchiveInputStream.kt new file mode 100644 index 000000000..a9bb87879 --- /dev/null +++ b/core/common/src/main/kotlin/mihon/core/common/archive/ArchiveInputStream.kt @@ -0,0 +1,52 @@ +package mihon.core.common.archive + +import me.zhanghai.android.libarchive.Archive +import me.zhanghai.android.libarchive.ArchiveEntry +import me.zhanghai.android.libarchive.ArchiveException +import java.io.InputStream +import java.nio.ByteBuffer + +class ArchiveInputStream(buffer: Long, size: Long) : InputStream() { + private val archive = Archive.readNew() + + init { + try { + Archive.setCharset(archive, Charsets.UTF_8.name().toByteArray()) + Archive.readSupportFilterAll(archive) + Archive.readSupportFormatAll(archive) + Archive.readOpenMemoryUnsafe(archive, buffer, size) + } catch (e: ArchiveException) { + close() + throw e + } + } + + private val oneByteBuffer = ByteBuffer.allocateDirect(1) + + override fun read(): Int { + read(oneByteBuffer) + return if (oneByteBuffer.hasRemaining()) oneByteBuffer.get().toUByte().toInt() else -1 + } + + override fun read(b: ByteArray, off: Int, len: Int): Int { + val buffer = ByteBuffer.wrap(b, off, len) + read(buffer) + return if (buffer.hasRemaining()) buffer.remaining() else -1 + } + + private fun read(buffer: ByteBuffer) { + buffer.clear() + Archive.readData(archive, buffer) + buffer.flip() + } + + override fun close() { + Archive.readFree(archive) + } + + fun getNextEntry() = Archive.readNextHeader(archive).takeUnless { it == 0L }?.let { entry -> + val name = ArchiveEntry.pathnameUtf8(entry) ?: ArchiveEntry.pathname(entry)?.decodeToString() ?: return null + val isFile = ArchiveEntry.filetype(entry) == ArchiveEntry.AE_IFREG + ArchiveEntry(name, isFile) + } +} diff --git a/core/common/src/main/kotlin/mihon/core/common/archive/ArchiveReader.kt b/core/common/src/main/kotlin/mihon/core/common/archive/ArchiveReader.kt new file mode 100644 index 000000000..28467d0fe --- /dev/null +++ b/core/common/src/main/kotlin/mihon/core/common/archive/ArchiveReader.kt @@ -0,0 +1,42 @@ +package mihon.core.common.archive + +import android.content.Context +import android.os.ParcelFileDescriptor +import android.system.Os +import android.system.OsConstants +import com.hippo.unifile.UniFile +import me.zhanghai.android.libarchive.ArchiveException +import tachiyomi.core.common.storage.openFileDescriptor +import java.io.Closeable +import java.io.InputStream + +class ArchiveReader(pfd: ParcelFileDescriptor) : Closeable { + val size = pfd.statSize + val address = Os.mmap(0, size, OsConstants.PROT_READ, OsConstants.MAP_PRIVATE, pfd.fileDescriptor, 0) + + inline fun useEntries(block: (Sequence) -> T): T = + ArchiveInputStream(address, size).use { block(generateSequence { it.getNextEntry() }) } + + fun getInputStream(entryName: String): InputStream? { + val archive = ArchiveInputStream(address, size) + try { + while (true) { + val entry = archive.getNextEntry() ?: break + if (entry.name == entryName) { + return archive + } + } + } catch (e: ArchiveException) { + archive.close() + throw e + } + archive.close() + return null + } + + override fun close() { + Os.munmap(address, size) + } +} + +fun UniFile.archiveReader(context: Context) = openFileDescriptor(context, "r").use { ArchiveReader(it) } diff --git a/core/common/src/main/kotlin/mihon/core/common/archive/ZipWriter.kt b/core/common/src/main/kotlin/mihon/core/common/archive/ZipWriter.kt new file mode 100644 index 000000000..b5d201516 --- /dev/null +++ b/core/common/src/main/kotlin/mihon/core/common/archive/ZipWriter.kt @@ -0,0 +1,74 @@ +package mihon.core.common.archive + +import android.content.Context +import android.system.Os +import android.system.StructStat +import com.hippo.unifile.UniFile +import me.zhanghai.android.libarchive.Archive +import me.zhanghai.android.libarchive.ArchiveEntry +import me.zhanghai.android.libarchive.ArchiveException +import tachiyomi.core.common.storage.openFileDescriptor +import java.io.Closeable +import java.nio.ByteBuffer + +class ZipWriter(val context: Context, file: UniFile) : Closeable { + private val pfd = file.openFileDescriptor(context, "wt") + private val archive = Archive.writeNew() + private val entry = ArchiveEntry.new2(archive) + private val buffer = ByteBuffer.allocateDirect(8192) + + init { + try { + Archive.setCharset(archive, Charsets.UTF_8.name().toByteArray()) + Archive.writeSetFormatZip(archive) + Archive.writeZipSetCompressionStore(archive) + Archive.writeOpenFd(archive, pfd.fd) + } catch (e: ArchiveException) { + close() + throw e + } + } + + fun write(file: UniFile) { + file.openFileDescriptor(context, "r").use { + val fd = it.fileDescriptor + ArchiveEntry.clear(entry) + ArchiveEntry.setPathnameUtf8(entry, file.name) + val stat = Os.fstat(fd) + ArchiveEntry.setStat(entry, stat.toArchiveStat()) + Archive.writeHeader(archive, entry) + while (true) { + buffer.clear() + Os.read(fd, buffer) + if (buffer.position() == 0) break + buffer.flip() + Archive.writeData(archive, buffer) + } + Archive.writeFinishEntry(archive) + } + } + + override fun close() { + ArchiveEntry.free(entry) + Archive.writeFree(archive) + pfd.close() + } +} + +private fun StructStat.toArchiveStat() = ArchiveEntry.StructStat().apply { + stDev = st_dev + stMode = st_mode + stNlink = st_nlink.toInt() + stUid = st_uid + stGid = st_gid + stRdev = st_rdev + stSize = st_size + stBlksize = st_blksize + stBlocks = st_blocks + stAtim = timespec(st_atime) + stMtim = timespec(st_mtime) + stCtim = timespec(st_ctime) + stIno = st_ino +} + +private fun timespec(tvSec: Long) = ArchiveEntry.StructTimespec().also { it.tvSec = tvSec } diff --git a/core/common/src/main/kotlin/mihon/core/common/extensions/SeekableByteChannel.kt b/core/common/src/main/kotlin/mihon/core/common/extensions/SeekableByteChannel.kt deleted file mode 100644 index 69e2d7201..000000000 --- a/core/common/src/main/kotlin/mihon/core/common/extensions/SeekableByteChannel.kt +++ /dev/null @@ -1,8 +0,0 @@ -package mihon.core.common.extensions - -import org.apache.commons.compress.archivers.zip.ZipFile -import java.nio.channels.SeekableByteChannel - -fun SeekableByteChannel.toZipFile(): ZipFile { - return ZipFile.Builder().setSeekableByteChannel(this).get() -} diff --git a/core/common/src/main/kotlin/tachiyomi/core/common/storage/UniFileExtensions.kt b/core/common/src/main/kotlin/tachiyomi/core/common/storage/UniFileExtensions.kt index 257fe210d..4b04ff405 100644 --- a/core/common/src/main/kotlin/tachiyomi/core/common/storage/UniFileExtensions.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/storage/UniFileExtensions.kt @@ -3,7 +3,6 @@ package tachiyomi.core.common.storage import android.content.Context import android.os.ParcelFileDescriptor import com.hippo.unifile.UniFile -import java.nio.channels.FileChannel val UniFile.extension: String? get() = name?.substringAfterLast('.') @@ -14,6 +13,5 @@ val UniFile.nameWithoutExtension: String? val UniFile.displayablePath: String get() = filePath ?: uri.toString() -fun UniFile.openReadOnlyChannel(context: Context): FileChannel { - return ParcelFileDescriptor.AutoCloseInputStream(context.contentResolver.openFileDescriptor(uri, "r")).channel -} +fun UniFile.openFileDescriptor(context: Context, mode: String): ParcelFileDescriptor = + context.contentResolver.openFileDescriptor(uri, mode) ?: error("Failed to open file descriptor: $displayablePath") diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3942f468f..36bf03900 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -32,8 +32,7 @@ jsoup = "org.jsoup:jsoup:1.17.2" disklrucache = "com.jakewharton:disklrucache:2.0.2" unifile = "com.github.tachiyomiorg:unifile:e0def6b3dc" -common-compress = "org.apache.commons:commons-compress:1.26.2" -junrar = "com.github.junrar:junrar:7.5.5" +libarchive = "me.zhanghai.android.libarchive:library:1.1.0" sqlite-framework = { module = "androidx.sqlite:sqlite-framework", version.ref = "sqlite" } sqlite-ktx = { module = "androidx.sqlite:sqlite-ktx", version.ref = "sqlite" } @@ -104,7 +103,6 @@ detekt-rules-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatt detekt-rules-compose = { module = "io.nlopez.compose.rules:detekt", version.ref = "detektCompose" } [bundles] -archive = ["common-compress", "junrar"] okhttp = ["okhttp-core", "okhttp-logging", "okhttp-brotli", "okhttp-dnsoverhttps"] js-engine = ["quickjs-android"] sqlite = ["sqlite-framework", "sqlite-ktx", "sqlite-android"] diff --git a/i18n/src/commonMain/moko-resources/base/strings.xml b/i18n/src/commonMain/moko-resources/base/strings.xml index b0a397b1d..afbdf686c 100644 --- a/i18n/src/commonMain/moko-resources/base/strings.xml +++ b/i18n/src/commonMain/moko-resources/base/strings.xml @@ -781,7 +781,6 @@ Failed to load pages: %1$s No pages found Source not found - RARv5 format is not supported Updating library diff --git a/source-local/build.gradle.kts b/source-local/build.gradle.kts index b0a720b97..25c268a34 100644 --- a/source-local/build.gradle.kts +++ b/source-local/build.gradle.kts @@ -12,7 +12,6 @@ kotlin { api(projects.i18n) implementation(libs.unifile) - implementation(libs.bundles.archive) } } val androidMain by getting { diff --git a/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt b/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt index 8efea5fd2..2d9725ad8 100644 --- a/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt +++ b/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt @@ -17,13 +17,12 @@ import kotlinx.coroutines.awaitAll import kotlinx.serialization.json.Json import kotlinx.serialization.json.decodeFromStream import logcat.LogPriority -import mihon.core.common.extensions.toZipFile +import mihon.core.common.archive.archiveReader import nl.adaptivity.xmlutil.AndroidXmlReader import nl.adaptivity.xmlutil.serialization.XML import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.storage.extension import tachiyomi.core.common.storage.nameWithoutExtension -import tachiyomi.core.common.storage.openReadOnlyChannel import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.core.common.util.system.logcat @@ -45,7 +44,6 @@ import uy.kohesive.injekt.injectLazy import java.io.InputStream import java.nio.charset.StandardCharsets import kotlin.time.Duration.Companion.days -import com.github.junrar.Archive as JunrarArchive import tachiyomi.domain.source.model.Source as DomainSource actual class LocalSource( @@ -187,9 +185,7 @@ actual class LocalSource( // Copy ComicInfo.xml from chapter archive to top level if found noXmlFile == null -> { - val chapterArchives = mangaDirFiles - .filter(Archive::isSupported) - .toList() + val chapterArchives = mangaDirFiles.filter(Archive::isSupported) val copiedFile = copyComicInfoFileFromArchive(chapterArchives, mangaDir) if (copiedFile != null) { @@ -209,26 +205,10 @@ actual class LocalSource( private fun copyComicInfoFileFromArchive(chapterArchives: List, folder: UniFile): UniFile? { for (chapter in chapterArchives) { - when (Format.valueOf(chapter)) { - is Format.Zip -> { - chapter.openReadOnlyChannel(context).toZipFile().use { zip -> - zip.getEntry(COMIC_INFO_FILE)?.let { comicInfoFile -> - zip.getInputStream(comicInfoFile).buffered().use { stream -> - return copyComicInfoFile(stream, folder) - } - } - } + chapter.archiveReader(context).use { reader -> + reader.getInputStream(COMIC_INFO_FILE)?.use { stream -> + return copyComicInfoFile(stream, folder) } - is Format.Rar -> { - JunrarArchive(chapter.openInputStream()).use { rar -> - rar.fileHeaders.firstOrNull { it.fileName == COMIC_INFO_FILE }?.let { comicInfoFile -> - rar.getInputStream(comicInfoFile).buffered().use { stream -> - return copyComicInfoFile(stream, folder) - } - } - } - } - else -> {} } } return null @@ -254,7 +234,7 @@ actual class LocalSource( override suspend fun getChapterList(manga: SManga): List = withIOContext { val chapters = fileSystem.getFilesInMangaDirectory(manga.url) // Only keep supported formats - .filter { it.isDirectory || Archive.isSupported(it) } + .filter { it.isDirectory || Archive.isSupported(it) || it.extension.equals("epub", true) } .map { chapterFile -> SChapter.create().apply { url = "${manga.url}/${chapterFile.name}" @@ -270,7 +250,7 @@ actual class LocalSource( val format = Format.valueOf(chapterFile) if (format is Format.Epub) { - EpubFile(format.file.openReadOnlyChannel(context)).use { epub -> + EpubFile(format.file.archiveReader(context)).use { epub -> epub.fillMetadata(manga, this) } } @@ -328,31 +308,22 @@ actual class LocalSource( entry?.let { coverManager.update(manga, it.openInputStream()) } } - is Format.Zip -> { - format.file.openReadOnlyChannel(context).toZipFile().use { zip -> - val entry = zip.entries.toList() - .sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) } - .find { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } } + is Format.Archive -> { + format.file.archiveReader(context).use { reader -> + val entry = reader.useEntries { entries -> + entries + .sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) } + .find { it.isFile && ImageUtil.isImage(it.name) { reader.getInputStream(it.name)!! } } + } - entry?.let { coverManager.update(manga, zip.getInputStream(it)) } - } - } - is Format.Rar -> { - JunrarArchive(format.file.openInputStream()).use { archive -> - val entry = archive.fileHeaders - .sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) } - .find { !it.isDirectory && ImageUtil.isImage(it.fileName) { archive.getInputStream(it) } } - - entry?.let { coverManager.update(manga, archive.getInputStream(it)) } + entry?.let { coverManager.update(manga, reader.getInputStream(it.name)!!) } } } is Format.Epub -> { - EpubFile(format.file.openReadOnlyChannel(context)).use { epub -> - val entry = epub.getImagesFromPages() - .firstOrNull() - ?.let { epub.getEntry(it) } + EpubFile(format.file.archiveReader(context)).use { epub -> + val entry = epub.getImagesFromPages().firstOrNull() - entry?.let { coverManager.update(manga, epub.getInputStream(it)) } + entry?.let { coverManager.update(manga, epub.getInputStream(it)!!) } } } } diff --git a/source-local/src/commonMain/kotlin/tachiyomi/source/local/io/Archive.kt b/source-local/src/commonMain/kotlin/tachiyomi/source/local/io/Archive.kt index e968adc7d..ea18e9b53 100644 --- a/source-local/src/commonMain/kotlin/tachiyomi/source/local/io/Archive.kt +++ b/source-local/src/commonMain/kotlin/tachiyomi/source/local/io/Archive.kt @@ -5,9 +5,9 @@ import tachiyomi.core.common.storage.extension object Archive { - private val SUPPORTED_ARCHIVE_TYPES = listOf("zip", "cbz", "rar", "cbr", "epub") + private val SUPPORTED_ARCHIVE_TYPES = listOf("zip", "cbz", "rar", "cbr", "7z", "cb7", "tar", "cbt") fun isSupported(file: UniFile): Boolean { - return file.extension in SUPPORTED_ARCHIVE_TYPES + return file.extension?.lowercase() in SUPPORTED_ARCHIVE_TYPES } } diff --git a/source-local/src/commonMain/kotlin/tachiyomi/source/local/io/Format.kt b/source-local/src/commonMain/kotlin/tachiyomi/source/local/io/Format.kt index 5b22e41e2..ad53d407c 100644 --- a/source-local/src/commonMain/kotlin/tachiyomi/source/local/io/Format.kt +++ b/source-local/src/commonMain/kotlin/tachiyomi/source/local/io/Format.kt @@ -2,25 +2,22 @@ package tachiyomi.source.local.io import com.hippo.unifile.UniFile import tachiyomi.core.common.storage.extension +import tachiyomi.source.local.io.Archive.isSupported as isArchiveSupported sealed interface Format { data class Directory(val file: UniFile) : Format - data class Zip(val file: UniFile) : Format - data class Rar(val file: UniFile) : Format + data class Archive(val file: UniFile) : Format data class Epub(val file: UniFile) : Format class UnknownFormatException : Exception() companion object { - fun valueOf(file: UniFile) = with(file) { - when { - isDirectory -> Directory(this) - extension.equals("zip", true) || extension.equals("cbz", true) -> Zip(this) - extension.equals("rar", true) || extension.equals("cbr", true) -> Rar(this) - extension.equals("epub", true) -> Epub(this) - else -> throw UnknownFormatException() - } + fun valueOf(file: UniFile) = when { + file.isDirectory -> Directory(file) + file.extension.equals("epub", true) -> Epub(file) + isArchiveSupported(file) -> Archive(file) + else -> throw UnknownFormatException() } } } From 2f86f25d5b24c2054a604802dc65b8bc3a99c7c0 Mon Sep 17 00:00:00 2001 From: Maddie Witman Date: Wed, 26 Jun 2024 16:04:28 -0400 Subject: [PATCH 119/146] Added configuration options to e-ink page flashes (#625) * Recommit for e-ink pref changes * Fixed state holder for flash interval * Detekt * Refactor suggested by Antsy * inverted currentDisplayRefresh check for early exit --- .../settings/screen/SettingsReaderScreen.kt | 66 +++++++++++++++++-- .../presentation/reader/DisplayRefreshHost.kt | 60 ++++++++++++++--- .../reader/settings/GeneralSettingsPage.kt | 50 ++++++++++++++ .../ui/reader/setting/ReaderPreferences.kt | 14 ++++ .../moko-resources/base/plurals.xml | 5 ++ .../moko-resources/base/strings.xml | 7 ++ 6 files changed, 189 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt index be685a780..7b5aa15f3 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt @@ -14,6 +14,7 @@ import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentMapOf import kotlinx.collections.immutable.toImmutableMap import tachiyomi.i18n.MR +import tachiyomi.presentation.core.i18n.pluralStringResource import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.util.collectAsState import uy.kohesive.injekt.Injekt @@ -61,12 +62,8 @@ object SettingsReaderScreen : SearchableSettings { pref = readerPref.pageTransitions(), title = stringResource(MR.strings.pref_page_transitions), ), - Preference.PreferenceItem.SwitchPreference( - pref = readerPref.flashOnPageChange(), - title = stringResource(MR.strings.pref_flash_page), - subtitle = stringResource(MR.strings.pref_flash_page_summ), - ), getDisplayGroup(readerPreferences = readerPref), + getEInkGroup(readerPreferences = readerPref), getReadingGroup(readerPreferences = readerPref), getPagedGroup(readerPreferences = readerPref), getWebtoonGroup(readerPreferences = readerPref), @@ -122,6 +119,65 @@ object SettingsReaderScreen : SearchableSettings { ) } + @Composable + private fun getEInkGroup(readerPreferences: ReaderPreferences): Preference.PreferenceGroup { + val flashPageState by readerPreferences.flashOnPageChange().collectAsState() + + val flashMillisPref = readerPreferences.flashDurationMillis() + val flashMillis by flashMillisPref.collectAsState() + + val flashIntervalPref = readerPreferences.flashPageInterval() + val flashInterval by flashIntervalPref.collectAsState() + + val flashColorPref = readerPreferences.flashColor() + + return Preference.PreferenceGroup( + title = "E-Ink", + preferenceItems = persistentListOf( + Preference.PreferenceItem.SwitchPreference( + pref = readerPreferences.flashOnPageChange(), + title = stringResource(MR.strings.pref_flash_page), + subtitle = stringResource(MR.strings.pref_flash_page_summ), + ), + Preference.PreferenceItem.SliderPreference( + value = flashMillis / ReaderPreferences.MILLI_CONVERSION, + min = 1, + max = 15, + title = stringResource(MR.strings.pref_flash_duration), + subtitle = stringResource(MR.strings.pref_flash_duration_summary, flashMillis), + onValueChanged = { + flashMillisPref.set(it * ReaderPreferences.MILLI_CONVERSION) + true + }, + enabled = flashPageState, + ), + Preference.PreferenceItem.SliderPreference( + value = flashInterval, + min = 1, + max = 10, + title = stringResource(MR.strings.pref_flash_page_interval), + subtitle = pluralStringResource(MR.plurals.pref_pages, flashInterval, flashInterval), + onValueChanged = { + flashIntervalPref.set(it) + true + }, + enabled = flashPageState, + ), + Preference.PreferenceItem.ListPreference( + pref = flashColorPref, + title = stringResource(MR.strings.pref_flash_with), + entries = persistentMapOf( + ReaderPreferences.FlashColor.BLACK to stringResource(MR.strings.pref_flash_style_black), + ReaderPreferences.FlashColor.WHITE to stringResource(MR.strings.pref_flash_style_white), + ReaderPreferences.FlashColor.WHITE_BLACK + to stringResource(MR.strings.pref_flash_style_white_black), + ), + enabled = flashPageState, + ), + ), + ) + } + @Composable private fun getReadingGroup(readerPreferences: ReaderPreferences): Preference.PreferenceGroup { return Preference.PreferenceGroup( diff --git a/app/src/main/java/eu/kanade/presentation/reader/DisplayRefreshHost.kt b/app/src/main/java/eu/kanade/presentation/reader/DisplayRefreshHost.kt index 82d3cad0f..ecf26119d 100644 --- a/app/src/main/java/eu/kanade/presentation/reader/DisplayRefreshHost.kt +++ b/app/src/main/java/eu/kanade/presentation/reader/DisplayRefreshHost.kt @@ -7,19 +7,42 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.Stable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences import kotlinx.coroutines.delay -import kotlin.time.Duration.Companion.seconds +import tachiyomi.presentation.core.util.collectAsState +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get +import kotlin.time.Duration.Companion.milliseconds @Stable class DisplayRefreshHost { internal var currentDisplayRefresh by mutableStateOf(false) + private val readerPreferences = Injekt.get() + + internal val flashMillis = readerPreferences.flashDurationMillis() + internal val flashMode = readerPreferences.flashColor() + + internal val flashIntervalPref = readerPreferences.flashPageInterval() + + // Internal State for Flash + private var flashInterval = flashIntervalPref.get() + private var timesCalled = 0 fun flash() { - currentDisplayRefresh = true + if (timesCalled % flashInterval == 0) { + currentDisplayRefresh = true + } + timesCalled += 1 + } + + fun setInterval(interval: Int) { + flashInterval = interval + timesCalled = 0 } } @@ -29,18 +52,39 @@ fun DisplayRefreshHost( modifier: Modifier = Modifier, ) { val currentDisplayRefresh = hostState.currentDisplayRefresh + val refreshDuration by hostState.flashMillis.collectAsState() + val flashMode by hostState.flashMode.collectAsState() + val flashInterval by hostState.flashIntervalPref.collectAsState() + + var currentColor by remember { mutableStateOf(null) } + LaunchedEffect(currentDisplayRefresh) { - if (currentDisplayRefresh) { - delay(1.5.seconds) - hostState.currentDisplayRefresh = false + if (!currentDisplayRefresh) { + currentColor = null + return@LaunchedEffect } + + val refreshDurationHalf = refreshDuration.milliseconds / 2 + currentColor = if (flashMode == ReaderPreferences.FlashColor.BLACK) { + Color.Black + } else { + Color.White + } + delay(refreshDurationHalf) + if (flashMode == ReaderPreferences.FlashColor.WHITE_BLACK) { + currentColor = Color.Black + } + delay(refreshDurationHalf) + hostState.currentDisplayRefresh = false + } + + LaunchedEffect(flashInterval) { + hostState.setInterval(flashInterval) } Canvas( modifier = modifier.fillMaxSize(), ) { - if (currentDisplayRefresh) { - drawRect(Color.Black) - } + currentColor?.let { drawRect(it) } } } diff --git a/app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt b/app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt index 2f2832feb..0bb2da6cc 100644 --- a/app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt +++ b/app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt @@ -5,10 +5,13 @@ import androidx.compose.material3.FilterChip import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.CheckboxItem import tachiyomi.presentation.core.components.SettingsChipRow +import tachiyomi.presentation.core.components.SliderItem +import tachiyomi.presentation.core.i18n.pluralStringResource import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.util.collectAsState @@ -19,9 +22,27 @@ private val themes = listOf( MR.strings.automatic_background to 3, ) +private val flashColors = listOf( + MR.strings.pref_flash_style_black to ReaderPreferences.FlashColor.BLACK, + MR.strings.pref_flash_style_white to ReaderPreferences.FlashColor.WHITE, + MR.strings.pref_flash_style_white_black to ReaderPreferences.FlashColor.WHITE_BLACK, +) + @Composable internal fun ColumnScope.GeneralPage(screenModel: ReaderSettingsScreenModel) { val readerTheme by screenModel.preferences.readerTheme().collectAsState() + + val flashPageState by screenModel.preferences.flashOnPageChange().collectAsState() + + val flashMillisPref = screenModel.preferences.flashDurationMillis() + val flashMillis by flashMillisPref.collectAsState() + + val flashIntervalPref = screenModel.preferences.flashPageInterval() + val flashInterval by flashIntervalPref.collectAsState() + + val flashColorPref = screenModel.preferences.flashColor() + val flashColor by flashColorPref.collectAsState() + SettingsChipRow(MR.strings.pref_reader_theme) { themes.map { (labelRes, value) -> FilterChip( @@ -73,4 +94,33 @@ internal fun ColumnScope.GeneralPage(screenModel: ReaderSettingsScreenModel) { label = stringResource(MR.strings.pref_flash_page), pref = screenModel.preferences.flashOnPageChange(), ) + if (flashPageState) { + SliderItem( + value = flashMillis / ReaderPreferences.MILLI_CONVERSION, + label = stringResource(MR.strings.pref_flash_duration), + valueText = stringResource(MR.strings.pref_flash_duration_summary, flashMillis), + onChange = { flashMillisPref.set(it * ReaderPreferences.MILLI_CONVERSION) }, + min = 1, + max = 15, + ) + SliderItem( + value = flashInterval, + label = stringResource(MR.strings.pref_flash_page_interval), + valueText = pluralStringResource(MR.plurals.pref_pages, flashInterval, flashInterval), + onChange = { + flashIntervalPref.set(it) + }, + min = 1, + max = 10, + ) + SettingsChipRow(MR.strings.pref_flash_with) { + flashColors.map { (labelRes, value) -> + FilterChip( + selected = flashColor == value, + onClick = { flashColorPref.set(value) }, + label = { Text(stringResource(labelRes)) }, + ) + } + } + } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt index 63162788c..57def3c12 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt @@ -17,6 +17,12 @@ class ReaderPreferences( fun flashOnPageChange() = preferenceStore.getBoolean("pref_reader_flash", false) + fun flashDurationMillis() = preferenceStore.getInt("pref_reader_flash_duration", MILLI_CONVERSION) + + fun flashPageInterval() = preferenceStore.getInt("pref_reader_flash_interval", 1) + + fun flashColor() = preferenceStore.getEnum("pref_reader_flash_mode", FlashColor.BLACK) + fun doubleTapAnimSpeed() = preferenceStore.getInt("pref_double_tap_anim_speed", 500) fun showPageNumber() = preferenceStore.getBoolean("pref_show_page_number_key", true) @@ -133,6 +139,12 @@ class ReaderPreferences( // endregion + enum class FlashColor { + BLACK, + WHITE, + WHITE_BLACK + } + enum class TappingInvertMode( val titleRes: StringResource, val shouldInvertHorizontal: Boolean = false, @@ -155,6 +167,8 @@ class ReaderPreferences( const val WEBTOON_PADDING_MIN = 0 const val WEBTOON_PADDING_MAX = 25 + const val MILLI_CONVERSION = 100 + val TapZones = listOf( MR.strings.label_default, MR.strings.l_nav, diff --git a/i18n/src/commonMain/moko-resources/base/plurals.xml b/i18n/src/commonMain/moko-resources/base/plurals.xml index d9c958afc..e436a8cb5 100644 --- a/i18n/src/commonMain/moko-resources/base/plurals.xml +++ b/i18n/src/commonMain/moko-resources/base/plurals.xml @@ -40,6 +40,11 @@ %d days + + 1 page + %1$s pages + + Missing %1$s chapter diff --git a/i18n/src/commonMain/moko-resources/base/strings.xml b/i18n/src/commonMain/moko-resources/base/strings.xml index afbdf686c..a3e7c3afd 100644 --- a/i18n/src/commonMain/moko-resources/base/strings.xml +++ b/i18n/src/commonMain/moko-resources/base/strings.xml @@ -366,6 +366,13 @@ Animate page transitions Flash on page change Reduces ghosting on e-ink displays + Flash duration + %1$s ms + Flash every + Flash with + Black + White + White and Black Double tap animation speed Show page number Show reading mode From d8fe7d32ca6bacbb74e9e80173625a993621e4b2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 27 Jun 2024 03:30:34 +0600 Subject: [PATCH 120/146] fix(deps): update serialization.version to v1.7.1 (#951) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/kotlinx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/kotlinx.versions.toml b/gradle/kotlinx.versions.toml index ee3c15715..03b23177d 100644 --- a/gradle/kotlinx.versions.toml +++ b/gradle/kotlinx.versions.toml @@ -1,6 +1,6 @@ [versions] kotlin_version = "2.0.0" -serialization_version = "1.7.0" +serialization_version = "1.7.1" xml_serialization_version = "0.86.3" [libraries] From 2d41bf5589a3f26e549ecc7ddd5adafc17d503f4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 27 Jun 2024 03:42:32 +0600 Subject: [PATCH 121/146] fix(deps): update dependency dev.chrisbanes.compose:compose-bom to v2024.06.00-alpha01 (#957) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/compose.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index c36eb21cb..74ae656e9 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,5 +1,5 @@ [versions] -compose-bom = "2024.05.00-alpha03" +compose-bom = "2024.06.00-alpha01" accompanist = "0.35.1-alpha" [libraries] From 7823966ddf2289fee743feaa58b906ab9179a4ed Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 27 Jun 2024 11:48:58 +0600 Subject: [PATCH 122/146] fix(deps): update dependency androidx.test.ext:junit-ktx to v1.2.1 (#959) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 762d2a3d4..7e878e781 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -26,7 +26,7 @@ paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "pag paging-compose = { module = "androidx.paging:paging-compose", version.ref = "paging_version" } benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.4" -test-ext = "androidx.test.ext:junit-ktx:1.2.0" +test-ext = "androidx.test.ext:junit-ktx:1.2.1" test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0" test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0" From f34702d4fcc10f24953b21e883fb454778bbae77 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 27 Jun 2024 11:50:11 +0600 Subject: [PATCH 123/146] fix(deps): update dependency androidx.test.espresso:espresso-core to v3.6.1 (#958) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 7e878e781..778ea000c 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -27,7 +27,7 @@ paging-compose = { module = "androidx.paging:paging-compose", version.ref = "pag benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.4" test-ext = "androidx.test.ext:junit-ktx:1.2.1" -test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0" +test-espresso-core = "androidx.test.espresso:espresso-core:3.6.1" test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0" [bundles] From 2674b849746f20c051dab3fd6edfad1594e41b42 Mon Sep 17 00:00:00 2001 From: Caio Oliveira Date: Thu, 27 Jun 2024 02:50:30 -0300 Subject: [PATCH 124/146] buildSrc: Fix strange warning in ci build (#952) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * buildSrc: Fix strange warning ´Project accessors enabled, but root project name not explicitly set for 'buildSrc'. Checking out the project in different folders will impact the generated code and implicitly the buildscript classpath, breaking caching.´ * Update settings.gradle.kts --------- Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> --- buildSrc/settings.gradle.kts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts index 1b058e0d8..e45d92988 100644 --- a/buildSrc/settings.gradle.kts +++ b/buildSrc/settings.gradle.kts @@ -14,3 +14,5 @@ dependencyResolutionManagement { } } } + +rootProject.name = "mihon-buildSrc" From e132cc405f23e18dd8d73626730821eae9051149 Mon Sep 17 00:00:00 2001 From: CrepeTF <70870719+CrepeTF@users.noreply.github.com> Date: Fri, 28 Jun 2024 12:59:32 +0100 Subject: [PATCH 125/146] Theme fixes (#963) * Fix theme issue with download progress indicator * Fix theme issue with download progress indicator + better contrast --- .../presentation/theme/colorscheme/NordColorScheme.kt | 6 +++--- .../src/main/res/values-night/colors_lavender.xml | 2 +- .../src/main/res/values-night/colors_midnightdusk.xml | 2 +- presentation-core/src/main/res/values-night/colors_nord.xml | 2 +- presentation-core/src/main/res/values-night/colors_tako.xml | 2 +- presentation-core/src/main/res/values/colors_lavender.xml | 2 +- presentation-core/src/main/res/values/colors_nord.xml | 2 +- presentation-core/src/main/res/values/colors_tako.xml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/NordColorScheme.kt b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/NordColorScheme.kt index 2e89b9ea4..84fe7b49f 100644 --- a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/NordColorScheme.kt +++ b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/NordColorScheme.kt @@ -19,8 +19,8 @@ internal object NordColorScheme : BaseColorScheme() { inversePrimary = Color(0xFF397E91), secondary = Color(0xFF81A1C1), // Unread badge onSecondary = Color(0xFF2E3440), // Unread badge text - secondaryContainer = Color(0xFF81A1C1), // Navigation bar selector pill & progress indicator (remaining) - onSecondaryContainer = Color(0xFF2E3440), // Navigation bar selector icon + secondaryContainer = Color(0xFF506275), // Navigation bar selector pill & progress indicator (remaining) + onSecondaryContainer = Color(0xFF88C0D0), // Navigation bar selector icon tertiary = Color(0xFF5E81AC), // Downloaded badge onTertiary = Color(0xFF000000), // Downloaded badge text tertiaryContainer = Color(0xFF5E81AC), @@ -54,7 +54,7 @@ internal object NordColorScheme : BaseColorScheme() { inversePrimary = Color(0xFF8CA8CD), secondary = Color(0xFF81A1C1), // Unread badge onSecondary = Color(0xFF2E3440), // Unread badge text - secondaryContainer = Color(0xFF81A1C1), // Navigation bar selector pill & progress indicator (remaining) + secondaryContainer = Color(0xFF91B4D7), // Navigation bar selector pill & progress indicator (remaining) onSecondaryContainer = Color(0xFF2E3440), // Navigation bar selector icon tertiary = Color(0xFF88C0D0), // Downloaded badge onTertiary = Color(0xFF2E3440), // Downloaded badge text diff --git a/presentation-core/src/main/res/values-night/colors_lavender.xml b/presentation-core/src/main/res/values-night/colors_lavender.xml index d76e55486..f9d558025 100644 --- a/presentation-core/src/main/res/values-night/colors_lavender.xml +++ b/presentation-core/src/main/res/values-night/colors_lavender.xml @@ -17,7 +17,7 @@ #111129 #A177FF #111129 - #A177FF + #423271 #111129 #5E25E1 #E8E8E8 diff --git a/presentation-core/src/main/res/values-night/colors_midnightdusk.xml b/presentation-core/src/main/res/values-night/colors_midnightdusk.xml index d107312aa..f10e84662 100644 --- a/presentation-core/src/main/res/values-night/colors_midnightdusk.xml +++ b/presentation-core/src/main/res/values-night/colors_midnightdusk.xml @@ -17,7 +17,7 @@ #FFFFFF #F02475 #FFFFFF - #F02475 + #66183C #FFFFFF #55971C #FFFFFF diff --git a/presentation-core/src/main/res/values-night/colors_nord.xml b/presentation-core/src/main/res/values-night/colors_nord.xml index f9ab8ce50..058d52a4a 100644 --- a/presentation-core/src/main/res/values-night/colors_nord.xml +++ b/presentation-core/src/main/res/values-night/colors_nord.xml @@ -7,7 +7,7 @@ #2E3440 #81A1C1 #2E3440 - #81A1C1 + #506275 #2E3440 #5E81AC #000000 diff --git a/presentation-core/src/main/res/values-night/colors_tako.xml b/presentation-core/src/main/res/values-night/colors_tako.xml index 907486c60..85f898c32 100644 --- a/presentation-core/src/main/res/values-night/colors_tako.xml +++ b/presentation-core/src/main/res/values-night/colors_tako.xml @@ -15,7 +15,7 @@ #38294E #F3B375 #38294E - #F3B375 + #5C4D4B #38294E #F3B375 #38294E diff --git a/presentation-core/src/main/res/values/colors_lavender.xml b/presentation-core/src/main/res/values/colors_lavender.xml index f6cfda65c..02d6274e3 100644 --- a/presentation-core/src/main/res/values/colors_lavender.xml +++ b/presentation-core/src/main/res/values/colors_lavender.xml @@ -16,7 +16,7 @@ #EDE2FF #7B46AF #EDE2FF - #7B46AF + #C9B0E6 #EDE2FF #EDE2FF #7B46AF diff --git a/presentation-core/src/main/res/values/colors_nord.xml b/presentation-core/src/main/res/values/colors_nord.xml index 7aab3eb43..b04ae3eec 100644 --- a/presentation-core/src/main/res/values/colors_nord.xml +++ b/presentation-core/src/main/res/values/colors_nord.xml @@ -8,7 +8,7 @@ #000000 #81A1C1 #2E3440 - #81A1C1 + #91B4D7 #2E3440 #88C0D0 #2E3440 diff --git a/presentation-core/src/main/res/values/colors_tako.xml b/presentation-core/src/main/res/values/colors_tako.xml index 0976d1b14..6a002cd2c 100644 --- a/presentation-core/src/main/res/values/colors_tako.xml +++ b/presentation-core/src/main/res/values/colors_tako.xml @@ -17,7 +17,7 @@ #F3B375 #66577E #F3B375 - #66577E + #C8BED0 #F3B375 #F3B375 #574360 From 9e2f97eeb8a0f1d1b353dc3e77fb64d69b568674 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 28 Jun 2024 17:59:49 +0600 Subject: [PATCH 126/146] fix(deps): update dependency org.junit.jupiter:junit-jupiter to v5.10.3 (#962) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 36bf03900..7cccc999b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -89,7 +89,7 @@ sqldelight-android-paging = { module = "app.cash.sqldelight:androidx-paging3-ext sqldelight-dialects-sql = { module = "app.cash.sqldelight:sqlite-3-38-dialect", version.ref = "sqldelight" } sqldelight-gradle = { module = "app.cash.sqldelight:gradle-plugin", version.ref = "sqldelight" } -junit = "org.junit.jupiter:junit-jupiter:5.10.2" +junit = "org.junit.jupiter:junit-jupiter:5.10.3" kotest-assertions = "io.kotest:kotest-assertions-core:5.9.1" mockk = "io.mockk:mockk:1.13.11" From 80cdebcdf467ed00e530651aeed2b36cc63b8356 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 29 Jun 2024 23:26:00 +0600 Subject: [PATCH 127/146] fix(deps): update aboutlib.version to v11.2.2 (#965) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7cccc999b..0aa5ac0eb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -aboutlib_version = "11.2.1" +aboutlib_version = "11.2.2" leakcanary = "2.14" moko = "0.24.1" okhttp_version = "5.0.0-alpha.14" From c0f9de88e70ef1db97c521993462ae27550b5790 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 30 Jun 2024 01:44:17 +0600 Subject: [PATCH 128/146] fix(deps): update dependency io.coil-kt.coil3:coil-bom to v3.0.0-alpha07 (#960) * fix(deps): update dependency io.coil-kt.coil3:coil-bom to v3.0.0-alpha07 * Fix build --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> --- app/build.gradle.kts | 2 ++ .../kanade/presentation/manga/components/MangaCoverDialog.kt | 1 + .../eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt | 4 ++-- .../eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt | 1 + .../eu/kanade/tachiyomi/ui/manga/MangaCoverScreenModel.kt | 1 + .../java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt | 1 + .../kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt | 1 + .../eu/kanade/tachiyomi/util/system/DrawableExtensions.kt | 2 +- gradle/androidx.versions.toml | 3 +++ gradle/libs.versions.toml | 2 +- .../presentation/widget/BaseUpdatesGridGlanceWidget.kt | 1 + 11 files changed, 15 insertions(+), 4 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9ea8f6ccf..1dbee966e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -158,6 +158,8 @@ dependencies { implementation(compose.ui.tooling.preview) implementation(compose.ui.util) implementation(compose.accompanist.systemuicontroller) + + implementation(androidx.interpolator) implementation(androidx.paging.runtime) implementation(androidx.paging.compose) diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaCoverDialog.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaCoverDialog.kt index a70511326..87bad99c2 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaCoverDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaCoverDialog.kt @@ -37,6 +37,7 @@ import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties import androidx.core.view.updatePadding +import coil3.asDrawable import coil3.imageLoader import coil3.request.CachePolicy import coil3.request.ImageRequest diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt index 2929f4da3..6403f760b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt @@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.data.coil import android.graphics.Bitmap import coil3.ImageLoader -import coil3.asCoilImage +import coil3.asImage import coil3.decode.DecodeResult import coil3.decode.DecodeUtils import coil3.decode.Decoder @@ -58,7 +58,7 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti } return DecodeResult( - image = bitmap.asCoilImage(), + image = bitmap.asImage(), isSampled = sampleSize > 1, ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt index e18bf76c4..f7d6c5dbc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt @@ -9,6 +9,7 @@ import android.graphics.BitmapFactory import android.net.Uri import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat +import coil3.asDrawable import coil3.imageLoader import coil3.request.ImageRequest import coil3.request.transformations diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaCoverScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaCoverScreenModel.kt index 75a98c5df..de79d4d0d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaCoverScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaCoverScreenModel.kt @@ -5,6 +5,7 @@ import android.net.Uri import androidx.compose.material3.SnackbarHostState import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.screenModelScope +import coil3.asDrawable import coil3.imageLoader import coil3.request.ImageRequest import coil3.size.Size diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt index 373ed97a8..6b08dfeb3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt @@ -4,6 +4,7 @@ import android.content.Context import android.graphics.Bitmap import android.net.Uri import androidx.core.app.NotificationCompat +import coil3.asDrawable import coil3.imageLoader import coil3.request.CachePolicy import coil3.request.ImageRequest diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt index 17b973a7e..61e78c161 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt @@ -19,6 +19,7 @@ import androidx.appcompat.widget.AppCompatImageView import androidx.core.os.postDelayed import androidx.core.view.isVisible import coil3.BitmapImage +import coil3.asDrawable import coil3.dispose import coil3.imageLoader import coil3.request.CachePolicy diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/DrawableExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/DrawableExtensions.kt index 2877768af..872552ef9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/DrawableExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/DrawableExtensions.kt @@ -4,7 +4,7 @@ import android.graphics.Bitmap import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable import androidx.core.graphics.drawable.toBitmap -import coil3.gif.ScaleDrawable +import coil3.size.ScaleDrawable fun Drawable.getBitmapOrNull(): Bitmap? = when (this) { is BitmapDrawable -> bitmap diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 778ea000c..73b2a6546 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -2,6 +2,7 @@ agp_version = "8.5.0" lifecycle_version = "2.8.2" paging_version = "3.3.0" +interpolator_version = "1.0.0" [libraries] gradle = { module = "com.android.tools.build:gradle", version.ref = "agp_version" } @@ -25,6 +26,8 @@ workmanager = "androidx.work:work-runtime:2.9.0" paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "paging_version" } paging-compose = { module = "androidx.paging:paging-compose", version.ref = "paging_version" } +interpolator = { group = "androidx.interpolator", name = "interpolator", version.ref = "interpolator_version" } + benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.4" test-ext = "androidx.test.ext:junit-ktx:1.2.1" test-espresso-core = "androidx.test.espresso:espresso-core:3.6.1" diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0aa5ac0eb..37f29e6bc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -42,7 +42,7 @@ preferencektx = "androidx.preference:preference-ktx:1.2.1" injekt-core = "com.github.inorichi.injekt:injekt-core:65b0440" -coil-bom = { module = "io.coil-kt.coil3:coil-bom", version = "3.0.0-alpha06" } +coil-bom = { module = "io.coil-kt.coil3:coil-bom", version = "3.0.0-alpha07" } coil-core = { module = "io.coil-kt.coil3:coil" } coil-gif = { module = "io.coil-kt.coil3:coil-gif" } coil-compose = { module = "io.coil-kt.coil3:coil-compose" } diff --git a/presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt b/presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt index 4fa7850a8..bc9c08faf 100644 --- a/presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt +++ b/presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt @@ -22,6 +22,7 @@ import androidx.glance.layout.fillMaxSize import androidx.glance.layout.padding import androidx.glance.unit.ColorProvider import coil3.annotation.ExperimentalCoilApi +import coil3.asDrawable import coil3.executeBlocking import coil3.imageLoader import coil3.request.CachePolicy From e620665dda9eb5cc39f09e6087ea4f60a3cbe150 Mon Sep 17 00:00:00 2001 From: Ahmad Ansori Palembani <46041660+null2264@users.noreply.github.com> Date: Sun, 30 Jun 2024 20:57:29 +0700 Subject: [PATCH 129/146] Add safeguard to prevent ArchiveInputStream from being closed twice (#967) * fix: Add safeguard to prevent ArchiveInputStream from being closed twice * detekt * lint: Make detekt happy --------- Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> --- .../mihon/core/common/archive/ArchiveInputStream.kt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/core/common/src/main/kotlin/mihon/core/common/archive/ArchiveInputStream.kt b/core/common/src/main/kotlin/mihon/core/common/archive/ArchiveInputStream.kt index a9bb87879..1499867c8 100644 --- a/core/common/src/main/kotlin/mihon/core/common/archive/ArchiveInputStream.kt +++ b/core/common/src/main/kotlin/mihon/core/common/archive/ArchiveInputStream.kt @@ -5,8 +5,14 @@ import me.zhanghai.android.libarchive.ArchiveEntry import me.zhanghai.android.libarchive.ArchiveException import java.io.InputStream import java.nio.ByteBuffer +import kotlin.concurrent.Volatile class ArchiveInputStream(buffer: Long, size: Long) : InputStream() { + private val lock = Any() + + @Volatile + private var isClosed = false + private val archive = Archive.readNew() init { @@ -41,6 +47,11 @@ class ArchiveInputStream(buffer: Long, size: Long) : InputStream() { } override fun close() { + synchronized(lock) { + if (isClosed) return + isClosed = true + } + Archive.readFree(archive) } From bff6183cf3ef400d8ddcdccf7180e4139816cc09 Mon Sep 17 00:00:00 2001 From: WerctFourth <88225220+WerctFourth@users.noreply.github.com> Date: Mon, 1 Jul 2024 21:15:46 +0500 Subject: [PATCH 130/146] Update image-decoder revision (#971) --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 37f29e6bc..84e9797c5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -49,7 +49,7 @@ coil-compose = { module = "io.coil-kt.coil3:coil-compose" } coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp" } subsamplingscaleimageview = "com.github.tachiyomiorg:subsampling-scale-image-view:b8e1b0ed2b" -image-decoder = "com.github.tachiyomiorg:image-decoder:e08e9be535" +image-decoder = "com.github.tachiyomiorg:image-decoder:41c059e540" natural-comparator = "com.github.gpanther:java-nat-sort:natural-comparator-1.1" From 77db8873f6753cc9db8f67b39d53685563380cc6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2024 01:58:48 +0600 Subject: [PATCH 131/146] fix(deps): update lifecycle.version to v2.8.3 (#972) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 73b2a6546..f7aa3acb6 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -1,6 +1,6 @@ [versions] agp_version = "8.5.0" -lifecycle_version = "2.8.2" +lifecycle_version = "2.8.3" paging_version = "3.3.0" interpolator_version = "1.0.0" From 75b5d966018aa917f57adf37370088a51e4914b2 Mon Sep 17 00:00:00 2001 From: CrepeTF <70870719+CrepeTF@users.noreply.github.com> Date: Tue, 2 Jul 2024 11:52:55 +0100 Subject: [PATCH 132/146] Correct tako variable colours (#976) --- presentation-core/src/main/res/values-night/colors_tako.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/presentation-core/src/main/res/values-night/colors_tako.xml b/presentation-core/src/main/res/values-night/colors_tako.xml index 85f898c32..63c2db1ce 100644 --- a/presentation-core/src/main/res/values-night/colors_tako.xml +++ b/presentation-core/src/main/res/values-night/colors_tako.xml @@ -15,8 +15,8 @@ #38294E #F3B375 #38294E - #5C4D4B - #38294E + #F3B375 + #5C4D4B #F3B375 #38294E #66577E From ddba71df37359e6abbbcc96b18685435961710dc Mon Sep 17 00:00:00 2001 From: Roshan Varughese <40583749+Animeboynz@users.noreply.github.com> Date: Wed, 3 Jul 2024 00:08:33 +1200 Subject: [PATCH 133/146] Smart Update Dialog Tweak (#977) * Smart Update Dialog Fix * Build Fail Change 1 * Commit Suggested Change Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> * Build Fail Change 2 --------- Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> --- .../kanade/presentation/manga/components/MangaDialogs.kt | 7 +++++-- i18n/src/commonMain/moko-resources/base/strings.xml | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaDialogs.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaDialogs.kt index a6ef3f9a5..bee8a8844 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaDialogs.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaDialogs.kt @@ -102,9 +102,12 @@ fun SetIntervalDialog( ), ), ) - - Spacer(Modifier.height(MaterialTheme.padding.small)) + } else { + Text( + stringResource(MR.strings.manga_interval_expected_update_null), + ) } + Spacer(Modifier.height(MaterialTheme.padding.small)) if (onValueChanged != null && (isDevFlavor || isPreviewBuildType)) { Text(stringResource(MR.strings.manga_interval_custom_amount)) diff --git a/i18n/src/commonMain/moko-resources/base/strings.xml b/i18n/src/commonMain/moko-resources/base/strings.xml index a3e7c3afd..08bbe2d76 100644 --- a/i18n/src/commonMain/moko-resources/base/strings.xml +++ b/i18n/src/commonMain/moko-resources/base/strings.xml @@ -693,6 +693,7 @@ Set to update every New chapters predicted to be released in around %1$s, checking around every %2$s. + This manga is either completed, or there is no predicted release date. Soon Custom update frequency: Downloading (%1$d/%2$d) From 5a61ca5535fe0d9e8e7bcb9e665ba2f9cb0cf649 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Wed, 3 Jul 2024 06:00:04 +0600 Subject: [PATCH 134/146] Make global search "Has result" sticky Closes #133 --- .../domain/source/service/SourcePreferences.kt | 5 +++++ .../browse/source/globalsearch/SearchScreenModel.kt | 13 ++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt b/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt index d4d3989c0..72989891e 100644 --- a/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt +++ b/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt @@ -48,4 +48,9 @@ class SourcePreferences( Preference.appStateKey("trusted_extensions"), emptySet(), ) + + fun globalSearchFilterState() = preferenceStore.getBoolean( + Preference.appStateKey("has_filters_toggle_state"), + false, + ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/SearchScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/SearchScreenModel.kt index 3f7438b7c..1cb9ba3ff 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/SearchScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/SearchScreenModel.kt @@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable import androidx.compose.runtime.produceState import cafe.adriel.voyager.core.model.StateScreenModel +import cafe.adriel.voyager.core.model.screenModelScope import eu.kanade.domain.manga.model.toDomainManga import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.presentation.util.ioCoroutineScope @@ -23,6 +24,7 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import tachiyomi.core.common.preference.toggle import tachiyomi.domain.manga.interactor.GetManga import tachiyomi.domain.manga.interactor.NetworkToLocalManga import tachiyomi.domain.manga.model.Manga @@ -38,6 +40,7 @@ abstract class SearchScreenModel( private val extensionManager: ExtensionManager = Injekt.get(), private val networkToLocalManga: NetworkToLocalManga = Injekt.get(), private val getManga: GetManga = Injekt.get(), + private val preferences: SourcePreferences = Injekt.get(), ) : StateScreenModel(initialState) { private val coroutineDispatcher = Executors.newFixedThreadPool(5).asCoroutineDispatcher() @@ -60,6 +63,14 @@ abstract class SearchScreenModel( ) } + init { + screenModelScope.launch { + preferences.globalSearchFilterState().changes().collectLatest { state -> + mutableState.update { it.copy(onlyShowHasResults = state) } + } + } + } + @Composable fun getManga(initialManga: Manga): androidx.compose.runtime.State { return produceState(initialValue = initialManga) { @@ -107,7 +118,7 @@ abstract class SearchScreenModel( } fun toggleFilterResults() { - mutableState.update { it.copy(onlyShowHasResults = !it.onlyShowHasResults) } + preferences.globalSearchFilterState().toggle() } fun search() { From 2092c81bad59fd745a8514af320e534ecf40a5da Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 6 Jul 2024 07:25:33 +0600 Subject: [PATCH 135/146] Observe tracker login state instead of fetching once (#987) * Observe tracker login state instead of fetching once * Review changes --- .../library/LibrarySettingsDialog.kt | 15 ++++---- .../more/settings/PreferenceItem.kt | 20 +++++------ .../tachiyomi/data/track/BaseTracker.kt | 11 ++++++ .../eu/kanade/tachiyomi/data/track/Tracker.kt | 3 ++ .../tachiyomi/data/track/TrackerManager.kt | 9 +++++ .../ui/library/LibraryScreenModel.kt | 35 ++++++++++--------- .../ui/library/LibrarySettingsScreenModel.kt | 13 +++++-- .../kanade/tachiyomi/ui/manga/MangaScreen.kt | 2 +- .../tachiyomi/ui/manga/MangaScreenModel.kt | 34 +++++++++++------- .../ui/manga/track/TrackInfoDialog.kt | 2 +- .../tachiyomi/ui/stats/StatsScreenModel.kt | 2 +- .../main/java/eu/kanade/test/DummyTracker.kt | 3 ++ 12 files changed, 96 insertions(+), 53 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt b/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt index ad0733606..bf5daae99 100644 --- a/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt @@ -9,6 +9,7 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material3.FilterChip import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier @@ -125,7 +126,7 @@ private fun ColumnScope.FilterPage( ) } - val trackers = remember { screenModel.trackers } + val trackers by screenModel.trackersFlow.collectAsState() when (trackers.size) { 0 -> { // No trackers @@ -158,15 +159,15 @@ private fun ColumnScope.SortPage( category: Category?, screenModel: LibrarySettingsScreenModel, ) { + val trackers by screenModel.trackersFlow.collectAsState() val sortingMode = category.sort.type val sortDescending = !category.sort.isAscending - val trackerSortOption = - if (screenModel.trackers.isEmpty()) { - emptyList() - } else { - listOf(MR.strings.action_sort_tracker_score to LibrarySort.Type.TrackerMean) - } + val trackerSortOption = if (trackers.isEmpty()) { + emptyList() + } else { + listOf(MR.strings.action_sort_tracker_score to LibrarySort.Type.TrackerMean) + } listOf( MR.strings.action_sort_alpha to LibrarySort.Type.Alphabetical, diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceItem.kt b/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceItem.kt index b22e69323..5a3c9d53b 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceItem.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceItem.kt @@ -7,12 +7,12 @@ import androidx.compose.animation.fadeOut import androidx.compose.animation.shrinkVertically import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.structuralEqualityPolicy import androidx.compose.ui.unit.dp -import eu.kanade.domain.track.service.TrackPreferences import eu.kanade.presentation.more.settings.widget.EditTextPreferenceWidget import eu.kanade.presentation.more.settings.widget.InfoWidget import eu.kanade.presentation.more.settings.widget.ListPreferenceWidget @@ -23,8 +23,6 @@ import eu.kanade.presentation.more.settings.widget.TrackingPreferenceWidget import kotlinx.coroutines.launch import tachiyomi.presentation.core.components.SliderItem import tachiyomi.presentation.core.util.collectAsState -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get val LocalPreferenceHighlighted = compositionLocalOf(structuralEqualityPolicy()) { false } val LocalPreferenceMinHeight = compositionLocalOf(structuralEqualityPolicy()) { 56.dp } @@ -156,16 +154,14 @@ internal fun PreferenceItem( ) } is Preference.PreferenceItem.TrackerPreference -> { - val uName by Injekt.get() - .trackUsername(item.tracker) - .collectAsState() - item.tracker.run { - TrackingPreferenceWidget( - tracker = this, - checked = uName.isNotEmpty(), - onClick = { if (isLoggedIn) item.logout() else item.login() }, - ) + val isLoggedIn by item.tracker.let { tracker -> + tracker.isLoggedInFlow.collectAsState(tracker.isLoggedIn) } + TrackingPreferenceWidget( + tracker = item.tracker, + checked = isLoggedIn, + onClick = { if (isLoggedIn) item.logout() else item.login() }, + ) } is Preference.PreferenceItem.InfoPreference -> { InfoWidget(text = item.title) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt index 8f88f1051..33caf6555 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt @@ -8,6 +8,8 @@ import eu.kanade.domain.track.service.TrackPreferences import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.util.system.toast +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine import logcat.LogPriority import okhttp3.OkHttpClient import tachiyomi.core.common.util.lang.withIOContext @@ -53,6 +55,15 @@ abstract class BaseTracker( get() = getUsername().isNotEmpty() && getPassword().isNotEmpty() + override val isLoggedInFlow: Flow by lazy { + combine( + trackPreferences.trackUsername(this).changes(), + trackPreferences.trackPassword(this).changes(), + ) { username, password -> + username.isNotEmpty() && password.isNotEmpty() + } + } + override fun getUsername() = trackPreferences.trackUsername(this).get() override fun getPassword() = trackPreferences.trackPassword(this).get() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/Tracker.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/Tracker.kt index 06644e932..fd3a9f45e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/Tracker.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/Tracker.kt @@ -7,6 +7,7 @@ import dev.icerock.moko.resources.StringResource import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.track.model.TrackSearch import kotlinx.collections.immutable.ImmutableList +import kotlinx.coroutines.flow.Flow import okhttp3.OkHttpClient import tachiyomi.domain.track.model.Track as DomainTrack @@ -61,6 +62,8 @@ interface Tracker { val isLoggedIn: Boolean + val isLoggedInFlow: Flow + fun getUsername(): String fun getPassword(): String diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackerManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackerManager.kt index 598a0c06c..1071fa7ee 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackerManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackerManager.kt @@ -9,6 +9,7 @@ import eu.kanade.tachiyomi.data.track.mangaupdates.MangaUpdates import eu.kanade.tachiyomi.data.track.myanimelist.MyAnimeList import eu.kanade.tachiyomi.data.track.shikimori.Shikimori import eu.kanade.tachiyomi.data.track.suwayomi.Suwayomi +import kotlinx.coroutines.flow.combine class TrackerManager { @@ -32,5 +33,13 @@ class TrackerManager { fun loggedInTrackers() = trackers.filter { it.isLoggedIn } + fun loggedInTrackersFlow() = combine(trackers.map { it.isLoggedInFlow }) { + it.mapIndexedNotNull { index, isLoggedIn -> + if (isLoggedIn) trackers[index] else null + } + } + fun get(id: Long) = trackers.find { it.id == id } + + fun getAll(ids: Set) = trackers.filter { it.id in ids } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt index b2771d4e2..e866c98c9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt @@ -39,6 +39,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map @@ -106,10 +107,10 @@ class LibraryScreenModel( getTracksPerManga.subscribe(), getTrackingFilterFlow(), downloadCache.changes, - ) { searchQuery, library, tracks, loggedInTrackers, _ -> + ) { searchQuery, library, tracks, trackingFiler, _ -> library - .applyFilters(tracks, loggedInTrackers) - .applySort(tracks) + .applyFilters(tracks, trackingFiler) + .applySort(tracks, trackingFiler.keys) .mapValues { (_, value) -> if (searchQuery != null) { // Filter query @@ -173,9 +174,10 @@ class LibraryScreenModel( /** * Applies library filters to the given map of manga. */ + @Suppress("LongMethod", "CyclomaticComplexMethod") private suspend fun LibraryMap.applyFilters( trackMap: Map>, - loggedInTrackers: Map, + trackingFiler: Map, ): LibraryMap { val prefs = getLibraryItemPreferencesFlow().first() val downloadedOnly = prefs.globalFilterDownloaded @@ -187,10 +189,10 @@ class LibraryScreenModel( val filterCompleted = prefs.filterCompleted val filterIntervalCustom = prefs.filterIntervalCustom - val isNotLoggedInAnyTrack = loggedInTrackers.isEmpty() + val isNotLoggedInAnyTrack = trackingFiler.isEmpty() - val excludedTracks = loggedInTrackers.mapNotNull { if (it.value == TriState.ENABLED_NOT) it.key else null } - val includedTracks = loggedInTrackers.mapNotNull { if (it.value == TriState.ENABLED_IS) it.key else null } + val excludedTracks = trackingFiler.mapNotNull { if (it.value == TriState.ENABLED_NOT) it.key else null } + val includedTracks = trackingFiler.mapNotNull { if (it.value == TriState.ENABLED_IS) it.key else null } val trackFiltersIsIgnored = includedTracks.isEmpty() && excludedTracks.isEmpty() val filterFnDownloaded: (LibraryItem) -> Boolean = { @@ -254,9 +256,11 @@ class LibraryScreenModel( /** * Applies library sorting to the given map of manga. */ + @Suppress("LongMethod", "CyclomaticComplexMethod") private fun LibraryMap.applySort( // Map> trackMap: Map>, + loggedInTrackerIds: Set ): LibraryMap { val sortAlphabetically: (LibraryItem, LibraryItem) -> Int = { i1, i2 -> i1.libraryManga.manga.title.lowercase().compareToWithCollator(i2.libraryManga.manga.title.lowercase()) @@ -264,7 +268,7 @@ class LibraryScreenModel( val defaultTrackerScoreSortValue = -1.0 val trackerScores by lazy { - val trackerMap = trackerManager.loggedInTrackers().associateBy { e -> e.id } + val trackerMap = trackerManager.getAll(loggedInTrackerIds).associateBy { e -> e.id } trackMap.mapValues { entry -> when { entry.value.isEmpty() -> null @@ -405,18 +409,17 @@ class LibraryScreenModel( * @return map of track id with the filter value */ private fun getTrackingFilterFlow(): Flow> { - val loggedInTrackers = trackerManager.loggedInTrackers() - return if (loggedInTrackers.isNotEmpty()) { - val prefFlows = loggedInTrackers - .map { libraryPreferences.filterTracking(it.id.toInt()).changes() } - .toTypedArray() - combine(*prefFlows) { + return trackerManager.loggedInTrackersFlow().flatMapLatest { loggedInTrackers -> + if (loggedInTrackers.isEmpty()) return@flatMapLatest flowOf(emptyMap()) + + val prefFlows = loggedInTrackers.map { tracker -> + libraryPreferences.filterTracking(tracker.id.toInt()).changes() + } + combine(prefFlows) { loggedInTrackers .mapIndexed { index, tracker -> tracker.id to it[index] } .toMap() } - } else { - flowOf(emptyMap()) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsScreenModel.kt index f0856f830..bd5867797 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsScreenModel.kt @@ -4,6 +4,8 @@ import cafe.adriel.voyager.core.model.ScreenModel import cafe.adriel.voyager.core.model.screenModelScope import eu.kanade.domain.base.BasePreferences import eu.kanade.tachiyomi.data.track.TrackerManager +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.stateIn import tachiyomi.core.common.preference.Preference import tachiyomi.core.common.preference.TriState import tachiyomi.core.common.preference.getAndSet @@ -16,17 +18,22 @@ import tachiyomi.domain.library.model.LibrarySort import tachiyomi.domain.library.service.LibraryPreferences import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get +import kotlin.time.Duration.Companion.seconds class LibrarySettingsScreenModel( val preferences: BasePreferences = Injekt.get(), val libraryPreferences: LibraryPreferences = Injekt.get(), private val setDisplayMode: SetDisplayMode = Injekt.get(), private val setSortModeForCategory: SetSortModeForCategory = Injekt.get(), - private val trackerManager: TrackerManager = Injekt.get(), + trackerManager: TrackerManager = Injekt.get(), ) : ScreenModel { - val trackers - get() = trackerManager.trackers.filter { it.isLoggedIn } + val trackersFlow = trackerManager.loggedInTrackersFlow() + .stateIn( + scope = screenModelScope, + started = SharingStarted.WhileSubscribed(5.seconds.inWholeMilliseconds), + initialValue = trackerManager.loggedInTrackers() + ) fun toggleFilter(preference: (LibraryPreferences) -> Preference) { preference(libraryPreferences).getAndSet { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt index c2466ffd4..b4b82584e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt @@ -138,7 +138,7 @@ class MangaScreen( ) }.takeIf { isHttpSource }, onTrackingClicked = { - if (screenModel.loggedInTrackers.isEmpty()) { + if (successState.loggedInTracker.isEmpty()) { navigator.push(SettingsScreen(SettingsScreen.Destination.Tracking)) } else { screenModel.showTrackDialog() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt index 14cb037df..682c45fcd 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt @@ -28,6 +28,7 @@ import eu.kanade.tachiyomi.data.download.DownloadCache import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.track.EnhancedTracker +import eu.kanade.tachiyomi.data.track.Tracker import eu.kanade.tachiyomi.data.track.TrackerManager import eu.kanade.tachiyomi.network.HttpException import eu.kanade.tachiyomi.source.Source @@ -45,7 +46,6 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.update import kotlinx.coroutines.isActive import kotlinx.coroutines.launch @@ -117,8 +117,6 @@ class MangaScreenModel( private val successState: State.Success? get() = state.value as? State.Success - val loggedInTrackers by lazy { trackerManager.trackers.filter { it.isLoggedIn } } - val manga: Manga? get() = successState?.manga @@ -194,6 +192,16 @@ class MangaScreenModel( } } + screenModelScope.launchIO { + trackerManager.loggedInTrackersFlow() + .distinctUntilChanged() + .collectLatest { trackers -> + updateSuccessState { + it.copy(loggedInTracker = trackers) + } + } + } + observeDownloads() screenModelScope.launchIO { @@ -978,15 +986,16 @@ class MangaScreenModel( val manga = successState?.manga ?: return screenModelScope.launchIO { - getTracks.subscribe(manga.id) - .catch { logcat(LogPriority.ERROR, it) } - .map { tracks -> - loggedInTrackers - // Map to TrackItem - .map { service -> TrackItem(tracks.find { it.trackerId == service.id }, service) } - // Show only if the service supports this manga's source - .filter { (it.tracker as? EnhancedTracker)?.accept(source!!) ?: true } - } + combine( + getTracks.subscribe(manga.id).catch { logcat(LogPriority.ERROR, it) }, + trackerManager.loggedInTrackersFlow(), + ) { mangaTracks, loggedInTrackers -> + loggedInTrackers + // Map to TrackItem + .map { service -> TrackItem(mangaTracks.find { it.trackerId == service.id }, service) } + // Show only if the service supports this manga's source + .filter { (it.tracker as? EnhancedTracker)?.accept(source!!) ?: true } + } .distinctUntilChanged() .collectLatest { trackItems -> updateSuccessState { it.copy(trackItems = trackItems) } @@ -1057,6 +1066,7 @@ class MangaScreenModel( val isRefreshingData: Boolean = false, val dialog: Dialog? = null, val hasPromptedToAddBefore: Boolean = false, + val loggedInTracker: List = emptyList(), ) : State { val processedChapters by lazy { chapters.applyFilters(manga).toList() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt index 1ba697f24..4c66940c9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt @@ -239,7 +239,7 @@ data class TrackInfoDialogHomeScreen( } private fun List.mapToTrackItem(): List { - val loggedInTrackers = Injekt.get().trackers.filter { it.isLoggedIn } + val loggedInTrackers = Injekt.get().loggedInTrackers() val source = Injekt.get().getOrStub(sourceId) return loggedInTrackers // Map to TrackItem diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreenModel.kt index 0d553ab4b..174ac26dc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreenModel.kt @@ -36,7 +36,7 @@ class StatsScreenModel( private val trackerManager: TrackerManager = Injekt.get(), ) : StateScreenModel(StatsScreenState.Loading) { - private val loggedInTrackers by lazy { trackerManager.trackers.fastFilter { it.isLoggedIn } } + private val loggedInTrackers by lazy { trackerManager.loggedInTrackers() } init { screenModelScope.launchIO { diff --git a/app/src/main/java/eu/kanade/test/DummyTracker.kt b/app/src/main/java/eu/kanade/test/DummyTracker.kt index 56092b440..4c77e660d 100644 --- a/app/src/main/java/eu/kanade/test/DummyTracker.kt +++ b/app/src/main/java/eu/kanade/test/DummyTracker.kt @@ -7,6 +7,8 @@ import eu.kanade.tachiyomi.data.track.Tracker import eu.kanade.tachiyomi.data.track.model.TrackSearch import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf import okhttp3.OkHttpClient import tachiyomi.domain.track.model.Track import tachiyomi.i18n.MR @@ -16,6 +18,7 @@ data class DummyTracker( override val name: String, override val supportsReadingDates: Boolean = false, override val isLoggedIn: Boolean = false, + override val isLoggedInFlow: Flow = flowOf(false), val valLogoColor: Int = Color.rgb(18, 25, 35), val valLogo: Int = R.drawable.ic_tracker_anilist, val valStatuses: List = (1L..6L).toList(), From cbcd8bd6682023f728568f2b44da26124618aed7 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Mon, 8 Jul 2024 09:20:02 +0600 Subject: [PATCH 136/146] Fix login prompts despite being logged in to trackers in Manga screen --- .../kanade/tachiyomi/ui/manga/MangaScreen.kt | 2 +- .../tachiyomi/ui/manga/MangaScreenModel.kt | 38 +++++++------------ 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt index b4b82584e..e500a04d3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt @@ -138,7 +138,7 @@ class MangaScreen( ) }.takeIf { isHttpSource }, onTrackingClicked = { - if (successState.loggedInTracker.isEmpty()) { + if (!successState.hasLoggedInTrackers) { navigator.push(SettingsScreen(SettingsScreen.Destination.Tracking)) } else { screenModel.showTrackDialog() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt index 682c45fcd..99a1b1612 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt @@ -28,11 +28,9 @@ import eu.kanade.tachiyomi.data.download.DownloadCache import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.track.EnhancedTracker -import eu.kanade.tachiyomi.data.track.Tracker import eu.kanade.tachiyomi.data.track.TrackerManager import eu.kanade.tachiyomi.network.HttpException import eu.kanade.tachiyomi.source.Source -import eu.kanade.tachiyomi.ui.manga.track.TrackItem import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences import eu.kanade.tachiyomi.util.chapter.getNextUnread import eu.kanade.tachiyomi.util.removeCovers @@ -192,16 +190,6 @@ class MangaScreenModel( } } - screenModelScope.launchIO { - trackerManager.loggedInTrackersFlow() - .distinctUntilChanged() - .collectLatest { trackers -> - updateSuccessState { - it.copy(loggedInTracker = trackers) - } - } - } - observeDownloads() screenModelScope.launchIO { @@ -990,15 +978,20 @@ class MangaScreenModel( getTracks.subscribe(manga.id).catch { logcat(LogPriority.ERROR, it) }, trackerManager.loggedInTrackersFlow(), ) { mangaTracks, loggedInTrackers -> - loggedInTrackers - // Map to TrackItem - .map { service -> TrackItem(mangaTracks.find { it.trackerId == service.id }, service) } - // Show only if the service supports this manga's source - .filter { (it.tracker as? EnhancedTracker)?.accept(source!!) ?: true } + // Show only if the service supports this manga's source + val supportedTrackers = loggedInTrackers.filter { (it as? EnhancedTracker)?.accept(source!!) ?: true } + val supportedTrackerIds = supportedTrackers.map { it.id }.toHashSet() + val supportedTrackerTracks = mangaTracks.filter { it.trackerId in supportedTrackerIds } + supportedTrackerTracks.size to supportedTrackers.isNotEmpty() } .distinctUntilChanged() - .collectLatest { trackItems -> - updateSuccessState { it.copy(trackItems = trackItems) } + .collectLatest { (trackingCount, hasLoggedInTrackers) -> + updateSuccessState { + it.copy( + trackingCount = trackingCount, + hasLoggedInTrackers = hasLoggedInTrackers, + ) + } } } } @@ -1062,11 +1055,11 @@ class MangaScreenModel( val chapters: List, val availableScanlators: Set, val excludedScanlators: Set, - val trackItems: List = emptyList(), + val trackingCount: Int = 0, + val hasLoggedInTrackers: Boolean = false, val isRefreshingData: Boolean = false, val dialog: Dialog? = null, val hasPromptedToAddBefore: Boolean = false, - val loggedInTracker: List = emptyList(), ) : State { val processedChapters by lazy { chapters.applyFilters(manga).toList() @@ -1109,9 +1102,6 @@ class MangaScreenModel( val filterActive: Boolean get() = scanlatorFilterActive || manga.chaptersFiltered() - val trackingCount: Int - get() = trackItems.count { it.track != null } - /** * Applies the view filters to the list of chapters obtained from the database. * @return an observable of the list of chapters filtered and sorted. From daa47e049327c4d8b1fe4724ed1b84897d81fcf2 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Mon, 8 Jul 2024 18:02:50 +0800 Subject: [PATCH 137/146] Fix some issues when reading/saving images (#993) * Fix unsupported mime type error when saving images Avoid using platform mime type map to get extensions as it may not have all mime types we support. * Fix jxl images downloading/reading --- .../data/download/DownloadManager.kt | 3 ++- .../tachiyomi/data/download/Downloader.kt | 8 +------ .../kanade/tachiyomi/data/saver/ImageSaver.kt | 18 ++++++++++----- .../core/common/util/system/ImageUtil.kt | 23 ++++--------------- 4 files changed, 20 insertions(+), 32 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt index 01a342859..5fba899ca 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt @@ -17,6 +17,7 @@ import logcat.LogPriority import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.storage.extension import tachiyomi.core.common.util.lang.launchIO +import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.category.interactor.GetCategories import tachiyomi.domain.chapter.model.Chapter @@ -160,7 +161,7 @@ class DownloadManager( fun buildPageList(source: Source, manga: Manga, chapter: Chapter): List { val chapterDir = provider.findChapterDir(chapter.name, chapter.scanlator, manga.title, source) val files = chapterDir?.listFiles().orEmpty() - .filter { "image" in it.type.orEmpty() } + .filter { it.isFile && ImageUtil.isImage(it.name) { it.openInputStream() } } if (files.isEmpty()) { throw Exception(context.stringResource(MR.strings.page_list_empty_error)) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt index ebf9df65e..57c3a9824 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt @@ -523,14 +523,8 @@ class Downloader( * @param file the file where the image is already downloaded. */ private fun getImageExtension(response: Response, file: UniFile): String { - // Read content type if available. val mime = response.body.contentType()?.run { if (type == "image") "image/$subtype" else null } - // Else guess from the uri. - ?: context.contentResolver.getType(file.uri) - // Else read magic numbers. - ?: ImageUtil.findImageType { file.openInputStream() }?.mime - - return ImageUtil.getExtensionFromMimeType(mime) + return ImageUtil.getExtensionFromMimeType(mime) { file.openInputStream() } } private fun splitTallImageIfNeeded(page: Page, tmpDir: UniFile) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt b/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt index b7b53d835..0cc0ebc04 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt @@ -7,6 +7,7 @@ import android.net.Uri import android.os.Build import android.os.Environment import android.provider.MediaStore +import android.webkit.MimeTypeMap import androidx.annotation.RequiresApi import androidx.core.content.contentValuesOf import androidx.core.net.toUri @@ -65,21 +66,26 @@ class ImageSaver( filename: String, data: () -> InputStream, ): Uri { - val pictureDir = + val isMimeTypeSupported = MimeTypeMap.getSingleton().hasMimeType(type.mime) + + val pictureDir = if (isMimeTypeSupported) { MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY) + } else { + MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY) + } val imageLocation = (image.location as Location.Pictures).relativePath val relativePath = listOf( - Environment.DIRECTORY_PICTURES, + if (isMimeTypeSupported) Environment.DIRECTORY_PICTURES else Environment.DIRECTORY_DOCUMENTS, context.stringResource(MR.strings.app_name), imageLocation, ).joinToString(File.separator) val contentValues = contentValuesOf( - MediaStore.Images.Media.RELATIVE_PATH to relativePath, - MediaStore.Images.Media.DISPLAY_NAME to image.name, - MediaStore.Images.Media.MIME_TYPE to type.mime, - MediaStore.Images.Media.DATE_MODIFIED to Instant.now().epochSecond, + MediaStore.MediaColumns.RELATIVE_PATH to relativePath, + MediaStore.MediaColumns.DISPLAY_NAME to if (isMimeTypeSupported) image.name else filename, + MediaStore.MediaColumns.MIME_TYPE to type.mime, + MediaStore.MediaColumns.DATE_MODIFIED to Instant.now().epochSecond, ) val picture = findUriOrDefault(relativePath, filename) { diff --git a/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt b/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt index f5e9a8098..2cdd2a306 100644 --- a/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt @@ -13,7 +13,6 @@ import android.graphics.drawable.ColorDrawable import android.graphics.drawable.Drawable import android.graphics.drawable.GradientDrawable import android.os.Build -import android.webkit.MimeTypeMap import androidx.annotation.ColorInt import androidx.core.graphics.alpha import androidx.core.graphics.applyCanvas @@ -29,7 +28,6 @@ import okio.BufferedSource import tachiyomi.decoder.Format import tachiyomi.decoder.ImageDecoder import java.io.InputStream -import java.net.URLConnection import java.util.Locale import kotlin.math.abs import kotlin.math.max @@ -40,12 +38,8 @@ object ImageUtil { fun isImage(name: String?, openStream: (() -> InputStream)? = null): Boolean { if (name == null) return false - val contentType = try { - URLConnection.guessContentTypeFromName(name) - } catch (e: Exception) { - null - } ?: openStream?.let { findImageType(it)?.mime } - return contentType?.startsWith("image/") ?: false + val extension = name.substringAfterLast('.') + return ImageType.entries.any { it.extension == extension } || openStream?.let { findImageType(it) } != null } fun findImageType(openStream: () -> InputStream): ImageType? { @@ -69,10 +63,9 @@ object ImageUtil { } } - fun getExtensionFromMimeType(mime: String?): String { - return MimeTypeMap.getSingleton().getExtensionFromMimeType(mime) - ?: SUPPLEMENTARY_MIMETYPE_MAPPING[mime] - ?: "jpg" + fun getExtensionFromMimeType(mime: String?, openStream: () -> InputStream): String { + val type = mime?.let { ImageType.entries.find { it.mime == mime } } ?: findImageType(openStream) + return type?.extension ?: "jpg" } fun isAnimatedAndSupported(source: BufferedSource): Boolean { @@ -558,12 +551,6 @@ object ImageUtil { } private val optimalImageHeight = getDisplayMaxHeightInPx * 2 - - // Android doesn't include some mappings - private val SUPPLEMENTARY_MIMETYPE_MAPPING = mapOf( - // https://issuetracker.google.com/issues/182703810 - "image/jxl" to "jxl", - ) } val getDisplayMaxHeightInPx: Int From e65634cb427eafe9e3bd192f9e8bf71f2243ce6c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 9 Jul 2024 07:27:03 +0000 Subject: [PATCH 138/146] Bump coil version and some cleanup Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- app/src/main/java/eu/kanade/tachiyomi/App.kt | 21 +++++++++++++------- gradle/libs.versions.toml | 2 +- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index 558ad213e..d0b77cec1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -153,26 +153,33 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor ) } + @Suppress("MagicNumber") override fun newImageLoader(context: Context): ImageLoader { return ImageLoader.Builder(this).apply { val callFactoryLazy = lazy { Injekt.get().client } components { + // NetworkFetcher.Factory add(OkHttpNetworkFetcherFactory(callFactoryLazy::value)) + // Decoder.Factory add(TachiyomiImageDecoder.Factory()) - add(MangaCoverFetcher.MangaFactory(callFactoryLazy)) - add(MangaCoverFetcher.MangaCoverFactory(callFactoryLazy)) - add(MangaKeyer()) - add(MangaCoverKeyer()) + // Fetcher.Factory add(BufferedSourceFetcher.Factory()) + add(MangaCoverFetcher.MangaCoverFactory(callFactoryLazy)) + add(MangaCoverFetcher.MangaFactory(callFactoryLazy)) + // Keyer + add(MangaCoverKeyer()) + add(MangaKeyer()) } + crossfade((300 * this@App.animatorDurationScale).toInt()) allowRgb565(DeviceUtil.isLowRamDevice(this@App)) if (networkPreferences.verboseLogging().get()) logger(DebugLogger()) // Coil spawns a new thread for every image load by default - fetcherDispatcher(Dispatchers.IO.limitedParallelism(8)) - decoderDispatcher(Dispatchers.IO.limitedParallelism(2)) - }.build() + fetcherCoroutineContext(Dispatchers.IO.limitedParallelism(8)) + decoderCoroutineContext(Dispatchers.IO.limitedParallelism(3)) + } + .build() } override fun onStart(owner: LifecycleOwner) { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 84e9797c5..520933e40 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -42,7 +42,7 @@ preferencektx = "androidx.preference:preference-ktx:1.2.1" injekt-core = "com.github.inorichi.injekt:injekt-core:65b0440" -coil-bom = { module = "io.coil-kt.coil3:coil-bom", version = "3.0.0-alpha07" } +coil-bom = { module = "io.coil-kt.coil3:coil-bom", version = "3.0.0-alpha08" } coil-core = { module = "io.coil-kt.coil3:coil" } coil-gif = { module = "io.coil-kt.coil3:coil-gif" } coil-compose = { module = "io.coil-kt.coil3:coil-compose" } From 4a7613d515578a2fb05f4f2953cce410dc229a95 Mon Sep 17 00:00:00 2001 From: Roshan Varughese <40583749+Animeboynz@users.noreply.github.com> Date: Thu, 11 Jul 2024 03:30:12 +1200 Subject: [PATCH 139/146] A Minor Milestone (#1000) * Fixes README.md Alignment * Adds parameter to both --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b059b312c..d40550b03 100644 --- a/README.md +++ b/README.md @@ -49,8 +49,8 @@ Before reporting a new issue, take a look at the [FAQ](https://mihon.app/docs/fa ### Repositories -[![mihonapp/website - GitHub](https://github-readme-stats.vercel.app/api/pin/?username=mihonapp&repo=website&bg_color=161B22&text_color=c9d1d9&title_color=0877d2&icon_color=0877d2&border_radius=8&hide_border=true)](https://github.com/mihonapp/website/) -[![mihonapp/bitmap.kt - GitHub](https://github-readme-stats.vercel.app/api/pin/?username=mihonapp&repo=bitmap.kt&bg_color=161B22&text_color=c9d1d9&title_color=0877d2&icon_color=0877d2&border_radius=8&hide_border=true)](https://github.com/mihonapp/bitmap.kt/) +[![mihonapp/website - GitHub](https://github-readme-stats.vercel.app/api/pin/?username=mihonapp&repo=website&bg_color=161B22&text_color=c9d1d9&title_color=0877d2&icon_color=0877d2&border_radius=8&hide_border=true&description_lines_count=2)](https://github.com/mihonapp/website/) +[![mihonapp/bitmap.kt - GitHub](https://github-readme-stats.vercel.app/api/pin/?username=mihonapp&repo=bitmap.kt&bg_color=161B22&text_color=c9d1d9&title_color=0877d2&icon_color=0877d2&border_radius=8&hide_border=true&description_lines_count=2)](https://github.com/mihonapp/bitmap.kt/) ### Credits From 7a2ca4bf4d41764705637d61c6d86249f8815b7b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 04:51:42 +0600 Subject: [PATCH 140/146] fix(deps): update dependency com.android.tools.build:gradle to v8.5.1 (#1010) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index f7aa3acb6..6cb627960 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -1,5 +1,5 @@ [versions] -agp_version = "8.5.0" +agp_version = "8.5.1" lifecycle_version = "2.8.3" paging_version = "3.3.0" interpolator_version = "1.0.0" From e94c8dac9425a1d3fbe6640a0c285e9f3a5f16a8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 04:51:55 +0600 Subject: [PATCH 141/146] chore(deps): update actions/dependency-review-action action to v4.3.4 (#1009) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index f12ac59d0..bdcacb4e2 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -26,7 +26,7 @@ jobs: uses: gradle/actions/wrapper-validation@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2 - name: Dependency Review - uses: actions/dependency-review-action@72eb03d02c7872a771aacd928f3123ac62ad6d3a # v4.3.3 + uses: actions/dependency-review-action@5a2ce3f5b92ee19cbb1541a4984c76d921601d7c # v4.3.4 - name: Set up JDK uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 From 14d687c5cdff1071098b3580e8b010d7aa01f485 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 04:52:08 +0600 Subject: [PATCH 142/146] fix(deps): update dependency dev.chrisbanes.compose:compose-bom to v2024.07.00-alpha01 (#1002) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/compose.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 74ae656e9..ea9e52efd 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,5 +1,5 @@ [versions] -compose-bom = "2024.06.00-alpha01" +compose-bom = "2024.07.00-alpha01" accompanist = "0.35.1-alpha" [libraries] From f3f2bd41c3974878bcf0e3a62d99ee89bf92fb41 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 04:52:17 +0600 Subject: [PATCH 143/146] fix(deps): update dependency org.jsoup:jsoup to v1.18.1 (#999) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 520933e40..8bf834d80 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -28,7 +28,7 @@ conscrypt-android = "org.conscrypt:conscrypt-android:2.5.2" quickjs-android = "app.cash.quickjs:quickjs-android:0.9.2" -jsoup = "org.jsoup:jsoup:1.17.2" +jsoup = "org.jsoup:jsoup:1.18.1" disklrucache = "com.jakewharton:disklrucache:2.0.2" unifile = "com.github.tachiyomiorg:unifile:e0def6b3dc" From f63e95091013320b27bfc3c7c975c4bdd4a983c5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 06:04:57 +0600 Subject: [PATCH 144/146] chore(deps): update dependency gradle to v8.9 (#1007) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a4413138c..09523c0e5 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 8c9d12a84050afe5f6930868461c16e56c03116d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 16 Jul 2024 16:19:14 +0600 Subject: [PATCH 145/146] chore(deps): update gradle/actions action to v3.5.0 (#1018) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 4 ++-- .github/workflows/build_push.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index bdcacb4e2..98907b52f 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Validate Gradle Wrapper - uses: gradle/actions/wrapper-validation@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2 + uses: gradle/actions/wrapper-validation@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3.5.0 - name: Dependency Review uses: actions/dependency-review-action@5a2ce3f5b92ee19cbb1541a4984c76d921601d7c # v4.3.4 @@ -35,7 +35,7 @@ jobs: distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2 + uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3.5.0 - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest testStandardReleaseUnitTest diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index bb91f0a4f..1f04e4759 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -20,7 +20,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Validate Gradle Wrapper - uses: gradle/actions/wrapper-validation@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2 + uses: gradle/actions/wrapper-validation@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3.5.0 - name: Setup Android SDK run: | @@ -33,7 +33,7 @@ jobs: distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2 + uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3.5.0 - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest testStandardReleaseUnitTest From 4f61b2e4e89bc257cf5e629823904805907bf75c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 16 Jul 2024 16:19:27 +0600 Subject: [PATCH 146/146] fix(deps): update dependency io.mockk:mockk to v1.13.12 (#1016) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8bf834d80..398a2f89b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -91,7 +91,7 @@ sqldelight-gradle = { module = "app.cash.sqldelight:gradle-plugin", version.ref junit = "org.junit.jupiter:junit-jupiter:5.10.3" kotest-assertions = "io.kotest:kotest-assertions-core:5.9.1" -mockk = "io.mockk:mockk:1.13.11" +mockk = "io.mockk:mockk:1.13.12" voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" } voyager-screenmodel = { module = "cafe.adriel.voyager:voyager-screenmodel", version.ref = "voyager" }