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",
- ),
- )
- }
-}