diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 1546dee1dd..8e2bd9f17b 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -1,6 +1,6 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": ["config:base"], + "extends": ["config:recommended"], "labels": ["Dependencies"], "semanticCommits": "disabled", "packageRules": [ @@ -8,6 +8,6 @@ "groupName": "GitHub Actions", "matchManagers": ["github-actions"], "pinDigests": true, - } - ] + }, + ], } diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 3d7b2aaff9..2764e0813d 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -31,10 +31,10 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Validate Gradle Wrapper - uses: gradle/actions/wrapper-validation@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0 + uses: gradle/actions/wrapper-validation@cc4fc85e6b35bafd578d5ffbc76a5518407e1af0 # v4.2.1 - name: Dependency Review - uses: actions/dependency-review-action@4081bf99e2866ebe428fc0477b69eb4fcda7220a # v4.4.0 + uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4.5.0 - name: Set up JDK uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4.5.0 @@ -43,7 +43,7 @@ jobs: distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0 + uses: gradle/actions/setup-gradle@cc4fc85e6b35bafd578d5ffbc76a5518407e1af0 # v4.2.1 - name: Build app and run unit tests run: ./gradlew spotlessCheck assembleStandardRelease testReleaseUnitTest testStandardReleaseUnitTest diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index 6e1177d0b1..160b6e0652 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -25,7 +25,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Validate Gradle Wrapper - uses: gradle/actions/wrapper-validation@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0 + uses: gradle/actions/wrapper-validation@cc4fc85e6b35bafd578d5ffbc76a5518407e1af0 # v4.2.1 - name: Set up JDK uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4.5.0 @@ -34,7 +34,7 @@ jobs: distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0 + uses: gradle/actions/setup-gradle@cc4fc85e6b35bafd578d5ffbc76a5518407e1af0 # v4.2.1 - name: Build app and run unit tests run: ./gradlew spotlessCheck assembleStandardRelease testReleaseUnitTest testStandardReleaseUnitTest @@ -99,7 +99,7 @@ jobs: - name: Create Release if: startsWith(github.ref, 'refs/tags/') && github.repository == 'aniyomiorg/aniyomi' - uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8 + uses: softprops/action-gh-release@01570a1f39cb168c169c802c3bceb9e93fb10974 # v2.1.0 with: tag_name: ${{ env.VERSION_TAG }} name: Aniyomi ${{ env.VERSION_TAG }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 41f5679d63..ebd4c763b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co - feat(player): video filters ([@abdallahmehiz](https://github.com/abdallahmehiz)) ([#1698](https://github.com/aniyomiorg/aniyomi/pull/1698)) - feat(player): Add better auto sub select ([@Secozzi](https://github.com/Secozzi)) ([#1706](https://github.com/aniyomiorg/aniyomi/pull/1706)) - feat(downloader): Copy the file location when using ext downloader ([@quickdesh](https://github.com/quickdesh)) ([#1758](https://github.com/aniyomiorg/aniyomi/pull/1758)) +- feat(player): Replace player with mpvKt ([@Secozzi](https://github.com/Secozzi)) ([#1834](https://github.com/aniyomiorg/aniyomi/pull/1834), [#1855](https://github.com/aniyomiorg/aniyomi/pull/1855), [#1859](https://github.com/aniyomiorg/aniyomi/pull/1859), [#1860](https://github.com/aniyomiorg/aniyomi/pull/1860)) ### Improved @@ -28,12 +29,14 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co - fix(animescreen): Fix airing time not showing ([@Secozzi](https://github.com/Secozzi)) ([#1720](https://github.com/aniyomiorg/aniyomi/pull/1720)) - fix hidden categories getting reset after delete/reorder ([@cuong-tran](https://github.com/cuong-tran)) ([#1780](https://github.com/aniyomiorg/aniyomi/pull/1780)) - Fix episode progress not being saved and duplicate tracks ([@perokhe](https://github.com/perokhe)) ([#1784](https://github.com/aniyomiorg/aniyomi/pull/1784), [#1785](https://github.com/aniyomiorg/aniyomi/pull/1785)) +- Fix history date header duplication ([@quickdesh](https://github.com/quickdesh)) ([#1817](https://github.com/aniyomiorg/aniyomi/pull/1817)) ### Other - Merge from mihon until 0.16.5 ([@Secozzi](https://github.com/Secozzi)) ([#1663](https://github.com/aniyomiorg/aniyomi/pull/1663)) - Merge until latest mihon commits ([@Secozzi](https://github.com/Secozzi)) ([#1693](https://github.com/aniyomiorg/aniyomi/pull/1693)) - Merge until latest mihon commits (v0.17.0) ([@Secozzi](https://github.com/Secozzi)) ([#1804](https://github.com/aniyomiorg/aniyomi/pull/1804)) + - Merge until latest mihon commits (TODO(merge)) ([@Secozzi](https://github.com/Secozzi)) ([#1863](https://github.com/aniyomiorg/aniyomi/pull/1863)) ## [v0.16.4.3] - 2024-07-01 ### Fixed diff --git a/README.md b/README.md index bbd46ceca9..f566804aa3 100644 --- a/README.md +++ b/README.md @@ -68,8 +68,8 @@ The developer(s) of this application does not have any affiliation with the cont
 Copyright © 2015 Javier Tomás
-Copyright © 2024 The Mihon Open Source Project
-Copyright © 2024 The Aniyomi Open Source Project
+Copyright © 2024 Mihon Open Source Project
+Copyright © 2024 Aniyomi Open Source Project
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -84,4 +84,4 @@ See the License for the specific language governing permissions and
 limitations under the License.
 
- \ No newline at end of file + diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 184f8de8ba..6bc36d0660 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,7 +1,8 @@ +@file:Suppress("ChromeOsAbiSupport") + import mihon.buildlogic.getBuildTime import mihon.buildlogic.getCommitCount import mihon.buildlogic.getGitSha -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { id("mihon.android.application") @@ -148,6 +149,24 @@ android { } } +kotlin { + compilerOptions { + freeCompilerArgs.addAll( + "-opt-in=androidx.compose.animation.ExperimentalAnimationApi", + "-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi", + "-opt-in=androidx.compose.foundation.ExperimentalFoundationApi", + "-opt-in=androidx.compose.foundation.layout.ExperimentalLayoutApi", + "-opt-in=androidx.compose.material3.ExperimentalMaterial3Api", + "-opt-in=androidx.compose.ui.ExperimentalComposeUiApi", + "-opt-in=coil3.annotation.ExperimentalCoilApi", + "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", + "-opt-in=kotlinx.coroutines.FlowPreview", + "-opt-in=kotlinx.coroutines.InternalCoroutinesApi", + "-opt-in=kotlinx.serialization.ExperimentalSerializationApi", + ) + } +} + dependencies { implementation(projects.i18n) implementation(projects.core.archive) @@ -293,30 +312,6 @@ androidComponents { } } -tasks { - // See https://kotlinlang.org/docs/reference/experimental.html#experimental-status-of-experimental-api(-markers) - withType { - compilerOptions.freeCompilerArgs.addAll( - listOf( - "-Xcontext-receivers", - "-opt-in=androidx.compose.foundation.layout.ExperimentalLayoutApi", - "-opt-in=androidx.compose.material.ExperimentalMaterialApi", - "-opt-in=androidx.compose.material3.ExperimentalMaterial3Api", - "-opt-in=androidx.compose.material.ExperimentalMaterialApi", - "-opt-in=androidx.compose.ui.ExperimentalComposeUiApi", - "-opt-in=androidx.compose.foundation.ExperimentalFoundationApi", - "-opt-in=androidx.compose.animation.ExperimentalAnimationApi", - "-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi", - "-opt-in=coil3.annotation.ExperimentalCoilApi", - "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", - "-opt-in=kotlinx.coroutines.FlowPreview", - "-opt-in=kotlinx.coroutines.InternalCoroutinesApi", - "-opt-in=kotlinx.serialization.ExperimentalSerializationApi", - ), - ) - } -} - buildscript { dependencies { classpath(kotlinx.gradle) diff --git a/app/src/main/java/eu/kanade/core/util/CollectionUtils.kt b/app/src/main/java/eu/kanade/core/util/CollectionUtils.kt index baf369e393..e8eed9d3d2 100644 --- a/app/src/main/java/eu/kanade/core/util/CollectionUtils.kt +++ b/app/src/main/java/eu/kanade/core/util/CollectionUtils.kt @@ -1,5 +1,6 @@ package eu.kanade.core.util +import androidx.compose.ui.util.fastFilter import androidx.compose.ui.util.fastForEach import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract @@ -45,21 +46,6 @@ fun HashSet.addOrRemove(value: E, shouldAdd: Boolean) { } } -/** - * Returns a list containing only elements matching the given [predicate]. - * - * **Do not use for collections that come from public APIs**, since they may not support random - * access in an efficient way, and this method may actually be a lot slower. Only use for - * collections that are created by code we control and are known to support random access. - */ -@OptIn(ExperimentalContracts::class) -inline fun List.fastFilter(predicate: (T) -> Boolean): List { - contract { callsInPlace(predicate) } - val destination = ArrayList() - fastForEach { if (predicate(it)) destination.add(it) } - return destination -} - /** * Returns a list containing all elements not matching the given [predicate]. * @@ -70,27 +56,7 @@ inline fun List.fastFilter(predicate: (T) -> Boolean): List { @OptIn(ExperimentalContracts::class) inline fun List.fastFilterNot(predicate: (T) -> Boolean): List { contract { callsInPlace(predicate) } - val destination = ArrayList() - fastForEach { if (!predicate(it)) destination.add(it) } - return destination -} - -/** - * Returns a list containing only the non-null results of applying the - * given [transform] function to each element in the original collection. - * - * **Do not use for collections that come from public APIs**, since they may not support random - * access in an efficient way, and this method may actually be a lot slower. Only use for - * collections that are created by code we control and are known to support random access. - */ -@OptIn(ExperimentalContracts::class) -inline fun List.fastMapNotNull(transform: (T) -> R?): List { - contract { callsInPlace(transform) } - val destination = ArrayList() - fastForEach { element -> - transform(element)?.let(destination::add) - } - return destination + return fastFilter { !predicate(it) } } /** @@ -131,26 +97,3 @@ inline fun List.fastCountNot(predicate: (T) -> Boolean): Int { fastForEach { if (predicate(it)) --count } return count } - -/** - * Returns a list containing only elements from the given collection - * having distinct keys returned by the given [selector] function. - * - * Among elements of the given collection with equal keys, only the first one will be present in the resulting list. - * The elements in the resulting list are in the same order as they were in the source collection. - * - * **Do not use for collections that come from public APIs**, since they may not support random - * access in an efficient way, and this method may actually be a lot slower. Only use for - * collections that are created by code we control and are known to support random access. - */ -@OptIn(ExperimentalContracts::class) -inline fun List.fastDistinctBy(selector: (T) -> K): List { - contract { callsInPlace(selector) } - val set = HashSet() - val list = ArrayList() - fastForEach { - val key = selector(it) - if (set.add(key)) list.add(it) - } - return list -} diff --git a/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt b/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt index 6f213d67c8..5002d6187e 100644 --- a/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt +++ b/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt @@ -3,6 +3,7 @@ package eu.kanade.domain.base import android.content.Context import android.content.pm.PackageManager import dev.icerock.moko.resources.StringResource +import eu.kanade.tachiyomi.util.system.GLUtil import tachiyomi.core.common.preference.Preference import tachiyomi.core.common.preference.PreferenceStore import tachiyomi.i18n.MR @@ -35,4 +36,8 @@ class BasePreferences( } fun displayProfile() = preferenceStore.getString("pref_display_profile_key", "") + + fun hardwareBitmapThreshold() = preferenceStore.getInt("pref_hardware_bitmap_threshold", GLUtil.SAFE_TEXTURE_LIMIT) + + fun alwaysDecodeLongStripWithSSIV() = preferenceStore.getBoolean("pref_always_decode_long_strip_with_ssiv", false) } diff --git a/app/src/main/java/eu/kanade/presentation/library/components/LibraryTabs.kt b/app/src/main/java/eu/kanade/presentation/library/components/LibraryTabs.kt index 862163f7d3..7344ed06f4 100644 --- a/app/src/main/java/eu/kanade/presentation/library/components/LibraryTabs.kt +++ b/app/src/main/java/eu/kanade/presentation/library/components/LibraryTabs.kt @@ -21,11 +21,12 @@ internal fun LibraryTabs( getNumberOfItemsForCategory: (Category) -> Int?, onTabItemClick: (Int) -> Unit, ) { + val currentPageIndex = pagerState.currentPage.coerceAtMost(categories.lastIndex) Column( modifier = Modifier.zIndex(1f), ) { PrimaryScrollableTabRow( - selectedTabIndex = pagerState.currentPage, + selectedTabIndex = currentPageIndex, edgePadding = 0.dp, // TODO: use default when width is fixed upstream // https://issuetracker.google.com/issues/242879624 @@ -33,7 +34,7 @@ internal fun LibraryTabs( ) { categories.forEachIndexed { index, category -> Tab( - selected = pagerState.currentPage == index, + selected = currentPageIndex == index, onClick = { onTabItemClick(index) }, text = { TabText( diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/Preference.kt b/app/src/main/java/eu/kanade/presentation/more/settings/Preference.kt index d6b404ae33..65f749e478 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/Preference.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/Preference.kt @@ -240,12 +240,12 @@ sealed class Preference { data class CustomPreference( override val title: String, - val content: @Composable (PreferenceItem) -> Unit, - ) : PreferenceItem() { + val content: @Composable () -> Unit, + ) : PreferenceItem() { override val enabled: Boolean = true override val subtitle: String? = null override val icon: ImageVector? = null - override val onValueChanged: suspend (newValue: String) -> Boolean = { true } + override val onValueChanged: suspend (newValue: Unit) -> Boolean = { true } } } 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 6e79956636..a28c653014 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 @@ -216,7 +216,7 @@ internal fun PreferenceItem( InfoWidget(text = item.title) } is Preference.PreferenceItem.CustomPreference -> { - item.content(item) + item.content() } } } diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt index d8bff14f22..3dd821c419 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt @@ -56,6 +56,7 @@ import eu.kanade.tachiyomi.network.PREF_DOH_QUAD9 import eu.kanade.tachiyomi.network.PREF_DOH_SHECAN import eu.kanade.tachiyomi.ui.more.OnboardingScreen import eu.kanade.tachiyomi.util.CrashLogUtil +import eu.kanade.tachiyomi.util.system.GLUtil import eu.kanade.tachiyomi.util.system.isDevFlavor import eu.kanade.tachiyomi.util.system.isPreviewBuildType import eu.kanade.tachiyomi.util.system.isShizukuInstalled @@ -72,6 +73,7 @@ import okhttp3.Headers import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.util.lang.launchNonCancellable import tachiyomi.core.common.util.lang.withUIContext +import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.entries.manga.interactor.ResetMangaViewerFlags import tachiyomi.i18n.MR @@ -360,6 +362,31 @@ object SettingsAdvancedScreen : SearchableSettings { return Preference.PreferenceGroup( title = stringResource(MR.strings.pref_category_reader), preferenceItems = persistentListOf( + Preference.PreferenceItem.ListPreference( + pref = basePreferences.hardwareBitmapThreshold(), + title = stringResource(MR.strings.pref_hardware_bitmap_threshold), + subtitleProvider = { value, options -> + stringResource(MR.strings.pref_hardware_bitmap_threshold_summary, options[value].orEmpty()) + }, + enabled = !ImageUtil.HARDWARE_BITMAP_UNSUPPORTED && + GLUtil.DEVICE_TEXTURE_LIMIT > GLUtil.SAFE_TEXTURE_LIMIT, + entries = GLUtil.CUSTOM_TEXTURE_LIMIT_OPTIONS + .mapIndexed { index, option -> + val display = if (index == 0) { + stringResource(MR.strings.pref_hardware_bitmap_threshold_default, option) + } else { + option.toString() + } + option to display + } + .toMap() + .toImmutableMap(), + ), + Preference.PreferenceItem.SwitchPreference( + pref = basePreferences.alwaysDecodeLongStripWithSSIV(), + title = stringResource(MR.strings.pref_always_decode_long_strip_with_ssiv), + subtitle = stringResource(MR.strings.pref_always_decode_long_strip_with_ssiv_summary), + ), Preference.PreferenceItem.TextPreference( title = stringResource(MR.strings.pref_display_profile), subtitle = basePreferences.displayProfile().get(), diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index a9a3fca52c..d6cd5903a4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -44,6 +44,7 @@ import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkPreferences import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegate import eu.kanade.tachiyomi.util.system.DeviceUtil +import eu.kanade.tachiyomi.util.system.GLUtil import eu.kanade.tachiyomi.util.system.WebViewUtil import eu.kanade.tachiyomi.util.system.animatorDurationScale import eu.kanade.tachiyomi.util.system.cancelNotification @@ -60,6 +61,7 @@ import org.conscrypt.Conscrypt import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.preference.Preference import tachiyomi.core.common.preference.PreferenceStore +import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.core.common.util.system.logcat import tachiyomi.i18n.MR import tachiyomi.presentation.widget.entries.anime.AnimeWidgetManager @@ -105,6 +107,8 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor ProcessLifecycleOwner.get().lifecycle.addObserver(this) + val scope = ProcessLifecycleOwner.get().lifecycleScope + // Show notification to disable Incognito Mode when it's enabled basePreferences.incognitoMode().changes() .onEach { enabled -> @@ -134,6 +138,14 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor } .launchIn(ProcessLifecycleOwner.get().lifecycleScope) + basePreferences.hardwareBitmapThreshold().let { preference -> + if (!preference.isSet()) preference.set(GLUtil.DEVICE_TEXTURE_LIMIT) + } + + basePreferences.hardwareBitmapThreshold().changes() + .onEach { ImageUtil.hardwareBitmapThreshold = it } + .launchIn(scope) + setAppCompatDelegateThemeMode(Injekt.get().themeMode().get()) // Updates widget update 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 6403f760bd..84e3f04960 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 @@ -10,7 +10,6 @@ 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 @@ -46,10 +45,7 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti check(bitmap != null) { "Failed to decode image" } - if ( - options.bitmapConfig == Bitmap.Config.HARDWARE && - maxOf(bitmap.width, bitmap.height) <= GLUtil.maxTextureSize - ) { + if (options.bitmapConfig == Bitmap.Config.HARDWARE && ImageUtil.canUseHardwareBitmap(bitmap)) { val hwBitmap = bitmap.copy(Bitmap.Config.HARDWARE, false) if (hwBitmap != null) { bitmap.recycle() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/anime/AnimeLibraryUpdateJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/anime/AnimeLibraryUpdateJob.kt index 1e81db12b7..22520b7fed 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/anime/AnimeLibraryUpdateJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/anime/AnimeLibraryUpdateJob.kt @@ -2,6 +2,8 @@ package eu.kanade.tachiyomi.data.library.anime import android.content.Context import android.content.pm.ServiceInfo +import android.net.NetworkCapabilities +import android.net.NetworkRequest import android.os.Build import androidx.work.BackoffPolicy import androidx.work.Constraints @@ -91,15 +93,12 @@ class AnimeLibraryUpdateJob(private val context: Context, workerParams: WorkerPa override suspend fun doWork(): Result { if (tags.contains(WORK_NAME_AUTO)) { - val preferences = Injekt.get() - val restrictions = preferences.autoUpdateDeviceRestrictions().get() - if ((DEVICE_ONLY_ON_WIFI in restrictions) && !context.isConnectedToWifi()) { - return Result.failure() - } - - // Find a running manual worker. If exists, try again later - if (context.workManager.isRunning(WORK_NAME_MANUAL)) { - return Result.retry() + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { + val preferences = Injekt.get() + val restrictions = preferences.autoUpdateDeviceRestrictions().get() + if ((DEVICE_ONLY_ON_WIFI in restrictions) && !context.isConnectedToWifi()) { + return Result.retry() + } } } @@ -443,15 +442,24 @@ class AnimeLibraryUpdateJob(private val context: Context, workerParams: WorkerPa val interval = prefInterval ?: preferences.autoUpdateInterval().get() if (interval > 0) { val restrictions = preferences.autoUpdateDeviceRestrictions().get() - val constraints = Constraints( - requiredNetworkType = if (DEVICE_NETWORK_NOT_METERED in restrictions) { - NetworkType.UNMETERED - } else { - NetworkType.CONNECTED - }, - requiresCharging = DEVICE_CHARGING in restrictions, - requiresBatteryNotLow = true, - ) + val networkType = if (DEVICE_NETWORK_NOT_METERED in restrictions) { + NetworkType.UNMETERED + } else { + NetworkType.CONNECTED + } + val networkRequestBuilder = NetworkRequest.Builder() + if (DEVICE_ONLY_ON_WIFI in restrictions) { + networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + } + if (DEVICE_NETWORK_NOT_METERED in restrictions) { + networkRequestBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) + } + val constraints = Constraints.Builder() + // 'networkRequest' only applies to Android 9+, otherwise 'networkType' is used + .setRequiredNetworkRequest(networkRequestBuilder.build(), networkType) + .setRequiresCharging(DEVICE_CHARGING in restrictions) + .setRequiresBatteryNotLow(true) + .build() val request = PeriodicWorkRequestBuilder( interval.toLong(), diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/manga/MangaLibraryUpdateJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/manga/MangaLibraryUpdateJob.kt index 059876d2c3..47ddc7ea4e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/manga/MangaLibraryUpdateJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/manga/MangaLibraryUpdateJob.kt @@ -2,6 +2,8 @@ package eu.kanade.tachiyomi.data.library.manga import android.content.Context import android.content.pm.ServiceInfo +import android.net.NetworkCapabilities +import android.net.NetworkRequest import android.os.Build import androidx.work.BackoffPolicy import androidx.work.Constraints @@ -91,10 +93,12 @@ class MangaLibraryUpdateJob(private val context: Context, workerParams: WorkerPa override suspend fun doWork(): Result { if (tags.contains(WORK_NAME_AUTO)) { - val preferences = Injekt.get() - val restrictions = preferences.autoUpdateDeviceRestrictions().get() - if ((DEVICE_ONLY_ON_WIFI in restrictions) && !context.isConnectedToWifi()) { - return Result.retry() + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { + val preferences = Injekt.get() + val restrictions = preferences.autoUpdateDeviceRestrictions().get() + if ((DEVICE_ONLY_ON_WIFI in restrictions) && !context.isConnectedToWifi()) { + return Result.retry() + } } // Find a running manual worker. If exists, try again later @@ -440,15 +444,24 @@ class MangaLibraryUpdateJob(private val context: Context, workerParams: WorkerPa val interval = prefInterval ?: preferences.autoUpdateInterval().get() if (interval > 0) { val restrictions = preferences.autoUpdateDeviceRestrictions().get() - val constraints = Constraints( - requiredNetworkType = if (DEVICE_NETWORK_NOT_METERED in restrictions) { - NetworkType.UNMETERED - } else { - NetworkType.CONNECTED - }, - requiresCharging = DEVICE_CHARGING in restrictions, - requiresBatteryNotLow = true, - ) + val networkType = if (DEVICE_NETWORK_NOT_METERED in restrictions) { + NetworkType.UNMETERED + } else { + NetworkType.CONNECTED + } + val networkRequestBuilder = NetworkRequest.Builder() + if (DEVICE_ONLY_ON_WIFI in restrictions) { + networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + } + if (DEVICE_NETWORK_NOT_METERED in restrictions) { + networkRequestBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) + } + val constraints = Constraints.Builder() + // 'networkRequest' only applies to Android 9+, otherwise 'networkType' is used + .setRequiredNetworkRequest(networkRequestBuilder.build(), networkType) + .setRequiresCharging(DEVICE_CHARGING in restrictions) + .setRequiresBatteryNotLow(true) + .build() val request = PeriodicWorkRequestBuilder( interval.toLong(), diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/notification/Notifications.kt b/app/src/main/java/eu/kanade/tachiyomi/data/notification/Notifications.kt index 6a1a14b00d..8f5574ed46 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/notification/Notifications.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/notification/Notifications.kt @@ -75,6 +75,7 @@ object Notifications { const val CHANNEL_APP_UPDATE = "app_apk_update_channel" const val ID_APP_UPDATER = 1 const val ID_APP_UPDATE_PROMPT = 2 + const val ID_APP_UPDATE_ERROR = 3 const val CHANNEL_EXTENSIONS_UPDATE = "ext_apk_update_channel" const val ID_UPDATES_TO_EXTS = -401 const val ID_EXTENSION_INSTALLER = -402 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 cf906b40ba..e36b4a5035 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 @@ -180,6 +180,7 @@ sealed class Image( } sealed interface Location { + @ConsistentCopyVisibility data class Pictures(val relativePath: String) : Location { companion object { fun create(relativePath: String = ""): Pictures { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt index dfc45d9014..b0d6d2faca 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt @@ -186,12 +186,9 @@ internal class AppUpdateNotifier(private val context: Context) { addAction( R.drawable.ic_close_24dp, context.stringResource(MR.strings.action_cancel), - NotificationReceiver.dismissNotificationPendingBroadcast( - context, - Notifications.ID_APP_UPDATER, - ), + NotificationReceiver.dismissNotificationPendingBroadcast(context, Notifications.ID_APP_UPDATE_ERROR), ) } - notificationBuilder.show(Notifications.ID_APP_UPDATER) + notificationBuilder.show(Notifications.ID_APP_UPDATE_ERROR) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/search/AnimeSourceSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/search/AnimeSourceSearchScreen.kt index 879f933a0c..d7bd1bebcb 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/search/AnimeSourceSearchScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/search/AnimeSourceSearchScreen.kt @@ -14,7 +14,6 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalUriHandler -import androidx.paging.compose.collectAsLazyPagingItems import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow @@ -30,6 +29,7 @@ import eu.kanade.tachiyomi.ui.entries.anime.AnimeScreen import eu.kanade.tachiyomi.ui.home.HomeScreen import eu.kanade.tachiyomi.ui.webview.WebViewScreen import kotlinx.coroutines.launch +import mihon.presentation.core.util.collectAsLazyPagingItems import tachiyomi.domain.entries.anime.model.Anime import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton @@ -81,13 +81,12 @@ data class AnimeSourceSearchScreen( }, snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, ) { paddingValues -> - val pagingFlow by screenModel.animePagerFlowFlow.collectAsState() val openMigrateDialog: (Anime) -> Unit = { screenModel.setDialog(BrowseAnimeSourceScreenModel.Dialog.Migrate(newAnime = it, oldAnime = oldAnime)) } BrowseAnimeSourceContent( source = screenModel.source, - animeList = pagingFlow.collectAsLazyPagingItems(), + animeList = screenModel.animePagerFlowFlow.collectAsLazyPagingItems(), columns = screenModel.getColumnsPreference(LocalConfiguration.current.orientation), displayMode = screenModel.displayMode, snackbarHostState = snackbarHostState, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/browse/BrowseAnimeSourceScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/browse/BrowseAnimeSourceScreen.kt index 71461593b0..9695b5c78a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/browse/BrowseAnimeSourceScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/browse/BrowseAnimeSourceScreen.kt @@ -31,7 +31,6 @@ import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.LocalUriHandler -import androidx.paging.compose.collectAsLazyPagingItems import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow @@ -57,6 +56,7 @@ import eu.kanade.tachiyomi.ui.webview.WebViewScreen import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.receiveAsFlow +import mihon.presentation.core.util.collectAsLazyPagingItems import tachiyomi.core.common.util.lang.launchIO import tachiyomi.domain.source.anime.model.StubAnimeSource import tachiyomi.i18n.MR @@ -208,11 +208,9 @@ data class BrowseAnimeSourceScreen( }, snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, ) { paddingValues -> - val pagingFlow by screenModel.animePagerFlowFlow.collectAsState() - BrowseAnimeSourceContent( source = screenModel.source, - animeList = pagingFlow.collectAsLazyPagingItems(), + animeList = screenModel.animePagerFlowFlow.collectAsLazyPagingItems(), columns = screenModel.getColumnsPreference(LocalConfiguration.current.orientation), displayMode = screenModel.displayMode, snackbarHostState = snackbarHostState, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/search/MangaSourceSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/search/MangaSourceSearchScreen.kt index 56c9c4728f..4b5251b0cd 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/search/MangaSourceSearchScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/search/MangaSourceSearchScreen.kt @@ -14,7 +14,6 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalUriHandler -import androidx.paging.compose.collectAsLazyPagingItems import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow @@ -30,6 +29,7 @@ import eu.kanade.tachiyomi.ui.entries.manga.MangaScreen import eu.kanade.tachiyomi.ui.home.HomeScreen import eu.kanade.tachiyomi.ui.webview.WebViewScreen import kotlinx.coroutines.launch +import mihon.presentation.core.util.collectAsLazyPagingItems import tachiyomi.domain.entries.manga.model.Manga import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton @@ -81,13 +81,12 @@ data class MangaSourceSearchScreen( }, snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, ) { paddingValues -> - val pagingFlow by screenModel.mangaPagerFlowFlow.collectAsState() val openMigrateDialog: (Manga) -> Unit = { screenModel.setDialog(BrowseMangaSourceScreenModel.Dialog.Migrate(newManga = it, oldManga = oldManga)) } BrowseSourceContent( source = screenModel.source, - mangaList = pagingFlow.collectAsLazyPagingItems(), + mangaList = screenModel.mangaPagerFlowFlow.collectAsLazyPagingItems(), columns = screenModel.getColumnsPreference(LocalConfiguration.current.orientation), displayMode = screenModel.displayMode, snackbarHostState = snackbarHostState, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/browse/BrowseMangaSourceScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/browse/BrowseMangaSourceScreen.kt index caa25aa3b9..c2b9c7634b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/browse/BrowseMangaSourceScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/browse/BrowseMangaSourceScreen.kt @@ -31,7 +31,6 @@ import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.LocalUriHandler -import androidx.paging.compose.collectAsLazyPagingItems import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow @@ -57,6 +56,7 @@ import eu.kanade.tachiyomi.ui.webview.WebViewScreen import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.receiveAsFlow +import mihon.presentation.core.util.collectAsLazyPagingItems import tachiyomi.core.common.util.lang.launchIO import tachiyomi.domain.source.manga.model.StubMangaSource import tachiyomi.i18n.MR @@ -208,11 +208,9 @@ data class BrowseMangaSourceScreen( }, snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, ) { paddingValues -> - val pagingFlow by screenModel.mangaPagerFlowFlow.collectAsState() - BrowseSourceContent( source = screenModel.source, - mangaList = pagingFlow.collectAsLazyPagingItems(), + mangaList = screenModel.mangaPagerFlowFlow.collectAsLazyPagingItems(), columns = screenModel.getColumnsPreference(LocalConfiguration.current.orientation), displayMode = screenModel.displayMode, snackbarHostState = snackbarHostState, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/entries/anime/AnimeScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/entries/anime/AnimeScreenModel.kt index 964eb98e0f..7cab8d69ec 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/entries/anime/AnimeScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/entries/anime/AnimeScreenModel.kt @@ -730,6 +730,7 @@ class AnimeScreenModel( */ fun markEpisodesSeen(episodes: List, seen: Boolean) { toggleAllSelection(false) + if (episodes.isEmpty()) return screenModelScope.launchIO { setSeenStatus.await( seen = seen, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/entries/manga/MangaScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/entries/manga/MangaScreenModel.kt index 48d19b0775..739aff5168 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/entries/manga/MangaScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/entries/manga/MangaScreenModel.kt @@ -751,6 +751,7 @@ class MangaScreenModel( */ fun markChaptersRead(chapters: List, read: Boolean) { toggleAllSelection(false) + if (chapters.isEmpty()) return screenModelScope.launchIO { setReadStatus.await( read = read, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/anime/AnimeLibraryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/anime/AnimeLibraryScreenModel.kt index a45bc5aa47..a3040ae0c3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/anime/AnimeLibraryScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/anime/AnimeLibraryScreenModel.kt @@ -4,15 +4,15 @@ import androidx.compose.runtime.Immutable import androidx.compose.runtime.getValue import androidx.compose.runtime.setValue import androidx.compose.ui.util.fastAny +import androidx.compose.ui.util.fastDistinctBy +import androidx.compose.ui.util.fastFilter import androidx.compose.ui.util.fastMap +import androidx.compose.ui.util.fastMapNotNull import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.screenModelScope import eu.kanade.core.preference.PreferenceMutableState import eu.kanade.core.preference.asState -import eu.kanade.core.util.fastDistinctBy -import eu.kanade.core.util.fastFilter import eu.kanade.core.util.fastFilterNot -import eu.kanade.core.util.fastMapNotNull import eu.kanade.core.util.fastPartition import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.entries.anime.interactor.UpdateAnime diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/manga/MangaLibraryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/manga/MangaLibraryScreenModel.kt index 0fce01456e..16ec824385 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/manga/MangaLibraryScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/manga/MangaLibraryScreenModel.kt @@ -4,15 +4,15 @@ import androidx.compose.runtime.Immutable import androidx.compose.runtime.getValue import androidx.compose.runtime.setValue import androidx.compose.ui.util.fastAny +import androidx.compose.ui.util.fastDistinctBy +import androidx.compose.ui.util.fastFilter import androidx.compose.ui.util.fastMap +import androidx.compose.ui.util.fastMapNotNull import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.screenModelScope import eu.kanade.core.preference.PreferenceMutableState import eu.kanade.core.preference.asState -import eu.kanade.core.util.fastDistinctBy -import eu.kanade.core.util.fastFilter import eu.kanade.core.util.fastFilterNot -import eu.kanade.core.util.fastMapNotNull import eu.kanade.core.util.fastPartition import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.entries.manga.interactor.UpdateManga 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 bcdae4da65..5df60a0c0c 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,13 +33,16 @@ import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.EASE_IN_OUT 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.domain.base.BasePreferences 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 import eu.kanade.tachiyomi.util.view.isVisibleOnScreen import okio.BufferedSource +import tachiyomi.core.common.util.system.ImageUtil +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get /** * A wrapper view for showing page image. @@ -57,6 +60,10 @@ open class ReaderPageImageView @JvmOverloads constructor( private val isWebtoon: Boolean = false, ) : FrameLayout(context, attrs, defStyleAttrs, defStyleRes) { + private val alwaysDecodeLongStripWithSSIV by lazy { + Injekt.get().alwaysDecodeLongStripWithSSIV().get() + } + private var pageView: View? = null private var config: Config? = null @@ -246,7 +253,7 @@ open class ReaderPageImageView @JvmOverloads constructor( } else { SubsamplingScaleImageView(context) }.apply { - setMaxTileSize(GLUtil.maxTextureSize) + setMaxTileSize(ImageUtil.hardwareBitmapThreshold) setDoubleTapZoomStyle(SubsamplingScaleImageView.ZOOM_FOCUS_CENTER) setPanLimit(SubsamplingScaleImageView.PAN_LIMIT_INSIDE) setMinimumTileDpi(180) @@ -301,35 +308,44 @@ open class ReaderPageImageView @JvmOverloads constructor( }, ) - 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}") + when (data) { + is BitmapDrawable -> { + setImage(ImageSource.bitmap(data.bitmap)) + isVisible = true + } + is BufferedSource -> { + if (!isWebtoon || alwaysDecodeLongStripWithSSIV) { + setHardwareConfig(ImageUtil.canUseHardwareBitmap(data)) + setImage(ImageSource.inputStream(data.inputStream())) + isVisible = true + return@apply + } + + 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 = { + onImageLoadError() + }, + ) + .size(ViewSizeResolver(this@ReaderPageImageView)) + .precision(Precision.INEXACT) + .cropBorders(config.cropBorders) + .customDecoder(true) + .crossfade(false) + .build() + .let(context.imageLoader::enqueue) + } + else -> { + throw IllegalArgumentException("Not implemented for class ${data::class.simpleName}") } - isVisible = true } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/stats/anime/AnimeStatsScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/stats/anime/AnimeStatsScreenModel.kt index 18d410f42e..0de889f86e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/stats/anime/AnimeStatsScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/stats/anime/AnimeStatsScreenModel.kt @@ -1,12 +1,12 @@ package eu.kanade.tachiyomi.ui.stats.anime +import androidx.compose.ui.util.fastDistinctBy +import androidx.compose.ui.util.fastFilter +import androidx.compose.ui.util.fastMapNotNull import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.screenModelScope import eu.kanade.core.util.fastCountNot -import eu.kanade.core.util.fastDistinctBy -import eu.kanade.core.util.fastFilter import eu.kanade.core.util.fastFilterNot -import eu.kanade.core.util.fastMapNotNull import eu.kanade.presentation.more.stats.StatsScreenState import eu.kanade.presentation.more.stats.data.StatsData import eu.kanade.tachiyomi.animesource.model.SAnime diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/stats/manga/MangaStatsScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/stats/manga/MangaStatsScreenModel.kt index ac84d887b4..0bb6174256 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/stats/manga/MangaStatsScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/stats/manga/MangaStatsScreenModel.kt @@ -1,12 +1,12 @@ package eu.kanade.tachiyomi.ui.stats.manga +import androidx.compose.ui.util.fastDistinctBy +import androidx.compose.ui.util.fastFilter +import androidx.compose.ui.util.fastMapNotNull import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.screenModelScope import eu.kanade.core.util.fastCountNot -import eu.kanade.core.util.fastDistinctBy -import eu.kanade.core.util.fastFilter import eu.kanade.core.util.fastFilterNot -import eu.kanade.core.util.fastMapNotNull import eu.kanade.presentation.more.stats.StatsScreenState import eu.kanade.presentation.more.stats.data.StatsData import eu.kanade.tachiyomi.data.download.manga.MangaDownloadManager diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt index 85a53bf1dd..7aad042742 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt @@ -16,6 +16,7 @@ import androidx.core.content.getSystemService import androidx.core.net.toUri import com.hippo.unifile.UniFile import eu.kanade.domain.ui.UiPreferences +import eu.kanade.domain.ui.model.ThemeMode import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.base.delegate.ThemingDelegate @@ -119,9 +120,13 @@ fun Context.createFileInCacheDir(name: String): File { fun Context.createReaderThemeContext(): Context { val preferences = Injekt.get() val readerPreferences = Injekt.get() + val themeMode = preferences.themeMode().get() val isDarkBackground = when (readerPreferences.readerTheme().get()) { 1, 2 -> true // Black, Gray - 3 -> applicationContext.isNightMode() // Automatic bg uses activity background by default + 3 -> when (themeMode) { // Automatic bg uses activity background by default + ThemeMode.SYSTEM -> applicationContext.isNightMode() + else -> themeMode == ThemeMode.DARK + } else -> false // White } val expected = if (isDarkBackground) Configuration.UI_MODE_NIGHT_YES else Configuration.UI_MODE_NIGHT_NO diff --git a/buildSrc/src/main/kotlin/mihon/buildlogic/ProjectExtensions.kt b/buildSrc/src/main/kotlin/mihon/buildlogic/ProjectExtensions.kt index 366f1ad584..072641c47f 100644 --- a/buildSrc/src/main/kotlin/mihon/buildlogic/ProjectExtensions.kt +++ b/buildSrc/src/main/kotlin/mihon/buildlogic/ProjectExtensions.kt @@ -45,8 +45,8 @@ internal fun Project.configureAndroid(commonExtension: CommonExtension<*, *, *, compilerOptions { jvmTarget.set(AndroidConfig.JvmTarget) freeCompilerArgs.addAll( - "-opt-in=kotlin.RequiresOptIn", "-Xcontext-receivers", + "-opt-in=kotlin.RequiresOptIn", ) // Treat all Kotlin warnings as errors (disabled by default) diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts index 30d20f78e3..b4c701f917 100644 --- a/core/common/build.gradle.kts +++ b/core/common/build.gradle.kts @@ -6,10 +6,11 @@ plugins { android { namespace = "eu.kanade.tachiyomi.core.common" +} - kotlinOptions { - freeCompilerArgs += listOf( - "-Xcontext-receivers", +kotlin { + compilerOptions { + freeCompilerArgs.addAll( "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", "-opt-in=kotlinx.serialization.ExperimentalSerializationApi", ) diff --git a/core/common/src/main/java/eu/kanade/tachiyomi/network/NetworkPreferences.kt b/core/common/src/main/java/eu/kanade/tachiyomi/network/NetworkPreferences.kt index 23b02e3154..17adb7a47c 100644 --- a/core/common/src/main/java/eu/kanade/tachiyomi/network/NetworkPreferences.kt +++ b/core/common/src/main/java/eu/kanade/tachiyomi/network/NetworkPreferences.kt @@ -19,7 +19,7 @@ class NetworkPreferences( fun defaultUserAgent(): Preference { return preferenceStore.getString( "default_user_agent", - "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:132.0) Gecko/20100101 Firefox/132.0", ) } } diff --git a/core/common/src/main/java/eu/kanade/tachiyomi/util/system/DeviceUtil.kt b/core/common/src/main/java/eu/kanade/tachiyomi/util/system/DeviceUtil.kt index c9f20326d0..821545760e 100644 --- a/core/common/src/main/java/eu/kanade/tachiyomi/util/system/DeviceUtil.kt +++ b/core/common/src/main/java/eu/kanade/tachiyomi/util/system/DeviceUtil.kt @@ -64,6 +64,7 @@ object DeviceUtil { val invalidDefaultBrowsers = listOf( "android", + "com.hihonor.android.internal.app", "com.huawei.android.internal.app", "com.zui.resolver", ) diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/GLUtil.kt b/core/common/src/main/java/eu/kanade/tachiyomi/util/system/GLUtil.kt similarity index 69% rename from app/src/main/java/eu/kanade/tachiyomi/util/system/GLUtil.kt rename to core/common/src/main/java/eu/kanade/tachiyomi/util/system/GLUtil.kt index 5a7df2b964..e8d941a812 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/GLUtil.kt +++ b/core/common/src/main/java/eu/kanade/tachiyomi/util/system/GLUtil.kt @@ -6,7 +6,7 @@ import javax.microedition.khronos.egl.EGLContext import kotlin.math.max object GLUtil { - val maxTextureSize: Int by lazy { + val DEVICE_TEXTURE_LIMIT: Int by lazy { // Get EGL Display val egl = EGLContext.getEGL() as EGL10 val display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY) @@ -43,10 +43,23 @@ object GLUtil { // Release egl.eglTerminate(display) - // Return largest texture size found, or default - max(maximumTextureSize, IMAGE_MAX_BITMAP_DIMENSION) + // Return largest texture size found (after making it a multiplier of [Multiplier]), or default + max(maximumTextureSize, SAFE_TEXTURE_LIMIT) + } + + const val SAFE_TEXTURE_LIMIT: Int = 2048 + + val CUSTOM_TEXTURE_LIMIT_OPTIONS: List by lazy { + val steps = DEVICE_TEXTURE_LIMIT / MULTIPLIER + buildList(steps) { + add(DEVICE_TEXTURE_LIMIT) + for (step in steps downTo 2) { + val value = step * MULTIPLIER + if (value >= DEVICE_TEXTURE_LIMIT) continue + add(value) + } + } } } -// Safe minimum default size -private const val IMAGE_MAX_BITMAP_DIMENSION = 2048 +private const val MULTIPLIER: Int = 1024 diff --git a/core/common/src/main/java/eu/kanade/tachiyomi/util/system/WebViewUtil.kt b/core/common/src/main/java/eu/kanade/tachiyomi/util/system/WebViewUtil.kt index b2ebc477e7..51069398af 100644 --- a/core/common/src/main/java/eu/kanade/tachiyomi/util/system/WebViewUtil.kt +++ b/core/common/src/main/java/eu/kanade/tachiyomi/util/system/WebViewUtil.kt @@ -68,7 +68,6 @@ fun WebView.setDefaultSettings() { with(settings) { javaScriptEnabled = true domStorageEnabled = true - databaseEnabled = true useWideViewPort = true loadWithOverviewMode = true cacheMode = WebSettings.LOAD_DEFAULT diff --git a/core/common/src/main/java/tachiyomi/core/common/util/system/ImageUtil.kt b/core/common/src/main/java/tachiyomi/core/common/util/system/ImageUtil.kt index 1933d49e37..3e709a359f 100644 --- a/core/common/src/main/java/tachiyomi/core/common/util/system/ImageUtil.kt +++ b/core/common/src/main/java/tachiyomi/core/common/util/system/ImageUtil.kt @@ -22,6 +22,7 @@ import androidx.core.graphics.get import androidx.core.graphics.green import androidx.core.graphics.red import com.hippo.unifile.UniFile +import eu.kanade.tachiyomi.util.system.GLUtil import logcat.LogPriority import okio.Buffer import okio.BufferedSource @@ -314,6 +315,23 @@ object ImageUtil { val bottomOffset = topOffset + splitHeight } + fun canUseHardwareBitmap(bitmap: Bitmap): Boolean { + return canUseHardwareBitmap(bitmap.width, bitmap.height) + } + + fun canUseHardwareBitmap(imageSource: BufferedSource): Boolean { + return with(extractImageOptions(imageSource)) { + canUseHardwareBitmap(outWidth, outHeight) + } + } + + var hardwareBitmapThreshold: Int = GLUtil.SAFE_TEXTURE_LIMIT + + private fun canUseHardwareBitmap(width: Int, height: Int): Boolean { + if (HARDWARE_BITMAP_UNSUPPORTED) return false + return maxOf(width, height) <= hardwareBitmapThreshold + } + /** * Algorithm for determining what background to accompany a comic/manga page */ @@ -589,6 +607,121 @@ object ImageUtil { } private val optimalImageHeight = getDisplayMaxHeightInPx * 2 + + /** + * Taken from Coil + * (https://github.com/coil-kt/coil/blob/1674d3516f061aeacbe749a435b1924f9648fd41/coil-core/src/androidMain/kotlin/coil3/util/hardwareBitmaps.kt) + * --- + * Maintains a list of devices with broken/incomplete/unstable hardware bitmap implementations. + * + * Model names are retrieved from + * [Google's official device list](https://support.google.com/googleplay/answer/1727131?hl=en). + * + */ + val HARDWARE_BITMAP_UNSUPPORTED = when (Build.VERSION.SDK_INT) { + 26 -> run { + val model = Build.MODEL ?: return@run false + + // Samsung Galaxy (ALL) + if (model.removePrefix("SAMSUNG-").startsWith("SM-")) return@run true + + val device = Build.DEVICE ?: return@run false + + return@run device in arrayOf( + "nora", "nora_8917", "nora_8917_n", // Moto E5 + "james", "rjames_f", "rjames_go", "pettyl", // Moto E5 Play + "hannah", "ahannah", "rhannah", // Moto E5 Plus + + "ali", "ali_n", // Moto G6 + "aljeter", "aljeter_n", "jeter", // Moto G6 Play + "evert", "evert_n", "evert_nt", // Moto G6 Plus + + "G3112", "G3116", "G3121", "G3123", "G3125", // Xperia XA1 + "G3412", "G3416", "G3421", "G3423", "G3426", // Xperia XA1 Plus + "G3212", "G3221", "G3223", "G3226", // Xperia XA1 Ultra + + "BV6800Pro", // BlackView BV6800Pro + "CatS41", // Cat S41 + "Hi9Pro", // CHUWI Hi9 Pro + "manning", // Lenovo K8 Note + "N5702L", // NUU Mobile G3 + ) + } + + 27 -> run { + val device = Build.DEVICE ?: return@run false + + return@run device in arrayOf( + "mcv1s", // LG Tribute Empire + "mcv3", // LG K11 + "mcv5a", // LG Q7 + "mcv7a", // LG Stylo 4 + + "A30ATMO", // T-Mobile REVVL 2 + "A70AXLTMO", // T-Mobile REVVL 2 PLUS + + "A3A_8_4G_TMO", // Alcatel 9027W + "Edison_CKT", // Alcatel ONYX + "EDISON_TF", // Alcatel TCL XL2 + "FERMI_TF", // Alcatel A501DL + "U50A_ATT", // Alcatel TETRA + "U50A_PLUS_ATT", // Alcatel 5059R + "U50A_PLUS_TF", // Alcatel TCL LX + "U50APLUSTMO", // Alcatel 5059Z + "U5A_PLUS_4G", // Alcatel 1X + + "RCT6513W87DK5e", // RCA Galileo Pro + "RCT6873W42BMF9A", // RCA Voyager + "RCT6A03W13", // RCA 10 Viking + "RCT6B03W12", // RCA Atlas 10 Pro + "RCT6B03W13", // RCA Atlas 10 Pro+ + "RCT6T06E13", // RCA Artemis 10 + + "A3_Pro", // Umidigi A3 Pro + "One", // Umidigi One + "One_Max", // Umidigi One Max + "One_Pro", // Umidigi One Pro + "Z2", // Umidigi Z2 + "Z2_PRO", // Umidigi Z2 Pro + + "Armor_3", // Ulefone Armor 3 + "Armor_6", // Ulefone Armor 6 + + "Blackview", // Blackview BV6000 + "BV9500", // Blackview BV9500 + "BV9500Pro", // Blackview BV9500Pro + + "A6L-C", // Nuu A6L-C + "N5002LA", // Nuu A7L + "N5501LA", // Nuu A5L + + "Power_2_Pro", // Leagoo Power 2 Pro + "Power_5", // Leagoo Power 5 + "Z9", // Leagoo Z9 + + "V0310WW", // Blu VIVO VI+ + "V0330WW", // Blu VIVO XI + + "A3", // BenQ A3 + "ASUS_X018_4", // Asus ZenFone Max Plus M1 (ZB570TL) + "C210AE", // Wiko Life + "fireball", // DROID Incredible 4G LTE + "ILA_X1", // iLA X1 + "Infinix-X605_sprout", // Infinix NOTE 5 Stylus + "j7maxlte", // Samsung Galaxy J7 Max + "KING_KONG_3", // Cubot King Kong 3 + "M10500", // Packard Bell M10500 + "S70", // Altice ALTICE S70 + "S80Lite", // Doogee S80Lite + "SGINO6", // SGiNO 6 + "st18c10bnn", // Barnes and Noble BNTV650 + "TECNO-CA8", // Tecno CAMON X Pro, + "SHIFT6m", // SHIFT 6m + ) + } + + else -> false + } } val getDisplayMaxHeightInPx: Int diff --git a/data/build.gradle.kts b/data/build.gradle.kts index 6d2603190b..e17ea68055 100644 --- a/data/build.gradle.kts +++ b/data/build.gradle.kts @@ -30,6 +30,12 @@ android { } } +kotlin { + compilerOptions { + freeCompilerArgs.add("-opt-in=kotlinx.serialization.ExperimentalSerializationApi") + } +} + dependencies { implementation(projects.sourceApi) implementation(projects.domain) @@ -37,14 +43,3 @@ dependencies { api(libs.bundles.sqldelight) } - -tasks { - withType { - compilerOptions.freeCompilerArgs.addAll( - listOf( - "-Xcontext-receivers", - "-opt-in=kotlinx.serialization.ExperimentalSerializationApi", - ), - ) - } -} diff --git a/domain/build.gradle.kts b/domain/build.gradle.kts index da4b6c942e..fd4c194176 100644 --- a/domain/build.gradle.kts +++ b/domain/build.gradle.kts @@ -13,6 +13,12 @@ android { } } +kotlin { + compilerOptions { + freeCompilerArgs.add("-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi") + } +} + dependencies { implementation(projects.sourceApi) implementation(projects.core.common) @@ -30,14 +36,3 @@ dependencies { testImplementation(libs.bundles.test) testImplementation(kotlinx.coroutines.test) } - -tasks { - withType { - compilerOptions.freeCompilerArgs.addAll( - listOf( - "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", - "-Xcontext-receivers", - ), - ) - } -} diff --git a/domain/src/main/java/mihon/domain/extensionrepo/anime/interactor/CreateAnimeExtensionRepo.kt b/domain/src/main/java/mihon/domain/extensionrepo/anime/interactor/CreateAnimeExtensionRepo.kt index b6c47ee5ed..e66d329163 100644 --- a/domain/src/main/java/mihon/domain/extensionrepo/anime/interactor/CreateAnimeExtensionRepo.kt +++ b/domain/src/main/java/mihon/domain/extensionrepo/anime/interactor/CreateAnimeExtensionRepo.kt @@ -5,6 +5,7 @@ import mihon.domain.extensionrepo.anime.repository.AnimeExtensionRepoRepository import mihon.domain.extensionrepo.exception.SaveExtensionRepoException import mihon.domain.extensionrepo.model.ExtensionRepo import mihon.domain.extensionrepo.service.ExtensionRepoService +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import tachiyomi.core.common.util.system.logcat class CreateAnimeExtensionRepo( @@ -13,12 +14,13 @@ class CreateAnimeExtensionRepo( ) { private val repoRegex = """^https://.*/index\.min\.json$""".toRegex() - suspend fun await(repoUrl: String): Result { - if (!repoUrl.matches(repoRegex)) { - return Result.InvalidUrl - } + suspend fun await(indexUrl: String): Result { + val formattedIndexUrl = indexUrl.toHttpUrlOrNull() + ?.toString() + ?.takeIf { it.matches(repoRegex) } + ?: return Result.InvalidUrl - val baseUrl = repoUrl.removeSuffix("/index.min.json") + val baseUrl = formattedIndexUrl.removeSuffix("/index.min.json") return service.fetchRepoDetails(baseUrl)?.let { insert(it) } ?: Result.InvalidUrl } diff --git a/domain/src/main/java/mihon/domain/extensionrepo/manga/interactor/CreateMangaExtensionRepo.kt b/domain/src/main/java/mihon/domain/extensionrepo/manga/interactor/CreateMangaExtensionRepo.kt index 94a07650eb..5ac5263a96 100644 --- a/domain/src/main/java/mihon/domain/extensionrepo/manga/interactor/CreateMangaExtensionRepo.kt +++ b/domain/src/main/java/mihon/domain/extensionrepo/manga/interactor/CreateMangaExtensionRepo.kt @@ -5,6 +5,7 @@ import mihon.domain.extensionrepo.exception.SaveExtensionRepoException import mihon.domain.extensionrepo.manga.repository.MangaExtensionRepoRepository import mihon.domain.extensionrepo.model.ExtensionRepo import mihon.domain.extensionrepo.service.ExtensionRepoService +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import tachiyomi.core.common.util.system.logcat class CreateMangaExtensionRepo( @@ -13,12 +14,13 @@ class CreateMangaExtensionRepo( ) { private val repoRegex = """^https://.*/index\.min\.json$""".toRegex() - suspend fun await(repoUrl: String): Result { - if (!repoUrl.matches(repoRegex)) { - return Result.InvalidUrl - } + suspend fun await(indexUrl: String): Result { + val formattedIndexUrl = indexUrl.toHttpUrlOrNull() + ?.toString() + ?.takeIf { it.matches(repoRegex) } + ?: return Result.InvalidUrl - val baseUrl = repoUrl.removeSuffix("/index.min.json") + val baseUrl = formattedIndexUrl.removeSuffix("/index.min.json") return service.fetchRepoDetails(baseUrl)?.let { insert(it) } ?: Result.InvalidUrl } diff --git a/domain/src/main/java/mihon/domain/extensionrepo/service/ExtensionRepoService.kt b/domain/src/main/java/mihon/domain/extensionrepo/service/ExtensionRepoService.kt index 27e00f3a27..2d86b89d11 100644 --- a/domain/src/main/java/mihon/domain/extensionrepo/service/ExtensionRepoService.kt +++ b/domain/src/main/java/mihon/domain/extensionrepo/service/ExtensionRepoService.kt @@ -1,6 +1,5 @@ package mihon.domain.extensionrepo.service -import androidx.core.net.toUri import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.awaitSuccess @@ -21,11 +20,9 @@ class ExtensionRepoService( repo: String, ): ExtensionRepo? { return withIOContext { - val url = "$repo/repo.json".toUri() - try { with(json) { - client.newCall(GET(url.toString())) + client.newCall(GET("$repo/repo.json")) .awaitSuccess() .parseAs() .toExtensionRepo(baseUrl = repo) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index ab3ea6b471..bcc09d285e 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -1,7 +1,7 @@ [versions] -agp_version = "8.7.1" +agp_version = "8.7.3" lifecycle_version = "2.8.7" -paging_version = "3.3.2" +paging_version = "3.3.5" interpolator_version = "1.0.0" [libraries] @@ -12,10 +12,10 @@ appcompat = "androidx.appcompat:appcompat:1.7.0" biometricktx = "androidx.biometric:biometric-ktx:1.2.0-alpha05" constraintlayout = "androidx.constraintlayout:constraintlayout:2.2.0" compose-constraintlayout = "androidx.constraintlayout:constraintlayout-compose:1.1.0" -corektx = "androidx.core:core-ktx:1.13.1" +corektx = "androidx.core:core-ktx:1.15.0" splashscreen = "androidx.core:core-splashscreen:1.0.1" recyclerview = "androidx.recyclerview:recyclerview:1.3.2" -viewpager = "androidx.viewpager:viewpager:1.1.0-beta01" +viewpager = "androidx.viewpager:viewpager:1.1.0" profileinstaller = "androidx.profileinstaller:profileinstaller:1.4.1" mediasession = "androidx.media:media:1.7.0" @@ -23,7 +23,7 @@ lifecycle-common = { module = "androidx.lifecycle:lifecycle-common", version.ref lifecycle-process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "lifecycle_version" } lifecycle-runtimektx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycle_version" } -workmanager = "androidx.work:work-runtime:2.9.1" +workmanager = "androidx.work:work-runtime:2.10.0" paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "paging_version" } paging-compose = { module = "androidx.paging:paging-compose", version.ref = "paging_version" } diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 391ac76b54..a802d31902 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,5 +1,5 @@ [versions] -compose-bom = "2024.10.00" +compose-bom = "2024.12.01" [libraries] activity = "androidx.activity:activity-compose:1.9.3" diff --git a/gradle/kotlinx.versions.toml b/gradle/kotlinx.versions.toml index c5233ea8e5..c76f4f97eb 100644 --- a/gradle/kotlinx.versions.toml +++ b/gradle/kotlinx.versions.toml @@ -1,7 +1,7 @@ [versions] -kotlin_version = "2.0.21" +kotlin_version = "2.1.0" serialization_version = "1.7.3" -xml_serialization_version = "0.90.2" +xml_serialization_version = "0.90.3" [libraries] reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin_version" } @@ -10,7 +10,7 @@ compose-compiler-gradle = { module = "org.jetbrains.kotlin:compose-compiler-grad immutables = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version = "0.3.8" } -coroutines-bom = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-bom", version = "1.9.0" } +coroutines-bom = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-bom", version = "1.10.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" } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3f0eaf87be..535a48a299 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,18 +1,18 @@ [versions] aboutlib_version = "11.2.3" leakcanary = "2.14" -moko = "0.24.2" +moko = "0.24.4" okhttp_version = "5.0.0-alpha.14" richtext = "0.20.0" shizuku_version = "13.1.0" sqldelight = "2.0.2" sqlite = "2.4.0" -voyager = "1.0.0" +voyager = "1.0.1" spotless = "7.0.0.BETA4" -ktlint-core = "1.4.0" +ktlint-core = "1.5.0" [libraries] -desugar = "com.android.tools:desugar_jdk_libs:2.1.2" +desugar = "com.android.tools:desugar_jdk_libs:2.1.4" android-shortcut-gradle = "com.github.zellius:android-shortcut-gradle-plugin:0.1.2" rxjava = "io.reactivex:rxjava:1.3.8" @@ -27,7 +27,7 @@ conscrypt-android = "org.conscrypt:conscrypt-android:2.5.3" quickjs-android = "app.cash.quickjs:quickjs-android:0.9.2" -jsoup = "org.jsoup:jsoup:1.18.1" +jsoup = "org.jsoup:jsoup:1.18.3" disklrucache = "com.jakewharton:disklrucache:2.0.2" unifile = "com.github.tachiyomiorg:unifile:e0def6b3dc" @@ -41,13 +41,13 @@ preferencektx = "androidx.preference:preference-ktx:1.2.1" injekt = "com.github.mihonapp:injekt:91edab2317" -coil-bom = { module = "io.coil-kt.coil3:coil-bom", version = "3.0.0-rc02" } +coil-bom = { module = "io.coil-kt.coil3:coil-bom", version = "3.0.4" } 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" } coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp" } -subsamplingscaleimageview = "com.github.tachiyomiorg:subsampling-scale-image-view:b8e1b0ed2b" +subsamplingscaleimageview = "com.github.tachiyomiorg:subsampling-scale-image-view:66e0db195d" image-decoder = "com.github.tachiyomiorg:image-decoder:41c059e540" natural-comparator = "com.github.gpanther:java-nat-sort:natural-comparator-1.1" @@ -84,9 +84,9 @@ sqldelight-coroutines = { module = "app.cash.sqldelight:coroutines-extensions-jv sqldelight-android-paging = { module = "app.cash.sqldelight:androidx-paging3-extensions", version.ref = "sqldelight" } sqldelight-dialects-sql = { module = "app.cash.sqldelight:sqlite-3-38-dialect", version.ref = "sqldelight" } -junit = "org.junit.jupiter:junit-jupiter:5.11.3" +junit = "org.junit.jupiter:junit-jupiter:5.11.4" kotest-assertions = "io.kotest:kotest-assertions-core:5.9.1" -mockk = "io.mockk:mockk:1.13.13" +mockk = "io.mockk:mockk:1.13.14" voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" } voyager-screenmodel = { module = "cafe.adriel.voyager:voyager-screenmodel", version.ref = "voyager" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index df97d72b8b..cea7a793a8 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.10.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index f5feea6d6b..f3b75f3b0d 100755 --- a/gradlew +++ b/gradlew @@ -86,8 +86,7 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s -' "$PWD" ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/i18n/build.gradle.kts b/i18n/build.gradle.kts index 09dff6452a..7259e46fdb 100644 --- a/i18n/build.gradle.kts +++ b/i18n/build.gradle.kts @@ -1,6 +1,6 @@ import mihon.buildlogic.generatedBuildDir import mihon.buildlogic.tasks.getLocalesConfigTask -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi plugins { id("mihon.library") @@ -20,6 +20,11 @@ kotlin { } } } + + @OptIn(ExperimentalKotlinGradlePluginApi::class) + compilerOptions { + freeCompilerArgs.add("-Xexpect-actual-classes") + } } val generatedAndroidResourceDir = generatedBuildDir.resolve("android/res") @@ -49,12 +54,4 @@ tasks { preBuild { dependsOn(localesConfigTask) } - - withType { - compilerOptions.freeCompilerArgs.addAll( - listOf( - "-Xexpect-actual-classes", - ), - ) - } } diff --git a/i18n/src/commonMain/moko-resources/as/plurals.xml b/i18n/src/commonMain/moko-resources/as/plurals.xml new file mode 100644 index 0000000000..ef702b20ba --- /dev/null +++ b/i18n/src/commonMain/moko-resources/as/plurals.xml @@ -0,0 +1,79 @@ + + + + এক্সটেনচন আপডেট উপলব্ধ + %d এক্সটেনচন আপডেটসমূহ উপলব্ধ + + + %d ৰেপো + %d ৰেপোসমূহ + + + %1$s মিনিট পিছত + %1$s মিনিটৰ পিছত + + + কালি + %1$d দিনৰ আগতে + + + কাইলৈ + %1$d দিনত + + + %d শিতান + %d শিতানসমূহ + + + পৰৱৰ্তী অপঠিত অধ্যায় + পৰৱৰ্তী %d অপঠিত অধ্যায়সমূহ + + + %1$sত %2$s ত্ৰুটি সৈতে সমাপ্ত + %1$sত %2$s ত্ৰুটিৰ সৈতে সমাপ্ত + + + %1$s বাকী আছে + %1$s বাকী আছে + + + ১ দিন + %d দিন + + + ১ পৃষ্ঠা + %1$s পৃষ্ঠাসমূহ + + + অপস্থিত %1$s অধ্যায় + অপস্থিত %1$s অধ্যায়সমূহ + + + %1$s অধ্যায় + %1$s অধ্যায়সমূহ + + + পৰৱৰ্তী অধ্যায় + পৰৱৰ্তী %d অধ্যায়সমূহ + + + %d ট্ৰেকাৰ + %d ট্ৰেকাৰসমূহ + + + %d অধ্যায় স্কিপ কৰা হৈছে, উৎসত নাইবা ফিল্টাৰ কৰি আঁতৰোৱা হৈছে + %d অধ্যায়সমূহ স্কিপ কৰা হৈছে, উৎসত নাইবা ফিল্টাৰ কৰি আঁতৰোৱা হৈছে + + + %d প্ৰৱিষ্টিৰ বাবে + %d প্ৰৱিষ্টিসমূহৰ বাবে + + + %1$d নতুন অধ্যায় + %1$d নতুন অধ্যায়সমূহ + + + অধ্যায়সমূহ %1$s আৰু 1 অধিক + অধ্যায়সমূহ %1$s আৰু %2$d অধিক + + \ No newline at end of file diff --git a/i18n/src/commonMain/moko-resources/base/strings.xml b/i18n/src/commonMain/moko-resources/base/strings.xml index 9710a4a924..d6696ea6c0 100644 --- a/i18n/src/commonMain/moko-resources/base/strings.xml +++ b/i18n/src/commonMain/moko-resources/base/strings.xml @@ -630,6 +630,11 @@ Show page number Show reading mode Briefly show current mode when reader is opened + Custom hardware bitmap threshold + Default (%d) + If reader loads a blank image incrementally reduce the threshold.\nSelected: %s + Always decode long strip images with SSIV + Affects performance. Only enable if reducing bitmap threshold doesn\'t fix blank image issues Custom display profile Crop borders Custom brightness @@ -767,7 +772,7 @@ Trackers not logged into: You may need to install any missing extensions and log in to tracking services afterwards to use them. Restore completed - %02d min, %02d sec + %1$02d min, %2$02d sec Backup is already in progress What do you want to backup? App settings diff --git a/i18n/src/commonMain/moko-resources/eo/plurals.xml b/i18n/src/commonMain/moko-resources/eo/plurals.xml index 1ce57bb8c3..671eb40e16 100644 --- a/i18n/src/commonMain/moko-resources/eo/plurals.xml +++ b/i18n/src/commonMain/moko-resources/eo/plurals.xml @@ -92,4 +92,12 @@ Disponebla ĝisdatigo de etendaĵo Disponeblaj ĝisdatigoj de %d etendaĵoj - + + Morgaŭ + post %1$d tagoj + + + 1 paĝo + %1$s paĝoj + + \ No newline at end of file diff --git a/i18n/src/commonMain/moko-resources/es/plurals.xml b/i18n/src/commonMain/moko-resources/es/plurals.xml index b3672bc3a3..c8f30d5dad 100644 --- a/i18n/src/commonMain/moko-resources/es/plurals.xml +++ b/i18n/src/commonMain/moko-resources/es/plurals.xml @@ -1,9 +1,9 @@ - Tras %1$s minuto - Tras %1$s minutos - Tras %1$s minutos + Después de %1$s minuto + Después de %1$s minutos + Después de %1$s minutos %1$d nuevo capítulo @@ -16,9 +16,9 @@ Capítulos %1$s y %2$d más - Para %d título - Para %d títulos - Para %d títulos + Para %d entrada + Para %d entradas + Para %d entradas Actualización de extensión disponible @@ -26,9 +26,9 @@ %d actualizaciones de extensiones disponibles - Restante %1$s - Restantes %1$s - Restantes %1$s + Queda %1$s + Quedan %1$s + Quedan %1$s %d categoría @@ -46,9 +46,9 @@ %1$s capítulos - %d servicio de seguimiento - %d servicios de seguimiento - %d servicios de seguimiento + %d rastreador + %d rastreadores + %d rastreadores Se omite %d capítulo, o bien falta en la fuente o ha sido filtrado @@ -63,12 +63,12 @@ Siguiente capítulo sin leer Siguientes %d capítulos sin leer - Los siguientes %d capítulos sin leer + Siguientes %d capítulos sin leer - El siguiente capítulo - Los siguientes %d capítulos - Los siguientes %d capítulos + Siguiente capítulo + Siguientes %d capítulos + Siguientes %d capítulos Un día @@ -120,4 +120,4 @@ %1$s páginas %1$s páginas - + \ No newline at end of file diff --git a/i18n/src/commonMain/moko-resources/hi/strings.xml b/i18n/src/commonMain/moko-resources/hi/strings.xml index af74dc5fdc..2605b005c2 100644 --- a/i18n/src/commonMain/moko-resources/hi/strings.xml +++ b/i18n/src/commonMain/moko-resources/hi/strings.xml @@ -7,7 +7,7 @@ पदचिह्न इतिहास सेटिंग्स - डाउनलोड कतार + डाउनलोड सूची पुस्तकालय इतिहास नए अपडेट diff --git a/i18n/src/commonMain/moko-resources/hr/plurals.xml b/i18n/src/commonMain/moko-resources/hr/plurals.xml index c1fe0b9656..45079078d0 100644 --- a/i18n/src/commonMain/moko-resources/hr/plurals.xml +++ b/i18n/src/commonMain/moko-resources/hr/plurals.xml @@ -56,7 +56,7 @@ Preskače se %d poglavlja. Ne postoje u izvoru ili su filtrirana - Prije %1$d dan + Jučer Prije %1$d dana Prije %1$d dana @@ -115,4 +115,9 @@ Za %1$d dana Za %1$d dana - + + %1$s stranica + %1$s stranice + %1$s stranica + + \ No newline at end of file diff --git a/i18n/src/commonMain/moko-resources/it/plurals.xml b/i18n/src/commonMain/moko-resources/it/plurals.xml index 7d4b179641..e0e393ea3a 100644 --- a/i18n/src/commonMain/moko-resources/it/plurals.xml +++ b/i18n/src/commonMain/moko-resources/it/plurals.xml @@ -110,4 +110,9 @@ Prossimi %d episodi - + + 1 pagina + %1$s pagine + %1$s pagine + + \ No newline at end of file diff --git a/i18n/src/commonMain/moko-resources/ja/strings.xml b/i18n/src/commonMain/moko-resources/ja/strings.xml index 6a0e646fb6..4bd57d4b31 100644 --- a/i18n/src/commonMain/moko-resources/ja/strings.xml +++ b/i18n/src/commonMain/moko-resources/ja/strings.xml @@ -318,7 +318,7 @@ 既に復元中です バックアップできませんでした 既にバックアップ中です - %02d分%02d秒 + %1$02d分%2$02d 秒 ソースがありません: バックアップにはライブラリの項目が含まれません。 無効なバックアップファイル: diff --git a/i18n/src/commonMain/moko-resources/my/strings.xml b/i18n/src/commonMain/moko-resources/my/strings.xml new file mode 100644 index 0000000000..4261029cb6 --- /dev/null +++ b/i18n/src/commonMain/moko-resources/my/strings.xml @@ -0,0 +1,201 @@ + + + ဖွင့်သည်။ + ပိတ်သည်။ + ရွေးချယ်ထားသည်။ + မရွေးချယ်ထားပါ။ + ရွေးချယ်စရာများ + အပေါ်သို့ + အမည် + အမျိုးအစားများ + စာအုပ်စင် + အပိုင်းများ + ခြေရာခံ + ဘာသာပြန်သူ + ပိုမို၍ + ဒေါင်းလုဒ် စာရင်း + စာအုပ်စင် + အပ်ဒိတ်များ + မှတ်တမ်းများ + ရင်းမြစ်များ + ဒေတာနှင့် သိမ်းဆည်းမှု + စာရင်းအင်းများ + ပေါင်းစပ်မှု + ချိတ်ဆက်မှု အချက်အလက် + အကူအညီ + ပုံသေ + သတိပေးချက် + စတင်သည်။ + Local + ဒေါင်းလုဒ်လုပ်ထားသည်။ + ဒေါင်းလုဒ်များ ဖျက်မည်။ + မှတ်တမ်း + ဆက်တင်များ + နောက်ဆက်တွဲ + ချိတ်ဆက်မှုများ + အရံသိမ်းဆည်းခြင်းနှင့် ပြန်လည်ရယူခြင်း + အက်ပ်ကို ဖွင့်ရန် + ဖြည့်ထားသည်ကို အတည်ပြုပါ + ဆက်တင်များ + မီနူး + စစ်ထုတ်မှု + အချိန်ကြားကို သတ်မှတ်ပါ + စာအမှတ်အထားများ စစ်ထုတ်မှု + နောက်ဆုံး ဖတ်ခဲ့သည်အားဖြင့် စီပါ + နောက်ဆုံး အပ်ဒိတ်အားဖြင့် စီပါ + တည်းဖြတ်ပါ + စတင်ပါ + ဆက်လက်ဖတ်ရှုရန် ခလုတ်ပြသရန် + မလုပ်ဆောင်ပါ + ပင်အပ်မည် + ပင်အပ်ခြင်းဖယ်ရှားမည် + ပြီးစီးသည် + ကူညီပါ + ခွင့်ပြုခြင်း + အသစ်အသုံးပြုသူများအတွက်လမ်းညွှန် + ပြန်လည်အသုံးပြုသူများအတွက်လမ်းညွှန် + ယေဘူယျ + ပုံစံ + စာကြည့်တိုက် + စာဖတ်သူ + ဒေါင်းလုဒ်များ + လမ်းညွှန်စက်များ + မြင့်မားသော + အကြောင်း + ပုံစံအနှစ်ချုပ် + စာကြည့်တိုက်အနှစ်ချုပ် + လမ်းညွှန်စက်များအနှစ်ချုပ် + အမှောင် + အလွတ် စစ်ထုတ်မှု + ခြေရာခံထားမှု စစ်ထုတ်မှု + မဖတ်ရသေးသော စစ်ထုတ်မှု + စိတ်ကြိုက် အချိန်ကြား စစ်ထုတ်မှု + အက္ခရာစဉ်အားဖြင့် စီပါ + ရေတွက်မှုအားဖြင့် စီပါ + နောက်တစ်ခု အပ်ဒိတ်မည်အားဖြင့် စီပါ + နောက်ဆုံး အပိုင်းအားဖြင့် စီပါ + စုစုပေါင်းအားဖြင့် စီပါ + မဖတ်ရသေးသော အရေအတွက်အားဖြင့် စီပါ + အပိုင်းရရှိသည့် ရက်စွဲအားဖြင့် စီပါ + ထည့်သွင်းသည့် ရက်စွဲအားဖြင့် စီပါ + ခြေရာခံ အမှတ်အားဖြင့် စီပါ + ကျပန်းစီမှု + ရှာဖွေပါ + ရှာဖွေလိုသည်ကို ထည့်ပါ + ရှာဖွေမှု ဆက်တင်များ + အားလုံးကို ရွေးချယ်ပါ + ရွေးချယ်မှု ပြောင်းပြန်ပါ + ဖတ်ပြီးဟု မှတ်သားပါ + ကမ္ဘာလုံးဆိုင်ရာ ရှာဖွေမှု + မဖတ်ရသေးဟု မှတ်သားပါ + ယခင် အပိုင်းများကို ဖတ်ပြီးဟု မှတ်သားပါ + ဒေါင်းလုဒ်လုပ်ပါ + စာကြည့်တိုက်ကို အပ်ဒိတ်လုပ်ပါ + အားလုံးကို ဖွင့်ပါ + အားလုံးကို ပိတ်ပါ + ထည့်ပါ + အမျိုးအစားကို အပ်ဒိတ်လုပ်ပါ + ကျပန်း စာအုပ်တစ်အုပ် ဖွင့်ပါ + အမျိုးအစား ထည့်ပါ + အမျိုးအစားများ တည်းဖြတ်ပါ + စာအမှတ်အထား ထည့်ပါ + စာအမှတ်အထား ဖယ်ရှားပါ + ဖျက်ပါ + အမျိုးအစား အမည်ပြောင်းပါ + အမျိုးအစား ရွှေ့ပါ + အမျိုးအစား ဖျက်မည်ဟု အတည်ပြုပါ + အမျိုးအစား ဖျက်ပါ + အမျိုးအစားများ စီပါ + ရပ်လိုက်ပါ + စီစဉ်မှုကို အတည်ပြုပါ + အဖုံးကို တည်းဖြတ်ပါ + အပိုင်းများကို ကြည့်ရှုပါ + ယခင် အပိုင်း + နောက်ထပ် အပိုင်း + ထပ်ကြိုးစားပါ + ဘာသာစကားဘေ့ခ််ပြသမည် + ထပ်အလုံးပြသရန် + ပစ္စည်းအရေအတွက်ပြသရန် + လျှောက်ထားမည် + မလုပ်ဆောင်ပါ + အတည်ပြုမည် + ဖယ်ရှားပါ + အားလုံးကို ဖယ်ရှားပါ + ပြန်စပါ + ဘရောက်ဇာတွင် ဖွင့်ပါ + စာအုပ်စင်ပြပါ + ကလစ်ပို့ဒ်သို့ မိတ္တူပြုပါ + လင့် မိတ္တူပြုပါ + ဝဘ်အမြင်တွင်ဖွင့်ရန် + အချက်အလက်ပြောင်းရွှေ့မည် + ပြသမှုမode + ပြသမည် + ဂရစ်အဖြစ်ပြသမည် + သက်တောင့်သက်သာရှိသောဂရစ်အဖြစ်ပြသမည် + စာရင်းအဖြစ်ပြသမည် + အကဲဖြတ်စာမျက်နှာသာပြသသောဂရစ် + ဒေါင်းလုဒ်ဘေ့ခ််ပြသမည် + အားလုံးဖျက်မည် + ဤအကြောင်းအရာအားလုံးကိုဖျက်မည် + အစီစဉ်ပေးမည် + ဒေသခံဘေ့ခ််ပြသမည် + တင်သွင်းချိန်အလိုက်အစီအစဉ်ပေးမည် + အခန်းနံပါတ်အလိုက်အစီအစဉ်ပေးမည် + အနောက်ဆုံးအသစ်များ + အရိုးရိုးအဟောင်းများ + အဆင့်မြှင့် + အဆင့်ချော့ + အောက်ဆုံးသို့ရွှေ့မည် + ဤအကြောင်းအရာအားလုံးကိုအောက်ဆုံးသို့ရွှေ့မည် + တင်သွင်းမည် + မျှဝေမည် + အထက်ဆုံးသို့ရွှေ့မည် + ဤအကြောင်းအရာအားလုံးကိုအထက်ဆုံးသို့ရွှေ့မည် + သိမ်းမည် + ပြန်လည်သတ်မှတ်မည် + ပုံမှန်ပြန်အလွဲမပြင် + အမှားပြင်မည် + ပိတ်မည် + အနာဂတ်အမြင်သို့ရွှေ့မည် + ပြန်လည်ဖြစ်စေမည် + မှတ်တမ်းဖွင့်မည် + အမှားများပြသမည် + ပြန်လည်သုံးစွဲမည် + အတိတ်အမြင်မှပြန်သွားမည် + ယခုဒေါင်းလုဒ်စတင်မည် + ယခုမဟုတ်ပါ + အကယ်၍ထည့်မည် + ထပ်မံပြောင်းရွှေ့ရန် + မည့်လုပ်ဆောင်နေပါ + အတွင်းပိုင်းအမှား + အက်ပလီကေးရှင်းရရှိနိုင်ခြင်းမရှိပါ + လမ်းညွှန်အခန်း + လမ်းညွှန်ဖော်ပြချက် + နောက်တစ်ဆင့် + ကျော်ဖြတ်မည် + လမ်းညွှန်အကြောင်း + သိုလှောင်မှုအချက်အလက် + ရွေးချယ်ပါ + အက်ပလီကေးရှင်းများတပ်ဆင်ခွင့် + အက်ပလီကေးရှင်းတပ်ဆင်ရန်ခွင့်ပြုပါ + ရွေးချယ်မှုအကန့်အသတ်မရှိပါ + သတိပေးချက်များခွင့်ပြုမည် + သိုလှောင်မှုကူညီမှုအချက်အလက် + သတိပေးချက်များတင်ပြပါ + ဘက်ထရီအနေအထားများကိုသွင်းခြင်း + ဘက်ထရီစွမ်းအင်ကြီးလွန်ခြင်းများအားလုံးဖော်ပြပါ + အမှားဖြစ်သောအချက်အလက်များမှတ်တမ်းတင်ခြင်း + အမှားအချက်များအတွက်ပါဝင်ရန် + စွမ်းဆောင်မှုများစာရင်း + မှတ်တမ်းများနှင့်ယခုနှင့်အတူ စွမ်းဆောင်မှုစစ်ဆေးမှု + စာဖတ်သူအနှစ်ချုပ် + ဒေါင်းလုဒ်များအနှစ်ချုပ် + လမ်းညွှန်ရှာဖွေရေးအနှစ်ချုပ် + အပေါ်မှာလမ်းညွှန်မှတ်သား + လုံခြုံရေးအနှစ်ချုပ် + မြင့်မားသောအနှစ်ချုပ် + ပုံစံအဆင့် + အက်ပလီကေးရှင်းပုံစံ + စနစ်ပုံစံ + အလင်း + \ No newline at end of file diff --git a/i18n/src/commonMain/moko-resources/nl/plurals.xml b/i18n/src/commonMain/moko-resources/nl/plurals.xml index b57fcf5a6d..554aaf5282 100644 --- a/i18n/src/commonMain/moko-resources/nl/plurals.xml +++ b/i18n/src/commonMain/moko-resources/nl/plurals.xml @@ -41,7 +41,7 @@ %d trackers - 1 hoofdstuk is overgeslagen, de bron mist 1 hoofdstuk of het is uitgefilterd + %d hoofdstuk is overgeslagen, de bron mist of het is uitgefilterd %d hoofdstukken zijn overgeslagen, de bron mist ze of ze zijn uitgefilterd @@ -49,8 +49,8 @@ %1$d dagen geleden - Volgende ongelezen hoofdstuk - Volgende aantal %d ongelezen hoofdstukken + Volgend ongelezen hoofdstuk + Volgende %d ongelezen hoofdstukken Volgend hoofdstuk @@ -92,4 +92,12 @@ Morgen Over %1$d dagen - + + 1 pagina + %1$s pagina\'s + + + %d repo + %d repo\'s + + \ No newline at end of file diff --git a/i18n/src/commonMain/moko-resources/pt-rBR/strings.xml b/i18n/src/commonMain/moko-resources/pt-rBR/strings.xml index eb7bda97ee..343866b7b6 100644 --- a/i18n/src/commonMain/moko-resources/pt-rBR/strings.xml +++ b/i18n/src/commonMain/moko-resources/pt-rBR/strings.xml @@ -276,7 +276,7 @@ Avançar Recarregar Biblioteca - Obsoleta + Obsoleto Esta extensão não está mais disponível. Ela pode não funcionar corretamente e pode causar problemas no aplicativo. É recomendado desinstalá-la. Nenhum resultado encontrado Selecione uma fonte da qual migrar diff --git a/i18n/src/commonMain/moko-resources/sq/strings.xml b/i18n/src/commonMain/moko-resources/sq/strings.xml index 6b21d272c2..ecc68c5365 100644 --- a/i18n/src/commonMain/moko-resources/sq/strings.xml +++ b/i18n/src/commonMain/moko-resources/sq/strings.xml @@ -676,4 +676,4 @@ Gjurmuesi i identifikimit Koha relative \"%1$s\" në vend të \"%2$s\" - + \ No newline at end of file diff --git a/i18n/src/commonMain/moko-resources/uk/plurals.xml b/i18n/src/commonMain/moko-resources/uk/plurals.xml index 8d5ffd3b10..0a7c56e514 100644 --- a/i18n/src/commonMain/moko-resources/uk/plurals.xml +++ b/i18n/src/commonMain/moko-resources/uk/plurals.xml @@ -131,4 +131,10 @@ Наступні %d епізодів Наступні %d епізодів - + + %1$s сторінка + %1$s сторінки + %1$s сторінок + %1$s сторінок + + \ No newline at end of file diff --git a/i18n/src/commonMain/moko-resources/zh-rCN/plurals.xml b/i18n/src/commonMain/moko-resources/zh-rCN/plurals.xml index a837b44ae4..2b17877eae 100644 --- a/i18n/src/commonMain/moko-resources/zh-rCN/plurals.xml +++ b/i18n/src/commonMain/moko-resources/zh-rCN/plurals.xml @@ -60,4 +60,7 @@ %1$d 天后 - + + 第 %1$s 页 + + \ No newline at end of file diff --git a/i18n/src/commonMain/moko-resources/zh-rTW/plurals.xml b/i18n/src/commonMain/moko-resources/zh-rTW/plurals.xml index 5036de4742..feb927f6e8 100644 --- a/i18n/src/commonMain/moko-resources/zh-rTW/plurals.xml +++ b/i18n/src/commonMain/moko-resources/zh-rTW/plurals.xml @@ -25,7 +25,7 @@ 第 %1$s 章以及另外 %2$d 章 - %d 個歷程平台 + %d個歷程平臺 略過了 %d 章,也許是來源沒有這些章節,或其已被篩選規則排除 @@ -54,4 +54,4 @@ %1$s pages - + \ No newline at end of file diff --git a/presentation-core/build.gradle.kts b/presentation-core/build.gradle.kts index ea2710cc44..332c1fffea 100644 --- a/presentation-core/build.gradle.kts +++ b/presentation-core/build.gradle.kts @@ -13,6 +13,20 @@ android { } } +kotlin { + compilerOptions { + freeCompilerArgs.addAll( + "-opt-in=androidx.compose.animation.ExperimentalAnimationApi", + "-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi", + "-opt-in=androidx.compose.foundation.ExperimentalFoundationApi", + "-opt-in=androidx.compose.foundation.layout.ExperimentalLayoutApi", + "-opt-in=androidx.compose.material3.ExperimentalMaterial3Api", + "-opt-in=androidx.compose.ui.ExperimentalComposeUiApi", + "-opt-in=kotlinx.coroutines.FlowPreview", + ) + } +} + dependencies { api(projects.core.common) api(projects.i18n) @@ -28,25 +42,7 @@ dependencies { implementation(compose.ui.tooling.preview) implementation(compose.ui.util) + implementation(androidx.paging.runtime) + implementation(androidx.paging.compose) implementation(kotlinx.immutables) } - -tasks { - // See https://kotlinlang.org/docs/reference/experimental.html#experimental-status-of-experimental-api(-markers) - withType { - compilerOptions.freeCompilerArgs.addAll( - listOf( - "-opt-in=androidx.compose.foundation.layout.ExperimentalLayoutApi", - "-opt-in=androidx.compose.material.ExperimentalMaterialApi", - "-opt-in=androidx.compose.material3.ExperimentalMaterial3Api", - "-opt-in=androidx.compose.material.ExperimentalMaterialApi", - "-opt-in=androidx.compose.ui.ExperimentalComposeUiApi", - "-opt-in=androidx.compose.foundation.ExperimentalFoundationApi", - "-opt-in=androidx.compose.animation.ExperimentalAnimationApi", - "-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi", - "-opt-in=coil3.annotation.ExperimentalCoilApi", - "-opt-in=kotlinx.coroutines.FlowPreview", - ), - ) - } -} diff --git a/presentation-core/src/main/java/mihon/presentation/core/util/PagingDataUtil.kt b/presentation-core/src/main/java/mihon/presentation/core/util/PagingDataUtil.kt new file mode 100644 index 0000000000..a48acae509 --- /dev/null +++ b/presentation-core/src/main/java/mihon/presentation/core/util/PagingDataUtil.kt @@ -0,0 +1,16 @@ +package mihon.presentation.core.util + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.paging.PagingData +import androidx.paging.compose.LazyPagingItems +import androidx.paging.compose.collectAsLazyPagingItems +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.StateFlow + +@Composable +fun StateFlow>>.collectAsLazyPagingItems(): LazyPagingItems { + val flow by collectAsState() + return flow.collectAsLazyPagingItems() +} 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 79b9cd10b7..72f5118282 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 @@ -33,6 +33,7 @@ import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.MenuAnchorType import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.RadioButton import androidx.compose.material3.Text @@ -254,7 +255,7 @@ fun SelectItem( ) { OutlinedTextField( modifier = Modifier - .menuAnchor() + .menuAnchor(MenuAnchorType.PrimaryNotEditable) .fillMaxWidth() .padding( horizontal = SettingsItemsPaddings.Horizontal, @@ -363,7 +364,7 @@ fun SelectItem( ) { OutlinedTextField( modifier = Modifier - .menuAnchor() + .menuAnchor(MenuAnchorType.PrimaryNotEditable) .fillMaxWidth() .padding( horizontal = SettingsItemsPaddings.Horizontal, diff --git a/source-api/build.gradle.kts b/source-api/build.gradle.kts index 284a30d1e7..330e5f29d5 100644 --- a/source-api/build.gradle.kts +++ b/source-api/build.gradle.kts @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi + plugins { id("mihon.library") kotlin("multiplatform") @@ -29,6 +31,11 @@ kotlin { } } } + + @OptIn(ExperimentalKotlinGradlePluginApi::class) + compilerOptions { + freeCompilerArgs.add("-Xexpect-actual-classes") + } } android { @@ -38,14 +45,3 @@ android { consumerProguardFile("consumer-proguard.pro") } } - -tasks { - withType { - compilerOptions.freeCompilerArgs.addAll( - listOf( - "-Xexpect-actual-classes", - "-opt-in=kotlinx.serialization.ExperimentalSerializationApi", - ), - ) - } -} diff --git a/source-local/build.gradle.kts b/source-local/build.gradle.kts index 06e08cd452..36ac3d8d4b 100644 --- a/source-local/build.gradle.kts +++ b/source-local/build.gradle.kts @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi + plugins { id("mihon.library") kotlin("multiplatform") @@ -27,6 +29,14 @@ kotlin { } } } + + @OptIn(ExperimentalKotlinGradlePluginApi::class) + compilerOptions { + freeCompilerArgs.addAll( + "-Xexpect-actual-classes", + "-opt-in=kotlinx.serialization.ExperimentalSerializationApi", + ) + } } android { @@ -42,13 +52,3 @@ android { implementation(libs.ffmpeg.kit) } } - -tasks { - withType { - compilerOptions.freeCompilerArgs.addAll( - listOf( - "-opt-in=kotlinx.serialization.ExperimentalSerializationApi", - ), - ) - } -}