Compare commits
68 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 31f967235f | |||
| 7d6e746257 | |||
| d306139047 | |||
| 7e6811692e | |||
| 8df0446020 | |||
| 8ccf8fc74d | |||
| 4deaa41c53 | |||
| c843789f66 | |||
| 78da81fa42 | |||
| 4fc96f263d | |||
| 6a12b54ecb | |||
| 8ffcd5efec | |||
| 26121efeb4 | |||
| 1ecf3a567b | |||
| 20fff5798d | |||
| 0ba580ba30 | |||
| aaf28ee4f1 | |||
| 8558c110a9 | |||
| 3865583c28 | |||
| b2cc61f6fd | |||
| 47dd58de2a | |||
| 67d42c9c2b | |||
| b97f322d6f | |||
| d8cc4f8b45 | |||
| f78752fbdf | |||
| d968d58cd6 | |||
| 54d5f9baaf | |||
| c1bf53e28a | |||
| 517fd3a8f4 | |||
| 089d1aba57 | |||
| 46bf139f01 | |||
| c3fb5c0bec | |||
| 000a4ffc3f | |||
| 7b0b879d65 | |||
| 8a622f6c7d | |||
| 253060a3bc | |||
| b6b33e8c00 | |||
| 2e4f811090 | |||
| 215a1908f7 | |||
| 082acf000c | |||
| 2d1240b274 | |||
| 1e98709cc3 | |||
| 5550ddad4e | |||
| f9148c0c5e | |||
| 089e6268e7 | |||
| 712cd1493f | |||
| bbc8adc3e8 | |||
| 077b673c0a | |||
| 49eacf5178 | |||
| 98d1dddf4a | |||
| 37a616f3db | |||
| ad18696a1a | |||
| 34bb012a1c | |||
| 08c4989aa3 | |||
| 14dae420f5 | |||
| 65ed3c5ae6 | |||
| 5ae3508665 | |||
| e32eb0e009 | |||
| e0812ab5c8 | |||
| df9f79c120 | |||
| 990eb33b98 | |||
| e1bab1172a | |||
| aeeff72bed | |||
| 5895e78b39 | |||
| b24719a3e9 | |||
| d551619d9d | |||
| 06ad6c2e16 | |||
| df7e470e08 |
@@ -40,6 +40,12 @@ jobs:
|
|||||||
"ignoreCase": true,
|
"ignoreCase": true,
|
||||||
"labels": ["Cloudflare protected"],
|
"labels": ["Cloudflare protected"],
|
||||||
"message": "Refer to the **Solving Cloudflare issues** section at https://mihon.app/docs/guides/troubleshooting/#cloudflare. If it doesn't work, migrate to other sources or wait until they lower their protection."
|
"message": "Refer to the **Solving Cloudflare issues** section at https://mihon.app/docs/guides/troubleshooting/#cloudflare. If it doesn't work, migrate to other sources or wait until they lower their protection."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "both",
|
||||||
|
"regex": "^.*(myanimelist|mal).*$",
|
||||||
|
"ignoreCase": true,
|
||||||
|
"message": "For issues with linking MyAnimeList, please follow these steps:\n1. Update Mihon to version 0.16.4 or newer\n2. Change your default User-Agent (`More → Settings → Advanced → Default user agent string`)\n3. Close and restart Mihon\n4. Attempt to link MyAnimeList again\n\nIf you had MyAnimeList linked before, try to unlink it first before trying these steps."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
auto-close-ignore-label: do-not-autoclose
|
auto-close-ignore-label: do-not-autoclose
|
||||||
|
|||||||
@@ -88,9 +88,7 @@ Please make sure to read the full guidelines. Your issue may be closed without w
|
|||||||
* If it could be device-dependent, try reproducing on another device (if possible)
|
* If it could be device-dependent, try reproducing on another device (if possible)
|
||||||
* Don't group unrelated requests into one issue
|
* Don't group unrelated requests into one issue
|
||||||
|
|
||||||
DO: https://github.com/tachiyomiorg/tachiyomi/issues/24 https://github.com/tachiyomiorg/tachiyomi/issues/71
|
Use the [issue forms](https://github.com/jobobby04/TachiyomiSY/issues/new/choose) to submit a bug.
|
||||||
|
|
||||||
DON'T: https://github.com/tachiyomiorg/tachiyomi/issues/75
|
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "eu.kanade.tachiyomi.sy"
|
applicationId = "eu.kanade.tachiyomi.sy"
|
||||||
|
|
||||||
versionCode = 67
|
versionCode = 68
|
||||||
versionName = "1.10.5"
|
versionName = "1.10.5"
|
||||||
|
|
||||||
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
||||||
@@ -155,7 +155,6 @@ dependencies {
|
|||||||
implementation(compose.activity)
|
implementation(compose.activity)
|
||||||
implementation(compose.foundation)
|
implementation(compose.foundation)
|
||||||
implementation(compose.material3.core)
|
implementation(compose.material3.core)
|
||||||
implementation(compose.material.core)
|
|
||||||
implementation(compose.material.icons)
|
implementation(compose.material.icons)
|
||||||
implementation(compose.animation)
|
implementation(compose.animation)
|
||||||
implementation(compose.animation.graphics)
|
implementation(compose.animation.graphics)
|
||||||
@@ -254,7 +253,6 @@ dependencies {
|
|||||||
implementation(libs.logcat)
|
implementation(libs.logcat)
|
||||||
|
|
||||||
// Crash reports/analytics
|
// Crash reports/analytics
|
||||||
// implementation(libs.bundles.acra)
|
|
||||||
// "standardImplementation"(libs.firebase.analytics)
|
// "standardImplementation"(libs.firebase.analytics)
|
||||||
|
|
||||||
// Shizuku
|
// Shizuku
|
||||||
@@ -315,7 +313,6 @@ tasks {
|
|||||||
"-opt-in=androidx.compose.animation.ExperimentalAnimationApi",
|
"-opt-in=androidx.compose.animation.ExperimentalAnimationApi",
|
||||||
"-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi",
|
"-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi",
|
||||||
"-opt-in=coil3.annotation.ExperimentalCoilApi",
|
"-opt-in=coil3.annotation.ExperimentalCoilApi",
|
||||||
"-opt-in=com.google.accompanist.permissions.ExperimentalPermissionsApi",
|
|
||||||
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
|
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
|
||||||
"-opt-in=kotlinx.coroutines.FlowPreview",
|
"-opt-in=kotlinx.coroutines.FlowPreview",
|
||||||
"-opt-in=kotlinx.coroutines.InternalCoroutinesApi",
|
"-opt-in=kotlinx.coroutines.InternalCoroutinesApi",
|
||||||
|
|||||||
Vendored
+4
@@ -47,6 +47,10 @@
|
|||||||
-dontnote rx.internal.util.PlatformDependent
|
-dontnote rx.internal.util.PlatformDependent
|
||||||
##---------------End: proguard configuration for RxJava 1.x ----------
|
##---------------End: proguard configuration for RxJava 1.x ----------
|
||||||
|
|
||||||
|
##---------------Begin: proguard configuration for okhttp ----------
|
||||||
|
-keepclasseswithmembers class okhttp3.MultipartBody$Builder { *; }
|
||||||
|
##---------------End: proguard configuration for okhttp ----------
|
||||||
|
|
||||||
##---------------Begin: proguard configuration for kotlinx.serialization ----------
|
##---------------Begin: proguard configuration for kotlinx.serialization ----------
|
||||||
-keepattributes *Annotation*, InnerClasses
|
-keepattributes *Annotation*, InnerClasses
|
||||||
-dontnote kotlinx.serialization.** # core serialization annotations
|
-dontnote kotlinx.serialization.** # core serialization annotations
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ class DomainModule : InjektModule {
|
|||||||
addFactory { ToggleLanguage(get()) }
|
addFactory { ToggleLanguage(get()) }
|
||||||
addFactory { ToggleSource(get()) }
|
addFactory { ToggleSource(get()) }
|
||||||
addFactory { ToggleSourcePin(get()) }
|
addFactory { ToggleSourcePin(get()) }
|
||||||
addFactory { TrustExtension(get()) }
|
addFactory { TrustExtension(get(), get()) }
|
||||||
|
|
||||||
addSingletonFactory<ExtensionRepoRepository> { ExtensionRepoRepositoryImpl(get()) }
|
addSingletonFactory<ExtensionRepoRepository> { ExtensionRepoRepositoryImpl(get()) }
|
||||||
addFactory { ExtensionRepoService(get(), get()) }
|
addFactory { ExtensionRepoService(get(), get()) }
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ package eu.kanade.domain.base
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import dev.icerock.moko.resources.StringResource
|
import dev.icerock.moko.resources.StringResource
|
||||||
import eu.kanade.tachiyomi.util.system.isPreviewBuildType
|
|
||||||
import eu.kanade.tachiyomi.util.system.isReleaseBuildType
|
|
||||||
import tachiyomi.core.common.preference.Preference
|
import tachiyomi.core.common.preference.Preference
|
||||||
import tachiyomi.core.common.preference.PreferenceStore
|
import tachiyomi.core.common.preference.PreferenceStore
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
@@ -22,8 +20,6 @@ class BasePreferences(
|
|||||||
|
|
||||||
fun extensionInstaller() = ExtensionInstallerPreference(context, preferenceStore)
|
fun extensionInstaller() = ExtensionInstallerPreference(context, preferenceStore)
|
||||||
|
|
||||||
fun acraEnabled() = preferenceStore.getBoolean("acra.enable", isPreviewBuildType || isReleaseBuildType)
|
|
||||||
|
|
||||||
fun shownOnboardingFlow() = preferenceStore.getBoolean(Preference.appStateKey("onboarding_complete"), false)
|
fun shownOnboardingFlow() = preferenceStore.getBoolean(Preference.appStateKey("onboarding_complete"), false)
|
||||||
|
|
||||||
enum class ExtensionInstaller(val titleRes: StringResource, val requiresSystemPermission: Boolean) {
|
enum class ExtensionInstaller(val titleRes: StringResource, val requiresSystemPermission: Boolean) {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class GetExtensionsByType(
|
|||||||
extensionManager.installedExtensionsFlow,
|
extensionManager.installedExtensionsFlow,
|
||||||
extensionManager.untrustedExtensionsFlow,
|
extensionManager.untrustedExtensionsFlow,
|
||||||
extensionManager.availableExtensionsFlow,
|
extensionManager.availableExtensionsFlow,
|
||||||
) { _activeLanguages, _installed, _untrusted, _available ->
|
) { enabledLanguages, _installed, _untrusted, _available ->
|
||||||
val (updates, installed) = _installed
|
val (updates, installed) = _installed
|
||||||
.filter { (showNsfwSources || !it.isNsfw) }
|
.filter { (showNsfwSources || !it.isNsfw) }
|
||||||
.sortedWith(
|
.sortedWith(
|
||||||
@@ -41,9 +41,9 @@ class GetExtensionsByType(
|
|||||||
}
|
}
|
||||||
.flatMap { ext ->
|
.flatMap { ext ->
|
||||||
if (ext.sources.isEmpty()) {
|
if (ext.sources.isEmpty()) {
|
||||||
return@flatMap if (ext.lang in _activeLanguages) listOf(ext) else emptyList()
|
return@flatMap if (ext.lang in enabledLanguages) listOf(ext) else emptyList()
|
||||||
}
|
}
|
||||||
ext.sources.filter { it.lang in _activeLanguages }
|
ext.sources.filter { it.lang in enabledLanguages }
|
||||||
.map {
|
.map {
|
||||||
ext.copy(
|
ext.copy(
|
||||||
name = it.name,
|
name = it.name,
|
||||||
|
|||||||
@@ -3,15 +3,18 @@ package eu.kanade.domain.extension.interactor
|
|||||||
import android.content.pm.PackageInfo
|
import android.content.pm.PackageInfo
|
||||||
import androidx.core.content.pm.PackageInfoCompat
|
import androidx.core.content.pm.PackageInfoCompat
|
||||||
import eu.kanade.domain.source.service.SourcePreferences
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
|
import mihon.domain.extensionrepo.repository.ExtensionRepoRepository
|
||||||
import tachiyomi.core.common.preference.getAndSet
|
import tachiyomi.core.common.preference.getAndSet
|
||||||
|
|
||||||
class TrustExtension(
|
class TrustExtension(
|
||||||
|
private val extensionRepoRepository: ExtensionRepoRepository,
|
||||||
private val preferences: SourcePreferences,
|
private val preferences: SourcePreferences,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun isTrusted(pkgInfo: PackageInfo, signatureHash: String): Boolean {
|
suspend fun isTrusted(pkgInfo: PackageInfo, fingerprints: List<String>): Boolean {
|
||||||
val key = "${pkgInfo.packageName}:${PackageInfoCompat.getLongVersionCode(pkgInfo)}:$signatureHash"
|
val trustedFingerprints = extensionRepoRepository.getAll().map { it.signingKeyFingerprint }.toHashSet()
|
||||||
return key in preferences.trustedExtensions().get()
|
val key = "${pkgInfo.packageName}:${PackageInfoCompat.getLongVersionCode(pkgInfo)}:${fingerprints.last()}"
|
||||||
|
return trustedFingerprints.any { fingerprints.contains(it) } || key in preferences.trustedExtensions().get()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun trust(pkgName: String, versionCode: Long, signatureHash: String) {
|
fun trust(pkgName: String, versionCode: Long, signatureHash: String) {
|
||||||
@@ -19,9 +22,7 @@ class TrustExtension(
|
|||||||
// Remove previously trusted versions
|
// Remove previously trusted versions
|
||||||
val removed = exts.filterNot { it.startsWith("$pkgName:") }.toMutableSet()
|
val removed = exts.filterNot { it.startsWith("$pkgName:") }.toMutableSet()
|
||||||
|
|
||||||
removed.also {
|
removed.also { it += "$pkgName:$versionCode:$signatureHash" }
|
||||||
it += "$pkgName:$versionCode:$signatureHash"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import android.net.Uri
|
|||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.util.DisplayMetrics
|
import android.util.DisplayMetrics
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
@@ -362,10 +361,8 @@ private fun InfoText(
|
|||||||
primaryTextStyle: TextStyle = MaterialTheme.typography.bodyLarge,
|
primaryTextStyle: TextStyle = MaterialTheme.typography.bodyLarge,
|
||||||
onClick: (() -> Unit)? = null,
|
onClick: (() -> Unit)? = null,
|
||||||
) {
|
) {
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
|
||||||
|
|
||||||
val clickableModifier = if (onClick != null) {
|
val clickableModifier = if (onClick != null) {
|
||||||
Modifier.clickable(interactionSource, indication = null) { onClick() }
|
Modifier.clickable(interactionSource = null, indication = null, onClick = onClick)
|
||||||
} else {
|
} else {
|
||||||
Modifier
|
Modifier
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ import androidx.compose.animation.fadeOut
|
|||||||
import androidx.compose.animation.togetherWith
|
import androidx.compose.animation.togetherWith
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.Dp
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import cafe.adriel.voyager.core.annotation.InternalVoyagerApi
|
import cafe.adriel.voyager.core.annotation.InternalVoyagerApi
|
||||||
@@ -23,7 +21,6 @@ import tachiyomi.presentation.core.components.AdaptiveSheet as AdaptiveSheetImpl
|
|||||||
@Composable
|
@Composable
|
||||||
fun NavigatorAdaptiveSheet(
|
fun NavigatorAdaptiveSheet(
|
||||||
screen: Screen,
|
screen: Screen,
|
||||||
tonalElevation: Dp = 1.dp,
|
|
||||||
enableSwipeDismiss: (Navigator) -> Boolean = { true },
|
enableSwipeDismiss: (Navigator) -> Boolean = { true },
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
) {
|
) {
|
||||||
@@ -31,7 +28,6 @@ fun NavigatorAdaptiveSheet(
|
|||||||
screen = screen,
|
screen = screen,
|
||||||
content = { sheetNavigator ->
|
content = { sheetNavigator ->
|
||||||
AdaptiveSheet(
|
AdaptiveSheet(
|
||||||
tonalElevation = tonalElevation,
|
|
||||||
enableSwipeDismiss = enableSwipeDismiss(sheetNavigator),
|
enableSwipeDismiss = enableSwipeDismiss(sheetNavigator),
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
) {
|
) {
|
||||||
@@ -73,7 +69,6 @@ fun NavigatorAdaptiveSheet(
|
|||||||
fun AdaptiveSheet(
|
fun AdaptiveSheet(
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
tonalElevation: Dp = 1.dp,
|
|
||||||
enableSwipeDismiss: Boolean = true,
|
enableSwipeDismiss: Boolean = true,
|
||||||
content: @Composable () -> Unit,
|
content: @Composable () -> Unit,
|
||||||
) {
|
) {
|
||||||
@@ -86,7 +81,6 @@ fun AdaptiveSheet(
|
|||||||
AdaptiveSheetImpl(
|
AdaptiveSheetImpl(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
isTabletUi = isTabletUi,
|
isTabletUi = isTabletUi,
|
||||||
tonalElevation = tonalElevation,
|
|
||||||
enableSwipeDismiss = enableSwipeDismiss,
|
enableSwipeDismiss = enableSwipeDismiss,
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||||||
import androidx.compose.foundation.text.BasicTextField
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
import androidx.compose.foundation.text.KeyboardActions
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.material.TextFieldDefaults
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.outlined.ArrowBack
|
import androidx.compose.material.icons.automirrored.outlined.ArrowBack
|
||||||
import androidx.compose.material.icons.outlined.Close
|
import androidx.compose.material.icons.outlined.Close
|
||||||
@@ -21,6 +20,7 @@ import androidx.compose.material3.LocalContentColor
|
|||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.PlainTooltip
|
import androidx.compose.material3.PlainTooltip
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextFieldDefaults
|
||||||
import androidx.compose.material3.TooltipBox
|
import androidx.compose.material3.TooltipBox
|
||||||
import androidx.compose.material3.TooltipDefaults
|
import androidx.compose.material3.TooltipDefaults
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
@@ -312,7 +312,7 @@ fun SearchToolbar(
|
|||||||
visualTransformation = visualTransformation,
|
visualTransformation = visualTransformation,
|
||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
decorationBox = { innerTextField ->
|
decorationBox = { innerTextField ->
|
||||||
TextFieldDefaults.TextFieldDecorationBox(
|
TextFieldDefaults.DecorationBox(
|
||||||
value = searchQuery,
|
value = searchQuery,
|
||||||
innerTextField = innerTextField,
|
innerTextField = innerTextField,
|
||||||
enabled = true,
|
enabled = true,
|
||||||
@@ -331,6 +331,7 @@ fun SearchToolbar(
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
container = {},
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ fun TabbedDialog(
|
|||||||
PrimaryTabRow(
|
PrimaryTabRow(
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
selectedTabIndex = pagerState.currentPage,
|
selectedTabIndex = pagerState.currentPage,
|
||||||
|
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||||
divider = {},
|
divider = {},
|
||||||
) {
|
) {
|
||||||
tabTitles.fastForEachIndexed { index, tab ->
|
tabTitles.fastForEachIndexed { index, tab ->
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ fun CrashScreen(
|
|||||||
acceptText = stringResource(MR.strings.pref_dump_crash_logs),
|
acceptText = stringResource(MR.strings.pref_dump_crash_logs),
|
||||||
onAcceptClick = {
|
onAcceptClick = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
CrashLogUtil(context).dumpLogs()
|
CrashLogUtil(context).dumpLogs(exception)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
rejectText = stringResource(MR.strings.crash_screen_restart_application),
|
rejectText = stringResource(MR.strings.crash_screen_restart_application),
|
||||||
|
|||||||
+1
-1
@@ -9,13 +9,13 @@ import androidx.compose.material.icons.Icons
|
|||||||
import androidx.compose.material.icons.filled.CheckCircle
|
import androidx.compose.material.icons.filled.CheckCircle
|
||||||
import androidx.compose.material.icons.outlined.ArrowDownward
|
import androidx.compose.material.icons.outlined.ArrowDownward
|
||||||
import androidx.compose.material.icons.outlined.ErrorOutline
|
import androidx.compose.material.icons.outlined.ErrorOutline
|
||||||
import androidx.compose.material.ripple
|
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.ProgressIndicatorDefaults
|
import androidx.compose.material3.ProgressIndicatorDefaults
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.ripple
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import androidx.compose.animation.fadeIn
|
|||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.animation.shrinkVertically
|
import androidx.compose.animation.shrinkVertically
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
@@ -33,12 +32,12 @@ import androidx.compose.material.icons.outlined.Label
|
|||||||
import androidx.compose.material.icons.outlined.MoreVert
|
import androidx.compose.material.icons.outlined.MoreVert
|
||||||
import androidx.compose.material.icons.outlined.RemoveDone
|
import androidx.compose.material.icons.outlined.RemoveDone
|
||||||
import androidx.compose.material.icons.outlined.SwapCalls
|
import androidx.compose.material.icons.outlined.SwapCalls
|
||||||
import androidx.compose.material.ripple
|
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.ripple
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateListOf
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
@@ -90,7 +89,7 @@ fun MangaBottomActionMenu(
|
|||||||
Surface(
|
Surface(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
shape = MaterialTheme.shapes.large.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize),
|
shape = MaterialTheme.shapes.large.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize),
|
||||||
tonalElevation = 3.dp,
|
color = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||||
) {
|
) {
|
||||||
val haptic = LocalHapticFeedback.current
|
val haptic = LocalHapticFeedback.current
|
||||||
val confirm = remember { mutableStateListOf(false, false, false, false, false, false, false) }
|
val confirm = remember { mutableStateListOf(false, false, false, false, false, false, false) }
|
||||||
@@ -199,7 +198,7 @@ private fun RowScope.Button(
|
|||||||
.size(48.dp)
|
.size(48.dp)
|
||||||
.weight(animatedWeight)
|
.weight(animatedWeight)
|
||||||
.combinedClickable(
|
.combinedClickable(
|
||||||
interactionSource = remember { MutableInteractionSource() },
|
interactionSource = null,
|
||||||
indication = ripple(bounded = false),
|
indication = ripple(bounded = false),
|
||||||
onLongClick = onLongClick,
|
onLongClick = onLongClick,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
@@ -252,7 +251,7 @@ fun LibraryBottomActionMenu(
|
|||||||
Surface(
|
Surface(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
shape = MaterialTheme.shapes.large.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize),
|
shape = MaterialTheme.shapes.large.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize),
|
||||||
tonalElevation = 3.dp,
|
color = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||||
) {
|
) {
|
||||||
val haptic = LocalHapticFeedback.current
|
val haptic = LocalHapticFeedback.current
|
||||||
val confirm =
|
val confirm =
|
||||||
|
|||||||
+3
@@ -1,6 +1,7 @@
|
|||||||
package eu.kanade.presentation.more.settings.screen.browse.components
|
package eu.kanade.presentation.more.settings.screen.browse.components
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@@ -14,6 +15,7 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.focus.focusRequester
|
import androidx.compose.ui.focus.focusRequester
|
||||||
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
import kotlinx.collections.immutable.ImmutableSet
|
import kotlinx.collections.immutable.ImmutableSet
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import mihon.domain.extensionrepo.model.ExtensionRepo
|
import mihon.domain.extensionrepo.model.ExtensionRepo
|
||||||
@@ -74,6 +76,7 @@ fun ExtensionRepoCreateDialog(
|
|||||||
Text(text = stringResource(msgRes))
|
Text(text = stringResource(msgRes))
|
||||||
},
|
},
|
||||||
isError = name.isNotEmpty() && nameAlreadyExists,
|
isError = name.isNotEmpty() && nameAlreadyExists,
|
||||||
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Uri),
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -223,13 +223,12 @@ fun AppThemePreviewItem(
|
|||||||
contentAlignment = Alignment.BottomCenter,
|
contentAlignment = Alignment.BottomCenter,
|
||||||
) {
|
) {
|
||||||
Surface(
|
Surface(
|
||||||
tonalElevation = 3.dp,
|
color = MaterialTheme.colorScheme.surfaceContainer,
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.height(32.dp)
|
.height(32.dp)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.background(MaterialTheme.colorScheme.surfaceVariant)
|
|
||||||
.padding(horizontal = 8.dp),
|
.padding(horizontal = 8.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -43,9 +43,7 @@ fun ReaderPageActionsDialog(
|
|||||||
var useExtraPage by remember { mutableStateOf(false) }
|
var useExtraPage by remember { mutableStateOf(false) }
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
AdaptiveSheet(
|
AdaptiveSheet(onDismissRequest = onDismissRequest) {
|
||||||
onDismissRequest = onDismissRequest,
|
|
||||||
) {
|
|
||||||
Column(modifier = Modifier.padding(vertical = 16.dp)) {
|
Column(modifier = Modifier.padding(vertical = 16.dp)) {
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
|
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ internal abstract class BaseColorScheme {
|
|||||||
abstract val darkScheme: ColorScheme
|
abstract val darkScheme: ColorScheme
|
||||||
abstract val lightScheme: ColorScheme
|
abstract val lightScheme: ColorScheme
|
||||||
|
|
||||||
|
// Cannot be pure black as there's content scrolling behind it
|
||||||
|
// https://m3.material.io/components/navigation-bar/guidelines#90615a71-607e-485e-9e09-778bfc080563
|
||||||
|
private val surfaceContainer = Color(0xFF0C0C0C)
|
||||||
|
private val surfaceContainerHigh = Color(0xFF131313)
|
||||||
|
private val surfaceContainerHighest = Color(0xFF1B1B1B)
|
||||||
|
|
||||||
fun getColorScheme(isDark: Boolean, isAmoled: Boolean): ColorScheme {
|
fun getColorScheme(isDark: Boolean, isAmoled: Boolean): ColorScheme {
|
||||||
if (!isDark) return lightScheme
|
if (!isDark) return lightScheme
|
||||||
|
|
||||||
@@ -18,6 +24,12 @@ internal abstract class BaseColorScheme {
|
|||||||
onBackground = Color.White,
|
onBackground = Color.White,
|
||||||
surface = Color.Black,
|
surface = Color.Black,
|
||||||
onSurface = Color.White,
|
onSurface = Color.White,
|
||||||
|
surfaceVariant = surfaceContainer, // Navigation bar background (ThemePrefWidget)
|
||||||
|
surfaceContainerLowest = surfaceContainer,
|
||||||
|
surfaceContainerLow = surfaceContainer,
|
||||||
|
surfaceContainer = surfaceContainer, // Navigation bar background
|
||||||
|
surfaceContainerHigh = surfaceContainerHigh,
|
||||||
|
surfaceContainerHighest = surfaceContainerHighest,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+67
-43
@@ -19,53 +19,77 @@ internal object GreenAppleColorScheme : BaseColorScheme() {
|
|||||||
|
|
||||||
override val darkScheme = darkColorScheme(
|
override val darkScheme = darkColorScheme(
|
||||||
primary = Color(0xFF7ADB8F),
|
primary = Color(0xFF7ADB8F),
|
||||||
onPrimary = Color(0xFF003915),
|
onPrimary = Color(0xFF003917),
|
||||||
primaryContainer = Color(0xFF005322),
|
primaryContainer = Color(0xFF017737),
|
||||||
onPrimaryContainer = Color(0xFF96F8A9),
|
onPrimaryContainer = Color(0xFFFFFFFF),
|
||||||
inversePrimary = Color(0xFF006D2F),
|
secondary = Color(0xFF7ADB8F), // Unread badge
|
||||||
secondary = Color(0xFF7ADB8F),
|
onSecondary = Color(0xFF003917), // Unread badge text
|
||||||
onSecondary = Color(0xFF003915),
|
secondaryContainer = Color(0xFF017737), // Navigation bar selector pill & progress indicator (remaining)
|
||||||
secondaryContainer = Color(0xFF005322),
|
onSecondaryContainer = Color(0xFFFFFFFF), // Navigation bar selected icon
|
||||||
onSecondaryContainer = Color(0xFF96F8A9),
|
tertiary = Color(0xFFFFB3AC), // Downloaded badge
|
||||||
tertiary = Color(0xFFFFB3AA),
|
onTertiary = Color(0xFF680008), // Downloaded badge text
|
||||||
onTertiary = Color(0xFF680006),
|
tertiaryContainer = Color(0xFFC7282A),
|
||||||
tertiaryContainer = Color(0xFF93000D),
|
onTertiaryContainer = Color(0xFFFFFFFF),
|
||||||
onTertiaryContainer = Color(0xFFFFDAD5),
|
error = Color(0xFFFFB4AB),
|
||||||
background = Color(0xFF1A1C19),
|
onError = Color(0xFF690005),
|
||||||
onBackground = Color(0xFFE1E3DD),
|
errorContainer = Color(0xFF93000A),
|
||||||
surface = Color(0xFF1A1C19),
|
onErrorContainer = Color(0xFFFFDAD6),
|
||||||
onSurface = Color(0xFFE1E3DD),
|
background = Color(0xFF0F1510),
|
||||||
surfaceVariant = Color(0xFF414941),
|
onBackground = Color(0xFFDFE4DB),
|
||||||
onSurfaceVariant = Color(0xFFC1C8BE),
|
surface = Color(0xFF0F1510),
|
||||||
surfaceTint = Color(0xFF7ADB8F),
|
onSurface = Color(0xFFDFE4DB),
|
||||||
inverseSurface = Color(0xFFE1E3DD),
|
surfaceVariant = Color(0xFF3F493F), // Navigation bar background (ThemePrefWidget)
|
||||||
inverseOnSurface = Color(0xFF1A1C19),
|
onSurfaceVariant = Color(0xFFBECABC),
|
||||||
outline = Color(0xFF8B9389),
|
outline = Color(0xFF889487),
|
||||||
|
outlineVariant = Color(0xFF3F493F),
|
||||||
|
scrim = Color(0xFF000000),
|
||||||
|
inverseSurface = Color(0xFFDFE4DB),
|
||||||
|
inverseOnSurface = Color(0xFF2C322C),
|
||||||
|
inversePrimary = Color(0xFF006D32),
|
||||||
|
surfaceDim = Color(0xFF0F1510),
|
||||||
|
surfaceBright = Color(0xFF353B35),
|
||||||
|
surfaceContainerLowest = Color(0xFF0A0F0B),
|
||||||
|
surfaceContainerLow = Color(0xFF181D18),
|
||||||
|
surfaceContainer = Color(0xFF1C211C), // Navigation bar background
|
||||||
|
surfaceContainerHigh = Color(0xFF262B26),
|
||||||
|
surfaceContainerHighest = Color(0xFF313630),
|
||||||
)
|
)
|
||||||
|
|
||||||
override val lightScheme = lightColorScheme(
|
override val lightScheme = lightColorScheme(
|
||||||
primary = Color(0xFF006D2F),
|
primary = Color(0xFF005927),
|
||||||
onPrimary = Color(0xFFFFFFFF),
|
onPrimary = Color(0xFFFFFFFF),
|
||||||
primaryContainer = Color(0xFF96F8A9),
|
primaryContainer = Color(0xFF188140),
|
||||||
onPrimaryContainer = Color(0xFF002109),
|
onPrimaryContainer = Color(0xFFFFFFFF),
|
||||||
|
secondary = Color(0xFF005927), // Unread badge
|
||||||
|
onSecondary = Color(0xFFFFFFFF), // Unread badge text
|
||||||
|
secondaryContainer = Color(0xFF97f7a9), // Navigation bar selector pill & progress indicator (remaining)
|
||||||
|
onSecondaryContainer = Color(0xFF000000), // Navigation bar selected icon
|
||||||
|
tertiary = Color(0xFF9D0012), // Downloaded badge
|
||||||
|
onTertiary = Color(0xFFFFFFFF), // Downloaded badge text
|
||||||
|
tertiaryContainer = Color(0xFFD33131),
|
||||||
|
onTertiaryContainer = Color(0xFFFFFFFF),
|
||||||
|
error = Color(0xFFBA1A1A),
|
||||||
|
onError = Color(0xFFFFFFFF),
|
||||||
|
errorContainer = Color(0xFFFFDAD6),
|
||||||
|
onErrorContainer = Color(0xFF410002),
|
||||||
|
background = Color(0xFFF6FBF2),
|
||||||
|
onBackground = Color(0xFF181D18),
|
||||||
|
surface = Color(0xFFF6FBF2),
|
||||||
|
onSurface = Color(0xFF181D18),
|
||||||
|
surfaceVariant = Color(0xFFDAE6D7), // Navigation bar background (ThemePrefWidget)
|
||||||
|
onSurfaceVariant = Color(0xFF3F493F),
|
||||||
|
outline = Color(0xFF6F7A6E),
|
||||||
|
outlineVariant = Color(0xFFBECABC),
|
||||||
|
scrim = Color(0xFF000000),
|
||||||
|
inverseSurface = Color(0xFF2C322C),
|
||||||
|
inverseOnSurface = Color(0xFFEDF2E9),
|
||||||
inversePrimary = Color(0xFF7ADB8F),
|
inversePrimary = Color(0xFF7ADB8F),
|
||||||
secondary = Color(0xFF006D2F),
|
surfaceDim = Color(0xFFD6DCD3),
|
||||||
onSecondary = Color(0xFFFFFFFF),
|
surfaceBright = Color(0xFFF6FBF2),
|
||||||
secondaryContainer = Color(0xFF96F8A9),
|
surfaceContainerLowest = Color(0xFFFFFFFF),
|
||||||
onSecondaryContainer = Color(0xFF002109),
|
surfaceContainerLow = Color(0xFFF0F5EC),
|
||||||
tertiary = Color(0xFFB91D22),
|
surfaceContainer = Color(0xFFEAEFE6), // Navigation bar background
|
||||||
onTertiary = Color(0xFFFFFFFF),
|
surfaceContainerHigh = Color(0xFFE4EAE1),
|
||||||
tertiaryContainer = Color(0xFFFFDAD5),
|
surfaceContainerHighest = Color(0xFFDFE4DB),
|
||||||
onTertiaryContainer = Color(0xFF410003),
|
|
||||||
background = Color(0xFFFBFDF7),
|
|
||||||
onBackground = Color(0xFF1A1C19),
|
|
||||||
surface = Color(0xFFFBFDF7),
|
|
||||||
onSurface = Color(0xFF1A1C19),
|
|
||||||
surfaceVariant = Color(0xFFDDE5DA),
|
|
||||||
onSurfaceVariant = Color(0xFF414941),
|
|
||||||
surfaceTint = Color(0xFF006D2F),
|
|
||||||
inverseSurface = Color(0xFF2F312E),
|
|
||||||
inverseOnSurface = Color(0xFFF0F2EC),
|
|
||||||
outline = Color(0xFF717970),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,53 +18,77 @@ internal object LavenderColorScheme : BaseColorScheme() {
|
|||||||
|
|
||||||
override val darkScheme = darkColorScheme(
|
override val darkScheme = darkColorScheme(
|
||||||
primary = Color(0xFFA177FF),
|
primary = Color(0xFFA177FF),
|
||||||
onPrimary = Color(0xFF111129),
|
onPrimary = Color(0xFF3D0090),
|
||||||
primaryContainer = Color(0xFFA177FF),
|
primaryContainer = Color(0xFFA177FF),
|
||||||
onPrimaryContainer = Color(0xFF111129),
|
onPrimaryContainer = Color(0xFFFFFFFF),
|
||||||
inversePrimary = Color(0xFF006D2F),
|
secondary = Color(0xFFA177FF), // Unread badge
|
||||||
secondary = Color(0xFFA177FF),
|
onSecondary = Color(0xFFFFFFFF), // Unread badge text
|
||||||
onSecondary = Color(0xFF111129),
|
secondaryContainer = Color(0xFF423271), // Navigation bar selector pill & progress indicator (remaining)
|
||||||
secondaryContainer = Color(0xFFA177FF),
|
onSecondaryContainer = Color(0xFFA177FF), // Navigation bar selected icon
|
||||||
onSecondaryContainer = Color(0xFF111129),
|
tertiary = Color(0xFFCDBDFF), // Downloaded badge
|
||||||
tertiary = Color(0xFF5E25E1),
|
onTertiary = Color(0xFF360096), // Downloaded badge text
|
||||||
onTertiary = Color(0xFFE8E8E8),
|
tertiaryContainer = Color(0xFF5512D8),
|
||||||
tertiaryContainer = Color(0xFF111129),
|
onTertiaryContainer = Color(0xFFEFE6FF),
|
||||||
onTertiaryContainer = Color(0xFFDEE8FF),
|
error = Color(0xFFFFB4AB),
|
||||||
|
onError = Color(0xFF690005),
|
||||||
|
errorContainer = Color(0xFF93000A),
|
||||||
|
onErrorContainer = Color(0xFFFFDAD6),
|
||||||
background = Color(0xFF111129),
|
background = Color(0xFF111129),
|
||||||
onBackground = Color(0xFFDEE8FF),
|
onBackground = Color(0xFFE7E0EC),
|
||||||
surface = Color(0xFF111129),
|
surface = Color(0xFF111129),
|
||||||
onSurface = Color(0xFFDEE8FF),
|
onSurface = Color(0xFFE7E0EC),
|
||||||
surfaceVariant = Color(0x2CB6B6B6),
|
surfaceVariant = Color(0xFF3D2F6B), // Navigation bar background (ThemePrefWidget)
|
||||||
onSurfaceVariant = Color(0xFFE8E8E8),
|
onSurfaceVariant = Color(0xFFCBC3D6),
|
||||||
surfaceTint = Color(0xFFA177FF),
|
outline = Color(0xFF958E9F),
|
||||||
inverseSurface = Color(0xFF221247),
|
outlineVariant = Color(0xFF4A4453),
|
||||||
inverseOnSurface = Color(0xFFDEE8FF),
|
scrim = Color(0xFF000000),
|
||||||
outline = Color(0xA8905FFF),
|
inverseSurface = Color(0xFFE7E0EC),
|
||||||
|
inverseOnSurface = Color(0xFF322F38),
|
||||||
|
inversePrimary = Color(0xFF6D41C8),
|
||||||
|
surfaceDim = Color(0xFF111129),
|
||||||
|
surfaceBright = Color(0xFF3B3841),
|
||||||
|
surfaceContainerLowest = Color(0xFF15132d),
|
||||||
|
surfaceContainerLow = Color(0xFF171531),
|
||||||
|
surfaceContainer = Color(0xFF1D193B), // Navigation bar background
|
||||||
|
surfaceContainerHigh = Color(0xFF241f41),
|
||||||
|
surfaceContainerHighest = Color(0xFF282446),
|
||||||
)
|
)
|
||||||
|
|
||||||
override val lightScheme = lightColorScheme(
|
override val lightScheme = lightColorScheme(
|
||||||
primary = Color(0xFF7B46AF),
|
primary = Color(0xFF6D41C8),
|
||||||
onPrimary = Color(0xFFEDE2FF),
|
onPrimary = Color(0xFFFFFFFF),
|
||||||
primaryContainer = Color(0xFF7B46AF),
|
primaryContainer = Color(0xFF7B46AF),
|
||||||
onPrimaryContainer = Color(0xFFEDE2FF),
|
onPrimaryContainer = Color(0xFF130038),
|
||||||
inversePrimary = Color(0xFFD6BAFF),
|
secondary = Color(0xFF7B46AF), // Unread badge
|
||||||
secondary = Color(0xFF7B46AF),
|
onSecondary = Color(0xFFEDE2FF), // Unread badge text
|
||||||
onSecondary = Color(0xFFEDE2FF),
|
secondaryContainer = Color(0xFFC9B0E6), // Navigation bar selector pill & progress indicator (remaining)
|
||||||
secondaryContainer = Color(0xFF7B46AF),
|
onSecondaryContainer = Color(0xFF7B46AF), // Navigation bar selector icon
|
||||||
onSecondaryContainer = Color(0xFFEDE2FF),
|
tertiary = Color(0xFFEDE2FF), // Downloaded badge
|
||||||
tertiary = Color(0xFFEDE2FF),
|
onTertiary = Color(0xFF7B46AF), // Downloaded badge text
|
||||||
onTertiary = Color(0xFF7B46AF),
|
tertiaryContainer = Color(0xFF6D3BF0),
|
||||||
tertiaryContainer = Color(0xFFEDE2FF),
|
onTertiaryContainer = Color(0xFFFFFFFF),
|
||||||
onTertiaryContainer = Color(0xFF7B46AF),
|
error = Color(0xFFBA1A1A),
|
||||||
|
onError = Color(0xFFFFFFFF),
|
||||||
|
errorContainer = Color(0xFFFFDAD6),
|
||||||
|
onErrorContainer = Color(0xFF410002),
|
||||||
background = Color(0xFFEDE2FF),
|
background = Color(0xFFEDE2FF),
|
||||||
onBackground = Color(0xFF1B1B22),
|
onBackground = Color(0xFF1D1A22),
|
||||||
surface = Color(0xFFEDE2FF),
|
surface = Color(0xFFEDE2FF),
|
||||||
onSurface = Color(0xFF1B1B22),
|
onSurface = Color(0xFF1D1A22),
|
||||||
surfaceVariant = Color(0xFFB9B0CC),
|
surfaceVariant = Color(0xFFE4D5F8), // Navigation bar background (ThemePrefWidget)
|
||||||
onSurfaceVariant = Color(0xD849454E),
|
onSurfaceVariant = Color(0xFF4A4453),
|
||||||
surfaceTint = Color(0xFF7B46AF),
|
outline = Color(0xFF7B7485),
|
||||||
inverseSurface = Color(0xFF313033),
|
outlineVariant = Color(0xFFCBC3D6),
|
||||||
inverseOnSurface = Color(0xFFF3EFF4),
|
scrim = Color(0xFF000000),
|
||||||
outline = Color(0xFF7B46AF),
|
inverseSurface = Color(0xFF322F38),
|
||||||
|
inverseOnSurface = Color(0xFFF5EEFA),
|
||||||
|
inversePrimary = Color(0xFFA177FF),
|
||||||
|
surfaceDim = Color(0xFFDED7E3),
|
||||||
|
surfaceBright = Color(0xFFEDE2FF),
|
||||||
|
surfaceContainerLowest = Color(0xFFDACCEC),
|
||||||
|
surfaceContainerLow = Color(0xFFDED0F1),
|
||||||
|
surfaceContainer = Color(0xFFE4D5F8), // Navigation bar background
|
||||||
|
surfaceContainerHigh = Color(0xFFEADCFD),
|
||||||
|
surfaceContainerHighest = Color(0xFFEEE2FF),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-14
@@ -23,24 +23,29 @@ internal object MidnightDuskColorScheme : BaseColorScheme() {
|
|||||||
primaryContainer = Color(0xFFBD1C5C),
|
primaryContainer = Color(0xFFBD1C5C),
|
||||||
onPrimaryContainer = Color(0xFFFFFFFF),
|
onPrimaryContainer = Color(0xFFFFFFFF),
|
||||||
inversePrimary = Color(0xFFF02475),
|
inversePrimary = Color(0xFFF02475),
|
||||||
secondary = Color(0xFFF02475),
|
secondary = Color(0xFFF02475), // Unread badge
|
||||||
onSecondary = Color(0xFFFFFFFF),
|
onSecondary = Color(0xFF16151D), // Unread badge text
|
||||||
secondaryContainer = Color(0xFFF02475),
|
secondaryContainer = Color(0xFF66183C), // Navigation bar selector pill & progress indicator (remaining)
|
||||||
onSecondaryContainer = Color(0xFFFFFFFF),
|
onSecondaryContainer = Color(0xFFF02475), // Navigation bar selector icon
|
||||||
tertiary = Color(0xFF55971C),
|
tertiary = Color(0xFF55971C), // Downloaded badge
|
||||||
onTertiary = Color(0xFFFFFFFF),
|
onTertiary = Color(0xFF16151D), // Downloaded badge text
|
||||||
tertiaryContainer = Color(0xFF386412),
|
tertiaryContainer = Color(0xFF386412),
|
||||||
onTertiaryContainer = Color(0xFFE5E1E5),
|
onTertiaryContainer = Color(0xFFE5E1E5),
|
||||||
background = Color(0xFF16151D),
|
background = Color(0xFF16151D),
|
||||||
onBackground = Color(0xFFE5E1E5),
|
onBackground = Color(0xFFE5E1E5),
|
||||||
surface = Color(0xFF16151D),
|
surface = Color(0xFF16151D),
|
||||||
onSurface = Color(0xFFE5E1E5),
|
onSurface = Color(0xFFE5E1E5),
|
||||||
surfaceVariant = Color(0xFF524346),
|
surfaceVariant = Color(0xFF281624), // Navigation bar background (ThemePrefWidget)
|
||||||
onSurfaceVariant = Color(0xFFD6C1C4),
|
onSurfaceVariant = Color(0xFFD6C1C4),
|
||||||
surfaceTint = Color(0xFFF02475),
|
surfaceTint = Color(0xFFF02475),
|
||||||
inverseSurface = Color(0xFF333043),
|
inverseSurface = Color(0xFF333043),
|
||||||
inverseOnSurface = Color(0xFFFFFFFF),
|
inverseOnSurface = Color(0xFFFFFFFF),
|
||||||
outline = Color(0xFF9F8C8F),
|
outline = Color(0xFF9F8C8F),
|
||||||
|
surfaceContainerLowest = Color(0xFF221320),
|
||||||
|
surfaceContainerLow = Color(0xFF251522),
|
||||||
|
surfaceContainer = Color(0xFF281624), // Navigation bar background
|
||||||
|
surfaceContainerHigh = Color(0xFF2D1C2A),
|
||||||
|
surfaceContainerHighest = Color(0xFF2F1F2C),
|
||||||
)
|
)
|
||||||
|
|
||||||
override val lightScheme = lightColorScheme(
|
override val lightScheme = lightColorScheme(
|
||||||
@@ -49,23 +54,28 @@ internal object MidnightDuskColorScheme : BaseColorScheme() {
|
|||||||
primaryContainer = Color(0xFFFFD9E1),
|
primaryContainer = Color(0xFFFFD9E1),
|
||||||
onPrimaryContainer = Color(0xFF3F0017),
|
onPrimaryContainer = Color(0xFF3F0017),
|
||||||
inversePrimary = Color(0xFFFFB1C4),
|
inversePrimary = Color(0xFFFFB1C4),
|
||||||
secondary = Color(0xFFBB0054),
|
secondary = Color(0xFFBB0054), // Unread badge
|
||||||
onSecondary = Color(0xFFFFFFFF),
|
onSecondary = Color(0xFFFFFFFF), // Unread badge text
|
||||||
secondaryContainer = Color(0xFFFFD9E1),
|
secondaryContainer = Color(0xFFEFBAD4), // Navigation bar selector pill & progress indicator (remaining)
|
||||||
onSecondaryContainer = Color(0xFF3F0017),
|
onSecondaryContainer = Color(0xFFD1377C), // Navigation bar selector icon
|
||||||
tertiary = Color(0xFF006638),
|
tertiary = Color(0xFF006638), // Downloaded badge
|
||||||
onTertiary = Color(0xFFFFFFFF),
|
onTertiary = Color(0xFFFFFFFF), // Downloaded badge text
|
||||||
tertiaryContainer = Color(0xFF00894b),
|
tertiaryContainer = Color(0xFF00894b),
|
||||||
onTertiaryContainer = Color(0xFF2D1600),
|
onTertiaryContainer = Color(0xFF2D1600),
|
||||||
background = Color(0xFFFFFBFF),
|
background = Color(0xFFFFFBFF),
|
||||||
onBackground = Color(0xFF1C1B1F),
|
onBackground = Color(0xFF1C1B1F),
|
||||||
surface = Color(0xFFFFFBFF),
|
surface = Color(0xFFFFFBFF),
|
||||||
onSurface = Color(0xFF1C1B1F),
|
onSurface = Color(0xFF1C1B1F),
|
||||||
surfaceVariant = Color(0xFFF3DDE0),
|
surfaceVariant = Color(0xFFF9E6F1), // Navigation bar background (ThemePrefWidget)
|
||||||
onSurfaceVariant = Color(0xFF524346),
|
onSurfaceVariant = Color(0xFF524346),
|
||||||
surfaceTint = Color(0xFFBB0054),
|
surfaceTint = Color(0xFFBB0054),
|
||||||
inverseSurface = Color(0xFF313033),
|
inverseSurface = Color(0xFF313033),
|
||||||
inverseOnSurface = Color(0xFFF4F0F4),
|
inverseOnSurface = Color(0xFFF4F0F4),
|
||||||
outline = Color(0xFF847376),
|
outline = Color(0xFF847376),
|
||||||
|
surfaceContainerLowest = Color(0xFFDAC0CD),
|
||||||
|
surfaceContainerLow = Color(0xFFE8D1DD),
|
||||||
|
surfaceContainer = Color(0xFFF9E6F1), // Navigation bar background
|
||||||
|
surfaceContainerHigh = Color(0xFFFCF3F8),
|
||||||
|
surfaceContainerHighest = Color(0xFFFEF9FC),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,19 +17,19 @@ internal object NordColorScheme : BaseColorScheme() {
|
|||||||
primaryContainer = Color(0xFF88C0D0),
|
primaryContainer = Color(0xFF88C0D0),
|
||||||
onPrimaryContainer = Color(0xFF2E3440),
|
onPrimaryContainer = Color(0xFF2E3440),
|
||||||
inversePrimary = Color(0xFF397E91),
|
inversePrimary = Color(0xFF397E91),
|
||||||
secondary = Color(0xFF81A1C1),
|
secondary = Color(0xFF81A1C1), // Unread badge
|
||||||
onSecondary = Color(0xFF2E3440),
|
onSecondary = Color(0xFF2E3440), // Unread badge text
|
||||||
secondaryContainer = Color(0xFF81A1C1),
|
secondaryContainer = Color(0xFF81A1C1), // Navigation bar selector pill & progress indicator (remaining)
|
||||||
onSecondaryContainer = Color(0xFF2E3440),
|
onSecondaryContainer = Color(0xFF2E3440), // Navigation bar selector icon
|
||||||
tertiary = Color(0xFF5E81AC),
|
tertiary = Color(0xFF5E81AC), // Downloaded badge
|
||||||
onTertiary = Color(0xFF000000),
|
onTertiary = Color(0xFF000000), // Downloaded badge text
|
||||||
tertiaryContainer = Color(0xFF5E81AC),
|
tertiaryContainer = Color(0xFF5E81AC),
|
||||||
onTertiaryContainer = Color(0xFF000000),
|
onTertiaryContainer = Color(0xFF000000),
|
||||||
background = Color(0xFF2E3440),
|
background = Color(0xFF2E3440),
|
||||||
onBackground = Color(0xFFECEFF4),
|
onBackground = Color(0xFFECEFF4),
|
||||||
surface = Color(0xFF3B4252),
|
surface = Color(0xFF2E3440),
|
||||||
onSurface = Color(0xFFECEFF4),
|
onSurface = Color(0xFFECEFF4),
|
||||||
surfaceVariant = Color(0xFF2E3440),
|
surfaceVariant = Color(0xFF414C5C), // Navigation bar background (ThemePrefWidget)
|
||||||
onSurfaceVariant = Color(0xFFECEFF4),
|
onSurfaceVariant = Color(0xFFECEFF4),
|
||||||
surfaceTint = Color(0xFF88C0D0),
|
surfaceTint = Color(0xFF88C0D0),
|
||||||
inverseSurface = Color(0xFFD8DEE9),
|
inverseSurface = Color(0xFFD8DEE9),
|
||||||
@@ -39,6 +39,11 @@ internal object NordColorScheme : BaseColorScheme() {
|
|||||||
onError = Color(0xFF2E3440),
|
onError = Color(0xFF2E3440),
|
||||||
errorContainer = Color(0xFFBF616A),
|
errorContainer = Color(0xFFBF616A),
|
||||||
onErrorContainer = Color(0xFF000000),
|
onErrorContainer = Color(0xFF000000),
|
||||||
|
surfaceContainerLowest = Color(0xFF373F4D),
|
||||||
|
surfaceContainerLow = Color(0xFF3E4756),
|
||||||
|
surfaceContainer = Color(0xFF414C5C),
|
||||||
|
surfaceContainerHigh = Color(0xFF4E5766),
|
||||||
|
surfaceContainerHighest = Color(0xFF505968), // Navigation bar background
|
||||||
)
|
)
|
||||||
|
|
||||||
override val lightScheme = lightColorScheme(
|
override val lightScheme = lightColorScheme(
|
||||||
@@ -47,19 +52,19 @@ internal object NordColorScheme : BaseColorScheme() {
|
|||||||
primaryContainer = Color(0xFF5E81AC),
|
primaryContainer = Color(0xFF5E81AC),
|
||||||
onPrimaryContainer = Color(0xFF000000),
|
onPrimaryContainer = Color(0xFF000000),
|
||||||
inversePrimary = Color(0xFF8CA8CD),
|
inversePrimary = Color(0xFF8CA8CD),
|
||||||
secondary = Color(0xFF81A1C1),
|
secondary = Color(0xFF81A1C1), // Unread badge
|
||||||
onSecondary = Color(0xFF2E3440),
|
onSecondary = Color(0xFF2E3440), // Unread badge text
|
||||||
secondaryContainer = Color(0xFF81A1C1),
|
secondaryContainer = Color(0xFF81A1C1), // Navigation bar selector pill & progress indicator (remaining)
|
||||||
onSecondaryContainer = Color(0xFF2E3440),
|
onSecondaryContainer = Color(0xFF2E3440), // Navigation bar selector icon
|
||||||
tertiary = Color(0xFF88C0D0),
|
tertiary = Color(0xFF88C0D0), // Downloaded badge
|
||||||
onTertiary = Color(0xFF2E3440),
|
onTertiary = Color(0xFF2E3440), // Downloaded badge text
|
||||||
tertiaryContainer = Color(0xFF88C0D0),
|
tertiaryContainer = Color(0xFF88C0D0),
|
||||||
onTertiaryContainer = Color(0xFF2E3440),
|
onTertiaryContainer = Color(0xFF2E3440),
|
||||||
background = Color(0xFFECEFF4),
|
background = Color(0xFFECEFF4),
|
||||||
onBackground = Color(0xFF2E3440),
|
onBackground = Color(0xFF2E3440),
|
||||||
surface = Color(0xFFE5E9F0),
|
surface = Color(0xFFE5E9F0),
|
||||||
onSurface = Color(0xFF2E3440),
|
onSurface = Color(0xFF2E3440),
|
||||||
surfaceVariant = Color(0xFFffffff),
|
surfaceVariant = Color(0xFFDAE0EA), // Navigation bar background (ThemePrefWidget)
|
||||||
onSurfaceVariant = Color(0xFF2E3440),
|
onSurfaceVariant = Color(0xFF2E3440),
|
||||||
surfaceTint = Color(0xFF5E81AC),
|
surfaceTint = Color(0xFF5E81AC),
|
||||||
inverseSurface = Color(0xFF3B4252),
|
inverseSurface = Color(0xFF3B4252),
|
||||||
@@ -68,5 +73,10 @@ internal object NordColorScheme : BaseColorScheme() {
|
|||||||
onError = Color(0xFFECEFF4),
|
onError = Color(0xFFECEFF4),
|
||||||
errorContainer = Color(0xFFBF616A),
|
errorContainer = Color(0xFFBF616A),
|
||||||
onErrorContainer = Color(0xFF000000),
|
onErrorContainer = Color(0xFF000000),
|
||||||
|
surfaceContainerLowest = Color(0xFFD1D7E0),
|
||||||
|
surfaceContainerLow = Color(0xFFD6DCE6),
|
||||||
|
surfaceContainer = Color(0xFFDAE0EA), // Navigation bar background
|
||||||
|
surfaceContainerHigh = Color(0xFFE9EDF3),
|
||||||
|
surfaceContainerHighest = Color(0xFFF2F4F8),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+67
-43
@@ -18,54 +18,78 @@ import androidx.compose.ui.graphics.Color
|
|||||||
internal object StrawberryColorScheme : BaseColorScheme() {
|
internal object StrawberryColorScheme : BaseColorScheme() {
|
||||||
|
|
||||||
override val darkScheme = darkColorScheme(
|
override val darkScheme = darkColorScheme(
|
||||||
primary = Color(0xFFFFB2B9),
|
primary = Color(0xFFFFB2B8),
|
||||||
onPrimary = Color(0xFF67001B),
|
onPrimary = Color(0xFF67001D),
|
||||||
primaryContainer = Color(0xFF91002A),
|
primaryContainer = Color(0xFFD53855),
|
||||||
onPrimaryContainer = Color(0xFFFFDADD),
|
onPrimaryContainer = Color(0xFFFFFFFF),
|
||||||
inversePrimary = Color(0xFFB61E40),
|
secondary = Color(0xFFED4A65), // Unread badge
|
||||||
secondary = Color(0xFFFFB2B9),
|
onSecondary = Color(0xFF201A1A), // Unread badge text
|
||||||
onSecondary = Color(0xFF67001B),
|
secondaryContainer = Color(0xFF91002A), // Navigation bar selector pill & progress indicator (remaining)
|
||||||
secondaryContainer = Color(0xFF91002A),
|
onSecondaryContainer = Color(0xFFFFFFFF), // Navigation bar selector icon
|
||||||
onSecondaryContainer = Color(0xFFFFDADD),
|
tertiary = Color(0xFFE8C08E), // Downloaded badge
|
||||||
tertiary = Color(0xFFE8C08E),
|
onTertiary = Color(0xFF201A1A), // Downloaded badge text
|
||||||
onTertiary = Color(0xFF432C06),
|
tertiaryContainer = Color(0xFF775930),
|
||||||
tertiaryContainer = Color(0xFF5D421B),
|
onTertiaryContainer = Color(0xFFFFF7F1),
|
||||||
onTertiaryContainer = Color(0xFFFFDDB1),
|
error = Color(0xFFFFB4AB),
|
||||||
|
onError = Color(0xFF690005),
|
||||||
|
errorContainer = Color(0xFF93000A),
|
||||||
|
onErrorContainer = Color(0xFFFFDAD6),
|
||||||
background = Color(0xFF201A1A),
|
background = Color(0xFF201A1A),
|
||||||
onBackground = Color(0xFFECDFDF),
|
onBackground = Color(0xFFF7DCDD),
|
||||||
surface = Color(0xFF201A1A),
|
surface = Color(0xFF201A1A),
|
||||||
onSurface = Color(0xFFECDFDF),
|
onSurface = Color(0xFFF7DCDD),
|
||||||
surfaceVariant = Color(0xFF534344),
|
surfaceVariant = Color(0xFF322727), // Navigation bar background (ThemePrefWidget)
|
||||||
onSurfaceVariant = Color(0xFFD7C1C2),
|
onSurfaceVariant = Color(0xFFE1BEC0),
|
||||||
surfaceTint = Color(0xFFFFB2B9),
|
outline = Color(0xFFA9898B),
|
||||||
inverseSurface = Color(0xFFECDFDF),
|
outlineVariant = Color(0xFF594042),
|
||||||
inverseOnSurface = Color(0xFF201A1A),
|
scrim = Color(0xFF000000),
|
||||||
outline = Color(0xFFA08C8D),
|
inverseSurface = Color(0xFFF7DCDD),
|
||||||
|
inverseOnSurface = Color(0xFF3D2C2D),
|
||||||
|
inversePrimary = Color(0xFFB61F40),
|
||||||
|
surfaceDim = Color(0xFF1D1011),
|
||||||
|
surfaceBright = Color(0xFF463536),
|
||||||
|
surfaceContainerLowest = Color(0xFF2C2222),
|
||||||
|
surfaceContainerLow = Color(0xFF302525),
|
||||||
|
surfaceContainer = Color(0xFF322727), // Navigation bar background
|
||||||
|
surfaceContainerHigh = Color(0xFF3C2F2F),
|
||||||
|
surfaceContainerHighest = Color(0xFF463737),
|
||||||
)
|
)
|
||||||
|
|
||||||
override val lightScheme = lightColorScheme(
|
override val lightScheme = lightColorScheme(
|
||||||
primary = Color(0xFFB61E40),
|
primary = Color(0xFFA10833),
|
||||||
onPrimary = Color(0xFFFFFFFF),
|
onPrimary = Color(0xFFFFFFFF),
|
||||||
primaryContainer = Color(0xFFFFDADD),
|
primaryContainer = Color(0xFFD53855),
|
||||||
onPrimaryContainer = Color(0xFF40000D),
|
onPrimaryContainer = Color(0xFFFFFFFF),
|
||||||
inversePrimary = Color(0xFFFFB2B9),
|
secondary = Color(0xFFA10833), // Unread badge
|
||||||
secondary = Color(0xFFB61E40),
|
onSecondary = Color(0xFFFFFFFF), // Unread badge text
|
||||||
onSecondary = Color(0xFFFFFFFF),
|
secondaryContainer = Color(0xFFD53855), // Navigation bar selector pill & progress indicator (remaining)
|
||||||
secondaryContainer = Color(0xFFFFDADD),
|
onSecondaryContainer = Color(0xFFF6EAED), // Navigation bar selector icon
|
||||||
onSecondaryContainer = Color(0xFF40000D),
|
tertiary = Color(0xFF5F441D), // Downloaded badge
|
||||||
tertiary = Color(0xFF775930),
|
onTertiary = Color(0xFFFFFFFF), // Downloaded badge text
|
||||||
onTertiary = Color(0xFFFFFFFF),
|
tertiaryContainer = Color(0xFF87683D),
|
||||||
tertiaryContainer = Color(0xFFFFDDB1),
|
onTertiaryContainer = Color(0xFFFFFFFF),
|
||||||
onTertiaryContainer = Color(0xFF2A1800),
|
error = Color(0xFFBA1A1A),
|
||||||
background = Color(0xFFFCFCFC),
|
onError = Color(0xFFFFFFFF),
|
||||||
onBackground = Color(0xFF201A1A),
|
errorContainer = Color(0xFFFFDAD6),
|
||||||
surface = Color(0xFFFCFCFC),
|
onErrorContainer = Color(0xFF410002),
|
||||||
onSurface = Color(0xFF201A1A),
|
background = Color(0xFFFAFAFA),
|
||||||
surfaceVariant = Color(0xFFF4DDDD),
|
onBackground = Color(0xFF261819),
|
||||||
onSurfaceVariant = Color(0xFF534344),
|
surface = Color(0xFFFAFAFA),
|
||||||
surfaceTint = Color(0xFFB61E40),
|
onSurface = Color(0xFF261819),
|
||||||
inverseSurface = Color(0xFF362F2F),
|
surfaceVariant = Color(0xFFF6EAED), // Navigation bar background (ThemePrefWidget)
|
||||||
inverseOnSurface = Color(0xFFFBEDED),
|
onSurfaceVariant = Color(0xFF594042),
|
||||||
outline = Color(0xFF857374),
|
outline = Color(0xFF8D7071),
|
||||||
|
outlineVariant = Color(0xFFE1BEC0),
|
||||||
|
scrim = Color(0xFF000000),
|
||||||
|
inverseSurface = Color(0xFF3D2C2D),
|
||||||
|
inverseOnSurface = Color(0xFFFFECED),
|
||||||
|
inversePrimary = Color(0xFFFFB2B8),
|
||||||
|
surfaceDim = Color(0xFFEED4D5),
|
||||||
|
surfaceBright = Color(0xFFFFF8F7),
|
||||||
|
surfaceContainerLowest = Color(0xFFF7DCDD),
|
||||||
|
surfaceContainerLow = Color(0xFFFDE2E3),
|
||||||
|
surfaceContainer = Color(0xFFF6EAED), // Navigation bar background
|
||||||
|
surfaceContainerHigh = Color(0xFFFFF0F0),
|
||||||
|
surfaceContainerHighest = Color(0xFFFFFFFF),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-14
@@ -22,19 +22,19 @@ internal object TachiyomiColorScheme : BaseColorScheme() {
|
|||||||
primaryContainer = Color(0xFF00429B),
|
primaryContainer = Color(0xFF00429B),
|
||||||
onPrimaryContainer = Color(0xFFD9E2FF),
|
onPrimaryContainer = Color(0xFFD9E2FF),
|
||||||
inversePrimary = Color(0xFF0058CA),
|
inversePrimary = Color(0xFF0058CA),
|
||||||
secondary = Color(0xFFB0C6FF),
|
secondary = Color(0xFFB0C6FF), // Unread badge
|
||||||
onSecondary = Color(0xFF002D6E),
|
onSecondary = Color(0xFF002D6E), // Unread badge text
|
||||||
secondaryContainer = Color(0xFF00429B),
|
secondaryContainer = Color(0xFF00429B), // Navigation bar selector pill & pro
|
||||||
onSecondaryContainer = Color(0xFFD9E2FF),
|
onSecondaryContainer = Color(0xFFD9E2FF), // Navigation bar selector icon
|
||||||
tertiary = Color(0xFF7ADC77),
|
tertiary = Color(0xFF7ADC77), // Downloaded badge
|
||||||
onTertiary = Color(0xFF003909),
|
onTertiary = Color(0xFF003909), // Downloaded badge text
|
||||||
tertiaryContainer = Color(0xFF005312),
|
tertiaryContainer = Color(0xFF005312),
|
||||||
onTertiaryContainer = Color(0xFF95F990),
|
onTertiaryContainer = Color(0xFF95F990),
|
||||||
background = Color(0xFF1B1B1F),
|
background = Color(0xFF1B1B1F),
|
||||||
onBackground = Color(0xFFE3E2E6),
|
onBackground = Color(0xFFE3E2E6),
|
||||||
surface = Color(0xFF1B1B1F),
|
surface = Color(0xFF1B1B1F),
|
||||||
onSurface = Color(0xFFE3E2E6),
|
onSurface = Color(0xFFE3E2E6),
|
||||||
surfaceVariant = Color(0xFF44464F),
|
surfaceVariant = Color(0xFF211F26), // Navigation bar background (ThemePrefWidget)
|
||||||
onSurfaceVariant = Color(0xFFC5C6D0),
|
onSurfaceVariant = Color(0xFFC5C6D0),
|
||||||
surfaceTint = Color(0xFFB0C6FF),
|
surfaceTint = Color(0xFFB0C6FF),
|
||||||
inverseSurface = Color(0xFFE3E2E6),
|
inverseSurface = Color(0xFFE3E2E6),
|
||||||
@@ -45,6 +45,11 @@ internal object TachiyomiColorScheme : BaseColorScheme() {
|
|||||||
onErrorContainer = Color(0xFFFFDAD6),
|
onErrorContainer = Color(0xFFFFDAD6),
|
||||||
outline = Color(0xFF8F9099),
|
outline = Color(0xFF8F9099),
|
||||||
outlineVariant = Color(0xFF44464F),
|
outlineVariant = Color(0xFF44464F),
|
||||||
|
surfaceContainerLowest = Color(0xFF1A181D),
|
||||||
|
surfaceContainerLow = Color(0xFF1E1C22),
|
||||||
|
surfaceContainer = Color(0xFF211F26), // Navigation bar background
|
||||||
|
surfaceContainerHigh = Color(0xFF292730),
|
||||||
|
surfaceContainerHighest = Color(0xFF302E38),
|
||||||
)
|
)
|
||||||
|
|
||||||
override val lightScheme = lightColorScheme(
|
override val lightScheme = lightColorScheme(
|
||||||
@@ -53,19 +58,19 @@ internal object TachiyomiColorScheme : BaseColorScheme() {
|
|||||||
primaryContainer = Color(0xFFD9E2FF),
|
primaryContainer = Color(0xFFD9E2FF),
|
||||||
onPrimaryContainer = Color(0xFF001945),
|
onPrimaryContainer = Color(0xFF001945),
|
||||||
inversePrimary = Color(0xFFB0C6FF),
|
inversePrimary = Color(0xFFB0C6FF),
|
||||||
secondary = Color(0xFF0058CA),
|
secondary = Color(0xFF0058CA), // Unread badge
|
||||||
onSecondary = Color(0xFFFFFFFF),
|
onSecondary = Color(0xFFFFFFFF), // Unread badge text
|
||||||
secondaryContainer = Color(0xFFD9E2FF),
|
secondaryContainer = Color(0xFFD9E2FF), // Navigation bar selector pill & progress indicator (remaining)
|
||||||
onSecondaryContainer = Color(0xFF001945),
|
onSecondaryContainer = Color(0xFF001945), // Navigation bar selector icon
|
||||||
tertiary = Color(0xFF006E1B),
|
tertiary = Color(0xFF006E1B), // Downloaded badge
|
||||||
onTertiary = Color(0xFFFFFFFF),
|
onTertiary = Color(0xFFFFFFFF), // Downloaded badge text
|
||||||
tertiaryContainer = Color(0xFF95F990),
|
tertiaryContainer = Color(0xFF95F990),
|
||||||
onTertiaryContainer = Color(0xFF002203),
|
onTertiaryContainer = Color(0xFF002203),
|
||||||
background = Color(0xFFFEFBFF),
|
background = Color(0xFFFEFBFF),
|
||||||
onBackground = Color(0xFF1B1B1F),
|
onBackground = Color(0xFF1B1B1F),
|
||||||
surface = Color(0xFFFEFBFF),
|
surface = Color(0xFFFEFBFF),
|
||||||
onSurface = Color(0xFF1B1B1F),
|
onSurface = Color(0xFF1B1B1F),
|
||||||
surfaceVariant = Color(0xFFE1E2EC),
|
surfaceVariant = Color(0xFFF3EDF7), // Navigation bar background (ThemePrefWidget)
|
||||||
onSurfaceVariant = Color(0xFF44464F),
|
onSurfaceVariant = Color(0xFF44464F),
|
||||||
surfaceTint = Color(0xFF0058CA),
|
surfaceTint = Color(0xFF0058CA),
|
||||||
inverseSurface = Color(0xFF303034),
|
inverseSurface = Color(0xFF303034),
|
||||||
@@ -76,5 +81,10 @@ internal object TachiyomiColorScheme : BaseColorScheme() {
|
|||||||
onErrorContainer = Color(0xFF410002),
|
onErrorContainer = Color(0xFF410002),
|
||||||
outline = Color(0xFF757780),
|
outline = Color(0xFF757780),
|
||||||
outlineVariant = Color(0xFFC5C6D0),
|
outlineVariant = Color(0xFFC5C6D0),
|
||||||
|
surfaceContainerLowest = Color(0xFFF5F1F8),
|
||||||
|
surfaceContainerLow = Color(0xFFF7F2FA),
|
||||||
|
surfaceContainer = Color(0xFFF3EDF7), // Navigation bar background
|
||||||
|
surfaceContainerHigh = Color(0xFFFCF7FF),
|
||||||
|
surfaceContainerHighest = Color(0xFFFCF7FF),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,24 +23,29 @@ internal object TakoColorScheme : BaseColorScheme() {
|
|||||||
primaryContainer = Color(0xFFF3B375),
|
primaryContainer = Color(0xFFF3B375),
|
||||||
onPrimaryContainer = Color(0xFF38294E),
|
onPrimaryContainer = Color(0xFF38294E),
|
||||||
inversePrimary = Color(0xFF84531E),
|
inversePrimary = Color(0xFF84531E),
|
||||||
secondary = Color(0xFFF3B375),
|
secondary = Color(0xFFF3B375), // Unread badge
|
||||||
onSecondary = Color(0xFF38294E),
|
onSecondary = Color(0xFF38294E), // Unread badge text
|
||||||
secondaryContainer = Color(0xFFF3B375),
|
secondaryContainer = Color(0xFF5C4D4B), // Navigation bar selector pill & progress indicator (remaining)
|
||||||
onSecondaryContainer = Color(0xFF38294E),
|
onSecondaryContainer = Color(0xFFF3B375), // Navigation bar selector icon
|
||||||
tertiary = Color(0xFF66577E),
|
tertiary = Color(0xFF66577E), // Downloaded badge
|
||||||
onTertiary = Color(0xFFF3B375),
|
onTertiary = Color(0xFFF3B375), // Downloaded badge text
|
||||||
tertiaryContainer = Color(0xFF4E4065),
|
tertiaryContainer = Color(0xFF4E4065),
|
||||||
onTertiaryContainer = Color(0xFFEDDCFF),
|
onTertiaryContainer = Color(0xFFEDDCFF),
|
||||||
background = Color(0xFF21212E),
|
background = Color(0xFF21212E),
|
||||||
onBackground = Color(0xFFE3E0F2),
|
onBackground = Color(0xFFE3E0F2),
|
||||||
surface = Color(0xFF21212E),
|
surface = Color(0xFF21212E),
|
||||||
onSurface = Color(0xFFE3E0F2),
|
onSurface = Color(0xFFE3E0F2),
|
||||||
surfaceVariant = Color(0xFF49454E),
|
surfaceVariant = Color(0xFF2A2A3C), // Navigation bar background (ThemePrefWidget)
|
||||||
onSurfaceVariant = Color(0xFFCBC4CE),
|
onSurfaceVariant = Color(0xFFCBC4CE),
|
||||||
surfaceTint = Color(0xFF66577E),
|
surfaceTint = Color(0xFF66577E),
|
||||||
inverseSurface = Color(0xFFE5E1E6),
|
inverseSurface = Color(0xFFE5E1E6),
|
||||||
inverseOnSurface = Color(0xFF1B1B1E),
|
inverseOnSurface = Color(0xFF1B1B1E),
|
||||||
outline = Color(0xFF958F99),
|
outline = Color(0xFF958F99),
|
||||||
|
surfaceContainerLowest = Color(0xFF20202E),
|
||||||
|
surfaceContainerLow = Color(0xFF262636),
|
||||||
|
surfaceContainer = Color(0xFF2A2A3C), // Navigation bar background
|
||||||
|
surfaceContainerHigh = Color(0xFF303044),
|
||||||
|
surfaceContainerHighest = Color(0xFF36364D),
|
||||||
)
|
)
|
||||||
|
|
||||||
override val lightScheme = lightColorScheme(
|
override val lightScheme = lightColorScheme(
|
||||||
@@ -49,23 +54,28 @@ internal object TakoColorScheme : BaseColorScheme() {
|
|||||||
primaryContainer = Color(0xFF66577E),
|
primaryContainer = Color(0xFF66577E),
|
||||||
onPrimaryContainer = Color(0xFFF3B375),
|
onPrimaryContainer = Color(0xFFF3B375),
|
||||||
inversePrimary = Color(0xFFD6BAFF),
|
inversePrimary = Color(0xFFD6BAFF),
|
||||||
secondary = Color(0xFF66577E),
|
secondary = Color(0xFF66577E), // Unread badge
|
||||||
onSecondary = Color(0xFFF3B375),
|
onSecondary = Color(0xFFF3B375), // Unread badge text
|
||||||
secondaryContainer = Color(0xFF66577E),
|
secondaryContainer = Color(0xFFC8BED0), // Navigation bar selector pill & progress indicator (remaining)
|
||||||
onSecondaryContainer = Color(0xFFF3B375),
|
onSecondaryContainer = Color(0xFF66577E), // Navigation bar selector icon
|
||||||
tertiary = Color(0xFFF3B375),
|
tertiary = Color(0xFFF3B375), // Downloaded badge
|
||||||
onTertiary = Color(0xFF574360),
|
onTertiary = Color(0xFF574360), // Downloaded badge text
|
||||||
tertiaryContainer = Color(0xFFFDD6B0),
|
tertiaryContainer = Color(0xFFFDD6B0),
|
||||||
onTertiaryContainer = Color(0xFF221437),
|
onTertiaryContainer = Color(0xFF221437),
|
||||||
background = Color(0xFFF7F5FF),
|
background = Color(0xFFF7F5FF),
|
||||||
onBackground = Color(0xFF1B1B22),
|
onBackground = Color(0xFF1B1B22),
|
||||||
surface = Color(0xFFF7F5FF),
|
surface = Color(0xFFF7F5FF),
|
||||||
onSurface = Color(0xFF1B1B22),
|
onSurface = Color(0xFF1B1B22),
|
||||||
surfaceVariant = Color(0xFFE8E0EB),
|
surfaceVariant = Color(0xFFE8E0EB), // Navigation bar background (ThemePrefWidget)
|
||||||
onSurfaceVariant = Color(0xFF49454E),
|
onSurfaceVariant = Color(0xFF49454E),
|
||||||
surfaceTint = Color(0xFF66577E),
|
surfaceTint = Color(0xFF66577E),
|
||||||
inverseSurface = Color(0xFF313033),
|
inverseSurface = Color(0xFF313033),
|
||||||
inverseOnSurface = Color(0xFFF3EFF4),
|
inverseOnSurface = Color(0xFFF3EFF4),
|
||||||
outline = Color(0xFF7A757E),
|
outline = Color(0xFF7A757E),
|
||||||
|
surfaceContainerLowest = Color(0xFFD7D0DA),
|
||||||
|
surfaceContainerLow = Color(0xFFDFD8E2),
|
||||||
|
surfaceContainer = Color(0xFFE8E0EB), // Navigation bar background
|
||||||
|
surfaceContainerHigh = Color(0xFFEEE6F1),
|
||||||
|
surfaceContainerHighest = Color(0xFFF7EEFA),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-14
@@ -15,24 +15,29 @@ internal object TealTurqoiseColorScheme : BaseColorScheme() {
|
|||||||
primaryContainer = Color(0xFF40E0D0),
|
primaryContainer = Color(0xFF40E0D0),
|
||||||
onPrimaryContainer = Color(0xFF000000),
|
onPrimaryContainer = Color(0xFF000000),
|
||||||
inversePrimary = Color(0xFF008080),
|
inversePrimary = Color(0xFF008080),
|
||||||
secondary = Color(0xFF40E0D0),
|
secondary = Color(0xFF40E0D0), // Unread badge
|
||||||
onSecondary = Color(0xFF000000),
|
onSecondary = Color(0xFF000000), // Unread badge text
|
||||||
secondaryContainer = Color(0xFF18544E),
|
secondaryContainer = Color(0xFF18544E), // Navigation bar selector pill & progress indicator (remaining)
|
||||||
onSecondaryContainer = Color(0xFF40E0D0),
|
onSecondaryContainer = Color(0xFF40E0D0), // Navigation bar selector icon
|
||||||
tertiary = Color(0xFFBF1F2F),
|
tertiary = Color(0xFFBF1F2F), // Downloaded badge
|
||||||
onTertiary = Color(0xFFFFFFFF),
|
onTertiary = Color(0xFFFFFFFF), // Downloaded badge text
|
||||||
tertiaryContainer = Color(0xFF200508),
|
tertiaryContainer = Color(0xFF200508),
|
||||||
onTertiaryContainer = Color(0xFFBF1F2F),
|
onTertiaryContainer = Color(0xFFBF1F2F),
|
||||||
background = Color(0xFF202125),
|
background = Color(0xFF202125),
|
||||||
onBackground = Color(0xFFDFDEDA),
|
onBackground = Color(0xFFDFDEDA),
|
||||||
surface = Color(0xFF202125),
|
surface = Color(0xFF202125),
|
||||||
onSurface = Color(0xFFDFDEDA),
|
onSurface = Color(0xFFDFDEDA),
|
||||||
surfaceVariant = Color(0xFF3F4947),
|
surfaceVariant = Color(0xFF233133), // Navigation bar background (ThemePrefWidget)
|
||||||
onSurfaceVariant = Color(0xFFDFDEDA),
|
onSurfaceVariant = Color(0xFFDFDEDA),
|
||||||
surfaceTint = Color(0xFF40E0D0),
|
surfaceTint = Color(0xFF40E0D0),
|
||||||
inverseSurface = Color(0xFFDFDEDA),
|
inverseSurface = Color(0xFFDFDEDA),
|
||||||
inverseOnSurface = Color(0xFF202125),
|
inverseOnSurface = Color(0xFF202125),
|
||||||
outline = Color(0xFF899391),
|
outline = Color(0xFF899391),
|
||||||
|
surfaceContainerLowest = Color(0xFF202C2E),
|
||||||
|
surfaceContainerLow = Color(0xFF222F31),
|
||||||
|
surfaceContainer = Color(0xFF233133), // Navigation bar background
|
||||||
|
surfaceContainerHigh = Color(0xFF28383A),
|
||||||
|
surfaceContainerHighest = Color(0xFF2F4244),
|
||||||
)
|
)
|
||||||
|
|
||||||
override val lightScheme = lightColorScheme(
|
override val lightScheme = lightColorScheme(
|
||||||
@@ -41,23 +46,28 @@ internal object TealTurqoiseColorScheme : BaseColorScheme() {
|
|||||||
primaryContainer = Color(0xFF008080),
|
primaryContainer = Color(0xFF008080),
|
||||||
onPrimaryContainer = Color(0xFFFFFFFF),
|
onPrimaryContainer = Color(0xFFFFFFFF),
|
||||||
inversePrimary = Color(0xFF40E0D0),
|
inversePrimary = Color(0xFF40E0D0),
|
||||||
secondary = Color(0xFF008080),
|
secondary = Color(0xFF008080), // Unread badge text
|
||||||
onSecondary = Color(0xFFFFFFFF),
|
onSecondary = Color(0xFFFFFFFF), // Unread badge text
|
||||||
secondaryContainer = Color(0xFFBFDFDF),
|
secondaryContainer = Color(0xFFCFE5E4), // Navigation bar selector pill & progress indicator (remaining)
|
||||||
onSecondaryContainer = Color(0xFF008080),
|
onSecondaryContainer = Color(0xFF008080), // Navigation bar selector icon
|
||||||
tertiary = Color(0xFFFF7F7F),
|
tertiary = Color(0xFFFF7F7F), // Downloaded badge
|
||||||
onTertiary = Color(0xFF000000),
|
onTertiary = Color(0xFF000000), // Downloaded badge text
|
||||||
tertiaryContainer = Color(0xFF2A1616),
|
tertiaryContainer = Color(0xFF2A1616),
|
||||||
onTertiaryContainer = Color(0xFFFF7F7F),
|
onTertiaryContainer = Color(0xFFFF7F7F),
|
||||||
background = Color(0xFFFAFAFA),
|
background = Color(0xFFFAFAFA),
|
||||||
onBackground = Color(0xFF050505),
|
onBackground = Color(0xFF050505),
|
||||||
surface = Color(0xFFFAFAFA),
|
surface = Color(0xFFFAFAFA),
|
||||||
onSurface = Color(0xFF050505),
|
onSurface = Color(0xFF050505),
|
||||||
surfaceVariant = Color(0xFFDAE5E2),
|
surfaceVariant = Color(0xFFEBF3F1), // Navigation bar background (ThemePrefWidget)
|
||||||
onSurfaceVariant = Color(0xFF050505),
|
onSurfaceVariant = Color(0xFF050505),
|
||||||
surfaceTint = Color(0xFFBFDFDF),
|
surfaceTint = Color(0xFFBFDFDF),
|
||||||
inverseSurface = Color(0xFF050505),
|
inverseSurface = Color(0xFF050505),
|
||||||
inverseOnSurface = Color(0xFFFAFAFA),
|
inverseOnSurface = Color(0xFFFAFAFA),
|
||||||
outline = Color(0xFF6F7977),
|
outline = Color(0xFF6F7977),
|
||||||
|
surfaceContainerLowest = Color(0xFFE1E9E7),
|
||||||
|
surfaceContainerLow = Color(0xFFE6EEEC),
|
||||||
|
surfaceContainer = Color(0xFFEBF3F1), // Navigation bar background
|
||||||
|
surfaceContainerHigh = Color(0xFFF0F8F6),
|
||||||
|
surfaceContainerHighest = Color(0xFFF7FFFD),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-14
@@ -22,24 +22,29 @@ internal object TidalWaveColorScheme : BaseColorScheme() {
|
|||||||
primaryContainer = Color(0xFF004d61),
|
primaryContainer = Color(0xFF004d61),
|
||||||
onPrimaryContainer = Color(0xFFb8eaff),
|
onPrimaryContainer = Color(0xFFb8eaff),
|
||||||
inversePrimary = Color(0xFFa12b03),
|
inversePrimary = Color(0xFFa12b03),
|
||||||
secondary = Color(0xFF5ed4fc),
|
secondary = Color(0xFF5ed4fc), // Unread badge
|
||||||
onSecondary = Color(0xFF003544),
|
onSecondary = Color(0xFF003544), // Unread badge text
|
||||||
secondaryContainer = Color(0xFF004d61),
|
secondaryContainer = Color(0xFF004d61), // Navigation bar selector pill & progress indicator (remaining)
|
||||||
onSecondaryContainer = Color(0xFFb8eaff),
|
onSecondaryContainer = Color(0xFFb8eaff), // Navigation bar selector icon
|
||||||
tertiary = Color(0xFF92f7bc),
|
tertiary = Color(0xFF92f7bc), // Downloaded badge
|
||||||
onTertiary = Color(0xFF001c3b),
|
onTertiary = Color(0xFF001c3b), // Downloaded badge text
|
||||||
tertiaryContainer = Color(0xFFc3fada),
|
tertiaryContainer = Color(0xFFc3fada),
|
||||||
onTertiaryContainer = Color(0xFF78ffd6),
|
onTertiaryContainer = Color(0xFF78ffd6),
|
||||||
background = Color(0xFF001c3b),
|
background = Color(0xFF001c3b),
|
||||||
onBackground = Color(0xFFd5e3ff),
|
onBackground = Color(0xFFd5e3ff),
|
||||||
surface = Color(0xFF001c3b),
|
surface = Color(0xFF001c3b),
|
||||||
onSurface = Color(0xFFd5e3ff),
|
onSurface = Color(0xFFd5e3ff),
|
||||||
surfaceVariant = Color(0xFF40484c),
|
surfaceVariant = Color(0xFF082b4b), // Navigation bar background (ThemePrefWidget)
|
||||||
onSurfaceVariant = Color(0xFFbfc8cc),
|
onSurfaceVariant = Color(0xFFbfc8cc),
|
||||||
surfaceTint = Color(0xFF5ed4fc),
|
surfaceTint = Color(0xFF5ed4fc),
|
||||||
inverseSurface = Color(0xFFffe3c4),
|
inverseSurface = Color(0xFFffe3c4),
|
||||||
inverseOnSurface = Color(0xFF001c3b),
|
inverseOnSurface = Color(0xFF001c3b),
|
||||||
outline = Color(0xFF8a9296),
|
outline = Color(0xFF8a9296),
|
||||||
|
surfaceContainerLowest = Color(0xFF072642),
|
||||||
|
surfaceContainerLow = Color(0xFF072947),
|
||||||
|
surfaceContainer = Color(0xFF082b4b), // Navigation bar background
|
||||||
|
surfaceContainerHigh = Color(0xFF093257),
|
||||||
|
surfaceContainerHighest = Color(0xFF0A3861),
|
||||||
)
|
)
|
||||||
|
|
||||||
override val lightScheme = lightColorScheme(
|
override val lightScheme = lightColorScheme(
|
||||||
@@ -48,23 +53,28 @@ internal object TidalWaveColorScheme : BaseColorScheme() {
|
|||||||
primaryContainer = Color(0xFFB4D4DF),
|
primaryContainer = Color(0xFFB4D4DF),
|
||||||
onPrimaryContainer = Color(0xFF001f28),
|
onPrimaryContainer = Color(0xFF001f28),
|
||||||
inversePrimary = Color(0xFFff987f),
|
inversePrimary = Color(0xFFff987f),
|
||||||
secondary = Color(0xFF006780),
|
secondary = Color(0xFF006780), // Unread badge
|
||||||
onSecondary = Color(0xFFffffff),
|
onSecondary = Color(0xFFffffff), // Unread badge text
|
||||||
secondaryContainer = Color(0xFFb8eaff),
|
secondaryContainer = Color(0xFF9AE1FF), // Navigation bar selector pill & progress indicator (remaining)
|
||||||
onSecondaryContainer = Color(0xFF001f28),
|
onSecondaryContainer = Color(0xFF001f28), // Navigation bar selector icon
|
||||||
tertiary = Color(0xFF92f7bc),
|
tertiary = Color(0xFF92f7bc), // Downloaded badge
|
||||||
onTertiary = Color(0xFF001c3b),
|
onTertiary = Color(0xFF001c3b), // Downloaded badge text
|
||||||
tertiaryContainer = Color(0xFFc3fada),
|
tertiaryContainer = Color(0xFFc3fada),
|
||||||
onTertiaryContainer = Color(0xFF78ffd6),
|
onTertiaryContainer = Color(0xFF78ffd6),
|
||||||
background = Color(0xFFfdfbff),
|
background = Color(0xFFfdfbff),
|
||||||
onBackground = Color(0xFF001c3b),
|
onBackground = Color(0xFF001c3b),
|
||||||
surface = Color(0xFFfdfbff),
|
surface = Color(0xFFfdfbff),
|
||||||
onSurface = Color(0xFF001c3b),
|
onSurface = Color(0xFF001c3b),
|
||||||
surfaceVariant = Color(0xFFdce4e8),
|
surfaceVariant = Color(0xFFe8eff5), // Navigation bar background (ThemePrefWidget)
|
||||||
onSurfaceVariant = Color(0xFF40484c),
|
onSurfaceVariant = Color(0xFF40484c),
|
||||||
surfaceTint = Color(0xFF006780),
|
surfaceTint = Color(0xFF006780),
|
||||||
inverseSurface = Color(0xFF020400),
|
inverseSurface = Color(0xFF020400),
|
||||||
inverseOnSurface = Color(0xFFffe3c4),
|
inverseOnSurface = Color(0xFFffe3c4),
|
||||||
outline = Color(0xFF70787c),
|
outline = Color(0xFF70787c),
|
||||||
|
surfaceContainerLowest = Color(0xFFe2e8ec),
|
||||||
|
surfaceContainerLow = Color(0xFFe5ecf1),
|
||||||
|
surfaceContainer = Color(0xFFe8eff5), // Navigation bar background
|
||||||
|
surfaceContainerHigh = Color(0xFFedf4fA),
|
||||||
|
surfaceContainerHighest = Color(0xFFf5faff),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,24 +17,29 @@ internal object YinYangColorScheme : BaseColorScheme() {
|
|||||||
primaryContainer = Color(0xFFFFFFFF),
|
primaryContainer = Color(0xFFFFFFFF),
|
||||||
onPrimaryContainer = Color(0xFF000000),
|
onPrimaryContainer = Color(0xFF000000),
|
||||||
inversePrimary = Color(0xFFCECECE),
|
inversePrimary = Color(0xFFCECECE),
|
||||||
secondary = Color(0xFFFFFFFF),
|
secondary = Color(0xFFFFFFFF), // Unread badge
|
||||||
onSecondary = Color(0xFF5A5A5A),
|
onSecondary = Color(0xFF5A5A5A), // Unread badge text
|
||||||
secondaryContainer = Color(0xFF717171),
|
secondaryContainer = Color(0xFF717171), // Navigation bar selector pill & progress indicator (remaining)
|
||||||
onSecondaryContainer = Color(0xFFE4E4E4),
|
onSecondaryContainer = Color(0xFFE4E4E4), // Navigation bar selector icon
|
||||||
tertiary = Color(0xFF000000),
|
tertiary = Color(0xFF000000), // Downloaded badge
|
||||||
onTertiary = Color(0xFFFFFFFF),
|
onTertiary = Color(0xFFFFFFFF), // Downloaded badge text
|
||||||
tertiaryContainer = Color(0xFF00419E),
|
tertiaryContainer = Color(0xFF00419E),
|
||||||
onTertiaryContainer = Color(0xFFD8E2FF),
|
onTertiaryContainer = Color(0xFFD8E2FF),
|
||||||
background = Color(0xFF1E1E1E),
|
background = Color(0xFF1E1E1E),
|
||||||
onBackground = Color(0xFFE6E6E6),
|
onBackground = Color(0xFFE6E6E6),
|
||||||
surface = Color(0xFF1E1E1E),
|
surface = Color(0xFF1E1E1E),
|
||||||
onSurface = Color(0xFFE6E6E6),
|
onSurface = Color(0xFFE6E6E6),
|
||||||
surfaceVariant = Color(0xFF4E4E4E),
|
surfaceVariant = Color(0xFF313131), // Navigation bar background (ThemePrefWidget)
|
||||||
onSurfaceVariant = Color(0xFFD1D1D1),
|
onSurfaceVariant = Color(0xFFD1D1D1),
|
||||||
surfaceTint = Color(0xFFFFFFFF),
|
surfaceTint = Color(0xFFFFFFFF),
|
||||||
inverseSurface = Color(0xFFE6E6E6),
|
inverseSurface = Color(0xFFE6E6E6),
|
||||||
inverseOnSurface = Color(0xFF1E1E1E),
|
inverseOnSurface = Color(0xFF1E1E1E),
|
||||||
outline = Color(0xFF999999),
|
outline = Color(0xFF999999),
|
||||||
|
surfaceContainerLowest = Color(0xFF2A2A2A),
|
||||||
|
surfaceContainerLow = Color(0xFF2D2D2D),
|
||||||
|
surfaceContainer = Color(0xFF313131), // Navigation bar background
|
||||||
|
surfaceContainerHigh = Color(0xFF383838),
|
||||||
|
surfaceContainerHighest = Color(0xFF3F3F3F),
|
||||||
)
|
)
|
||||||
|
|
||||||
override val lightScheme = lightColorScheme(
|
override val lightScheme = lightColorScheme(
|
||||||
@@ -43,23 +48,28 @@ internal object YinYangColorScheme : BaseColorScheme() {
|
|||||||
primaryContainer = Color(0xFF000000),
|
primaryContainer = Color(0xFF000000),
|
||||||
onPrimaryContainer = Color(0xFFFFFFFF),
|
onPrimaryContainer = Color(0xFFFFFFFF),
|
||||||
inversePrimary = Color(0xFFA6A6A6),
|
inversePrimary = Color(0xFFA6A6A6),
|
||||||
secondary = Color(0xFF000000),
|
secondary = Color(0xFF000000), // Unread badge
|
||||||
onSecondary = Color(0xFFFFFFFF),
|
onSecondary = Color(0xFFFFFFFF), // Unread badge text
|
||||||
secondaryContainer = Color(0xFFDDDDDD),
|
secondaryContainer = Color(0xFFDDDDDD), // Navigation bar selector pill & progress indicator (remaining)
|
||||||
onSecondaryContainer = Color(0xFF0C0C0C),
|
onSecondaryContainer = Color(0xFF0C0C0C), // Navigation bar selector icon
|
||||||
tertiary = Color(0xFFFFFFFF),
|
tertiary = Color(0xFFFFFFFF), // Downloaded badge
|
||||||
onTertiary = Color(0xFF000000),
|
onTertiary = Color(0xFF000000), // Downloaded badge text
|
||||||
tertiaryContainer = Color(0xFFD8E2FF),
|
tertiaryContainer = Color(0xFFD8E2FF),
|
||||||
onTertiaryContainer = Color(0xFF001947),
|
onTertiaryContainer = Color(0xFF001947),
|
||||||
background = Color(0xFFFDFDFD),
|
background = Color(0xFFFDFDFD),
|
||||||
onBackground = Color(0xFF222222),
|
onBackground = Color(0xFF222222),
|
||||||
surface = Color(0xFFFDFDFD),
|
surface = Color(0xFFFDFDFD),
|
||||||
onSurface = Color(0xFF222222),
|
onSurface = Color(0xFF222222),
|
||||||
surfaceVariant = Color(0xFFEDEDED),
|
surfaceVariant = Color(0xFFE8E8E8), // Navigation bar background (ThemePrefWidget)
|
||||||
onSurfaceVariant = Color(0xFF515151),
|
onSurfaceVariant = Color(0xFF515151),
|
||||||
surfaceTint = Color(0xFF000000),
|
surfaceTint = Color(0xFF000000),
|
||||||
inverseSurface = Color(0xFF333333),
|
inverseSurface = Color(0xFF333333),
|
||||||
inverseOnSurface = Color(0xFFF4F4F4),
|
inverseOnSurface = Color(0xFFF4F4F4),
|
||||||
outline = Color(0xFF838383),
|
outline = Color(0xFF838383),
|
||||||
|
surfaceContainerLowest = Color(0xFFCFCFCF),
|
||||||
|
surfaceContainerLow = Color(0xFFDADADA),
|
||||||
|
surfaceContainer = Color(0xFFE8E8E8), // Navigation bar background
|
||||||
|
surfaceContainerHigh = Color(0xFFECECEC),
|
||||||
|
surfaceContainerHighest = Color(0xFFEFEFEF),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,24 +23,29 @@ internal object YotsubaColorScheme : BaseColorScheme() {
|
|||||||
primaryContainer = Color(0xFF862200),
|
primaryContainer = Color(0xFF862200),
|
||||||
onPrimaryContainer = Color(0xFFFFDBCF),
|
onPrimaryContainer = Color(0xFFFFDBCF),
|
||||||
inversePrimary = Color(0xFFAE3200),
|
inversePrimary = Color(0xFFAE3200),
|
||||||
secondary = Color(0xFFFFB59D),
|
secondary = Color(0xFFFFB59D), // Unread badge
|
||||||
onSecondary = Color(0xFF5F1600),
|
onSecondary = Color(0xFF5F1600), // Unread badge text
|
||||||
secondaryContainer = Color(0xFF862200),
|
secondaryContainer = Color(0xFF862200), // Navigation bar selector pill & progress indicator (remaining)
|
||||||
onSecondaryContainer = Color(0xFFFFDBCF),
|
onSecondaryContainer = Color(0xFFFFDBCF), // Navigation bar selector icon
|
||||||
tertiary = Color(0xFFD7C68D),
|
tertiary = Color(0xFFD7C68D), // Downloaded badge
|
||||||
onTertiary = Color(0xFF3A2F05),
|
onTertiary = Color(0xFF3A2F05), // Downloaded badge text
|
||||||
tertiaryContainer = Color(0xFF524619),
|
tertiaryContainer = Color(0xFF524619),
|
||||||
onTertiaryContainer = Color(0xFFF5E2A7),
|
onTertiaryContainer = Color(0xFFF5E2A7),
|
||||||
background = Color(0xFF211A18),
|
background = Color(0xFF211A18),
|
||||||
onBackground = Color(0xFFEDE0DD),
|
onBackground = Color(0xFFEDE0DD),
|
||||||
surface = Color(0xFF211A18),
|
surface = Color(0xFF211A18),
|
||||||
onSurface = Color(0xFFEDE0DD),
|
onSurface = Color(0xFFEDE0DD),
|
||||||
surfaceVariant = Color(0xFF53433F),
|
surfaceVariant = Color(0xFF332723), // Navigation bar background (ThemePrefWidget)
|
||||||
onSurfaceVariant = Color(0xFFD8C2BC),
|
onSurfaceVariant = Color(0xFFD8C2BC),
|
||||||
surfaceTint = Color(0xFFFFB59D),
|
surfaceTint = Color(0xFFFFB59D),
|
||||||
inverseSurface = Color(0xFFEDE0DD),
|
inverseSurface = Color(0xFFEDE0DD),
|
||||||
inverseOnSurface = Color(0xFF211A18),
|
inverseOnSurface = Color(0xFF211A18),
|
||||||
outline = Color(0xFFA08C87),
|
outline = Color(0xFFA08C87),
|
||||||
|
surfaceContainerLowest = Color(0xFF2E221F),
|
||||||
|
surfaceContainerLow = Color(0xFF312521),
|
||||||
|
surfaceContainer = Color(0xFF332723), // Navigation bar background
|
||||||
|
surfaceContainerHigh = Color(0xFF413531),
|
||||||
|
surfaceContainerHighest = Color(0xFF4C403D),
|
||||||
)
|
)
|
||||||
|
|
||||||
override val lightScheme = lightColorScheme(
|
override val lightScheme = lightColorScheme(
|
||||||
@@ -49,23 +54,28 @@ internal object YotsubaColorScheme : BaseColorScheme() {
|
|||||||
primaryContainer = Color(0xFFFFDBCF),
|
primaryContainer = Color(0xFFFFDBCF),
|
||||||
onPrimaryContainer = Color(0xFF3B0A00),
|
onPrimaryContainer = Color(0xFF3B0A00),
|
||||||
inversePrimary = Color(0xFFFFB59D),
|
inversePrimary = Color(0xFFFFB59D),
|
||||||
secondary = Color(0xFFAE3200),
|
secondary = Color(0xFFAE3200), // Unread badge
|
||||||
onSecondary = Color(0xFFFFFFFF),
|
onSecondary = Color(0xFFFFFFFF), // Unread badge text
|
||||||
secondaryContainer = Color(0xFFFFDBCF),
|
secondaryContainer = Color(0xFFEBCDC2), // Navigation bar selector pill & progress indicator (remaining)
|
||||||
onSecondaryContainer = Color(0xFF3B0A00),
|
onSecondaryContainer = Color(0xFF3B0A00), // Navigation bar selector icon
|
||||||
tertiary = Color(0xFF6B5E2F),
|
tertiary = Color(0xFF6B5E2F), // Downloaded badge
|
||||||
onTertiary = Color(0xFFFFFFFF),
|
onTertiary = Color(0xFFFFFFFF), // Downloaded badge text
|
||||||
tertiaryContainer = Color(0xFFF5E2A7),
|
tertiaryContainer = Color(0xFFF5E2A7),
|
||||||
onTertiaryContainer = Color(0xFF231B00),
|
onTertiaryContainer = Color(0xFF231B00),
|
||||||
background = Color(0xFFFCFCFC),
|
background = Color(0xFFFCFCFC),
|
||||||
onBackground = Color(0xFF211A18),
|
onBackground = Color(0xFF211A18),
|
||||||
surface = Color(0xFFFCFCFC),
|
surface = Color(0xFFFCFCFC),
|
||||||
onSurface = Color(0xFF211A18),
|
onSurface = Color(0xFF211A18),
|
||||||
surfaceVariant = Color(0xFFF5DED8),
|
surfaceVariant = Color(0xFFF6EBE7), // Navigation bar background (ThemePrefWidget)
|
||||||
onSurfaceVariant = Color(0xFF53433F),
|
onSurfaceVariant = Color(0xFF53433F),
|
||||||
surfaceTint = Color(0xFFAE3200),
|
surfaceTint = Color(0xFFAE3200),
|
||||||
inverseSurface = Color(0xFF362F2D),
|
inverseSurface = Color(0xFF362F2D),
|
||||||
inverseOnSurface = Color(0xFFFBEEEB),
|
inverseOnSurface = Color(0xFFFBEEEB),
|
||||||
outline = Color(0xFF85736E),
|
outline = Color(0xFF85736E),
|
||||||
|
surfaceContainerLowest = Color(0xFFECE3E0),
|
||||||
|
surfaceContainerLow = Color(0xFFF1E7E4),
|
||||||
|
surfaceContainer = Color(0xFFF6EBE7), // Navigation bar background
|
||||||
|
surfaceContainerHigh = Color(0xFFFAF4F2),
|
||||||
|
surfaceContainerHighest = Color(0xFFFBF6F4),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ private fun TrackInfoItem(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(top = 12.dp)
|
.padding(top = 12.dp)
|
||||||
.clip(MaterialTheme.shapes.medium)
|
.clip(MaterialTheme.shapes.medium)
|
||||||
.background(MaterialTheme.colorScheme.surface)
|
.background(MaterialTheme.colorScheme.surfaceContainerHighest)
|
||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
.clip(RoundedCornerShape(6.dp)),
|
.clip(RoundedCornerShape(6.dp)),
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import eu.kanade.domain.ui.UiPreferences
|
|||||||
import eu.kanade.domain.ui.model.setAppCompatDelegateThemeMode
|
import eu.kanade.domain.ui.model.setAppCompatDelegateThemeMode
|
||||||
import eu.kanade.tachiyomi.crash.CrashActivity
|
import eu.kanade.tachiyomi.crash.CrashActivity
|
||||||
import eu.kanade.tachiyomi.crash.GlobalExceptionHandler
|
import eu.kanade.tachiyomi.crash.GlobalExceptionHandler
|
||||||
|
import eu.kanade.tachiyomi.data.coil.BufferedSourceFetcher
|
||||||
import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher
|
import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher
|
||||||
import eu.kanade.tachiyomi.data.coil.MangaCoverKeyer
|
import eu.kanade.tachiyomi.data.coil.MangaCoverKeyer
|
||||||
import eu.kanade.tachiyomi.data.coil.MangaKeyer
|
import eu.kanade.tachiyomi.data.coil.MangaKeyer
|
||||||
@@ -208,6 +209,7 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
|
|||||||
add(MangaCoverFetcher.MangaCoverFactory(callFactoryLazy))
|
add(MangaCoverFetcher.MangaCoverFactory(callFactoryLazy))
|
||||||
add(MangaKeyer())
|
add(MangaKeyer())
|
||||||
add(MangaCoverKeyer())
|
add(MangaCoverKeyer())
|
||||||
|
add(BufferedSourceFetcher.Factory())
|
||||||
// SY -->
|
// SY -->
|
||||||
add(PagePreviewKeyer())
|
add(PagePreviewKeyer())
|
||||||
add(PagePreviewFetcher.Factory(callFactoryLazy))
|
add(PagePreviewFetcher.Factory(callFactoryLazy))
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package eu.kanade.tachiyomi.data.coil
|
||||||
|
|
||||||
|
import coil3.ImageLoader
|
||||||
|
import coil3.decode.DataSource
|
||||||
|
import coil3.decode.ImageSource
|
||||||
|
import coil3.fetch.FetchResult
|
||||||
|
import coil3.fetch.Fetcher
|
||||||
|
import coil3.fetch.SourceFetchResult
|
||||||
|
import coil3.request.Options
|
||||||
|
import okio.BufferedSource
|
||||||
|
|
||||||
|
class BufferedSourceFetcher(
|
||||||
|
private val data: BufferedSource,
|
||||||
|
private val options: Options,
|
||||||
|
) : Fetcher {
|
||||||
|
|
||||||
|
override suspend fun fetch(): FetchResult {
|
||||||
|
return SourceFetchResult(
|
||||||
|
source = ImageSource(
|
||||||
|
source = data,
|
||||||
|
fileSystem = options.fileSystem,
|
||||||
|
),
|
||||||
|
mimeType = null,
|
||||||
|
dataSource = DataSource.MEMORY,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Factory : Fetcher.Factory<BufferedSource> {
|
||||||
|
|
||||||
|
override fun create(
|
||||||
|
data: BufferedSource,
|
||||||
|
options: Options,
|
||||||
|
imageLoader: ImageLoader,
|
||||||
|
): Fetcher {
|
||||||
|
return BufferedSourceFetcher(data, options)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,18 @@
|
|||||||
package eu.kanade.tachiyomi.data.coil
|
package eu.kanade.tachiyomi.data.coil
|
||||||
|
|
||||||
|
import android.graphics.Bitmap
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.core.graphics.drawable.toDrawable
|
|
||||||
import coil3.ImageLoader
|
import coil3.ImageLoader
|
||||||
import coil3.asCoilImage
|
import coil3.asCoilImage
|
||||||
import coil3.decode.DecodeResult
|
import coil3.decode.DecodeResult
|
||||||
|
import coil3.decode.DecodeUtils
|
||||||
import coil3.decode.Decoder
|
import coil3.decode.Decoder
|
||||||
import coil3.decode.ImageSource
|
import coil3.decode.ImageSource
|
||||||
import coil3.fetch.SourceFetchResult
|
import coil3.fetch.SourceFetchResult
|
||||||
import coil3.request.Options
|
import coil3.request.Options
|
||||||
|
import coil3.request.bitmapConfig
|
||||||
import eu.kanade.tachiyomi.util.storage.CbzCrypto
|
import eu.kanade.tachiyomi.util.storage.CbzCrypto
|
||||||
|
import eu.kanade.tachiyomi.util.system.GLUtil
|
||||||
import net.lingala.zip4j.ZipFile
|
import net.lingala.zip4j.ZipFile
|
||||||
import net.lingala.zip4j.model.FileHeader
|
import net.lingala.zip4j.model.FileHeader
|
||||||
import okio.BufferedSource
|
import okio.BufferedSource
|
||||||
@@ -38,29 +41,58 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti
|
|||||||
}
|
}
|
||||||
val decoder = resources.sourceOrNull()?.use {
|
val decoder = resources.sourceOrNull()?.use {
|
||||||
zip4j.use { zipFile ->
|
zip4j.use { zipFile ->
|
||||||
ImageDecoder.newInstance(zipFile?.getInputStream(entry) ?: it.inputStream())
|
ImageDecoder.newInstance(zipFile?.getInputStream(entry) ?: it.inputStream(), options.cropBorders, displayProfile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
check(decoder != null && decoder.width > 0 && decoder.height > 0) { "Failed to initialize decoder" }
|
check(decoder != null && decoder.width > 0 && decoder.height > 0) { "Failed to initialize decoder" }
|
||||||
|
|
||||||
val bitmap = decoder.decode()
|
val srcWidth = decoder.width
|
||||||
|
val srcHeight = decoder.height
|
||||||
|
|
||||||
|
val dstWidth = options.size.widthPx(options.scale) { srcWidth }
|
||||||
|
val dstHeight = options.size.heightPx(options.scale) { srcHeight }
|
||||||
|
|
||||||
|
val sampleSize = DecodeUtils.calculateInSampleSize(
|
||||||
|
srcWidth = srcWidth,
|
||||||
|
srcHeight = srcHeight,
|
||||||
|
dstWidth = dstWidth,
|
||||||
|
dstHeight = dstHeight,
|
||||||
|
scale = options.scale,
|
||||||
|
)
|
||||||
|
|
||||||
|
var bitmap = decoder.decode(sampleSize = sampleSize)
|
||||||
decoder.recycle()
|
decoder.recycle()
|
||||||
|
|
||||||
check(bitmap != null) { "Failed to decode image" }
|
check(bitmap != null) { "Failed to decode image" }
|
||||||
|
|
||||||
|
if (
|
||||||
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
|
||||||
|
options.bitmapConfig == Bitmap.Config.HARDWARE &&
|
||||||
|
maxOf(bitmap.width, bitmap.height) <= GLUtil.maxTextureSize
|
||||||
|
) {
|
||||||
|
val hwBitmap = bitmap.copy(Bitmap.Config.HARDWARE, false)
|
||||||
|
if (hwBitmap != null) {
|
||||||
|
bitmap.recycle()
|
||||||
|
bitmap = hwBitmap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return DecodeResult(
|
return DecodeResult(
|
||||||
image = bitmap.asCoilImage(),
|
image = bitmap.asCoilImage(),
|
||||||
isSampled = false,
|
isSampled = sampleSize > 1,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
class Factory : Decoder.Factory {
|
class Factory : Decoder.Factory {
|
||||||
|
|
||||||
override fun create(result: SourceFetchResult, options: Options, imageLoader: ImageLoader): Decoder? {
|
override fun create(result: SourceFetchResult, options: Options, imageLoader: ImageLoader): Decoder? {
|
||||||
if (!isApplicable(result.source.source())) return null
|
return if (options.customDecoder || isApplicable(result.source.source())) {
|
||||||
return TachiyomiImageDecoder(result.source, options)
|
TachiyomiImageDecoder(result.source, options)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isApplicable(source: BufferedSource): Boolean {
|
private fun isApplicable(source: BufferedSource): Boolean {
|
||||||
@@ -83,4 +115,8 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti
|
|||||||
|
|
||||||
override fun hashCode() = javaClass.hashCode()
|
override fun hashCode() = javaClass.hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
var displayProfile: ByteArray? = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package eu.kanade.tachiyomi.data.coil
|
||||||
|
|
||||||
|
import coil3.Extras
|
||||||
|
import coil3.getExtra
|
||||||
|
import coil3.request.ImageRequest
|
||||||
|
import coil3.request.Options
|
||||||
|
import coil3.size.Dimension
|
||||||
|
import coil3.size.Scale
|
||||||
|
import coil3.size.Size
|
||||||
|
import coil3.size.isOriginal
|
||||||
|
import coil3.size.pxOrElse
|
||||||
|
|
||||||
|
internal inline fun Size.widthPx(scale: Scale, original: () -> Int): Int {
|
||||||
|
return if (isOriginal) original() else width.toPx(scale)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal inline fun Size.heightPx(scale: Scale, original: () -> Int): Int {
|
||||||
|
return if (isOriginal) original() else height.toPx(scale)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun Dimension.toPx(scale: Scale): Int = pxOrElse {
|
||||||
|
when (scale) {
|
||||||
|
Scale.FILL -> Int.MIN_VALUE
|
||||||
|
Scale.FIT -> Int.MAX_VALUE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ImageRequest.Builder.cropBorders(enable: Boolean) = apply {
|
||||||
|
extras[cropBordersKey] = enable
|
||||||
|
}
|
||||||
|
|
||||||
|
val Options.cropBorders: Boolean
|
||||||
|
get() = getExtra(cropBordersKey)
|
||||||
|
|
||||||
|
private val cropBordersKey = Extras.Key(default = false)
|
||||||
|
|
||||||
|
fun ImageRequest.Builder.customDecoder(enable: Boolean) = apply {
|
||||||
|
extras[customDecoderKey] = enable
|
||||||
|
}
|
||||||
|
|
||||||
|
val Options.customDecoder: Boolean
|
||||||
|
get() = getExtra(customDecoderKey)
|
||||||
|
|
||||||
|
private val customDecoderKey = Extras.Key(default = false)
|
||||||
@@ -7,6 +7,7 @@ import eu.kanade.tachiyomi.source.Source
|
|||||||
import eu.kanade.tachiyomi.source.model.Page
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||||
import exh.log.xLogE
|
import exh.log.xLogE
|
||||||
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.asFlow
|
import kotlinx.coroutines.flow.asFlow
|
||||||
import kotlinx.coroutines.flow.drop
|
import kotlinx.coroutines.flow.drop
|
||||||
@@ -35,6 +36,7 @@ import uy.kohesive.injekt.api.get
|
|||||||
* and retrieved through dependency injection. You can use this class to queue new chapters or query
|
* and retrieved through dependency injection. You can use this class to queue new chapters or query
|
||||||
* downloaded chapters.
|
* downloaded chapters.
|
||||||
*/
|
*/
|
||||||
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
class DownloadManager(
|
class DownloadManager(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val provider: DownloadProvider = Injekt.get(),
|
private val provider: DownloadProvider = Injekt.get(),
|
||||||
@@ -475,7 +477,7 @@ class DownloadManager(
|
|||||||
|
|
||||||
fun renameMangaDir(oldTitle: String, newTitle: String, source: Long) {
|
fun renameMangaDir(oldTitle: String, newTitle: String, source: Long) {
|
||||||
val sourceDir = provider.findSourceDir(sourceManager.getOrStub(source)) ?: return
|
val sourceDir = provider.findSourceDir(sourceManager.getOrStub(source)) ?: return
|
||||||
val mangaDir = sourceDir.findFile(DiskUtil.buildValidFilename(oldTitle), true) ?: return
|
val mangaDir = sourceDir.findFile(DiskUtil.buildValidFilename(oldTitle)) ?: return
|
||||||
mangaDir.renameTo(DiskUtil.buildValidFilename(newTitle))
|
mangaDir.renameTo(DiskUtil.buildValidFilename(newTitle))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ class DownloadProvider(
|
|||||||
* @param source the source to query.
|
* @param source the source to query.
|
||||||
*/
|
*/
|
||||||
fun findSourceDir(source: Source): UniFile? {
|
fun findSourceDir(source: Source): UniFile? {
|
||||||
return downloadsDir?.findFile(getSourceDirName(source), true)
|
return downloadsDir?.findFile(getSourceDirName(source))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -68,7 +68,7 @@ class DownloadProvider(
|
|||||||
*/
|
*/
|
||||||
fun findMangaDir(mangaTitle: String, source: Source): UniFile? {
|
fun findMangaDir(mangaTitle: String, source: Source): UniFile? {
|
||||||
val sourceDir = findSourceDir(source)
|
val sourceDir = findSourceDir(source)
|
||||||
return sourceDir?.findFile(getMangaDirName(mangaTitle), true)
|
return sourceDir?.findFile(getMangaDirName(mangaTitle))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -82,7 +82,7 @@ class DownloadProvider(
|
|||||||
fun findChapterDir(chapterName: String, chapterScanlator: String?, mangaTitle: String, source: Source): UniFile? {
|
fun findChapterDir(chapterName: String, chapterScanlator: String?, mangaTitle: String, source: Source): UniFile? {
|
||||||
val mangaDir = findMangaDir(mangaTitle, source)
|
val mangaDir = findMangaDir(mangaTitle, source)
|
||||||
return getValidChapterDirNames(chapterName, chapterScanlator).asSequence()
|
return getValidChapterDirNames(chapterName, chapterScanlator).asSequence()
|
||||||
.mapNotNull { mangaDir?.findFile(it, true) }
|
.mapNotNull { mangaDir?.findFile(it) }
|
||||||
.firstOrNull()
|
.firstOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,7 +97,7 @@ class DownloadProvider(
|
|||||||
val mangaDir = findMangaDir(/* SY --> */ manga.ogTitle /* SY <-- */, source) ?: return null to emptyList()
|
val mangaDir = findMangaDir(/* SY --> */ manga.ogTitle /* SY <-- */, source) ?: return null to emptyList()
|
||||||
return mangaDir to chapters.mapNotNull { chapter ->
|
return mangaDir to chapters.mapNotNull { chapter ->
|
||||||
getValidChapterDirNames(chapter.name, chapter.scanlator).asSequence()
|
getValidChapterDirNames(chapter.name, chapter.scanlator).asSequence()
|
||||||
.mapNotNull { mangaDir.findFile(it, true) }
|
.mapNotNull { mangaDir.findFile(it) }
|
||||||
.firstOrNull()
|
.firstOrNull()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,10 +16,12 @@ import eu.kanade.tachiyomi.util.storage.CbzCrypto
|
|||||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||||
import eu.kanade.tachiyomi.util.storage.DiskUtil.NOMEDIA_FILE
|
import eu.kanade.tachiyomi.util.storage.DiskUtil.NOMEDIA_FILE
|
||||||
import eu.kanade.tachiyomi.util.storage.saveTo
|
import eu.kanade.tachiyomi.util.storage.saveTo
|
||||||
|
import exh.source.isEhBasedSource
|
||||||
import exh.util.DataSaver
|
import exh.util.DataSaver
|
||||||
import exh.util.DataSaver.Companion.getImage
|
import exh.util.DataSaver.Companion.getImage
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
@@ -75,6 +77,7 @@ import java.util.zip.ZipOutputStream
|
|||||||
*
|
*
|
||||||
* Its queue contains the list of chapters to download.
|
* Its queue contains the list of chapters to download.
|
||||||
*/
|
*/
|
||||||
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
class Downloader(
|
class Downloader(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val provider: DownloadProvider,
|
private val provider: DownloadProvider,
|
||||||
@@ -511,6 +514,9 @@ class Downloader(
|
|||||||
.retryWhen { _, attempt ->
|
.retryWhen { _, attempt ->
|
||||||
if (attempt < 3) {
|
if (attempt < 3) {
|
||||||
delay((2L shl attempt.toInt()) * 1000)
|
delay((2L shl attempt.toInt()) * 1000)
|
||||||
|
if (source.isEhBasedSource()) {
|
||||||
|
page.imageUrl = source.getImageUrl(page)
|
||||||
|
}
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
@@ -709,7 +715,7 @@ class Downloader(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Remove the old file
|
// Remove the old file
|
||||||
dir.findFile(COMIC_INFO_FILE, true)?.delete()
|
dir.findFile(COMIC_INFO_FILE)?.delete()
|
||||||
dir.createFile(COMIC_INFO_FILE)!!.openOutputStream().use {
|
dir.createFile(COMIC_INFO_FILE)!!.openOutputStream().use {
|
||||||
val comicInfoString = xml.encodeToString(ComicInfo.serializer(), comicInfo)
|
val comicInfoString = xml.encodeToString(ComicInfo.serializer(), comicInfo)
|
||||||
it.write(comicInfoString.toByteArray())
|
it.write(comicInfoString.toByteArray())
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import eu.kanade.tachiyomi.util.system.cancelNotification
|
|||||||
import eu.kanade.tachiyomi.util.system.getBitmapOrNull
|
import eu.kanade.tachiyomi.util.system.getBitmapOrNull
|
||||||
import eu.kanade.tachiyomi.util.system.notificationBuilder
|
import eu.kanade.tachiyomi.util.system.notificationBuilder
|
||||||
import eu.kanade.tachiyomi.util.system.notify
|
import eu.kanade.tachiyomi.util.system.notify
|
||||||
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import tachiyomi.core.common.Constants
|
import tachiyomi.core.common.Constants
|
||||||
import tachiyomi.core.common.i18n.pluralStringResource
|
import tachiyomi.core.common.i18n.pluralStringResource
|
||||||
import tachiyomi.core.common.i18n.stringResource
|
import tachiyomi.core.common.i18n.stringResource
|
||||||
@@ -41,6 +42,7 @@ import uy.kohesive.injekt.api.get
|
|||||||
import java.math.RoundingMode
|
import java.math.RoundingMode
|
||||||
import java.text.NumberFormat
|
import java.text.NumberFormat
|
||||||
|
|
||||||
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
class LibraryUpdateNotifier(
|
class LibraryUpdateNotifier(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import eu.kanade.tachiyomi.util.system.getParcelableExtraCompat
|
|||||||
import eu.kanade.tachiyomi.util.system.notificationManager
|
import eu.kanade.tachiyomi.util.system.notificationManager
|
||||||
import eu.kanade.tachiyomi.util.system.toShareIntent
|
import eu.kanade.tachiyomi.util.system.toShareIntent
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import tachiyomi.core.common.Constants
|
import tachiyomi.core.common.Constants
|
||||||
import tachiyomi.core.common.util.lang.launchIO
|
import tachiyomi.core.common.util.lang.launchIO
|
||||||
@@ -41,6 +42,7 @@ import eu.kanade.tachiyomi.BuildConfig.APPLICATION_ID as ID
|
|||||||
* Pending Broadcasts should be made from here.
|
* Pending Broadcasts should be made from here.
|
||||||
* NOTE: Use local broadcasts if possible.
|
* NOTE: Use local broadcasts if possible.
|
||||||
*/
|
*/
|
||||||
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
class NotificationReceiver : BroadcastReceiver() {
|
class NotificationReceiver : BroadcastReceiver() {
|
||||||
|
|
||||||
private val getManga: GetManga by injectLazy()
|
private val getManga: GetManga by injectLazy()
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class SyncDataJob(private val context: Context, workerParams: WorkerParameters)
|
|||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logcat(LogPriority.ERROR, e)
|
logcat(LogPriority.ERROR, e)
|
||||||
notifier.showSyncError(e.message)
|
notifier.showSyncError(e.message)
|
||||||
Result.failure()
|
Result.success() // try again next time
|
||||||
} finally {
|
} finally {
|
||||||
context.cancelNotification(Notifications.ID_RESTORE_PROGRESS)
|
context.cancelNotification(Notifications.ID_RESTORE_PROGRESS)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ class SyncManager(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Stop the sync early if the remote backup is null or empty
|
// Stop the sync early if the remote backup is null or empty
|
||||||
if (remoteBackup.backupManga?.size == 0) {
|
if (remoteBackup.backupManga.size == 0) {
|
||||||
notifier.showSyncError("No data found on remote server.")
|
notifier.showSyncError("No data found on remote server.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,8 @@ import exh.source.BlacklistedSources
|
|||||||
import exh.source.EH_SOURCE_ID
|
import exh.source.EH_SOURCE_ID
|
||||||
import exh.source.EXH_SOURCE_ID
|
import exh.source.EXH_SOURCE_ID
|
||||||
import exh.source.MERGED_SOURCE_ID
|
import exh.source.MERGED_SOURCE_ID
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.async
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
@@ -32,7 +31,6 @@ import kotlinx.coroutines.flow.emptyFlow
|
|||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import tachiyomi.core.common.util.lang.launchNow
|
|
||||||
import tachiyomi.core.common.util.lang.withUIContext
|
import tachiyomi.core.common.util.lang.withUIContext
|
||||||
import tachiyomi.core.common.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
import tachiyomi.domain.source.model.StubSource
|
import tachiyomi.domain.source.model.StubSource
|
||||||
@@ -54,10 +52,10 @@ class ExtensionManager(
|
|||||||
private val trustExtension: TrustExtension = Injekt.get(),
|
private val trustExtension: TrustExtension = Injekt.get(),
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// SY -->
|
val scope = CoroutineScope(SupervisorJob())
|
||||||
|
|
||||||
private val _isInitialized = MutableStateFlow(false)
|
private val _isInitialized = MutableStateFlow(false)
|
||||||
val isInitialized: StateFlow<Boolean> = _isInitialized.asStateFlow()
|
val isInitialized: StateFlow<Boolean> = _isInitialized.asStateFlow()
|
||||||
// SY <--
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API where all the available extensions can be found.
|
* API where all the available extensions can be found.
|
||||||
@@ -71,13 +69,31 @@ class ExtensionManager(
|
|||||||
|
|
||||||
private val iconMap = mutableMapOf<String, Drawable>()
|
private val iconMap = mutableMapOf<String, Drawable>()
|
||||||
|
|
||||||
private val _installedExtensionsFlow = MutableStateFlow(emptyList<Extension.Installed>())
|
private val _installedExtensionsMapFlow = MutableStateFlow(emptyMap<String, Extension.Installed>())
|
||||||
val installedExtensionsFlow = _installedExtensionsFlow.asStateFlow()
|
val installedExtensionsFlow = _installedExtensionsMapFlow.mapExtensions(scope)
|
||||||
|
|
||||||
|
private val _availableExtensionsMapFlow = MutableStateFlow(emptyMap<String, Extension.Available>())
|
||||||
|
// SY -->
|
||||||
|
val availableExtensionsFlow = _availableExtensionsMapFlow.map { it.filterNotBlacklisted().values.toList() }
|
||||||
|
.stateIn(scope, SharingStarted.Lazily, _availableExtensionsMapFlow.value.values.toList())
|
||||||
|
// SY <--
|
||||||
|
|
||||||
|
private val _untrustedExtensionsMapFlow = MutableStateFlow(emptyMap<String, Extension.Untrusted>())
|
||||||
|
val untrustedExtensionsFlow = _untrustedExtensionsMapFlow.mapExtensions(scope)
|
||||||
|
|
||||||
|
init {
|
||||||
|
initExtensions()
|
||||||
|
ExtensionInstallReceiver(InstallationListener()).register(context)
|
||||||
|
}
|
||||||
|
|
||||||
private var subLanguagesEnabledOnFirstRun = preferences.enabledLanguages().isSet()
|
private var subLanguagesEnabledOnFirstRun = preferences.enabledLanguages().isSet()
|
||||||
|
|
||||||
fun getAppIconForSource(sourceId: Long): Drawable? {
|
fun getAppIconForSource(sourceId: Long): Drawable? {
|
||||||
val pkgName = _installedExtensionsFlow.value.find { ext -> ext.sources.any { it.id == sourceId } }?.pkgName
|
val pkgName = _installedExtensionsMapFlow.value.values
|
||||||
|
.find { ext ->
|
||||||
|
ext.sources.any { it.id == sourceId }
|
||||||
|
}
|
||||||
|
?.pkgName
|
||||||
if (pkgName != null) {
|
if (pkgName != null) {
|
||||||
return iconMap[pkgName] ?: iconMap.getOrPut(pkgName) {
|
return iconMap[pkgName] ?: iconMap.getOrPut(pkgName) {
|
||||||
ExtensionLoader.getExtensionPackageInfoFromPkgName(context, pkgName)!!.applicationInfo
|
ExtensionLoader.getExtensionPackageInfoFromPkgName(context, pkgName)!!.applicationInfo
|
||||||
@@ -95,15 +111,6 @@ class ExtensionManager(
|
|||||||
// SY <--
|
// SY <--
|
||||||
}
|
}
|
||||||
|
|
||||||
private val _availableExtensionsFlow = MutableStateFlow(emptyList<Extension.Available>())
|
|
||||||
|
|
||||||
// SY -->
|
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
|
||||||
val availableExtensionsFlow = _availableExtensionsFlow
|
|
||||||
.map { it.filterNotBlacklisted() }
|
|
||||||
.stateIn(GlobalScope, SharingStarted.Eagerly, emptyList())
|
|
||||||
// SY <--
|
|
||||||
|
|
||||||
private var availableExtensionsSourcesData: Map<Long, StubSource> = emptyMap()
|
private var availableExtensionsSourcesData: Map<Long, StubSource> = emptyMap()
|
||||||
|
|
||||||
private fun setupAvailableExtensionsSourcesDataMap(extensions: List<Extension.Available>) {
|
private fun setupAvailableExtensionsSourcesDataMap(extensions: List<Extension.Available>) {
|
||||||
@@ -115,38 +122,30 @@ class ExtensionManager(
|
|||||||
|
|
||||||
fun getSourceData(id: Long) = availableExtensionsSourcesData[id]
|
fun getSourceData(id: Long) = availableExtensionsSourcesData[id]
|
||||||
|
|
||||||
private val _untrustedExtensionsFlow = MutableStateFlow(emptyList<Extension.Untrusted>())
|
|
||||||
val untrustedExtensionsFlow = _untrustedExtensionsFlow.asStateFlow()
|
|
||||||
|
|
||||||
init {
|
|
||||||
initExtensions()
|
|
||||||
ExtensionInstallReceiver(InstallationListener()).register(context)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads and registers the installed extensions.
|
* Loads and registers the installed extensions.
|
||||||
*/
|
*/
|
||||||
private fun initExtensions() {
|
private fun initExtensions() {
|
||||||
val extensions = ExtensionLoader.loadExtensions(context)
|
val extensions = ExtensionLoader.loadExtensions(context)
|
||||||
|
|
||||||
_installedExtensionsFlow.value = extensions
|
_installedExtensionsMapFlow.value = extensions
|
||||||
.filterIsInstance<LoadResult.Success>()
|
.filterIsInstance<LoadResult.Success>()
|
||||||
.map { it.extension }
|
.associate { it.extension.pkgName to it.extension }
|
||||||
|
|
||||||
_untrustedExtensionsFlow.value = extensions
|
_untrustedExtensionsMapFlow.value = extensions
|
||||||
.filterIsInstance<LoadResult.Untrusted>()
|
.filterIsInstance<LoadResult.Untrusted>()
|
||||||
.map { it.extension }
|
.associate { it.extension.pkgName to it.extension }
|
||||||
// SY -->
|
// SY -->
|
||||||
.filterNotBlacklisted()
|
.filterNotBlacklisted()
|
||||||
|
// SY <--
|
||||||
|
|
||||||
_isInitialized.value = true
|
_isInitialized.value = true
|
||||||
// SY <--
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EXH -->
|
// EXH -->
|
||||||
private fun <T : Extension> Iterable<T>.filterNotBlacklisted(): List<T> {
|
private fun <T : Extension> Map<String, T>.filterNotBlacklisted(): Map<String, T> {
|
||||||
val blacklistEnabled = preferences.enableSourceBlacklist().get()
|
val blacklistEnabled = preferences.enableSourceBlacklist().get()
|
||||||
return filterNot { extension ->
|
return filterNot { (_, extension) ->
|
||||||
extension.isBlacklisted(blacklistEnabled)
|
extension.isBlacklisted(blacklistEnabled)
|
||||||
.also {
|
.also {
|
||||||
if (it) this@ExtensionManager.xLogD("Removing blacklisted extension: (name: %s, pkgName: %s)!", extension.name, extension.pkgName)
|
if (it) this@ExtensionManager.xLogD("Removing blacklisted extension: (name: %s, pkgName: %s)!", extension.name, extension.pkgName)
|
||||||
@@ -160,7 +159,7 @@ class ExtensionManager(
|
|||||||
// EXH <--
|
// EXH <--
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the available extensions in the [api] and updates [availableExtensions].
|
* Finds the available extensions in the [api] and updates [_availableExtensionsMapFlow].
|
||||||
*/
|
*/
|
||||||
suspend fun findAvailableExtensions() {
|
suspend fun findAvailableExtensions() {
|
||||||
val extensions: List<Extension.Available> = try {
|
val extensions: List<Extension.Available> = try {
|
||||||
@@ -173,7 +172,7 @@ class ExtensionManager(
|
|||||||
|
|
||||||
enableAdditionalSubLanguages(extensions)
|
enableAdditionalSubLanguages(extensions)
|
||||||
|
|
||||||
_availableExtensionsFlow.value = extensions
|
_availableExtensionsMapFlow.value = extensions.associateBy { it.pkgName }
|
||||||
updatedInstalledExtensionsStatuses(extensions)
|
updatedInstalledExtensionsStatuses(extensions)
|
||||||
setupAvailableExtensionsSourcesDataMap(extensions)
|
setupAvailableExtensionsSourcesDataMap(extensions)
|
||||||
}
|
}
|
||||||
@@ -219,42 +218,36 @@ class ExtensionManager(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val mutInstalledExtensions = _installedExtensionsFlow.value.toMutableList()
|
val installedExtensionsMap = _installedExtensionsMapFlow.value.toMutableMap()
|
||||||
var changed = false
|
var changed = false
|
||||||
|
for ((pkgName, extension) in installedExtensionsMap) {
|
||||||
|
val availableExt = availableExtensions.find { it.pkgName == pkgName }
|
||||||
|
|
||||||
for ((index, installedExt) in mutInstalledExtensions.withIndex()) {
|
if (availableExt == null && !extension.isObsolete) {
|
||||||
val pkgName = installedExt.pkgName
|
installedExtensionsMap[pkgName] = extension.copy(isObsolete = true)
|
||||||
// SY -->
|
|
||||||
val availableExt = _availableExtensionsFlow.value.find { it.pkgName == pkgName }
|
|
||||||
// SY <--
|
|
||||||
|
|
||||||
if (availableExt == null && !installedExt.isObsolete) {
|
|
||||||
mutInstalledExtensions[index] = installedExt.copy(isObsolete = true)
|
|
||||||
changed = true
|
changed = true
|
||||||
// SY -->
|
// SY -->
|
||||||
} else if (installedExt.isBlacklisted() && !installedExt.isRedundant) {
|
} else if (extension.isBlacklisted() && !extension.isRedundant) {
|
||||||
mutInstalledExtensions[index] = installedExt.copy(isRedundant = true)
|
installedExtensionsMap[pkgName] = extension.copy(isRedundant = true)
|
||||||
changed = true
|
changed = true
|
||||||
// SY <--
|
// SY <--
|
||||||
} else if (availableExt != null) {
|
} else if (availableExt != null) {
|
||||||
val hasUpdate = installedExt.updateExists(availableExt)
|
val hasUpdate = extension.updateExists(availableExt)
|
||||||
|
if (extension.hasUpdate != hasUpdate) {
|
||||||
if (installedExt.hasUpdate != hasUpdate) {
|
installedExtensionsMap[pkgName] = extension.copy(
|
||||||
mutInstalledExtensions[index] = installedExt.copy(
|
|
||||||
hasUpdate = hasUpdate,
|
hasUpdate = hasUpdate,
|
||||||
repoUrl = availableExt.repoUrl,
|
repoUrl = availableExt.repoUrl,
|
||||||
)
|
)
|
||||||
changed = true
|
|
||||||
} else {
|
} else {
|
||||||
mutInstalledExtensions[index] = installedExt.copy(
|
installedExtensionsMap[pkgName] = extension.copy(
|
||||||
repoUrl = availableExt.repoUrl,
|
repoUrl = availableExt.repoUrl,
|
||||||
)
|
)
|
||||||
changed = true
|
|
||||||
}
|
}
|
||||||
|
changed = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (changed) {
|
if (changed) {
|
||||||
_installedExtensionsFlow.value = mutInstalledExtensions
|
_installedExtensionsMapFlow.value = installedExtensionsMap
|
||||||
}
|
}
|
||||||
updatePendingUpdatesCount()
|
updatePendingUpdatesCount()
|
||||||
}
|
}
|
||||||
@@ -278,8 +271,7 @@ class ExtensionManager(
|
|||||||
* @param extension The extension to be updated.
|
* @param extension The extension to be updated.
|
||||||
*/
|
*/
|
||||||
fun updateExtension(extension: Extension.Installed): Flow<InstallStep> {
|
fun updateExtension(extension: Extension.Installed): Flow<InstallStep> {
|
||||||
val availableExt = _availableExtensionsFlow.value.find { it.pkgName == extension.pkgName }
|
val availableExt = _availableExtensionsMapFlow.value[extension.pkgName] ?: return emptyFlow()
|
||||||
?: return emptyFlow()
|
|
||||||
return installExtension(availableExt)
|
return installExtension(availableExt)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,24 +307,16 @@ class ExtensionManager(
|
|||||||
*
|
*
|
||||||
* @param extension the extension to trust
|
* @param extension the extension to trust
|
||||||
*/
|
*/
|
||||||
fun trust(extension: Extension.Untrusted) {
|
suspend fun trust(extension: Extension.Untrusted) {
|
||||||
val untrustedPkgNames = _untrustedExtensionsFlow.value.map { it.pkgName }.toSet()
|
_untrustedExtensionsMapFlow.value[extension.pkgName] ?: return
|
||||||
if (extension.pkgName !in untrustedPkgNames) return
|
|
||||||
|
|
||||||
trustExtension.trust(extension.pkgName, extension.versionCode, extension.signatureHash)
|
trustExtension.trust(extension.pkgName, extension.versionCode, extension.signatureHash)
|
||||||
|
|
||||||
val nowTrustedExtensions = _untrustedExtensionsFlow.value
|
_untrustedExtensionsMapFlow.value -= extension.pkgName
|
||||||
.filter { it.pkgName == extension.pkgName && it.versionCode == extension.versionCode }
|
|
||||||
_untrustedExtensionsFlow.value -= nowTrustedExtensions
|
|
||||||
|
|
||||||
launchNow {
|
ExtensionLoader.loadExtensionFromPkgName(context, extension.pkgName)
|
||||||
nowTrustedExtensions
|
.let { it as? LoadResult.Success }
|
||||||
.map { extension ->
|
?.let { registerNewExtension(it.extension) }
|
||||||
async { ExtensionLoader.loadExtensionFromPkgName(context, extension.pkgName) }.await()
|
|
||||||
}
|
|
||||||
.filterIsInstance<LoadResult.Success>()
|
|
||||||
.forEach { registerNewExtension(it.extension) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -348,7 +332,7 @@ class ExtensionManager(
|
|||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
_installedExtensionsFlow.value += extension
|
_installedExtensionsMapFlow.value += extension
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -365,13 +349,7 @@ class ExtensionManager(
|
|||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
val mutInstalledExtensions = _installedExtensionsFlow.value.toMutableList()
|
_installedExtensionsMapFlow.value += extension
|
||||||
val oldExtension = mutInstalledExtensions.find { it.pkgName == extension.pkgName }
|
|
||||||
if (oldExtension != null) {
|
|
||||||
mutInstalledExtensions -= oldExtension
|
|
||||||
}
|
|
||||||
mutInstalledExtensions += extension
|
|
||||||
_installedExtensionsFlow.value = mutInstalledExtensions
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -381,14 +359,8 @@ class ExtensionManager(
|
|||||||
* @param pkgName The package name of the uninstalled application.
|
* @param pkgName The package name of the uninstalled application.
|
||||||
*/
|
*/
|
||||||
private fun unregisterExtension(pkgName: String) {
|
private fun unregisterExtension(pkgName: String) {
|
||||||
val installedExtension = _installedExtensionsFlow.value.find { it.pkgName == pkgName }
|
_installedExtensionsMapFlow.value -= pkgName
|
||||||
if (installedExtension != null) {
|
_untrustedExtensionsMapFlow.value -= pkgName
|
||||||
_installedExtensionsFlow.value -= installedExtension
|
|
||||||
}
|
|
||||||
val untrustedExtension = _untrustedExtensionsFlow.value.find { it.pkgName == pkgName }
|
|
||||||
if (untrustedExtension != null) {
|
|
||||||
_untrustedExtensionsFlow.value -= untrustedExtension
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -407,14 +379,9 @@ class ExtensionManager(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onExtensionUntrusted(extension: Extension.Untrusted) {
|
override fun onExtensionUntrusted(extension: Extension.Untrusted) {
|
||||||
val installedExtension = _installedExtensionsFlow.value
|
_installedExtensionsMapFlow.value -= extension.pkgName
|
||||||
.find { it.pkgName == extension.pkgName }
|
_untrustedExtensionsMapFlow.value += extension
|
||||||
|
updatePendingUpdatesCount()
|
||||||
if (installedExtension != null) {
|
|
||||||
_installedExtensionsFlow.value -= installedExtension
|
|
||||||
} else {
|
|
||||||
_untrustedExtensionsFlow.value += extension
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPackageUninstalled(pkgName: String) {
|
override fun onPackageUninstalled(pkgName: String) {
|
||||||
@@ -436,17 +403,24 @@ class ExtensionManager(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun Extension.Installed.updateExists(availableExtension: Extension.Available? = null): Boolean {
|
private fun Extension.Installed.updateExists(availableExtension: Extension.Available? = null): Boolean {
|
||||||
val availableExt = availableExtension ?: _availableExtensionsFlow.value.find { it.pkgName == pkgName }
|
val availableExt = availableExtension
|
||||||
|
?: _availableExtensionsMapFlow.value[pkgName]
|
||||||
?: return false
|
?: return false
|
||||||
|
|
||||||
return (availableExt.versionCode > versionCode || availableExt.libVersion > libVersion)
|
return (availableExt.versionCode > versionCode || availableExt.libVersion > libVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updatePendingUpdatesCount() {
|
private fun updatePendingUpdatesCount() {
|
||||||
val pendingUpdateCount = _installedExtensionsFlow.value.count { it.hasUpdate }
|
val pendingUpdateCount = _installedExtensionsMapFlow.value.values.count { it.hasUpdate }
|
||||||
preferences.extensionUpdatesCount().set(pendingUpdateCount)
|
preferences.extensionUpdatesCount().set(pendingUpdateCount)
|
||||||
if (pendingUpdateCount == 0) {
|
if (pendingUpdateCount == 0) {
|
||||||
ExtensionUpdateNotifier(context).dismiss()
|
ExtensionUpdateNotifier(context).dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private operator fun <T : Extension> Map<String, T>.plus(extension: T) = plus(extension.pkgName to extension)
|
||||||
|
|
||||||
|
private fun <T : Extension> StateFlow<Map<String, T>>.mapExtensions(scope: CoroutineScope): StateFlow<List<T>> {
|
||||||
|
return map { it.values.toList() }.stateIn(scope, SharingStarted.Lazily, value.values.toList())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,12 +9,10 @@ import androidx.core.content.ContextCompat
|
|||||||
import eu.kanade.tachiyomi.BuildConfig
|
import eu.kanade.tachiyomi.BuildConfig
|
||||||
import eu.kanade.tachiyomi.extension.model.Extension
|
import eu.kanade.tachiyomi.extension.model.Extension
|
||||||
import eu.kanade.tachiyomi.extension.model.LoadResult
|
import eu.kanade.tachiyomi.extension.model.LoadResult
|
||||||
import kotlinx.coroutines.CoroutineStart
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.async
|
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import tachiyomi.core.common.util.lang.launchNow
|
|
||||||
import tachiyomi.core.common.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -23,29 +21,23 @@ import tachiyomi.core.common.util.system.logcat
|
|||||||
*
|
*
|
||||||
* @param listener The listener that should be notified of extension installation events.
|
* @param listener The listener that should be notified of extension installation events.
|
||||||
*/
|
*/
|
||||||
internal class ExtensionInstallReceiver(private val listener: Listener) :
|
internal class ExtensionInstallReceiver(private val listener: Listener) : BroadcastReceiver() {
|
||||||
BroadcastReceiver() {
|
|
||||||
|
val scope = CoroutineScope(SupervisorJob())
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers this broadcast receiver
|
|
||||||
*/
|
|
||||||
fun register(context: Context) {
|
fun register(context: Context) {
|
||||||
ContextCompat.registerReceiver(context, this, filter, ContextCompat.RECEIVER_NOT_EXPORTED)
|
ContextCompat.registerReceiver(context, this, filter, ContextCompat.RECEIVER_NOT_EXPORTED)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private val filter = IntentFilter().apply {
|
||||||
* Returns the intent filter this receiver should subscribe to.
|
addAction(Intent.ACTION_PACKAGE_ADDED)
|
||||||
*/
|
addAction(Intent.ACTION_PACKAGE_REPLACED)
|
||||||
private val filter
|
addAction(Intent.ACTION_PACKAGE_REMOVED)
|
||||||
get() = IntentFilter().apply {
|
addAction(ACTION_EXTENSION_ADDED)
|
||||||
addAction(Intent.ACTION_PACKAGE_ADDED)
|
addAction(ACTION_EXTENSION_REPLACED)
|
||||||
addAction(Intent.ACTION_PACKAGE_REPLACED)
|
addAction(ACTION_EXTENSION_REMOVED)
|
||||||
addAction(Intent.ACTION_PACKAGE_REMOVED)
|
addDataScheme("package")
|
||||||
addAction(ACTION_EXTENSION_ADDED)
|
}
|
||||||
addAction(ACTION_EXTENSION_REPLACED)
|
|
||||||
addAction(ACTION_EXTENSION_REMOVED)
|
|
||||||
addDataScheme("package")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when one of the events of the [filter] is received. When the package is an extension,
|
* Called when one of the events of the [filter] is received. When the package is an extension,
|
||||||
@@ -58,7 +50,7 @@ internal class ExtensionInstallReceiver(private val listener: Listener) :
|
|||||||
Intent.ACTION_PACKAGE_ADDED, ACTION_EXTENSION_ADDED -> {
|
Intent.ACTION_PACKAGE_ADDED, ACTION_EXTENSION_ADDED -> {
|
||||||
if (isReplacing(intent)) return
|
if (isReplacing(intent)) return
|
||||||
|
|
||||||
launchNow {
|
scope.launch {
|
||||||
when (val result = getExtensionFromIntent(context, intent)) {
|
when (val result = getExtensionFromIntent(context, intent)) {
|
||||||
is LoadResult.Success -> listener.onExtensionInstalled(result.extension)
|
is LoadResult.Success -> listener.onExtensionInstalled(result.extension)
|
||||||
is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension)
|
is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension)
|
||||||
@@ -67,7 +59,7 @@ internal class ExtensionInstallReceiver(private val listener: Listener) :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Intent.ACTION_PACKAGE_REPLACED, ACTION_EXTENSION_REPLACED -> {
|
Intent.ACTION_PACKAGE_REPLACED, ACTION_EXTENSION_REPLACED -> {
|
||||||
launchNow {
|
scope.launch {
|
||||||
when (val result = getExtensionFromIntent(context, intent)) {
|
when (val result = getExtensionFromIntent(context, intent)) {
|
||||||
is LoadResult.Success -> listener.onExtensionUpdated(result.extension)
|
is LoadResult.Success -> listener.onExtensionUpdated(result.extension)
|
||||||
is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension)
|
is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension)
|
||||||
@@ -107,9 +99,7 @@ internal class ExtensionInstallReceiver(private val listener: Listener) :
|
|||||||
logcat(LogPriority.WARN) { "Package name not found" }
|
logcat(LogPriority.WARN) { "Package name not found" }
|
||||||
return LoadResult.Error
|
return LoadResult.Error
|
||||||
}
|
}
|
||||||
return GlobalScope.async(Dispatchers.Default, CoroutineStart.DEFAULT) {
|
return ExtensionLoader.loadExtensionFromPkgName(context, pkgName)
|
||||||
ExtensionLoader.loadExtensionFromPkgName(context, pkgName)
|
|
||||||
}.await()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ internal object ExtensionLoader {
|
|||||||
* Attempts to load an extension from the given package name. It checks if the extension
|
* Attempts to load an extension from the given package name. It checks if the extension
|
||||||
* contains the required feature flag before trying to load it.
|
* contains the required feature flag before trying to load it.
|
||||||
*/
|
*/
|
||||||
fun loadExtensionFromPkgName(context: Context, pkgName: String): LoadResult {
|
suspend fun loadExtensionFromPkgName(context: Context, pkgName: String): LoadResult {
|
||||||
val extensionPackage = getExtensionInfoFromPkgName(context, pkgName)
|
val extensionPackage = getExtensionInfoFromPkgName(context, pkgName)
|
||||||
if (extensionPackage == null) {
|
if (extensionPackage == null) {
|
||||||
logcat(LogPriority.ERROR) { "Extension package is not found ($pkgName)" }
|
logcat(LogPriority.ERROR) { "Extension package is not found ($pkgName)" }
|
||||||
@@ -223,7 +223,8 @@ internal object ExtensionLoader {
|
|||||||
* @param context The application context.
|
* @param context The application context.
|
||||||
* @param extensionInfo The extension to load.
|
* @param extensionInfo The extension to load.
|
||||||
*/
|
*/
|
||||||
private fun loadExtension(context: Context, extensionInfo: ExtensionInfo): LoadResult {
|
@Suppress("LongMethod", "CyclomaticComplexMethod", "ReturnCount")
|
||||||
|
private suspend fun loadExtension(context: Context, extensionInfo: ExtensionInfo): LoadResult {
|
||||||
val pkgManager = context.packageManager
|
val pkgManager = context.packageManager
|
||||||
val pkgInfo = extensionInfo.packageInfo
|
val pkgInfo = extensionInfo.packageInfo
|
||||||
val appInfo = pkgInfo.applicationInfo
|
val appInfo = pkgInfo.applicationInfo
|
||||||
@@ -252,7 +253,7 @@ internal object ExtensionLoader {
|
|||||||
if (signatures.isNullOrEmpty()) {
|
if (signatures.isNullOrEmpty()) {
|
||||||
logcat(LogPriority.WARN) { "Package $pkgName isn't signed" }
|
logcat(LogPriority.WARN) { "Package $pkgName isn't signed" }
|
||||||
return LoadResult.Error
|
return LoadResult.Error
|
||||||
} else if (!trustExtension.isTrusted(pkgInfo, signatures.last())) {
|
} else if (!trustExtension.isTrusted(pkgInfo, signatures)) {
|
||||||
val extension = Extension.Untrusted(
|
val extension = Extension.Untrusted(
|
||||||
extName,
|
extName,
|
||||||
pkgName,
|
pkgName,
|
||||||
|
|||||||
@@ -806,6 +806,11 @@ class EHentai(
|
|||||||
override fun pageListParse(response: Response) =
|
override fun pageListParse(response: Response) =
|
||||||
throw UnsupportedOperationException("Unused method was called somehow!")
|
throw UnsupportedOperationException("Unused method was called somehow!")
|
||||||
|
|
||||||
|
override suspend fun getImageUrl(page: Page): String {
|
||||||
|
val imageUrlResponse = client.newCall(imageUrlRequest(page)).awaitSuccess()
|
||||||
|
return realImageUrlParse(imageUrlResponse, page)
|
||||||
|
}
|
||||||
|
|
||||||
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getImageUrl"))
|
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getImageUrl"))
|
||||||
override fun fetchImageUrl(page: Page): Observable<String> {
|
override fun fetchImageUrl(page: Page): Observable<String> {
|
||||||
return client.newCall(imageUrlRequest(page))
|
return client.newCall(imageUrlRequest(page))
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import exh.md.handlers.FollowsHandler
|
|||||||
import exh.md.handlers.MangaHandler
|
import exh.md.handlers.MangaHandler
|
||||||
import exh.md.handlers.MangaHotHandler
|
import exh.md.handlers.MangaHotHandler
|
||||||
import exh.md.handlers.MangaPlusHandler
|
import exh.md.handlers.MangaPlusHandler
|
||||||
|
import exh.md.handlers.NamicomiHandler
|
||||||
import exh.md.handlers.PageHandler
|
import exh.md.handlers.PageHandler
|
||||||
import exh.md.handlers.SimilarHandler
|
import exh.md.handlers.SimilarHandler
|
||||||
import exh.md.network.MangaDexLoginHelper
|
import exh.md.network.MangaDexLoginHelper
|
||||||
@@ -123,6 +124,9 @@ class MangaDex(delegate: HttpSource, val context: Context) :
|
|||||||
private val mangaHotHandler by lazy {
|
private val mangaHotHandler by lazy {
|
||||||
MangaHotHandler(network.client, network.defaultUserAgentProvider())
|
MangaHotHandler(network.client, network.defaultUserAgentProvider())
|
||||||
}
|
}
|
||||||
|
private val namicomiHandler by lazy {
|
||||||
|
NamicomiHandler(network.client, network.defaultUserAgentProvider())
|
||||||
|
}
|
||||||
private val pageHandler by lazy {
|
private val pageHandler by lazy {
|
||||||
PageHandler(
|
PageHandler(
|
||||||
headers,
|
headers,
|
||||||
@@ -132,6 +136,7 @@ class MangaDex(delegate: HttpSource, val context: Context) :
|
|||||||
bilibiliHandler,
|
bilibiliHandler,
|
||||||
azukHandler,
|
azukHandler,
|
||||||
mangaHotHandler,
|
mangaHotHandler,
|
||||||
|
namicomiHandler,
|
||||||
trackPreferences,
|
trackPreferences,
|
||||||
mdList,
|
mdList,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import kotlinx.coroutines.flow.map
|
|||||||
import kotlinx.coroutines.flow.onCompletion
|
import kotlinx.coroutines.flow.onCompletion
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import tachiyomi.core.common.util.lang.launchIO
|
import tachiyomi.core.common.util.lang.launchIO
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
@@ -195,7 +196,9 @@ class ExtensionsScreenModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun trustExtension(extension: Extension.Untrusted) {
|
fun trustExtension(extension: Extension.Untrusted) {
|
||||||
extensionManager.trust(extension)
|
screenModelScope.launch {
|
||||||
|
extensionManager.trust(extension)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
|
|||||||
@@ -56,9 +56,7 @@ fun SourceFilterDialog(
|
|||||||
) {
|
) {
|
||||||
val updateFilters = { onUpdate(filters) }
|
val updateFilters = { onUpdate(filters) }
|
||||||
|
|
||||||
AdaptiveSheet(
|
AdaptiveSheet(onDismissRequest = onDismissRequest) {
|
||||||
onDismissRequest = onDismissRequest,
|
|
||||||
) {
|
|
||||||
LazyColumn {
|
LazyColumn {
|
||||||
stickyHeader {
|
stickyHeader {
|
||||||
Row(
|
Row(
|
||||||
|
|||||||
@@ -234,6 +234,9 @@ class LibraryScreenModel(
|
|||||||
prefs.filterBookmarked,
|
prefs.filterBookmarked,
|
||||||
prefs.filterCompleted,
|
prefs.filterCompleted,
|
||||||
prefs.filterIntervalCustom,
|
prefs.filterIntervalCustom,
|
||||||
|
// SY -->
|
||||||
|
prefs.filterLewd,
|
||||||
|
// SY <--
|
||||||
) + trackFilter.values
|
) + trackFilter.values
|
||||||
).any { it != TriState.DISABLED }
|
).any { it != TriState.DISABLED }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ import eu.kanade.presentation.reader.appbars.NavBarType
|
|||||||
import eu.kanade.presentation.reader.appbars.ReaderAppBars
|
import eu.kanade.presentation.reader.appbars.ReaderAppBars
|
||||||
import eu.kanade.presentation.reader.settings.ReaderSettingsDialog
|
import eu.kanade.presentation.reader.settings.ReaderSettingsDialog
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.data.coil.TachiyomiImageDecoder
|
||||||
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
|
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
|
||||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||||
import eu.kanade.tachiyomi.databinding.ReaderActivityBinding
|
import eu.kanade.tachiyomi.databinding.ReaderActivityBinding
|
||||||
@@ -1280,7 +1281,9 @@ class ReaderActivity : BaseActivity() {
|
|||||||
input.copyTo(output)
|
input.copyTo(output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SubsamplingScaleImageView.setDisplayProfile(outputStream.toByteArray())
|
val data = outputStream.toByteArray()
|
||||||
|
SubsamplingScaleImageView.setDisplayProfile(data)
|
||||||
|
TachiyomiImageDecoder.displayProfile = data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1141,7 +1141,7 @@ class ReaderViewModel @JvmOverloads constructor(
|
|||||||
|
|
||||||
return imageSaver.save(
|
return imageSaver.save(
|
||||||
image = Image.Page(
|
image = Image.Page(
|
||||||
inputStream = { ImageUtil.mergeBitmaps(imageBitmap, imageBitmap2, isLTR, 0, bg) },
|
inputStream = { ImageUtil.mergeBitmaps(imageBitmap, imageBitmap2, isLTR, 0, bg).inputStream() },
|
||||||
name = filename,
|
name = filename,
|
||||||
location = location,
|
location = location,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import exh.util.DataSaver
|
|||||||
import exh.util.DataSaver.Companion.getImage
|
import exh.util.DataSaver.Companion.getImage
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.cancel
|
import kotlinx.coroutines.cancel
|
||||||
@@ -31,6 +32,7 @@ import kotlin.math.min
|
|||||||
/**
|
/**
|
||||||
* Loader used to load chapters from an online source.
|
* Loader used to load chapters from an online source.
|
||||||
*/
|
*/
|
||||||
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
internal class HttpPageLoader(
|
internal class HttpPageLoader(
|
||||||
private val chapter: ReaderChapter,
|
private val chapter: ReaderChapter,
|
||||||
private val source: HttpSource,
|
private val source: HttpSource,
|
||||||
|
|||||||
@@ -43,7 +43,9 @@ internal class ZipPageLoader(file: UniFile, context: Context) : PageLoader() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val apacheZip: ZipFile? = if (!file.isEncryptedZip() && Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {
|
private val apacheZip: ZipFile? = if (!file.isEncryptedZip() && Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {
|
||||||
ZipFile(channel)
|
ZipFile.Builder()
|
||||||
|
.setSeekableByteChannel(channel)
|
||||||
|
.get()
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,23 +18,27 @@ import androidx.annotation.StyleRes
|
|||||||
import androidx.appcompat.widget.AppCompatImageView
|
import androidx.appcompat.widget.AppCompatImageView
|
||||||
import androidx.core.os.postDelayed
|
import androidx.core.os.postDelayed
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import coil3.BitmapImage
|
||||||
import coil3.dispose
|
import coil3.dispose
|
||||||
import coil3.imageLoader
|
import coil3.imageLoader
|
||||||
import coil3.request.CachePolicy
|
import coil3.request.CachePolicy
|
||||||
import coil3.request.ImageRequest
|
import coil3.request.ImageRequest
|
||||||
import coil3.request.crossfade
|
import coil3.request.crossfade
|
||||||
|
import coil3.size.Precision
|
||||||
|
import coil3.size.ViewSizeResolver
|
||||||
import com.davemorrissey.labs.subscaleview.ImageSource
|
import com.davemorrissey.labs.subscaleview.ImageSource
|
||||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
||||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.EASE_IN_OUT_QUAD
|
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.EASE_IN_OUT_QUAD
|
||||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.EASE_OUT_QUAD
|
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.EASE_OUT_QUAD
|
||||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE
|
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE
|
||||||
import com.github.chrisbanes.photoview.PhotoView
|
import com.github.chrisbanes.photoview.PhotoView
|
||||||
|
import eu.kanade.tachiyomi.data.coil.cropBorders
|
||||||
|
import eu.kanade.tachiyomi.data.coil.customDecoder
|
||||||
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonSubsamplingImageView
|
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonSubsamplingImageView
|
||||||
import eu.kanade.tachiyomi.util.system.GLUtil
|
import eu.kanade.tachiyomi.util.system.GLUtil
|
||||||
import eu.kanade.tachiyomi.util.system.animatorDurationScale
|
import eu.kanade.tachiyomi.util.system.animatorDurationScale
|
||||||
import eu.kanade.tachiyomi.util.view.isVisibleOnScreen
|
import eu.kanade.tachiyomi.util.view.isVisibleOnScreen
|
||||||
import java.io.InputStream
|
import okio.BufferedSource
|
||||||
import java.nio.ByteBuffer
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A wrapper view for showing page image.
|
* A wrapper view for showing page image.
|
||||||
@@ -140,14 +144,14 @@ open class ReaderPageImageView @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setImage(inputStream: InputStream, isAnimated: Boolean, config: Config) {
|
fun setImage(source: BufferedSource, isAnimated: Boolean, config: Config) {
|
||||||
this.config = config
|
this.config = config
|
||||||
if (isAnimated) {
|
if (isAnimated) {
|
||||||
prepareAnimatedImageView()
|
prepareAnimatedImageView()
|
||||||
setAnimatedImage(inputStream, config)
|
setAnimatedImage(source, config)
|
||||||
} else {
|
} else {
|
||||||
prepareNonAnimatedImageView()
|
prepareNonAnimatedImageView()
|
||||||
setNonAnimatedImage(inputStream, config)
|
setNonAnimatedImage(source, config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,7 +260,7 @@ open class ReaderPageImageView @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setNonAnimatedImage(
|
private fun setNonAnimatedImage(
|
||||||
image: Any,
|
data: Any,
|
||||||
config: Config,
|
config: Config,
|
||||||
) = (pageView as? SubsamplingScaleImageView)?.apply {
|
) = (pageView as? SubsamplingScaleImageView)?.apply {
|
||||||
setDoubleTapZoomDuration(config.zoomDuration.getSystemScaledDuration())
|
setDoubleTapZoomDuration(config.zoomDuration.getSystemScaledDuration())
|
||||||
@@ -277,12 +281,36 @@ open class ReaderPageImageView @JvmOverloads constructor(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
when (image) {
|
if (isWebtoon) {
|
||||||
is BitmapDrawable -> setImage(ImageSource.bitmap(image.bitmap))
|
val request = ImageRequest.Builder(context)
|
||||||
is InputStream -> setImage(ImageSource.inputStream(image))
|
.data(data)
|
||||||
else -> throw IllegalArgumentException("Not implemented for class ${image::class.simpleName}")
|
.memoryCachePolicy(CachePolicy.DISABLED)
|
||||||
|
.diskCachePolicy(CachePolicy.DISABLED)
|
||||||
|
.target(
|
||||||
|
onSuccess = { result ->
|
||||||
|
val image = result as BitmapImage
|
||||||
|
setImage(ImageSource.bitmap(image.bitmap))
|
||||||
|
isVisible = true
|
||||||
|
},
|
||||||
|
onError = {
|
||||||
|
this@ReaderPageImageView.onImageLoadError()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.size(ViewSizeResolver(this@ReaderPageImageView))
|
||||||
|
.precision(Precision.INEXACT)
|
||||||
|
.cropBorders(config.cropBorders)
|
||||||
|
.customDecoder(true)
|
||||||
|
.crossfade(false)
|
||||||
|
.build()
|
||||||
|
context.imageLoader.enqueue(request)
|
||||||
|
} else {
|
||||||
|
when (data) {
|
||||||
|
is BitmapDrawable -> setImage(ImageSource.bitmap(data.bitmap))
|
||||||
|
is BufferedSource -> setImage(ImageSource.inputStream(data.inputStream()))
|
||||||
|
else -> throw IllegalArgumentException("Not implemented for class ${data::class.simpleName}")
|
||||||
|
}
|
||||||
|
isVisible = true
|
||||||
}
|
}
|
||||||
isVisible = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prepareAnimatedImageView() {
|
private fun prepareAnimatedImageView() {
|
||||||
@@ -325,18 +353,13 @@ open class ReaderPageImageView @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setAnimatedImage(
|
private fun setAnimatedImage(
|
||||||
image: Any,
|
data: Any,
|
||||||
config: Config,
|
config: Config,
|
||||||
) = (pageView as? AppCompatImageView)?.apply {
|
) = (pageView as? AppCompatImageView)?.apply {
|
||||||
if (this is PhotoView) {
|
if (this is PhotoView) {
|
||||||
setZoomTransitionDuration(config.zoomDuration.getSystemScaledDuration())
|
setZoomTransitionDuration(config.zoomDuration.getSystemScaledDuration())
|
||||||
}
|
}
|
||||||
|
|
||||||
val data = when (image) {
|
|
||||||
is Drawable -> image
|
|
||||||
is InputStream -> ByteBuffer.wrap(image.readBytes())
|
|
||||||
else -> throw IllegalArgumentException("Not implemented for class ${image::class.simpleName}")
|
|
||||||
}
|
|
||||||
val request = ImageRequest.Builder(context)
|
val request = ImageRequest.Builder(context)
|
||||||
.data(data)
|
.data(data)
|
||||||
.memoryCachePolicy(CachePolicy.DISABLED)
|
.memoryCachePolicy(CachePolicy.DISABLED)
|
||||||
|
|||||||
@@ -19,15 +19,14 @@ import kotlinx.coroutines.flow.collectLatest
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.supervisorScope
|
import kotlinx.coroutines.supervisorScope
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
|
import okio.Buffer
|
||||||
|
import okio.BufferedSource
|
||||||
import tachiyomi.core.common.util.lang.launchIO
|
import tachiyomi.core.common.util.lang.launchIO
|
||||||
import tachiyomi.core.common.util.lang.withIOContext
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
import tachiyomi.core.common.util.lang.withUIContext
|
import tachiyomi.core.common.util.lang.withUIContext
|
||||||
import tachiyomi.core.common.util.system.ImageUtil
|
import tachiyomi.core.common.util.system.ImageUtil
|
||||||
import tachiyomi.core.common.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
import tachiyomi.decoder.ImageDecoder
|
import tachiyomi.decoder.ImageDecoder
|
||||||
import java.io.BufferedInputStream
|
|
||||||
import java.io.ByteArrayInputStream
|
|
||||||
import java.io.InputStream
|
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -159,53 +158,45 @@ class PagerPageHolder(
|
|||||||
val streamFn2 = extraPage?.stream
|
val streamFn2 = extraPage?.stream
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val (bais, isAnimated, background) = withIOContext {
|
val (source, isAnimated, background) = withIOContext {
|
||||||
streamFn().buffered(16).use { stream ->
|
streamFn().buffered(16).use { source ->
|
||||||
// SY -->
|
// SY -->
|
||||||
(
|
if (extraPage != null) {
|
||||||
if (extraPage != null) {
|
streamFn2?.invoke()
|
||||||
streamFn2?.invoke()
|
?.buffered(16)
|
||||||
?.buffered(16)
|
} else {
|
||||||
|
null
|
||||||
|
}.use { source2 ->
|
||||||
|
val itemSource = if (viewer.config.dualPageSplit) {
|
||||||
|
process(item.first, Buffer().readFrom(source))
|
||||||
|
} else {
|
||||||
|
mergePages(Buffer().readFrom(source), source2?.let { Buffer().readFrom(it) })
|
||||||
|
}
|
||||||
|
// SY <--
|
||||||
|
val isAnimated = ImageUtil.isAnimatedAndSupported(itemSource)
|
||||||
|
val background = if (!isAnimated && viewer.config.automaticBackground) {
|
||||||
|
ImageUtil.chooseBackground(context, itemSource.peek())
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
).use { stream2 ->
|
Triple(itemSource, isAnimated, background)
|
||||||
if (viewer.config.dualPageSplit) {
|
}
|
||||||
process(item.first, stream)
|
|
||||||
} else {
|
|
||||||
mergePages(stream, stream2)
|
|
||||||
}.use { itemStream ->
|
|
||||||
// SY <--
|
|
||||||
val bais = ByteArrayInputStream(itemStream.readBytes())
|
|
||||||
val isAnimated = ImageUtil.isAnimatedAndSupported(bais)
|
|
||||||
bais.reset()
|
|
||||||
val background = if (!isAnimated && viewer.config.automaticBackground) {
|
|
||||||
ImageUtil.chooseBackground(context, bais)
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
bais.reset()
|
|
||||||
Triple(bais, isAnimated, background)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
withUIContext {
|
withUIContext {
|
||||||
bais.use {
|
setImage(
|
||||||
setImage(
|
source,
|
||||||
it,
|
isAnimated,
|
||||||
isAnimated,
|
Config(
|
||||||
Config(
|
zoomDuration = viewer.config.doubleTapAnimDuration,
|
||||||
zoomDuration = viewer.config.doubleTapAnimDuration,
|
minimumScaleType = viewer.config.imageScaleType,
|
||||||
minimumScaleType = viewer.config.imageScaleType,
|
cropBorders = viewer.config.imageCropBorders,
|
||||||
cropBorders = viewer.config.imageCropBorders,
|
zoomStartPosition = viewer.config.imageZoomType,
|
||||||
zoomStartPosition = viewer.config.imageZoomType,
|
landscapeZoom = viewer.config.landscapeZoom,
|
||||||
landscapeZoom = viewer.config.landscapeZoom,
|
),
|
||||||
),
|
)
|
||||||
)
|
if (!isAnimated) {
|
||||||
if (!isAnimated) {
|
pageBackground = background
|
||||||
pageBackground = background
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
removeErrorLayout()
|
removeErrorLayout()
|
||||||
}
|
}
|
||||||
@@ -217,124 +208,119 @@ class PagerPageHolder(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun process(page: ReaderPage, imageStream: BufferedInputStream): InputStream {
|
private fun process(page: ReaderPage, imageSource: BufferedSource): BufferedSource {
|
||||||
if (viewer.config.dualPageRotateToFit) {
|
if (viewer.config.dualPageRotateToFit) {
|
||||||
return rotateDualPage(imageStream)
|
return rotateDualPage(imageSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!viewer.config.dualPageSplit) {
|
if (!viewer.config.dualPageSplit) {
|
||||||
return imageStream
|
return imageSource
|
||||||
}
|
}
|
||||||
|
|
||||||
if (page is InsertPage) {
|
if (page is InsertPage) {
|
||||||
return splitInHalf(imageStream)
|
return splitInHalf(imageSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
val isDoublePage = ImageUtil.isWideImage(imageStream)
|
val isDoublePage = ImageUtil.isWideImage(imageSource)
|
||||||
if (!isDoublePage) {
|
if (!isDoublePage) {
|
||||||
return imageStream
|
return imageSource
|
||||||
}
|
}
|
||||||
|
|
||||||
onPageSplit(page)
|
onPageSplit(page)
|
||||||
|
|
||||||
return splitInHalf(imageStream)
|
return splitInHalf(imageSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun rotateDualPage(imageStream: BufferedInputStream): InputStream {
|
private fun rotateDualPage(imageSource: BufferedSource): BufferedSource {
|
||||||
val isDoublePage = ImageUtil.isWideImage(imageStream)
|
val isDoublePage = ImageUtil.isWideImage(imageSource)
|
||||||
return if (isDoublePage) {
|
return if (isDoublePage) {
|
||||||
val rotation = if (viewer.config.dualPageRotateToFitInvert) -90f else 90f
|
val rotation = if (viewer.config.dualPageRotateToFitInvert) -90f else 90f
|
||||||
ImageUtil.rotateImage(imageStream, rotation)
|
ImageUtil.rotateImage(imageSource, rotation)
|
||||||
} else {
|
} else {
|
||||||
imageStream
|
imageSource
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun mergePages(imageStream: InputStream, imageStream2: InputStream?): InputStream {
|
private fun mergePages(imageSource: BufferedSource, imageSource2: BufferedSource?): BufferedSource {
|
||||||
// Handle adding a center margin to wide images if requested
|
// Handle adding a center margin to wide images if requested
|
||||||
if (imageStream2 == null) {
|
if (imageSource2 == null) {
|
||||||
return if (imageStream is BufferedInputStream &&
|
return if (
|
||||||
!ImageUtil.isAnimatedAndSupported(imageStream) &&
|
!ImageUtil.isAnimatedAndSupported(imageSource) &&
|
||||||
ImageUtil.isWideImage(imageStream) &&
|
ImageUtil.isWideImage(imageSource) &&
|
||||||
viewer.config.centerMarginType and PagerConfig.CenterMarginType.WIDE_PAGE_CENTER_MARGIN > 0 &&
|
viewer.config.centerMarginType and PagerConfig.CenterMarginType.WIDE_PAGE_CENTER_MARGIN > 0 &&
|
||||||
!viewer.config.imageCropBorders
|
!viewer.config.imageCropBorders
|
||||||
) {
|
) {
|
||||||
ImageUtil.addHorizontalCenterMargin(imageStream, height, context)
|
ImageUtil.addHorizontalCenterMargin(imageSource, height, context)
|
||||||
} else {
|
} else {
|
||||||
imageStream
|
imageSource
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (page.fullPage) return imageStream
|
if (page.fullPage) return imageSource
|
||||||
if (ImageUtil.isAnimatedAndSupported(imageStream)) {
|
if (ImageUtil.isAnimatedAndSupported(imageSource)) {
|
||||||
page.fullPage = true
|
page.fullPage = true
|
||||||
splitDoublePages()
|
splitDoublePages()
|
||||||
return imageStream
|
return imageSource
|
||||||
} else if (ImageUtil.isAnimatedAndSupported(imageStream2)) {
|
} else if (ImageUtil.isAnimatedAndSupported(imageSource2)) {
|
||||||
page.isolatedPage = true
|
page.isolatedPage = true
|
||||||
extraPage?.fullPage = true
|
extraPage?.fullPage = true
|
||||||
splitDoublePages()
|
splitDoublePages()
|
||||||
return imageStream
|
return imageSource
|
||||||
}
|
}
|
||||||
val imageBytes = imageStream.readBytes()
|
|
||||||
val imageBitmap = try {
|
val imageBitmap = try {
|
||||||
ImageDecoder.newInstance(imageBytes.inputStream())?.decode()
|
ImageDecoder.newInstance(imageSource.inputStream())?.decode()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logcat(LogPriority.ERROR, e) { "Cannot combine pages" }
|
logcat(LogPriority.ERROR, e) { "Cannot combine pages" }
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
if (imageBitmap == null) {
|
if (imageBitmap == null) {
|
||||||
imageStream2.close()
|
imageSource2.close()
|
||||||
imageStream.close()
|
|
||||||
page.fullPage = true
|
page.fullPage = true
|
||||||
splitDoublePages()
|
splitDoublePages()
|
||||||
logcat(LogPriority.ERROR) { "Cannot combine pages" }
|
logcat(LogPriority.ERROR) { "Cannot combine pages" }
|
||||||
return imageBytes.inputStream()
|
return imageSource
|
||||||
}
|
}
|
||||||
scope.launch { progressIndicator.setProgress(96) }
|
scope.launch { progressIndicator.setProgress(96) }
|
||||||
val height = imageBitmap.height
|
val height = imageBitmap.height
|
||||||
val width = imageBitmap.width
|
val width = imageBitmap.width
|
||||||
|
|
||||||
if (height < width) {
|
if (height < width) {
|
||||||
imageStream2.close()
|
imageSource2.close()
|
||||||
imageStream.close()
|
|
||||||
page.fullPage = true
|
page.fullPage = true
|
||||||
splitDoublePages()
|
splitDoublePages()
|
||||||
return imageBytes.inputStream()
|
return imageSource
|
||||||
}
|
}
|
||||||
|
|
||||||
val imageBytes2 = imageStream2.readBytes()
|
|
||||||
val imageBitmap2 = try {
|
val imageBitmap2 = try {
|
||||||
ImageDecoder.newInstance(imageBytes2.inputStream())?.decode()
|
ImageDecoder.newInstance(imageSource2.inputStream())?.decode()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logcat(LogPriority.ERROR, e) { "Cannot combine pages" }
|
logcat(LogPriority.ERROR, e) { "Cannot combine pages" }
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
if (imageBitmap2 == null) {
|
if (imageBitmap2 == null) {
|
||||||
imageStream2.close()
|
imageSource2.close()
|
||||||
imageStream.close()
|
|
||||||
extraPage?.fullPage = true
|
extraPage?.fullPage = true
|
||||||
page.isolatedPage = true
|
page.isolatedPage = true
|
||||||
splitDoublePages()
|
splitDoublePages()
|
||||||
logcat(LogPriority.ERROR) { "Cannot combine pages" }
|
logcat(LogPriority.ERROR) { "Cannot combine pages" }
|
||||||
return imageBytes.inputStream()
|
return imageSource
|
||||||
}
|
}
|
||||||
scope.launch { progressIndicator.setProgress(97) }
|
scope.launch { progressIndicator.setProgress(97) }
|
||||||
val height2 = imageBitmap2.height
|
val height2 = imageBitmap2.height
|
||||||
val width2 = imageBitmap2.width
|
val width2 = imageBitmap2.width
|
||||||
|
|
||||||
if (height2 < width2) {
|
if (height2 < width2) {
|
||||||
imageStream2.close()
|
imageSource2.close()
|
||||||
imageStream.close()
|
|
||||||
extraPage?.fullPage = true
|
extraPage?.fullPage = true
|
||||||
page.isolatedPage = true
|
page.isolatedPage = true
|
||||||
splitDoublePages()
|
splitDoublePages()
|
||||||
return imageBytes.inputStream()
|
return imageSource
|
||||||
}
|
}
|
||||||
val isLTR = (viewer !is R2LPagerViewer) xor viewer.config.invertDoublePages
|
val isLTR = (viewer !is R2LPagerViewer) xor viewer.config.invertDoublePages
|
||||||
|
|
||||||
imageStream.close()
|
imageSource.close()
|
||||||
imageStream2.close()
|
imageSource2.close()
|
||||||
|
|
||||||
val centerMargin = if (viewer.config.centerMarginType and PagerConfig.CenterMarginType.DOUBLE_PAGE_CENTER_MARGIN > 0 && !viewer.config.imageCropBorders) {
|
val centerMargin = if (viewer.config.centerMarginType and PagerConfig.CenterMarginType.DOUBLE_PAGE_CENTER_MARGIN > 0 && !viewer.config.imageCropBorders) {
|
||||||
96 / (this.height.coerceAtLeast(1) / max(height, height2).coerceAtLeast(1)).coerceAtLeast(1)
|
96 / (this.height.coerceAtLeast(1) / max(height, height2).coerceAtLeast(1)).coerceAtLeast(1)
|
||||||
@@ -363,7 +349,7 @@ class PagerPageHolder(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun splitInHalf(imageStream: InputStream): InputStream {
|
private fun splitInHalf(imageSource: BufferedSource): BufferedSource {
|
||||||
var side = when {
|
var side = when {
|
||||||
viewer is L2RPagerViewer && page is InsertPage -> ImageUtil.Side.RIGHT
|
viewer is L2RPagerViewer && page is InsertPage -> ImageUtil.Side.RIGHT
|
||||||
viewer !is L2RPagerViewer && page is InsertPage -> ImageUtil.Side.LEFT
|
viewer !is L2RPagerViewer && page is InsertPage -> ImageUtil.Side.LEFT
|
||||||
@@ -387,7 +373,7 @@ class PagerPageHolder(
|
|||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
return ImageUtil.splitInHalf(imageStream, side, sideMargin)
|
return ImageUtil.splitInHalf(imageSource, side, sideMargin)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onPageSplit(page: ReaderPage) {
|
private fun onPageSplit(page: ReaderPage) {
|
||||||
|
|||||||
+16
-23
@@ -22,15 +22,14 @@ import kotlinx.coroutines.MainScope
|
|||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.supervisorScope
|
import kotlinx.coroutines.supervisorScope
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
|
import okio.Buffer
|
||||||
|
import okio.BufferedSource
|
||||||
import tachiyomi.core.common.util.lang.launchIO
|
import tachiyomi.core.common.util.lang.launchIO
|
||||||
import tachiyomi.core.common.util.lang.withIOContext
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
import tachiyomi.core.common.util.lang.withUIContext
|
import tachiyomi.core.common.util.lang.withUIContext
|
||||||
import tachiyomi.core.common.util.system.ImageUtil
|
import tachiyomi.core.common.util.system.ImageUtil
|
||||||
import tachiyomi.core.common.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
import java.io.BufferedInputStream
|
|
||||||
import java.io.InputStream
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holder of the webtoon reader for a single page of a chapter.
|
* Holder of the webtoon reader for a single page of a chapter.
|
||||||
@@ -188,16 +187,14 @@ class WebtoonPageHolder(
|
|||||||
val streamFn = page?.stream ?: return
|
val streamFn = page?.stream ?: return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val (openStream, isAnimated) = withIOContext {
|
val (source, isAnimated) = withIOContext {
|
||||||
val stream = streamFn().buffered(16)
|
val source = streamFn().use { process(Buffer().readFrom(it)) }
|
||||||
val openStream = process(stream)
|
val isAnimated = ImageUtil.isAnimatedAndSupported(source)
|
||||||
|
Pair(source, isAnimated)
|
||||||
val isAnimated = ImageUtil.isAnimatedAndSupported(stream)
|
|
||||||
Pair(openStream, isAnimated)
|
|
||||||
}
|
}
|
||||||
withUIContext {
|
withUIContext {
|
||||||
frame.setImage(
|
frame.setImage(
|
||||||
openStream,
|
source,
|
||||||
isAnimated,
|
isAnimated,
|
||||||
ReaderPageImageView.Config(
|
ReaderPageImageView.Config(
|
||||||
zoomDuration = viewer.config.doubleTapAnimDuration,
|
zoomDuration = viewer.config.doubleTapAnimDuration,
|
||||||
@@ -207,10 +204,6 @@ class WebtoonPageHolder(
|
|||||||
)
|
)
|
||||||
removeErrorLayout()
|
removeErrorLayout()
|
||||||
}
|
}
|
||||||
// Suspend the coroutine to close the input stream only when the WebtoonPageHolder is recycled
|
|
||||||
suspendCancellableCoroutine<Nothing> { continuation ->
|
|
||||||
continuation.invokeOnCancellation { openStream.close() }
|
|
||||||
}
|
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
logcat(LogPriority.ERROR, e)
|
logcat(LogPriority.ERROR, e)
|
||||||
withUIContext {
|
withUIContext {
|
||||||
@@ -219,29 +212,29 @@ class WebtoonPageHolder(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun process(imageStream: BufferedInputStream): InputStream {
|
private fun process(imageSource: BufferedSource): BufferedSource {
|
||||||
if (viewer.config.dualPageRotateToFit) {
|
if (viewer.config.dualPageRotateToFit) {
|
||||||
return rotateDualPage(imageStream)
|
return rotateDualPage(imageSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (viewer.config.dualPageSplit) {
|
if (viewer.config.dualPageSplit) {
|
||||||
val isDoublePage = ImageUtil.isWideImage(imageStream)
|
val isDoublePage = ImageUtil.isWideImage(imageSource)
|
||||||
if (isDoublePage) {
|
if (isDoublePage) {
|
||||||
val upperSide = if (viewer.config.dualPageInvert) ImageUtil.Side.LEFT else ImageUtil.Side.RIGHT
|
val upperSide = if (viewer.config.dualPageInvert) ImageUtil.Side.LEFT else ImageUtil.Side.RIGHT
|
||||||
return ImageUtil.splitAndMerge(imageStream, upperSide)
|
return ImageUtil.splitAndMerge(imageSource, upperSide)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return imageStream
|
return imageSource
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun rotateDualPage(imageStream: BufferedInputStream): InputStream {
|
private fun rotateDualPage(imageSource: BufferedSource): BufferedSource {
|
||||||
val isDoublePage = ImageUtil.isWideImage(imageStream)
|
val isDoublePage = ImageUtil.isWideImage(imageSource)
|
||||||
return if (isDoublePage) {
|
return if (isDoublePage) {
|
||||||
val rotation = if (viewer.config.dualPageRotateToFitInvert) -90f else 90f
|
val rotation = if (viewer.config.dualPageRotateToFitInvert) -90f else 90f
|
||||||
ImageUtil.rotateImage(imageStream, rotation)
|
ImageUtil.rotateImage(imageSource, rotation)
|
||||||
} else {
|
} else {
|
||||||
imageStream
|
imageSource
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -28,7 +28,8 @@ class WebtoonRecyclerView @JvmOverloads constructor(
|
|||||||
private var atFirstPosition = false
|
private var atFirstPosition = false
|
||||||
private var halfWidth = 0
|
private var halfWidth = 0
|
||||||
private var halfHeight = 0
|
private var halfHeight = 0
|
||||||
private var originalHeight = 0
|
var originalHeight = 0
|
||||||
|
private set
|
||||||
private var heightSet = false
|
private var heightSet = false
|
||||||
private var firstVisibleItemPosition = 0
|
private var firstVisibleItemPosition = 0
|
||||||
private var lastVisibleItemPosition = 0
|
private var lastVisibleItemPosition = 0
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ class WebtoonViewer(
|
|||||||
recycler.getLocationInWindow(viewPositionRelativeToWindow)
|
recycler.getLocationInWindow(viewPositionRelativeToWindow)
|
||||||
val pos = PointF(
|
val pos = PointF(
|
||||||
(event.rawX - viewPosition[0] + viewPositionRelativeToWindow[0]) / recycler.width,
|
(event.rawX - viewPosition[0] + viewPositionRelativeToWindow[0]) / recycler.width,
|
||||||
(event.rawY - viewPosition[1] + viewPositionRelativeToWindow[1]) / recycler.height,
|
(event.rawY - viewPosition[1] + viewPositionRelativeToWindow[1]) / recycler.originalHeight,
|
||||||
)
|
)
|
||||||
when (config.navigator.getAction(pos)) {
|
when (config.navigator.getAction(pos)) {
|
||||||
NavigationRegion.MENU -> activity.toggleMenu()
|
NavigationRegion.MENU -> activity.toggleMenu()
|
||||||
|
|||||||
@@ -20,12 +20,13 @@ class CrashLogUtil(
|
|||||||
private val extensionManager: ExtensionManager = Injekt.get(),
|
private val extensionManager: ExtensionManager = Injekt.get(),
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun dumpLogs() = withNonCancellableContext {
|
suspend fun dumpLogs(exception: Throwable? = null) = withNonCancellableContext {
|
||||||
try {
|
try {
|
||||||
val file = context.createFileInCacheDir("tachiyomi_crash_logs.txt")
|
val file = context.createFileInCacheDir("tachiyomi_crash_logs.txt")
|
||||||
|
|
||||||
file.appendText(getDebugInfo() + "\n\n")
|
file.appendText(getDebugInfo() + "\n\n")
|
||||||
getExtensionsInfo()?.let { file.appendText("$it\n\n") }
|
getExtensionsInfo()?.let { file.appendText("$it\n\n") }
|
||||||
|
exception?.let { file.appendText("$it\n\n") }
|
||||||
|
|
||||||
Runtime.getRuntime().exec("logcat *:E -d -f ${file.absolutePath}").waitFor()
|
Runtime.getRuntime().exec("logcat *:E -d -f ${file.absolutePath}").waitFor()
|
||||||
|
|
||||||
|
|||||||
@@ -61,14 +61,14 @@ class EHentaiUpdateWorker(private val context: Context, workerParams: WorkerPara
|
|||||||
override suspend fun doWork(): Result {
|
override suspend fun doWork(): Result {
|
||||||
return try {
|
return try {
|
||||||
if (requiresWifiConnection(preferences) && !context.isConnectedToWifi()) {
|
if (requiresWifiConnection(preferences) && !context.isConnectedToWifi()) {
|
||||||
Result.failure()
|
Result.success() // retry again later
|
||||||
} else {
|
} else {
|
||||||
startUpdating()
|
startUpdating()
|
||||||
logger.d("Update job completed!")
|
logger.d("Update job completed!")
|
||||||
Result.success()
|
Result.success()
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Result.failure()
|
Result.success() // retry again later
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,10 +17,13 @@ import kotlinx.coroutines.runBlocking
|
|||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import okio.BufferedSource
|
||||||
|
import okio.buffer
|
||||||
|
import okio.sink
|
||||||
|
import okio.source
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
import java.io.InputStream
|
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
@@ -67,7 +70,7 @@ class MemAutoFlushingLookupTable<T>(
|
|||||||
Runtime.getRuntime().addShutdownHook(shutdownHook)
|
Runtime.getRuntime().addShutdownHook(shutdownHook)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun InputStream.requireBytes(targetArray: ByteArray, byteCount: Int): Boolean {
|
private fun BufferedSource.requireBytes(targetArray: ByteArray, byteCount: Int): Boolean {
|
||||||
var readIter = 0
|
var readIter = 0
|
||||||
while (true) {
|
while (true) {
|
||||||
val readThisIter = read(targetArray, readIter, byteCount - readIter)
|
val readThisIter = read(targetArray, readIter, byteCount - readIter)
|
||||||
@@ -80,7 +83,7 @@ class MemAutoFlushingLookupTable<T>(
|
|||||||
private fun initialLoad() {
|
private fun initialLoad() {
|
||||||
launch {
|
launch {
|
||||||
try {
|
try {
|
||||||
atomicFile.openRead().buffered().use { input ->
|
atomicFile.openRead().source().buffer().use { input ->
|
||||||
val bb = ByteBuffer.allocate(8)
|
val bb = ByteBuffer.allocate(8)
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -126,7 +129,7 @@ class MemAutoFlushingLookupTable<T>(
|
|||||||
|
|
||||||
val fos = atomicFile.startWrite()
|
val fos = atomicFile.startWrite()
|
||||||
try {
|
try {
|
||||||
val out = fos.buffered()
|
val out = fos.sink().buffer()
|
||||||
table.forEach { key, value ->
|
table.forEach { key, value ->
|
||||||
val v = serializer.write(value).toByteArray(Charsets.UTF_8)
|
val v = serializer.write(value).toByteArray(Charsets.UTF_8)
|
||||||
bb.putInt(0, key)
|
bb.putInt(0, key)
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package exh.md.handlers
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.network.GET
|
||||||
|
import eu.kanade.tachiyomi.network.awaitSuccess
|
||||||
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlinx.serialization.json.jsonArray
|
||||||
|
import kotlinx.serialization.json.jsonObject
|
||||||
|
import kotlinx.serialization.json.jsonPrimitive
|
||||||
|
import okhttp3.Headers
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Response
|
||||||
|
|
||||||
|
class NamicomiHandler(currentClient: OkHttpClient, userAgent: String) {
|
||||||
|
private val apiUrl = "https://api.namicomi.com"
|
||||||
|
|
||||||
|
private val headers = Headers.Builder()
|
||||||
|
.add("User-Agent", userAgent)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val client: OkHttpClient = currentClient
|
||||||
|
|
||||||
|
suspend fun fetchPageList(externalUrl: String, dataSaver: Boolean): List<Page> {
|
||||||
|
val chapterId = externalUrl.substringBefore("?").substringAfterLast("/")
|
||||||
|
val request =
|
||||||
|
GET(
|
||||||
|
"$apiUrl/images/chapter/$chapterId?newQualities=true",
|
||||||
|
headers,
|
||||||
|
)
|
||||||
|
return pageListParse(client.newCall(request).awaitSuccess(), chapterId, dataSaver)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun pageListParse(response: Response, chapterId: String, dataSaver: Boolean): List<Page> {
|
||||||
|
val data = Json.parseToJsonElement(response.body.string()).jsonObject["data"]!!
|
||||||
|
val baseUrl = data.jsonObject["baseUrl"]!!.jsonPrimitive.content
|
||||||
|
val hash = data.jsonObject["hash"]!!.jsonPrimitive.content
|
||||||
|
|
||||||
|
/* Available quality levels: source, high, medium, low */
|
||||||
|
val quality = if(dataSaver) "low" else "high"
|
||||||
|
|
||||||
|
return data
|
||||||
|
.jsonObject[quality]!!
|
||||||
|
.jsonArray.mapIndexed { index, element ->
|
||||||
|
val fileName = element.jsonObject["filename"]!!.jsonPrimitive.content
|
||||||
|
val url = "$baseUrl/chapter/$chapterId/$hash/$quality/$fileName"
|
||||||
|
Page(index, url, url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,6 +27,7 @@ class PageHandler(
|
|||||||
private val bilibiliHandler: BilibiliHandler,
|
private val bilibiliHandler: BilibiliHandler,
|
||||||
private val azukiHandler: AzukiHandler,
|
private val azukiHandler: AzukiHandler,
|
||||||
private val mangaHotHandler: MangaHotHandler,
|
private val mangaHotHandler: MangaHotHandler,
|
||||||
|
private val namicomiHandler: NamicomiHandler,
|
||||||
private val preferences: TrackPreferences,
|
private val preferences: TrackPreferences,
|
||||||
private val mdList: MdList,
|
private val mdList: MdList,
|
||||||
) {
|
) {
|
||||||
@@ -54,6 +55,10 @@ class PageHandler(
|
|||||||
chapter.scanlator.equals("mangahot", true) -> mangaHotHandler.fetchPageList(
|
chapter.scanlator.equals("mangahot", true) -> mangaHotHandler.fetchPageList(
|
||||||
chapterResponse.data.attributes.externalUrl,
|
chapterResponse.data.attributes.externalUrl,
|
||||||
)
|
)
|
||||||
|
chapter.scanlator.equals("namicomi", true) -> namicomiHandler.fetchPageList(
|
||||||
|
chapterResponse.data.attributes.externalUrl,
|
||||||
|
dataSaver = dataSaver,
|
||||||
|
)
|
||||||
else -> throw Exception("${chapter.scanlator} not supported")
|
else -> throw Exception("${chapter.scanlator} not supported")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -122,6 +127,9 @@ class PageHandler(
|
|||||||
page.imageUrl?.contains("mangahot", true) == true -> {
|
page.imageUrl?.contains("mangahot", true) == true -> {
|
||||||
mangaHotHandler.client.newCachelessCallWithProgress(GET(page.imageUrl!!, mangaHotHandler.headers), page)
|
mangaHotHandler.client.newCachelessCallWithProgress(GET(page.imageUrl!!, mangaHotHandler.headers), page)
|
||||||
}
|
}
|
||||||
|
page.imageUrl?.contains("namicomi", true) == true -> {
|
||||||
|
mangaHotHandler.client.newCachelessCallWithProgress(GET(page.imageUrl!!, mangaHotHandler.headers), page)
|
||||||
|
}
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import tachiyomi.core.common.util.lang.withIOContext
|
|||||||
/**
|
/**
|
||||||
* Util for evaluating JavaScript in sources.
|
* Util for evaluating JavaScript in sources.
|
||||||
*/
|
*/
|
||||||
@Suppress("UNUSED", "UNCHECKED_CAST")
|
@Suppress("UNUSED", "UNCHECKED_CAST", "UNUSED_PARAMETER")
|
||||||
class JavaScriptEngine(context: Context) {
|
class JavaScriptEngine(context: Context) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+2
@@ -9,6 +9,7 @@ import eu.kanade.tachiyomi.util.system.DeviceUtil
|
|||||||
import eu.kanade.tachiyomi.util.system.WebViewUtil
|
import eu.kanade.tachiyomi.util.system.WebViewUtil
|
||||||
import eu.kanade.tachiyomi.util.system.setDefaultSettings
|
import eu.kanade.tachiyomi.util.system.setDefaultSettings
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import okhttp3.Headers
|
import okhttp3.Headers
|
||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
@@ -48,6 +49,7 @@ abstract class WebViewInterceptor(
|
|||||||
|
|
||||||
abstract fun intercept(chain: Interceptor.Chain, request: Request, response: Response): Response
|
abstract fun intercept(chain: Interceptor.Chain, request: Request, response: Response): Response
|
||||||
|
|
||||||
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
override fun intercept(chain: Interceptor.Chain): Response {
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
val request = chain.request()
|
val request = chain.request()
|
||||||
val response = chain.proceed(request)
|
val response = chain.proceed(request)
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ class EpubFile(file: UniFile, context: Context) : Closeable {
|
|||||||
* Zip file of this epub.
|
* Zip file of this epub.
|
||||||
*/
|
*/
|
||||||
private val zip = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
private val zip = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
||||||
ZipFile(tempFileManager.createTempFile(file))
|
ZipFile.Builder().setFile(tempFileManager.createTempFile(file)).get()
|
||||||
} else {
|
} else {
|
||||||
ZipFile(file.openReadOnlyChannel(context))
|
ZipFile.Builder().setSeekableByteChannel(file.openReadOnlyChannel(context)).get()
|
||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package tachiyomi.core.common.util.lang
|
|||||||
import kotlinx.coroutines.CancellableContinuation
|
import kotlinx.coroutines.CancellableContinuation
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.CoroutineStart
|
import kotlinx.coroutines.CoroutineStart
|
||||||
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
@@ -64,7 +65,7 @@ private suspend fun <T> Observable<T>.awaitOne(): T = suspendCancellableCoroutin
|
|||||||
internal fun <T> CancellableContinuation<T>.unsubscribeOnCancellation(sub: Subscription) =
|
internal fun <T> CancellableContinuation<T>.unsubscribeOnCancellation(sub: Subscription) =
|
||||||
invokeOnCancellation { sub.unsubscribe() }
|
invokeOnCancellation { sub.unsubscribe() }
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class, DelicateCoroutinesApi::class)
|
||||||
fun <T> runAsObservable(
|
fun <T> runAsObservable(
|
||||||
backpressureMode: Emitter.BackpressureMode = Emitter.BackpressureMode.NONE,
|
backpressureMode: Emitter.BackpressureMode = Emitter.BackpressureMode.NONE,
|
||||||
block: suspend () -> T,
|
block: suspend () -> T,
|
||||||
|
|||||||
@@ -26,11 +26,10 @@ import androidx.core.graphics.red
|
|||||||
import androidx.exifinterface.media.ExifInterface
|
import androidx.exifinterface.media.ExifInterface
|
||||||
import com.hippo.unifile.UniFile
|
import com.hippo.unifile.UniFile
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
|
import okio.Buffer
|
||||||
|
import okio.BufferedSource
|
||||||
import tachiyomi.decoder.Format
|
import tachiyomi.decoder.Format
|
||||||
import tachiyomi.decoder.ImageDecoder
|
import tachiyomi.decoder.ImageDecoder
|
||||||
import java.io.BufferedInputStream
|
|
||||||
import java.io.ByteArrayInputStream
|
|
||||||
import java.io.ByteArrayOutputStream
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.net.URLConnection
|
import java.net.URLConnection
|
||||||
@@ -83,9 +82,9 @@ object ImageUtil {
|
|||||||
?: "jpg"
|
?: "jpg"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isAnimatedAndSupported(stream: InputStream): Boolean {
|
fun isAnimatedAndSupported(source: BufferedSource): Boolean {
|
||||||
return try {
|
return try {
|
||||||
val type = getImageType(stream) ?: return false
|
val type = getImageType(source.peek().inputStream()) ?: return false
|
||||||
// https://coil-kt.github.io/coil/getting_started/#supported-image-formats
|
// https://coil-kt.github.io/coil/getting_started/#supported-image-formats
|
||||||
when (type.format) {
|
when (type.format) {
|
||||||
Format.Gif -> true
|
Format.Gif -> true
|
||||||
@@ -132,18 +131,16 @@ object ImageUtil {
|
|||||||
*
|
*
|
||||||
* @return true if the width is greater than the height
|
* @return true if the width is greater than the height
|
||||||
*/
|
*/
|
||||||
fun isWideImage(imageStream: BufferedInputStream): Boolean {
|
fun isWideImage(imageSource: BufferedSource): Boolean {
|
||||||
val options = extractImageOptions(imageStream)
|
val options = extractImageOptions(imageSource)
|
||||||
return options.outWidth > options.outHeight
|
return options.outWidth > options.outHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract the 'side' part from imageStream and return it as InputStream.
|
* Extract the 'side' part from [BufferedSource] and return it as [BufferedSource].
|
||||||
*/
|
*/
|
||||||
fun splitInHalf(imageStream: InputStream, side: Side, sidePadding: Int): InputStream {
|
fun splitInHalf(imageSource: BufferedSource, side: Side, sidePadding: Int): BufferedSource {
|
||||||
val imageBytes = imageStream.readBytes()
|
val imageBitmap = BitmapFactory.decodeStream(imageSource.inputStream())
|
||||||
|
|
||||||
val imageBitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
|
|
||||||
val height = imageBitmap.height
|
val height = imageBitmap.height
|
||||||
val width = imageBitmap.width
|
val width = imageBitmap.width
|
||||||
|
|
||||||
@@ -157,22 +154,20 @@ object ImageUtil {
|
|||||||
half.applyCanvas {
|
half.applyCanvas {
|
||||||
drawBitmap(imageBitmap, part, singlePage, null)
|
drawBitmap(imageBitmap, part, singlePage, null)
|
||||||
}
|
}
|
||||||
val output = ByteArrayOutputStream()
|
val output = Buffer()
|
||||||
half.compress(Bitmap.CompressFormat.JPEG, 100, output)
|
half.compress(Bitmap.CompressFormat.JPEG, 100, output.outputStream())
|
||||||
|
|
||||||
return ByteArrayInputStream(output.toByteArray())
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
fun rotateImage(imageStream: InputStream, degrees: Float): InputStream {
|
fun rotateImage(imageSource: BufferedSource, degrees: Float): BufferedSource {
|
||||||
val imageBytes = imageStream.readBytes()
|
val imageBitmap = BitmapFactory.decodeStream(imageSource.inputStream())
|
||||||
|
|
||||||
val imageBitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
|
|
||||||
val rotated = rotateBitMap(imageBitmap, degrees)
|
val rotated = rotateBitMap(imageBitmap, degrees)
|
||||||
|
|
||||||
val output = ByteArrayOutputStream()
|
val output = Buffer()
|
||||||
rotated.compress(Bitmap.CompressFormat.JPEG, 100, output)
|
rotated.compress(Bitmap.CompressFormat.JPEG, 100, output.outputStream())
|
||||||
|
|
||||||
return ByteArrayInputStream(output.toByteArray())
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun rotateBitMap(bitmap: Bitmap, degrees: Float): Bitmap {
|
private fun rotateBitMap(bitmap: Bitmap, degrees: Float): Bitmap {
|
||||||
@@ -184,10 +179,8 @@ object ImageUtil {
|
|||||||
* Split the image into left and right parts, then merge them into a
|
* Split the image into left and right parts, then merge them into a
|
||||||
* new vertically-aligned image.
|
* new vertically-aligned image.
|
||||||
*/
|
*/
|
||||||
fun splitAndMerge(imageStream: InputStream, upperSide: Side): InputStream {
|
fun splitAndMerge(imageSource: BufferedSource, upperSide: Side): BufferedSource {
|
||||||
val imageBytes = imageStream.readBytes()
|
val imageBitmap = BitmapFactory.decodeStream(imageSource.inputStream())
|
||||||
|
|
||||||
val imageBitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
|
|
||||||
val height = imageBitmap.height
|
val height = imageBitmap.height
|
||||||
val width = imageBitmap.width
|
val width = imageBitmap.width
|
||||||
|
|
||||||
@@ -209,9 +202,9 @@ object ImageUtil {
|
|||||||
drawBitmap(imageBitmap, leftPart, bottomPart, null)
|
drawBitmap(imageBitmap, leftPart, bottomPart, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
val output = ByteArrayOutputStream()
|
val output = Buffer()
|
||||||
result.compress(Bitmap.CompressFormat.JPEG, 100, output)
|
result.compress(Bitmap.CompressFormat.JPEG, 100, output.outputStream())
|
||||||
return ByteArrayInputStream(output.toByteArray())
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class Side {
|
enum class Side {
|
||||||
@@ -225,8 +218,8 @@ object ImageUtil {
|
|||||||
* to compensate for scaling.
|
* to compensate for scaling.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
fun addHorizontalCenterMargin(imageStream: InputStream, viewHeight: Int, backgroundContext: Context): InputStream {
|
fun addHorizontalCenterMargin(imageSource: BufferedSource, viewHeight: Int, backgroundContext: Context): BufferedSource {
|
||||||
val imageBitmap = ImageDecoder.newInstance(imageStream)?.decode()!!
|
val imageBitmap = ImageDecoder.newInstance(imageSource.inputStream())?.decode()!!
|
||||||
val height = imageBitmap.height
|
val height = imageBitmap.height
|
||||||
val width = imageBitmap.width
|
val width = imageBitmap.width
|
||||||
|
|
||||||
@@ -237,7 +230,7 @@ object ImageUtil {
|
|||||||
val leftTargetPart = Rect(0, 0, width / 2, height)
|
val leftTargetPart = Rect(0, 0, width / 2, height)
|
||||||
val rightTargetPart = Rect(width / 2 + centerPadding, 0, width + centerPadding, height)
|
val rightTargetPart = Rect(width / 2 + centerPadding, 0, width + centerPadding, height)
|
||||||
|
|
||||||
val bgColor = chooseBackground(backgroundContext, imageStream)
|
val bgColor = chooseBackground(backgroundContext, imageSource)
|
||||||
bgColor.setBounds(width / 2, 0, width / 2 + centerPadding, height)
|
bgColor.setBounds(width / 2, 0, width / 2 + centerPadding, height)
|
||||||
val result = createBitmap(width + centerPadding, height)
|
val result = createBitmap(width + centerPadding, height)
|
||||||
|
|
||||||
@@ -247,9 +240,9 @@ object ImageUtil {
|
|||||||
bgColor.draw(this)
|
bgColor.draw(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
val output = ByteArrayOutputStream()
|
val output = Buffer()
|
||||||
result.compress(Bitmap.CompressFormat.JPEG, 100, output)
|
result.compress(Bitmap.CompressFormat.JPEG, 100, output.outputStream())
|
||||||
return ByteArrayInputStream(output.toByteArray())
|
return output
|
||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
@@ -258,11 +251,8 @@ object ImageUtil {
|
|||||||
*
|
*
|
||||||
* @return true if the height:width ratio is greater than 3.
|
* @return true if the height:width ratio is greater than 3.
|
||||||
*/
|
*/
|
||||||
private fun isTallImage(imageStream: InputStream): Boolean {
|
private fun isTallImage(imageSource: BufferedSource): Boolean {
|
||||||
val options = extractImageOptions(
|
val options = extractImageOptions(imageSource)
|
||||||
imageStream,
|
|
||||||
resetAfterExtraction = false,
|
|
||||||
)
|
|
||||||
|
|
||||||
return (options.outHeight / options.outWidth) > 3
|
return (options.outHeight / options.outWidth) > 3
|
||||||
}
|
}
|
||||||
@@ -275,22 +265,18 @@ object ImageUtil {
|
|||||||
imageFile: UniFile,
|
imageFile: UniFile,
|
||||||
filenamePrefix: String,
|
filenamePrefix: String,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
if (isAnimatedAndSupported(imageFile.openInputStream()) ||
|
val imageSource = imageFile.openInputStream().use { Buffer().readFrom(it) }
|
||||||
!isTallImage(imageFile.openInputStream())
|
if (isAnimatedAndSupported(imageSource) || !isTallImage(imageSource)) {
|
||||||
) {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
val bitmapRegionDecoder = getBitmapRegionDecoder(imageFile.openInputStream())
|
val bitmapRegionDecoder = getBitmapRegionDecoder(imageSource.peek().inputStream())
|
||||||
if (bitmapRegionDecoder == null) {
|
if (bitmapRegionDecoder == null) {
|
||||||
logcat { "Failed to create new instance of BitmapRegionDecoder" }
|
logcat { "Failed to create new instance of BitmapRegionDecoder" }
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
val options = extractImageOptions(
|
val options = extractImageOptions(imageSource).apply {
|
||||||
imageFile.openInputStream(),
|
|
||||||
resetAfterExtraction = false,
|
|
||||||
).apply {
|
|
||||||
inJustDecodeBounds = false
|
inJustDecodeBounds = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,8 +366,8 @@ object ImageUtil {
|
|||||||
/**
|
/**
|
||||||
* Algorithm for determining what background to accompany a comic/manga page
|
* Algorithm for determining what background to accompany a comic/manga page
|
||||||
*/
|
*/
|
||||||
fun chooseBackground(context: Context, imageStream: InputStream): Drawable {
|
fun chooseBackground(context: Context, imageSource: BufferedSource): Drawable {
|
||||||
val decoder = ImageDecoder.newInstance(imageStream)
|
val decoder = ImageDecoder.newInstance(imageSource.inputStream())
|
||||||
val image = decoder?.decode()
|
val image = decoder?.decode()
|
||||||
decoder?.recycle()
|
decoder?.recycle()
|
||||||
|
|
||||||
@@ -603,16 +589,9 @@ object ImageUtil {
|
|||||||
/**
|
/**
|
||||||
* Used to check an image's dimensions without loading it in the memory.
|
* Used to check an image's dimensions without loading it in the memory.
|
||||||
*/
|
*/
|
||||||
private fun extractImageOptions(
|
private fun extractImageOptions(imageSource: BufferedSource): BitmapFactory.Options {
|
||||||
imageStream: InputStream,
|
|
||||||
resetAfterExtraction: Boolean = true,
|
|
||||||
): BitmapFactory.Options {
|
|
||||||
imageStream.mark(Int.MAX_VALUE)
|
|
||||||
|
|
||||||
val imageBytes = imageStream.readBytes()
|
|
||||||
val options = BitmapFactory.Options().apply { inJustDecodeBounds = true }
|
val options = BitmapFactory.Options().apply { inJustDecodeBounds = true }
|
||||||
BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size, options)
|
BitmapFactory.decodeStream(imageSource.peek().inputStream(), null, options)
|
||||||
if (resetAfterExtraction) imageStream.reset()
|
|
||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -657,7 +636,7 @@ object ImageUtil {
|
|||||||
centerMargin: Int,
|
centerMargin: Int,
|
||||||
@ColorInt background: Int = Color.WHITE,
|
@ColorInt background: Int = Color.WHITE,
|
||||||
progressCallback: ((Int) -> Unit)? = null,
|
progressCallback: ((Int) -> Unit)? = null,
|
||||||
): ByteArrayInputStream {
|
): BufferedSource {
|
||||||
val height = imageBitmap.height
|
val height = imageBitmap.height
|
||||||
val width = imageBitmap.width
|
val width = imageBitmap.width
|
||||||
val height2 = imageBitmap2.height
|
val height2 = imageBitmap2.height
|
||||||
@@ -687,10 +666,10 @@ object ImageUtil {
|
|||||||
canvas.drawBitmap(imageBitmap2, imageBitmap2.rect, bottomPart, null)
|
canvas.drawBitmap(imageBitmap2, imageBitmap2.rect, bottomPart, null)
|
||||||
progressCallback?.invoke(99)
|
progressCallback?.invoke(99)
|
||||||
|
|
||||||
val output = ByteArrayOutputStream()
|
val output = Buffer()
|
||||||
result.compress(Bitmap.CompressFormat.JPEG, 100, output)
|
result.compress(Bitmap.CompressFormat.JPEG, 100, output.outputStream())
|
||||||
progressCallback?.invoke(100)
|
progressCallback?.invoke(100)
|
||||||
return ByteArrayInputStream(output.toByteArray())
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
private val Bitmap.rect: Rect
|
private val Bitmap.rect: Rect
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
[versions]
|
[versions]
|
||||||
agp_version = "8.3.1"
|
agp_version = "8.4.1"
|
||||||
lifecycle_version = "2.7.0"
|
lifecycle_version = "2.8.1"
|
||||||
paging_version = "3.2.1"
|
paging_version = "3.3.0"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
gradle = { module = "com.android.tools.build:gradle", version.ref = "agp_version" }
|
gradle = { module = "com.android.tools.build:gradle", version.ref = "agp_version" }
|
||||||
|
|
||||||
annotation = "androidx.annotation:annotation:1.7.1"
|
annotation = "androidx.annotation:annotation:1.8.0"
|
||||||
appcompat = "androidx.appcompat:appcompat:1.6.1"
|
appcompat = "androidx.appcompat:appcompat:1.7.0"
|
||||||
biometricktx = "androidx.biometric:biometric-ktx:1.2.0-alpha05"
|
biometricktx = "androidx.biometric:biometric-ktx:1.2.0-alpha05"
|
||||||
constraintlayout = "androidx.constraintlayout:constraintlayout:2.1.4"
|
constraintlayout = "androidx.constraintlayout:constraintlayout:2.1.4"
|
||||||
corektx = "androidx.core:core-ktx:1.12.0"
|
corektx = "androidx.core:core-ktx:1.13.1"
|
||||||
splashscreen = "androidx.core:core-splashscreen:1.0.1"
|
splashscreen = "androidx.core:core-splashscreen:1.0.1"
|
||||||
recyclerview = "androidx.recyclerview:recyclerview:1.3.2"
|
recyclerview = "androidx.recyclerview:recyclerview:1.3.2"
|
||||||
viewpager = "androidx.viewpager:viewpager:1.1.0-alpha01"
|
viewpager = "androidx.viewpager:viewpager:1.1.0-alpha01"
|
||||||
@@ -25,9 +25,9 @@ workmanager = "androidx.work:work-runtime:2.9.0"
|
|||||||
paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "paging_version" }
|
paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "paging_version" }
|
||||||
paging-compose = { module = "androidx.paging:paging-compose", version.ref = "paging_version" }
|
paging-compose = { module = "androidx.paging:paging-compose", version.ref = "paging_version" }
|
||||||
|
|
||||||
benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.3"
|
benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.4"
|
||||||
test-ext = "androidx.test.ext:junit-ktx:1.2.0-alpha03"
|
test-ext = "androidx.test.ext:junit-ktx:1.2.0-rc01"
|
||||||
test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-alpha03"
|
test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-rc01"
|
||||||
test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0"
|
test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0"
|
||||||
|
|
||||||
[bundles]
|
[bundles]
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
[versions]
|
[versions]
|
||||||
compiler = "1.5.11"
|
compiler = "1.5.14"
|
||||||
compose-bom = "2024.02.00-alpha02"
|
# 2024.04.00-alpha01 has several bugs with the new animateItem() modifier
|
||||||
|
compose-bom = "2024.03.00-alpha02"
|
||||||
accompanist = "0.35.0-alpha"
|
accompanist = "0.35.0-alpha"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
compiler = { module = "androidx.compose.compiler:compiler", version.ref = "compiler" }
|
compiler = { module = "androidx.compose.compiler:compiler", version.ref = "compiler" }
|
||||||
|
|
||||||
activity = "androidx.activity:activity-compose:1.8.2"
|
activity = "androidx.activity:activity-compose:1.9.0"
|
||||||
bom = { group = "dev.chrisbanes.compose", name = "compose-bom", version.ref = "compose-bom" }
|
bom = { group = "dev.chrisbanes.compose", name = "compose-bom", version.ref = "compose-bom" }
|
||||||
foundation = { module = "androidx.compose.foundation:foundation" }
|
foundation = { module = "androidx.compose.foundation:foundation" }
|
||||||
animation = { module = "androidx.compose.animation:animation" }
|
animation = { module = "androidx.compose.animation:animation" }
|
||||||
@@ -18,9 +19,6 @@ ui-util = { module = "androidx.compose.ui:ui-util" }
|
|||||||
material3-core = { module = "androidx.compose.material3:material3" }
|
material3-core = { module = "androidx.compose.material3:material3" }
|
||||||
material-icons = { module = "androidx.compose.material:material-icons-extended" }
|
material-icons = { module = "androidx.compose.material:material-icons-extended" }
|
||||||
|
|
||||||
# Some components aren't available in Material3
|
|
||||||
material-core = { module = "androidx.compose.material:material" }
|
|
||||||
|
|
||||||
glance = "androidx.glance:glance-appwidget:1.0.0"
|
glance = "androidx.glance:glance-appwidget:1.0.0"
|
||||||
|
|
||||||
accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanist" }
|
accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanist" }
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[versions]
|
[versions]
|
||||||
kotlin_version = "1.9.23"
|
kotlin_version = "1.9.24"
|
||||||
serialization_version = "1.6.3"
|
serialization_version = "1.6.3"
|
||||||
xml_serialization_version = "0.86.3"
|
xml_serialization_version = "0.86.3"
|
||||||
|
|
||||||
|
|||||||
+10
-14
@@ -1,7 +1,6 @@
|
|||||||
[versions]
|
[versions]
|
||||||
aboutlib_version = "11.1.1"
|
aboutlib_version = "11.2.1"
|
||||||
acra = "5.11.3"
|
leakcanary = "2.14"
|
||||||
leakcanary = "2.13"
|
|
||||||
moko = "0.23.0"
|
moko = "0.23.0"
|
||||||
okhttp_version = "5.0.0-alpha.12"
|
okhttp_version = "5.0.0-alpha.12"
|
||||||
richtext = "0.20.0"
|
richtext = "0.20.0"
|
||||||
@@ -15,7 +14,7 @@ detektCompose = "0.3.12"
|
|||||||
[libraries]
|
[libraries]
|
||||||
desugar = "com.android.tools:desugar_jdk_libs:2.0.4"
|
desugar = "com.android.tools:desugar_jdk_libs:2.0.4"
|
||||||
android-shortcut-gradle = "com.github.zellius:android-shortcut-gradle-plugin:0.1.2"
|
android-shortcut-gradle = "com.github.zellius:android-shortcut-gradle-plugin:0.1.2"
|
||||||
google-services-gradle = "com.google.gms:google-services:4.4.1"
|
google-services-gradle = "com.google.gms:google-services:4.4.2"
|
||||||
|
|
||||||
rxjava = "io.reactivex:rxjava:1.3.8"
|
rxjava = "io.reactivex:rxjava:1.3.8"
|
||||||
|
|
||||||
@@ -32,8 +31,8 @@ quickjs-android = "app.cash.quickjs:quickjs-android:0.9.2"
|
|||||||
jsoup = "org.jsoup:jsoup:1.17.2"
|
jsoup = "org.jsoup:jsoup:1.17.2"
|
||||||
|
|
||||||
disklrucache = "com.jakewharton:disklrucache:2.0.2"
|
disklrucache = "com.jakewharton:disklrucache:2.0.2"
|
||||||
unifile = "com.github.tachiyomiorg:unifile:7c257e1c64"
|
unifile = "com.github.tachiyomiorg:unifile:e0def6b3dc"
|
||||||
common-compress = "org.apache.commons:commons-compress:1.26.1"
|
common-compress = "org.apache.commons:commons-compress:1.26.2"
|
||||||
junrar = "com.github.junrar:junrar:7.5.5"
|
junrar = "com.github.junrar:junrar:7.5.5"
|
||||||
zip4j = "net.lingala.zip4j:zip4j:2.11.5"
|
zip4j = "net.lingala.zip4j:zip4j:2.11.5"
|
||||||
|
|
||||||
@@ -52,7 +51,7 @@ coil-gif = { module = "io.coil-kt.coil3:coil-gif" }
|
|||||||
coil-compose = { module = "io.coil-kt.coil3:coil-compose" }
|
coil-compose = { module = "io.coil-kt.coil3:coil-compose" }
|
||||||
coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp" }
|
coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp" }
|
||||||
|
|
||||||
subsamplingscaleimageview = "com.github.tachiyomiorg:subsampling-scale-image-view:aeaa170036"
|
subsamplingscaleimageview = "com.github.tachiyomiorg:subsampling-scale-image-view:b8e1b0ed2b"
|
||||||
image-decoder = "com.github.tachiyomiorg:image-decoder:e08e9be535"
|
image-decoder = "com.github.tachiyomiorg:image-decoder:e08e9be535"
|
||||||
exifinterface = "androidx.exifinterface:exifinterface:1.3.7"
|
exifinterface = "androidx.exifinterface:exifinterface:1.3.7"
|
||||||
|
|
||||||
@@ -61,7 +60,7 @@ natural-comparator = "com.github.gpanther:java-nat-sort:natural-comparator-1.1"
|
|||||||
richtext-commonmark = { module = "com.halilibo.compose-richtext:richtext-commonmark", version.ref = "richtext" }
|
richtext-commonmark = { module = "com.halilibo.compose-richtext:richtext-commonmark", version.ref = "richtext" }
|
||||||
richtext-m3 = { module = "com.halilibo.compose-richtext:richtext-ui-material3", version.ref = "richtext" }
|
richtext-m3 = { module = "com.halilibo.compose-richtext:richtext-ui-material3", version.ref = "richtext" }
|
||||||
|
|
||||||
material = "com.google.android.material:material:1.11.0"
|
material = "com.google.android.material:material:1.12.0"
|
||||||
flexible-adapter-core = "com.github.arkon.FlexibleAdapter:flexible-adapter:c8013533"
|
flexible-adapter-core = "com.github.arkon.FlexibleAdapter:flexible-adapter:c8013533"
|
||||||
photoview = "com.github.chrisbanes:PhotoView:2.3.0"
|
photoview = "com.github.chrisbanes:PhotoView:2.3.0"
|
||||||
directionalviewpager = "com.github.tachiyomiorg:DirectionalViewPager:1.0.0"
|
directionalviewpager = "com.github.tachiyomiorg:DirectionalViewPager:1.0.0"
|
||||||
@@ -77,9 +76,7 @@ moko-gradle = { module = "dev.icerock.moko:resources-generator", version.ref = "
|
|||||||
|
|
||||||
logcat = "com.squareup.logcat:logcat:0.1"
|
logcat = "com.squareup.logcat:logcat:0.1"
|
||||||
|
|
||||||
acra-http = { module = "ch.acra:acra-http", version.ref = "acra" }
|
firebase-analytics = "com.google.firebase:firebase-analytics:22.0.1"
|
||||||
acra-scheduler = { module = "ch.acra:acra-advanced-scheduler", version.ref = "acra" }
|
|
||||||
firebase-analytics = "com.google.firebase:firebase-analytics-ktx:21.6.1"
|
|
||||||
|
|
||||||
aboutLibraries-gradle = { module = "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin", version.ref = "aboutlib_version" }
|
aboutLibraries-gradle = { module = "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin", version.ref = "aboutlib_version" }
|
||||||
aboutLibraries-compose = { module = "com.mikepenz:aboutlibraries-compose-m3", version.ref = "aboutlib_version" }
|
aboutLibraries-compose = { module = "com.mikepenz:aboutlibraries-compose-m3", version.ref = "aboutlib_version" }
|
||||||
@@ -97,8 +94,8 @@ sqldelight-dialects-sql = { module = "app.cash.sqldelight:sqlite-3-38-dialect",
|
|||||||
sqldelight-gradle = { module = "app.cash.sqldelight:gradle-plugin", version.ref = "sqldelight" }
|
sqldelight-gradle = { module = "app.cash.sqldelight:gradle-plugin", version.ref = "sqldelight" }
|
||||||
|
|
||||||
junit = "org.junit.jupiter:junit-jupiter:5.10.2"
|
junit = "org.junit.jupiter:junit-jupiter:5.10.2"
|
||||||
kotest-assertions = "io.kotest:kotest-assertions-core:5.8.1"
|
kotest-assertions = "io.kotest:kotest-assertions-core:5.9.0"
|
||||||
mockk = "io.mockk:mockk:1.13.10"
|
mockk = "io.mockk:mockk:1.13.11"
|
||||||
|
|
||||||
voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" }
|
voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" }
|
||||||
voyager-screenmodel = { module = "cafe.adriel.voyager:voyager-screenmodel", version.ref = "voyager" }
|
voyager-screenmodel = { module = "cafe.adriel.voyager:voyager-screenmodel", version.ref = "voyager" }
|
||||||
@@ -113,7 +110,6 @@ google-api-services-drive = "com.google.apis:google-api-services-drive:v3-rev197
|
|||||||
google-api-client-oauth = "com.google.oauth-client:google-oauth-client:1.34.1"
|
google-api-client-oauth = "com.google.oauth-client:google-oauth-client:1.34.1"
|
||||||
|
|
||||||
[bundles]
|
[bundles]
|
||||||
acra = ["acra-http", "acra-scheduler"]
|
|
||||||
archive = ["common-compress", "junrar"]
|
archive = ["common-compress", "junrar"]
|
||||||
okhttp = ["okhttp-core", "okhttp-logging", "okhttp-brotli", "okhttp-dnsoverhttps"]
|
okhttp = ["okhttp-core", "okhttp-logging", "okhttp-brotli", "okhttp-dnsoverhttps"]
|
||||||
js-engine = ["quickjs-android"]
|
js-engine = ["quickjs-android"]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
[versions]
|
[versions]
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
firebase-analytics = "com.google.firebase:firebase-analytics-ktx:21.6.1"
|
firebase-analytics = "com.google.firebase:firebase-analytics:22.0.0"
|
||||||
firebase-crashlytics-ktx = "com.google.firebase:firebase-crashlytics-ktx:18.6.3"
|
firebase-crashlytics-ktx = "com.google.firebase:firebase-crashlytics:19.0.0"
|
||||||
firebase-crashlytics-gradle = "com.google.firebase:firebase-crashlytics-gradle:2.9.9"
|
firebase-crashlytics-gradle = "com.google.firebase:firebase-crashlytics-gradle:2.9.9"
|
||||||
|
|
||||||
simularity = "info.debatty:java-string-similarity:2.0.0"
|
simularity = "info.debatty:java-string-similarity:2.0.0"
|
||||||
@@ -11,4 +11,4 @@ xlog = "com.elvishew:xlog:1.11.0"
|
|||||||
ratingbar = "me.zhanghai.android.materialratingbar:library:1.4.0"
|
ratingbar = "me.zhanghai.android.materialratingbar:library:1.4.0"
|
||||||
composeRatingbar = "com.github.a914-gowtham:compose-ratingbar:1.2.3"
|
composeRatingbar = "com.github.a914-gowtham:compose-ratingbar:1.2.3"
|
||||||
|
|
||||||
versionsx = "com.github.ben-manes:gradle-versions-plugin:0.42.0"
|
versionsx = "com.github.ben-manes:gradle-versions-plugin:0.51.0"
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (3) This script is generated from the Groovy template
|
||||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
# within the Gradle project.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
|||||||
@@ -203,10 +203,8 @@
|
|||||||
<string name="pref_local_source_hidden_folders_summery">Allow local source to read hidden folders</string>
|
<string name="pref_local_source_hidden_folders_summery">Allow local source to read hidden folders</string>
|
||||||
|
|
||||||
<!-- Backup settings -->
|
<!-- Backup settings -->
|
||||||
<string name="pref_backup_and_sync_summary">Manual & automatic backups and sync</string>
|
|
||||||
<string name="custom_entry_info">Custom entry info</string>
|
<string name="custom_entry_info">Custom entry info</string>
|
||||||
<string name="all_read_entries">All read entries</string>
|
<string name="all_read_entries">All read entries</string>
|
||||||
<string name="label_backup">Backup</string>
|
|
||||||
<string name="label_sync">Sync</string>
|
<string name="label_sync">Sync</string>
|
||||||
<string name="label_triggers">Triggers</string>
|
<string name="label_triggers">Triggers</string>
|
||||||
|
|
||||||
@@ -220,24 +218,16 @@
|
|||||||
<string name="pref_sync_api_key_summ">Enter the API key to synchronize your library</string>
|
<string name="pref_sync_api_key_summ">Enter the API key to synchronize your library</string>
|
||||||
<string name="pref_sync_now_group_title">Sync Actions</string>
|
<string name="pref_sync_now_group_title">Sync Actions</string>
|
||||||
<string name="pref_sync_now">Sync now</string>
|
<string name="pref_sync_now">Sync now</string>
|
||||||
<string name="pref_sync_confirmation_title">Sync confirmation</string>
|
|
||||||
<string name="pref_sync_now_subtitle">Initiate immediate synchronization of your data</string>
|
<string name="pref_sync_now_subtitle">Initiate immediate synchronization of your data</string>
|
||||||
<string name="pref_sync_confirmation_message">Syncing will overwrite your local library with the remote library. Are you sure you want to continue?</string>
|
|
||||||
<string name="pref_sync_service">Service</string>
|
<string name="pref_sync_service">Service</string>
|
||||||
<string name="pref_sync_service_summ">Select the service to sync your library with</string>
|
|
||||||
<string name="pref_sync_service_category">Sync</string>
|
<string name="pref_sync_service_category">Sync</string>
|
||||||
<string name="pref_sync_automatic_category">Automatic Synchronization</string>
|
<string name="pref_sync_automatic_category">Automatic Synchronization</string>
|
||||||
<string name="pref_sync_interval">Synchronization frequency</string>
|
<string name="pref_sync_interval">Synchronization frequency</string>
|
||||||
<string name="pref_choose_what_to_sync">Choose what to sync</string>
|
<string name="pref_choose_what_to_sync">Choose what to sync</string>
|
||||||
<string name="success_reset_sync_timestamp">Last sync timestamp reset</string>
|
|
||||||
<string name="syncyomi">SyncYomi</string>
|
<string name="syncyomi">SyncYomi</string>
|
||||||
<string name="sync_completed_message">Done in %1$s</string>
|
|
||||||
<string name="last_synchronization">Last Synchronization: %1$s</string>
|
<string name="last_synchronization">Last Synchronization: %1$s</string>
|
||||||
<string name="google_drive">Google Drive</string>
|
<string name="google_drive">Google Drive</string>
|
||||||
<string name="pref_google_drive_sign_in">Sign in</string>
|
<string name="pref_google_drive_sign_in">Sign in</string>
|
||||||
<string name="google_drive_sign_in_success">Signed in successfully</string>
|
|
||||||
<string name="google_drive_sign_in_failed">Sign in failed</string>
|
|
||||||
<string name="authentication">Authentication</string>
|
|
||||||
<string name="pref_google_drive_purge_sync_data">Clear Sync Data from Google Drive</string>
|
<string name="pref_google_drive_purge_sync_data">Clear Sync Data from Google Drive</string>
|
||||||
<string name="google_drive_sync_data_purged">Sync data purged from Google Drive</string>
|
<string name="google_drive_sync_data_purged">Sync data purged from Google Drive</string>
|
||||||
<string name="google_drive_sync_data_not_found">No sync data found in Google Drive</string>
|
<string name="google_drive_sync_data_not_found">No sync data found in Google Drive</string>
|
||||||
|
|||||||
@@ -134,7 +134,6 @@
|
|||||||
<string name="data_saver_image_format_summary_on">La taille du fichier Jpeg est considérablement plus petite que Webp (ce qui signifie que plus de données sont économisées), mais cela fait également perdre plus de qualité aux images. \n Actuellement compresse en Jpeg</string>
|
<string name="data_saver_image_format_summary_on">La taille du fichier Jpeg est considérablement plus petite que Webp (ce qui signifie que plus de données sont économisées), mais cela fait également perdre plus de qualité aux images. \n Actuellement compresse en Jpeg</string>
|
||||||
<string name="data_saver_image_format_summary_off">La taille du fichier Jpeg est considérablement plus petite que celle de Webp (ce qui signifie que plus de données sont économisées), mais cela fait également perdre plus de qualité aux images. \n. Actuellement compresse en Webp</string>
|
<string name="data_saver_image_format_summary_off">La taille du fichier Jpeg est considérablement plus petite que celle de Webp (ce qui signifie que plus de données sont économisées), mais cela fait également perdre plus de qualité aux images. \n. Actuellement compresse en Webp</string>
|
||||||
<string name="data_saver_color_bw">Convertir en noir et blanc</string>
|
<string name="data_saver_color_bw">Convertir en noir et blanc</string>
|
||||||
<string name="data_saver_server">Serveur proxy Bandwidth Hero</string>
|
|
||||||
<string name="data_saver_server_summary">Mettez ici l\'URL du serveur Bandwidth Hero Proxy</string>
|
<string name="data_saver_server_summary">Mettez ici l\'URL du serveur Bandwidth Hero Proxy</string>
|
||||||
|
|
||||||
<!-- Log Level -->
|
<!-- Log Level -->
|
||||||
|
|||||||
@@ -139,7 +139,6 @@
|
|||||||
<string name="data_saver_image_format_summary_on">Ukuran file Jpeg jauh lebih kecil daripada Webp (artinya lebih banyak menghemat data), tetapi itu juga membuat kualitas gambar menjadi lebih buruk.\nSaat ini kompres ke Jpeg</string>
|
<string name="data_saver_image_format_summary_on">Ukuran file Jpeg jauh lebih kecil daripada Webp (artinya lebih banyak menghemat data), tetapi itu juga membuat kualitas gambar menjadi lebih buruk.\nSaat ini kompres ke Jpeg</string>
|
||||||
<string name="data_saver_image_format_summary_off">Ukuran file Jpeg jauh lebih kecil daripada Webp (artinya lebih banyak menghemat data), tetapi itu juga membuat kualitas gambar menjadi lebih buruk.\nSaat ini kompres ke Webp</string>
|
<string name="data_saver_image_format_summary_off">Ukuran file Jpeg jauh lebih kecil daripada Webp (artinya lebih banyak menghemat data), tetapi itu juga membuat kualitas gambar menjadi lebih buruk.\nSaat ini kompres ke Webp</string>
|
||||||
<string name="data_saver_color_bw">Konversi ke Hitam Putih</string>
|
<string name="data_saver_color_bw">Konversi ke Hitam Putih</string>
|
||||||
<string name="data_saver_server">Server Bandwidth Hero Proxy</string>
|
|
||||||
<string name="data_saver_server_summary">Masukkan url server Bandwidth Hero Proxy disini</string>
|
<string name="data_saver_server_summary">Masukkan url server Bandwidth Hero Proxy disini</string>
|
||||||
<string name="clear_db_exclude_read">Abaikab manga yang sudah dibaca</string>
|
<string name="clear_db_exclude_read">Abaikab manga yang sudah dibaca</string>
|
||||||
|
|
||||||
|
|||||||
@@ -138,7 +138,6 @@
|
|||||||
<string name="data_saver_image_format_summary_on">O tamanho do arquivo Jpeg é notavelmente menor que o Webp (ou seja, mais dados são salvos), mas também faz as imagens perderem mais qualidade.\nAtual: comprimindo em Jpeg</string>
|
<string name="data_saver_image_format_summary_on">O tamanho do arquivo Jpeg é notavelmente menor que o Webp (ou seja, mais dados são salvos), mas também faz as imagens perderem mais qualidade.\nAtual: comprimindo em Jpeg</string>
|
||||||
<string name="data_saver_image_format_summary_off">O tamanho do arquivo Jpeg é notavelmente menor que o Webp (ou seja, mais dados são salvos), mas também faz as imagens perderem mais qualidade.\nAtual: comprimindo em Webp</string>
|
<string name="data_saver_image_format_summary_off">O tamanho do arquivo Jpeg é notavelmente menor que o Webp (ou seja, mais dados são salvos), mas também faz as imagens perderem mais qualidade.\nAtual: comprimindo em Webp</string>
|
||||||
<string name="data_saver_color_bw">Converter em Preto e Branco</string>
|
<string name="data_saver_color_bw">Converter em Preto e Branco</string>
|
||||||
<string name="data_saver_server">Servidor Bandwidth Hero Proxy</string>
|
|
||||||
<string name="data_saver_server_summary">Insira a url do servidor Bandwidth Hero Proxy aqui</string>
|
<string name="data_saver_server_summary">Insira a url do servidor Bandwidth Hero Proxy aqui</string>
|
||||||
<string name="clear_db_exclude_read">Manter mangás com capítulos lidos</string>
|
<string name="clear_db_exclude_read">Manter mangás com capítulos lidos</string>
|
||||||
|
|
||||||
|
|||||||
@@ -179,6 +179,9 @@
|
|||||||
<string name="pref_mark_read_dupe_chapters_summary">Помечать повторяющиеся главы как «Прочитано» после прочтения</string>
|
<string name="pref_mark_read_dupe_chapters_summary">Помечать повторяющиеся главы как «Прочитано» после прочтения</string>
|
||||||
<string name="pref_library_mark_duplicate_chapters">Помечать новые повторяющиеся главы как «Прочитано»</string>
|
<string name="pref_library_mark_duplicate_chapters">Помечать новые повторяющиеся главы как «Прочитано»</string>
|
||||||
<string name="pref_library_mark_duplicate_chapters_summary">Автоматически помечать новые главы как «Прочитано», если они были ранее прочитаны</string>
|
<string name="pref_library_mark_duplicate_chapters_summary">Автоматически помечать новые главы как «Прочитано», если они были ранее прочитаны</string>
|
||||||
|
<string name="update_30min">Каждые 30 минут</string>
|
||||||
|
<string name="update_1hour">Каждый час</string>
|
||||||
|
<string name="update_3hour">Каждые 3 часа</string>
|
||||||
|
|
||||||
<!-- Browse settings -->
|
<!-- Browse settings -->
|
||||||
<string name="pref_hide_feed">Скрыть вкладку «Лента»</string>
|
<string name="pref_hide_feed">Скрыть вкладку «Лента»</string>
|
||||||
@@ -194,6 +197,47 @@
|
|||||||
<!-- Backup settings -->
|
<!-- Backup settings -->
|
||||||
<string name="custom_entry_info">Сведенья пользователя</string>
|
<string name="custom_entry_info">Сведенья пользователя</string>
|
||||||
<string name="all_read_entries">Прочитанные серии</string>
|
<string name="all_read_entries">Прочитанные серии</string>
|
||||||
|
<string name="label_sync">Синхронизировать</string>
|
||||||
|
<string name="label_triggers">Триггеры</string>
|
||||||
|
|
||||||
|
<!-- Sync settings -->
|
||||||
|
<string name="sync_error">Не удалось синхронизировать библиотеку</string>
|
||||||
|
<string name="sync_complete">Синхронизация библиотеки завершена</string>
|
||||||
|
<string name="sync_in_progress">Синхронизация уже выполняется</string>
|
||||||
|
<string name="pref_sync_host">Хост</string>
|
||||||
|
<string name="pref_sync_host_summ">Введите адрес хоста для синхронизации вашей библиотеки</string>
|
||||||
|
<string name="pref_sync_api_key">Ключ API</string>
|
||||||
|
<string name="pref_sync_api_key_summ">Введите API ключ для синхронизации вашей библиотеки</string>
|
||||||
|
<string name="pref_sync_now_group_title">Действия синхронизации</string>
|
||||||
|
<string name="pref_sync_now">Синхронизировать сейчас</string>
|
||||||
|
<string name="pref_sync_now_subtitle">Начать немедленную синхронизацию ваших данных</string>
|
||||||
|
<string name="pref_sync_service">Сервис</string>
|
||||||
|
<string name="pref_sync_service_category">Синхронизация</string>
|
||||||
|
<string name="pref_sync_automatic_category">Автоматическая синхронизация</string>
|
||||||
|
<string name="pref_sync_interval">Частота синхронизации</string>
|
||||||
|
<string name="pref_choose_what_to_sync">Выберите, что синхронизировать</string>
|
||||||
|
<string name="last_synchronization">Последняя синхронизация: %1$s</string>
|
||||||
|
<string name="google_drive">Google Диск</string>
|
||||||
|
<string name="pref_google_drive_sign_in">Войти в Google Диск</string>
|
||||||
|
<string name="pref_google_drive_purge_sync_data">Очистить данные синхронизации с Google Диска</string>
|
||||||
|
<string name="google_drive_sync_data_purged">Данные синхронизации удалены из Google Диска</string>
|
||||||
|
<string name="google_drive_sync_data_not_found">Данные синхронизации не найдены в Google Диске</string>
|
||||||
|
<string name="google_drive_sync_data_purge_error">Ошибка при очистке данных синхронизации с Google Диска, попробуйте войти снова.</string>
|
||||||
|
<string name="google_drive_login_success">Выполнен вход в Google Диск</string>
|
||||||
|
<string name="google_drive_login_failed">Не удалось войти в Google Диск: %s</string>
|
||||||
|
<string name="google_drive_not_signed_in">Не выполнен вход в Google Диск</string>
|
||||||
|
<string name="error_uploading_sync_data">Ошибка при загрузке данных синхронизации на Google Диск</string>
|
||||||
|
<string name="error_deleting_google_drive_lock_file">Ошибка удаления файла блокировки Google Диска</string>
|
||||||
|
<string name="error_before_sync_gdrive">Ошибка перед синхронизацией: %s</string>
|
||||||
|
<string name="pref_purge_confirmation_title">Подтверждение очистки</string>
|
||||||
|
<string name="pref_purge_confirmation_message">Очистка данных синхронизации приведет к удалению всех данных синхронизации с Google Диска. Вы уверены, что хотите продолжить?</string>
|
||||||
|
<string name="pref_sync_options">Создать триггеры синхронизации</string>
|
||||||
|
<string name="pref_sync_options_summ">Можно использовать для настройки триггеров синхронизации</string>
|
||||||
|
<string name="sync_on_chapter_read">Синхронизовать при прочтении главы</string>
|
||||||
|
<string name="sync_on_chapter_open">Синхронизовать при открытии главы</string>
|
||||||
|
<string name="sync_on_app_start">Синхронизовать при запуске приложения</string>
|
||||||
|
<string name="sync_on_app_resume">Синхронизовать при возвращении в приложение</string>
|
||||||
|
<string name="sync_library">Синхронизировать библиотеку</string>
|
||||||
|
|
||||||
<!-- Security settings -->
|
<!-- Security settings -->
|
||||||
<string name="biometric_lock_times">Биометрическое время блокировки</string>
|
<string name="biometric_lock_times">Биометрическое время блокировки</string>
|
||||||
|
|||||||
@@ -496,7 +496,6 @@
|
|||||||
<string name="launching_app">應用程式啟動中……</string>
|
<string name="launching_app">應用程式啟動中……</string>
|
||||||
<string name="error_with_reason">錯誤:%1$s</string>
|
<string name="error_with_reason">錯誤:%1$s</string>
|
||||||
<string name="could_not_open_entry">無法開啟此作品:\n\n%1$s</string>
|
<string name="could_not_open_entry">無法開啟此作品:\n\n%1$s</string>
|
||||||
<string name="entry_id_is_null">作品 ID 為空!</string>
|
|
||||||
<string name="loading_entry">作品載入中……</string>
|
<string name="loading_entry">作品載入中……</string>
|
||||||
|
|
||||||
<!-- Page previews -->
|
<!-- Page previews -->
|
||||||
@@ -612,7 +611,6 @@
|
|||||||
|
|
||||||
<!-- Similar -->
|
<!-- Similar -->
|
||||||
<string name="similar">與 %1$s 相似</string>
|
<string name="similar">與 %1$s 相似</string>
|
||||||
<string name="similar_no_results">相似作品未找到</string>
|
|
||||||
|
|
||||||
<!-- MangaDex relations-->
|
<!-- MangaDex relations-->
|
||||||
<string name="relation_similar">相似</string>
|
<string name="relation_similar">相似</string>
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ dependencies {
|
|||||||
implementation(compose.activity)
|
implementation(compose.activity)
|
||||||
implementation(compose.foundation)
|
implementation(compose.foundation)
|
||||||
implementation(compose.material3.core)
|
implementation(compose.material3.core)
|
||||||
implementation(compose.material.core)
|
|
||||||
implementation(compose.material.icons)
|
implementation(compose.material.icons)
|
||||||
implementation(compose.animation)
|
implementation(compose.animation)
|
||||||
implementation(compose.animation.graphics)
|
implementation(compose.animation.graphics)
|
||||||
|
|||||||
+8
-12
@@ -10,7 +10,6 @@ import androidx.compose.foundation.gestures.DraggableAnchors
|
|||||||
import androidx.compose.foundation.gestures.Orientation
|
import androidx.compose.foundation.gestures.Orientation
|
||||||
import androidx.compose.foundation.gestures.anchoredDraggable
|
import androidx.compose.foundation.gestures.anchoredDraggable
|
||||||
import androidx.compose.foundation.gestures.animateTo
|
import androidx.compose.foundation.gestures.animateTo
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||||
@@ -39,7 +38,6 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollSource
|
|||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.layout.onSizeChanged
|
import androidx.compose.ui.layout.onSizeChanged
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.unit.Dp
|
|
||||||
import androidx.compose.ui.unit.IntOffset
|
import androidx.compose.ui.unit.IntOffset
|
||||||
import androidx.compose.ui.unit.Velocity
|
import androidx.compose.ui.unit.Velocity
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
@@ -54,7 +52,6 @@ private val sheetAnimationSpec = tween<Float>(durationMillis = 350)
|
|||||||
@Composable
|
@Composable
|
||||||
fun AdaptiveSheet(
|
fun AdaptiveSheet(
|
||||||
isTabletUi: Boolean,
|
isTabletUi: Boolean,
|
||||||
tonalElevation: Dp,
|
|
||||||
enableSwipeDismiss: Boolean,
|
enableSwipeDismiss: Boolean,
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
@@ -78,8 +75,7 @@ fun AdaptiveSheet(
|
|||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clickable(
|
.clickable(
|
||||||
enabled = true,
|
interactionSource = null,
|
||||||
interactionSource = remember { MutableInteractionSource() },
|
|
||||||
indication = null,
|
indication = null,
|
||||||
onClick = internalOnDismissRequest,
|
onClick = internalOnDismissRequest,
|
||||||
)
|
)
|
||||||
@@ -91,7 +87,7 @@ fun AdaptiveSheet(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.requiredWidthIn(max = 460.dp)
|
.requiredWidthIn(max = 460.dp)
|
||||||
.clickable(
|
.clickable(
|
||||||
interactionSource = remember { MutableInteractionSource() },
|
interactionSource = null,
|
||||||
indication = null,
|
indication = null,
|
||||||
onClick = {},
|
onClick = {},
|
||||||
)
|
)
|
||||||
@@ -99,7 +95,7 @@ fun AdaptiveSheet(
|
|||||||
.padding(vertical = 16.dp)
|
.padding(vertical = 16.dp)
|
||||||
.then(modifier),
|
.then(modifier),
|
||||||
shape = MaterialTheme.shapes.extraLarge,
|
shape = MaterialTheme.shapes.extraLarge,
|
||||||
tonalElevation = tonalElevation,
|
color = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||||
content = {
|
content = {
|
||||||
BackHandler(enabled = alpha > 0f, onBack = internalOnDismissRequest)
|
BackHandler(enabled = alpha > 0f, onBack = internalOnDismissRequest)
|
||||||
content()
|
content()
|
||||||
@@ -122,14 +118,14 @@ fun AdaptiveSheet(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
val internalOnDismissRequest = {
|
val internalOnDismissRequest = {
|
||||||
if (anchoredDraggableState.currentValue == 0) {
|
if (anchoredDraggableState.settledValue == 0) {
|
||||||
scope.launch { anchoredDraggableState.animateTo(1) }
|
scope.launch { anchoredDraggableState.animateTo(1) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clickable(
|
.clickable(
|
||||||
interactionSource = remember { MutableInteractionSource() },
|
interactionSource = null,
|
||||||
indication = null,
|
indication = null,
|
||||||
onClick = internalOnDismissRequest,
|
onClick = internalOnDismissRequest,
|
||||||
)
|
)
|
||||||
@@ -147,7 +143,7 @@ fun AdaptiveSheet(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.widthIn(max = 460.dp)
|
.widthIn(max = 460.dp)
|
||||||
.clickable(
|
.clickable(
|
||||||
interactionSource = remember { MutableInteractionSource() },
|
interactionSource = null,
|
||||||
indication = null,
|
indication = null,
|
||||||
onClick = {},
|
onClick = {},
|
||||||
)
|
)
|
||||||
@@ -180,7 +176,7 @@ fun AdaptiveSheet(
|
|||||||
.navigationBarsPadding()
|
.navigationBarsPadding()
|
||||||
.statusBarsPadding(),
|
.statusBarsPadding(),
|
||||||
shape = MaterialTheme.shapes.extraLarge,
|
shape = MaterialTheme.shapes.extraLarge,
|
||||||
tonalElevation = tonalElevation,
|
color = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||||
content = {
|
content = {
|
||||||
BackHandler(
|
BackHandler(
|
||||||
enabled = anchoredDraggableState.targetValue == 0,
|
enabled = anchoredDraggableState.targetValue == 0,
|
||||||
@@ -192,7 +188,7 @@ fun AdaptiveSheet(
|
|||||||
|
|
||||||
LaunchedEffect(anchoredDraggableState) {
|
LaunchedEffect(anchoredDraggableState) {
|
||||||
scope.launch { anchoredDraggableState.animateTo(0) }
|
scope.launch { anchoredDraggableState.animateTo(0) }
|
||||||
snapshotFlow { anchoredDraggableState.currentValue }
|
snapshotFlow { anchoredDraggableState.settledValue }
|
||||||
.drop(1)
|
.drop(1)
|
||||||
.filter { it == 1 }
|
.filter { it == 1 }
|
||||||
.collectLatest {
|
.collectLatest {
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.unit.Dp
|
|
||||||
import androidx.compose.ui.unit.TextUnit
|
import androidx.compose.ui.unit.TextUnit
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
@@ -18,9 +17,8 @@ import androidx.compose.ui.unit.dp
|
|||||||
fun Pill(
|
fun Pill(
|
||||||
text: String,
|
text: String,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
color: Color = MaterialTheme.colorScheme.background,
|
color: Color = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||||
contentColor: Color = MaterialTheme.colorScheme.onBackground,
|
contentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||||
elevation: Dp = 1.dp,
|
|
||||||
fontSize: TextUnit = LocalTextStyle.current.fontSize,
|
fontSize: TextUnit = LocalTextStyle.current.fontSize,
|
||||||
) {
|
) {
|
||||||
Surface(
|
Surface(
|
||||||
@@ -29,7 +27,6 @@ fun Pill(
|
|||||||
shape = MaterialTheme.shapes.extraLarge,
|
shape = MaterialTheme.shapes.extraLarge,
|
||||||
color = color,
|
color = color,
|
||||||
contentColor = contentColor,
|
contentColor = contentColor,
|
||||||
tonalElevation = elevation,
|
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|||||||
+4
-3
@@ -14,7 +14,6 @@ import androidx.compose.foundation.layout.size
|
|||||||
import androidx.compose.foundation.lazy.grid.GridCells
|
import androidx.compose.foundation.lazy.grid.GridCells
|
||||||
import androidx.compose.foundation.lazy.grid.LazyGridScope
|
import androidx.compose.foundation.lazy.grid.LazyGridScope
|
||||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||||
import androidx.compose.material.ContentAlpha
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowDownward
|
import androidx.compose.material.icons.filled.ArrowDownward
|
||||||
import androidx.compose.material.icons.filled.ArrowUpward
|
import androidx.compose.material.icons.filled.ArrowUpward
|
||||||
@@ -57,6 +56,8 @@ object SettingsItemsPaddings {
|
|||||||
val Vertical = 10.dp
|
val Vertical = 10.dp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const val DisabledContentAlpha = 0.38f
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HeadingItem(labelRes: StringResource) {
|
fun HeadingItem(labelRes: StringResource) {
|
||||||
HeadingItem(stringResource(labelRes))
|
HeadingItem(stringResource(labelRes))
|
||||||
@@ -282,7 +283,7 @@ fun TriStateItem(
|
|||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.large),
|
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.large),
|
||||||
) {
|
) {
|
||||||
val stateAlpha = if (enabled && onClick != null) 1f else ContentAlpha.disabled
|
val stateAlpha = if (enabled && onClick != null) 1f else DisabledContentAlpha
|
||||||
|
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = when (state) {
|
imageVector = when (state) {
|
||||||
@@ -295,7 +296,7 @@ fun TriStateItem(
|
|||||||
MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = stateAlpha)
|
MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = stateAlpha)
|
||||||
} else {
|
} else {
|
||||||
when (onClick) {
|
when (onClick) {
|
||||||
null -> MaterialTheme.colorScheme.onSurface.copy(alpha = ContentAlpha.disabled)
|
null -> MaterialTheme.colorScheme.onSurface.copy(alpha = DisabledContentAlpha)
|
||||||
else -> MaterialTheme.colorScheme.primary
|
else -> MaterialTheme.colorScheme.primary
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
-2
@@ -100,7 +100,6 @@ fun Button(
|
|||||||
val containerColor = colors.containerColor(enabled).value
|
val containerColor = colors.containerColor(enabled).value
|
||||||
val contentColor = colors.contentColor(enabled).value
|
val contentColor = colors.contentColor(enabled).value
|
||||||
val shadowElevation = elevation?.shadowElevation(enabled, interactionSource)?.value ?: 0.dp
|
val shadowElevation = elevation?.shadowElevation(enabled, interactionSource)?.value ?: 0.dp
|
||||||
val tonalElevation = elevation?.tonalElevation(enabled, interactionSource)?.value ?: 0.dp
|
|
||||||
|
|
||||||
Surface(
|
Surface(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
@@ -109,7 +108,6 @@ fun Button(
|
|||||||
shape = shape,
|
shape = shape,
|
||||||
color = containerColor,
|
color = containerColor,
|
||||||
contentColor = contentColor,
|
contentColor = contentColor,
|
||||||
tonalElevation = tonalElevation,
|
|
||||||
shadowElevation = shadowElevation,
|
shadowElevation = shadowElevation,
|
||||||
border = border,
|
border = border,
|
||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
|
|||||||
+1
-1
@@ -6,13 +6,13 @@ import androidx.compose.foundation.border
|
|||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.material.ripple
|
|
||||||
import androidx.compose.material3.ColorScheme
|
import androidx.compose.material3.ColorScheme
|
||||||
import androidx.compose.material3.LocalAbsoluteTonalElevation
|
import androidx.compose.material3.LocalAbsoluteTonalElevation
|
||||||
import androidx.compose.material3.LocalContentColor
|
import androidx.compose.material3.LocalContentColor
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.contentColorFor
|
import androidx.compose.material3.contentColorFor
|
||||||
import androidx.compose.material3.minimumInteractiveComponentSize
|
import androidx.compose.material3.minimumInteractiveComponentSize
|
||||||
|
import androidx.compose.material3.ripple
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.NonRestartableComposable
|
import androidx.compose.runtime.NonRestartableComposable
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package tachiyomi.presentation.core.util
|
package tachiyomi.presentation.core.util
|
||||||
|
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.isImeVisible
|
import androidx.compose.foundation.layout.isImeVisible
|
||||||
@@ -42,14 +41,12 @@ fun Modifier.secondaryItemAlpha(): Modifier = this.alpha(SecondaryItemAlpha)
|
|||||||
fun Modifier.clickableNoIndication(
|
fun Modifier.clickableNoIndication(
|
||||||
onLongClick: (() -> Unit)? = null,
|
onLongClick: (() -> Unit)? = null,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
): Modifier = composed {
|
) = this.combinedClickable(
|
||||||
Modifier.combinedClickable(
|
interactionSource = null,
|
||||||
interactionSource = remember { MutableInteractionSource() },
|
indication = null,
|
||||||
indication = null,
|
onLongClick = onLongClick,
|
||||||
onLongClick = onLongClick,
|
onClick = onClick,
|
||||||
onClick = onClick,
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For TextField, the provided [action] will be invoked when
|
* For TextField, the provided [action] will be invoked when
|
||||||
|
|||||||
@@ -17,16 +17,10 @@ import kotlinx.coroutines.async
|
|||||||
import kotlinx.coroutines.awaitAll
|
import kotlinx.coroutines.awaitAll
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.decodeFromStream
|
import kotlinx.serialization.json.decodeFromStream
|
||||||
import kotlinx.serialization.json.encodeToStream
|
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import nl.adaptivity.xmlutil.AndroidXmlReader
|
import nl.adaptivity.xmlutil.AndroidXmlReader
|
||||||
import nl.adaptivity.xmlutil.serialization.XML
|
import nl.adaptivity.xmlutil.serialization.XML
|
||||||
import tachiyomi.core.common.i18n.stringResource
|
import tachiyomi.core.common.i18n.stringResource
|
||||||
import tachiyomi.core.metadata.comicinfo.COMIC_INFO_FILE
|
|
||||||
import tachiyomi.core.metadata.comicinfo.ComicInfo
|
|
||||||
import tachiyomi.core.metadata.comicinfo.copyFromComicInfo
|
|
||||||
import tachiyomi.core.metadata.comicinfo.getComicInfo
|
|
||||||
import tachiyomi.core.metadata.tachiyomi.MangaDetails
|
|
||||||
import tachiyomi.core.common.storage.UniFileTempFileManager
|
import tachiyomi.core.common.storage.UniFileTempFileManager
|
||||||
import tachiyomi.core.common.storage.addStreamToZip
|
import tachiyomi.core.common.storage.addStreamToZip
|
||||||
import tachiyomi.core.common.storage.extension
|
import tachiyomi.core.common.storage.extension
|
||||||
@@ -37,6 +31,12 @@ import tachiyomi.core.common.storage.nameWithoutExtension
|
|||||||
import tachiyomi.core.common.util.lang.withIOContext
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
import tachiyomi.core.common.util.system.ImageUtil
|
import tachiyomi.core.common.util.system.ImageUtil
|
||||||
import tachiyomi.core.common.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
|
import tachiyomi.core.metadata.comicinfo.COMIC_INFO_FILE
|
||||||
|
import tachiyomi.core.metadata.comicinfo.ComicInfo
|
||||||
|
import tachiyomi.core.metadata.comicinfo.ComicInfoPublishingStatus
|
||||||
|
import tachiyomi.core.metadata.comicinfo.copyFromComicInfo
|
||||||
|
import tachiyomi.core.metadata.comicinfo.getComicInfo
|
||||||
|
import tachiyomi.core.metadata.tachiyomi.MangaDetails
|
||||||
import tachiyomi.domain.chapter.service.ChapterRecognition
|
import tachiyomi.domain.chapter.service.ChapterRecognition
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
@@ -157,17 +157,39 @@ actual class LocalSource(
|
|||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
fun updateMangaInfo(manga: SManga) {
|
fun updateMangaInfo(manga: SManga) {
|
||||||
val existingFile = fileSystem.getFilesInMangaDirectory(manga.url).find { it.extension == "json" }
|
val mangaDirFiles = fileSystem.getFilesInMangaDirectory(manga.url)
|
||||||
val file = existingFile
|
val existingFile = mangaDirFiles
|
||||||
?: fileSystem.getMangaDirectory(manga.url)?.createFile("info.json")
|
.firstOrNull { it.name == COMIC_INFO_FILE }
|
||||||
?: return
|
val comicInfoArchiveFile = mangaDirFiles
|
||||||
file.openOutputStream().use {
|
.firstOrNull { it.name == COMIC_INFO_ARCHIVE }
|
||||||
json.encodeToStream(manga.toJson(), it)
|
val existingComicInfo = (existingFile?.openInputStream() ?: comicInfoArchiveFile?.getZipInputStream(COMIC_INFO_FILE))?.use {
|
||||||
|
AndroidXmlReader(it, StandardCharsets.UTF_8.name()).use {
|
||||||
|
xml.decodeFromReader<ComicInfo>(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val newComicInfo = if (existingComicInfo != null) {
|
||||||
|
manga.run {
|
||||||
|
existingComicInfo.copy(
|
||||||
|
series = ComicInfo.Series(title),
|
||||||
|
summary = description?.let { ComicInfo.Summary(it) },
|
||||||
|
writer = author?.let { ComicInfo.Writer(it) },
|
||||||
|
penciller = artist?.let { ComicInfo.Penciller(it) },
|
||||||
|
genre = genre?.let { ComicInfo.Genre(it) },
|
||||||
|
publishingStatus = ComicInfo.PublishingStatusTachiyomi(
|
||||||
|
ComicInfoPublishingStatus.toComicInfoValue(status.toLong()),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
manga.getComicInfo()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun SManga.toJson(): MangaDetails {
|
fileSystem.getMangaDirectory(manga.url)?.let {
|
||||||
return MangaDetails(title, author, artist, description, genre?.split(", "), status)
|
copyComicInfoFile(
|
||||||
|
xml.encodeToString(ComicInfo.serializer(), newComicInfo).byteInputStream(),
|
||||||
|
it
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
@@ -368,8 +390,8 @@ actual class LocalSource(
|
|||||||
try {
|
try {
|
||||||
val (mangaDirName, chapterName) = chapter.url.split('/', limit = 2)
|
val (mangaDirName, chapterName) = chapter.url.split('/', limit = 2)
|
||||||
return fileSystem.getBaseDirectory()
|
return fileSystem.getBaseDirectory()
|
||||||
?.findFile(mangaDirName, true)
|
?.findFile(mangaDirName)
|
||||||
?.findFile(chapterName, true)
|
?.findFile(chapterName)
|
||||||
?.let(Format.Companion::valueOf)
|
?.let(Format.Companion::valueOf)
|
||||||
?: throw Exception(context.stringResource(MR.strings.chapter_not_found))
|
?: throw Exception(context.stringResource(MR.strings.chapter_not_found))
|
||||||
} catch (e: Format.UnknownFormatException) {
|
} catch (e: Format.UnknownFormatException) {
|
||||||
|
|||||||
+2
-2
@@ -17,13 +17,13 @@ actual class LocalSourceFileSystem(
|
|||||||
|
|
||||||
actual fun getMangaDirectory(name: String): UniFile? {
|
actual fun getMangaDirectory(name: String): UniFile? {
|
||||||
return getBaseDirectory()
|
return getBaseDirectory()
|
||||||
?.findFile(name, true)
|
?.findFile(name)
|
||||||
?.takeIf { it.isDirectory }
|
?.takeIf { it.isDirectory }
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun getFilesInMangaDirectory(name: String): List<UniFile> {
|
actual fun getFilesInMangaDirectory(name: String): List<UniFile> {
|
||||||
return getBaseDirectory()
|
return getBaseDirectory()
|
||||||
?.findFile(name, true)
|
?.findFile(name)
|
||||||
?.takeIf { it.isDirectory }
|
?.takeIf { it.isDirectory }
|
||||||
?.listFiles().orEmpty().toList()
|
?.listFiles().orEmpty().toList()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user