Merge branch 'sync-part-final' into feat/add-sync-triggers-experimental

This commit is contained in:
KaiserBh 2024-01-07 10:35:20 +11:00
commit ae3dacf913
No known key found for this signature in database
GPG Key ID: 14D73B142042BBA9
7 changed files with 103 additions and 58 deletions

View File

@ -77,7 +77,7 @@ internal fun GlobalSearchContent(
title = fromSourceId?.let { title = fromSourceId?.let {
"${source.name}".takeIf { source.id == fromSourceId } "${source.name}".takeIf { source.id == fromSourceId }
} ?: source.name, } ?: source.name,
subtitle = LocaleHelper.getDisplayName(source.lang), subtitle = LocaleHelper.getLocalizedDisplayName(source.lang),
onClick = { onClickSource(source) }, onClick = { onClickSource(source) },
) { ) {
when (result) { when (result) {

View File

@ -77,7 +77,7 @@ internal class StorageStep : OnboardingStep {
Text(stringResource(MR.strings.onboarding_storage_help_info, stringResource(MR.strings.app_name))) Text(stringResource(MR.strings.onboarding_storage_help_info, stringResource(MR.strings.app_name)))
Button( Button(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
onClick = { handler.openUri("https://tachiyomi.org/docs/faq/storage") }, onClick = { handler.openUri(SettingsDataScreen.HELP_URL) },
) { ) {
Text(stringResource(MR.strings.onboarding_storage_help_action)) Text(stringResource(MR.strings.onboarding_storage_help_action))
} }

View File

@ -7,9 +7,14 @@ import android.net.Uri
import androidx.activity.compose.ManagedActivityResultLauncher import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.HelpOutline
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MultiChoiceSegmentedButtonRow import androidx.compose.material3.MultiChoiceSegmentedButtonRow
import androidx.compose.material3.SegmentedButton import androidx.compose.material3.SegmentedButton
import androidx.compose.material3.SegmentedButtonDefaults import androidx.compose.material3.SegmentedButtonDefaults
@ -25,6 +30,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler
import androidx.core.net.toUri import androidx.core.net.toUri
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
@ -68,11 +74,23 @@ import uy.kohesive.injekt.api.get
object SettingsDataScreen : SearchableSettings { object SettingsDataScreen : SearchableSettings {
val restorePreferenceKeyString = MR.strings.label_backup val restorePreferenceKeyString = MR.strings.label_backup
const val HELP_URL = "https://tachiyomi.org/docs/faq/storage"
@ReadOnlyComposable @ReadOnlyComposable
@Composable @Composable
override fun getTitleRes() = MR.strings.label_data_storage override fun getTitleRes() = MR.strings.label_data_storage
@Composable
override fun RowScope.AppBarAction() {
val uriHandler = LocalUriHandler.current
IconButton(onClick = { uriHandler.openUri(HELP_URL) }) {
Icon(
imageVector = Icons.AutoMirrored.Outlined.HelpOutline,
contentDescription = stringResource(MR.strings.tracking_guide),
)
}
}
@Composable @Composable
override fun getPreferences(): List<Preference> { override fun getPreferences(): List<Preference> {
val backupPreferences = Injekt.get<BackupPreferences>() val backupPreferences = Injekt.get<BackupPreferences>()

View File

@ -34,7 +34,6 @@ import tachiyomi.core.i18n.stringResource
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import java.util.Locale
class AppLanguageScreen : Screen() { class AppLanguageScreen : Screen() {
@ -104,9 +103,9 @@ class AppLanguageScreen : Screen() {
for (i in 0..<parser.attributeCount) { for (i in 0..<parser.attributeCount) {
if (parser.getAttributeName(i) == "name") { if (parser.getAttributeName(i) == "name") {
val langTag = parser.getAttributeValue(i) val langTag = parser.getAttributeValue(i)
val displayName = LocaleHelper.getDisplayName(langTag) val displayName = LocaleHelper.getLocalizedDisplayName(langTag)
if (displayName.isNotEmpty()) { if (displayName.isNotEmpty()) {
langs.add(Language(langTag, displayName, Locale.forLanguageTag(langTag).displayName)) langs.add(Language(langTag, displayName, LocaleHelper.getDisplayName(langTag)))
} }
} }
} }

View File

@ -17,10 +17,12 @@ 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 logcat.LogPriority
import tachiyomi.core.util.lang.launchIO import tachiyomi.core.util.lang.launchIO
import tachiyomi.core.util.lang.withIOContext import tachiyomi.core.util.lang.withIOContext
import tachiyomi.core.util.lang.withUIContext import tachiyomi.core.util.lang.withUIContext
import tachiyomi.core.util.system.ImageUtil import tachiyomi.core.util.system.ImageUtil
import tachiyomi.core.util.system.logcat
import java.io.BufferedInputStream import java.io.BufferedInputStream
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.InputStream import java.io.InputStream
@ -136,40 +138,47 @@ class PagerPageHolder(
val streamFn = page.stream ?: return val streamFn = page.stream ?: return
val (bais, isAnimated, background) = withIOContext { try {
streamFn().buffered(16).use { stream -> val (bais, isAnimated, background) = withIOContext {
process(item, stream).use { itemStream -> streamFn().buffered(16).use { stream ->
val bais = ByteArrayInputStream(itemStream.readBytes()) process(item, stream).use { itemStream ->
val isAnimated = ImageUtil.isAnimatedAndSupported(bais) val bais = ByteArrayInputStream(itemStream.readBytes())
bais.reset() val isAnimated = ImageUtil.isAnimatedAndSupported(bais)
val background = if (!isAnimated && viewer.config.automaticBackground) { bais.reset()
ImageUtil.chooseBackground(context, bais) val background = if (!isAnimated && viewer.config.automaticBackground) {
} else { ImageUtil.chooseBackground(context, bais)
null } else {
null
}
bais.reset()
Triple(bais, isAnimated, background)
} }
bais.reset()
Triple(bais, isAnimated, background)
} }
} }
} withUIContext {
withUIContext { bais.use {
bais.use { setImage(
setImage( it,
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()
}
} catch (e: Throwable) {
logcat(LogPriority.ERROR, e)
withUIContext {
setError()
} }
removeErrorLayout()
} }
} }

View File

@ -23,10 +23,12 @@ 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 kotlinx.coroutines.suspendCancellableCoroutine
import logcat.LogPriority
import tachiyomi.core.util.lang.launchIO import tachiyomi.core.util.lang.launchIO
import tachiyomi.core.util.lang.withIOContext import tachiyomi.core.util.lang.withIOContext
import tachiyomi.core.util.lang.withUIContext import tachiyomi.core.util.lang.withUIContext
import tachiyomi.core.util.system.ImageUtil import tachiyomi.core.util.system.ImageUtil
import tachiyomi.core.util.system.logcat
import java.io.BufferedInputStream import java.io.BufferedInputStream
import java.io.InputStream import java.io.InputStream
@ -184,28 +186,35 @@ class WebtoonPageHolder(
val streamFn = page?.stream ?: return val streamFn = page?.stream ?: return
val (openStream, isAnimated) = withIOContext { try {
val stream = streamFn().buffered(16) val (openStream, isAnimated) = withIOContext {
val openStream = process(stream) val stream = streamFn().buffered(16)
val openStream = process(stream)
val isAnimated = ImageUtil.isAnimatedAndSupported(stream) val isAnimated = ImageUtil.isAnimatedAndSupported(stream)
Pair(openStream, isAnimated) Pair(openStream, isAnimated)
} }
withUIContext { withUIContext {
frame.setImage( frame.setImage(
openStream, openStream,
isAnimated, isAnimated,
ReaderPageImageView.Config( ReaderPageImageView.Config(
zoomDuration = viewer.config.doubleTapAnimDuration, zoomDuration = viewer.config.doubleTapAnimDuration,
minimumScaleType = SubsamplingScaleImageView.SCALE_TYPE_FIT_WIDTH, minimumScaleType = SubsamplingScaleImageView.SCALE_TYPE_FIT_WIDTH,
cropBorders = viewer.config.imageCropBorders, cropBorders = viewer.config.imageCropBorders,
), ),
) )
removeErrorLayout() removeErrorLayout()
} }
// Suspend the coroutine to close the input stream only when the WebtoonPageHolder is recycled // Suspend the coroutine to close the input stream only when the WebtoonPageHolder is recycled
suspendCancellableCoroutine<Nothing> { continuation -> suspendCancellableCoroutine<Nothing> { continuation ->
continuation.invokeOnCancellation { openStream.close() } continuation.invokeOnCancellation { openStream.close() }
}
} catch (e: Throwable) {
logcat(LogPriority.ERROR, e)
withUIContext {
setError()
}
} }
} }

View File

@ -21,7 +21,7 @@ object LocaleHelper {
} else if (b == "all") { } else if (b == "all") {
1 1
} else { } else {
getDisplayName(a).compareTo(getDisplayName(b)) getLocalizedDisplayName(a).compareTo(getLocalizedDisplayName(b))
} }
} }
@ -34,16 +34,26 @@ object LocaleHelper {
SourcesScreenModel.PINNED_KEY -> context.stringResource(MR.strings.pinned_sources) SourcesScreenModel.PINNED_KEY -> context.stringResource(MR.strings.pinned_sources)
"other" -> context.stringResource(MR.strings.other_source) "other" -> context.stringResource(MR.strings.other_source)
"all" -> context.stringResource(MR.strings.multi_lang) "all" -> context.stringResource(MR.strings.multi_lang)
else -> getDisplayName(lang) else -> getLocalizedDisplayName(lang)
} }
} }
fun getDisplayName(lang: String): String {
val normalizedLang = when (lang) {
"zh-CN" -> "zh-Hans"
"zh-TW" -> "zh-Hant"
else -> lang
}
return Locale.forLanguageTag(normalizedLang).displayName
}
/** /**
* Returns display name of a string language code. * Returns display name of a string language code.
* *
* @param lang empty for system language * @param lang empty for system language
*/ */
fun getDisplayName(lang: String?): String { fun getLocalizedDisplayName(lang: String?): String {
if (lang == null) { if (lang == null) {
return "" return ""
} }