mirror of
https://github.com/mihonapp/mihon.git
synced 2024-11-15 15:02:49 +01:00
chore: merge upstream
Signed-off-by: KaiserBh <kaiserbh@proton.me>
This commit is contained in:
commit
95b294ebd6
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
@ -3,7 +3,7 @@
|
||||
I acknowledge that:
|
||||
|
||||
- I have updated:
|
||||
- To the latest version of the app (stable is v0.14.7)
|
||||
- To the latest version of the app (stable is v0.15.0)
|
||||
- All extensions
|
||||
- I have gone through the FAQ (https://tachiyomi.org/docs/faq/general) and troubleshooting guide (https://tachiyomi.org/docs/guides/troubleshooting/)
|
||||
- If this is an issue with an extension, that I should be opening an issue in https://github.com/tachiyomiorg/tachiyomi-extensions
|
||||
|
4
.github/ISSUE_TEMPLATE/report_issue.yml
vendored
4
.github/ISSUE_TEMPLATE/report_issue.yml
vendored
@ -53,7 +53,7 @@ body:
|
||||
label: Tachiyomi version
|
||||
description: You can find your Tachiyomi version in **More → About**.
|
||||
placeholder: |
|
||||
Example: "0.14.7"
|
||||
Example: "0.15.0"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@ -98,7 +98,7 @@ body:
|
||||
required: true
|
||||
- label: I have gone through the [FAQ](https://tachiyomi.org/docs/faq/general) and [troubleshooting guide](https://tachiyomi.org/docs/guides/troubleshooting/).
|
||||
required: true
|
||||
- label: I have updated the app to version **[0.14.7](https://github.com/tachiyomiorg/tachiyomi/releases/latest)**.
|
||||
- label: I have updated the app to version **[0.15.0](https://github.com/tachiyomiorg/tachiyomi/releases/latest)**.
|
||||
required: true
|
||||
- label: I have updated all installed extensions.
|
||||
required: true
|
||||
|
2
.github/ISSUE_TEMPLATE/request_feature.yml
vendored
2
.github/ISSUE_TEMPLATE/request_feature.yml
vendored
@ -33,7 +33,7 @@ body:
|
||||
required: true
|
||||
- label: If this is an issue with an extension, I should be opening an issue in the [extensions repository](https://github.com/tachiyomiorg/tachiyomi-extensions/issues/new/choose).
|
||||
required: true
|
||||
- label: I have updated the app to version **[0.14.7](https://github.com/tachiyomiorg/tachiyomi/releases/latest)**.
|
||||
- label: I have updated the app to version **[0.15.0](https://github.com/tachiyomiorg/tachiyomi/releases/latest)**.
|
||||
required: true
|
||||
- label: I will fill out all of the requested information in this form.
|
||||
required: true
|
||||
|
@ -22,8 +22,8 @@ android {
|
||||
defaultConfig {
|
||||
applicationId = "eu.kanade.tachiyomi"
|
||||
|
||||
versionCode = 113
|
||||
versionName = "0.14.7"
|
||||
versionCode = 115
|
||||
versionName = "0.15.0"
|
||||
|
||||
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
||||
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")
|
||||
|
@ -11,8 +11,11 @@ import eu.kanade.domain.manga.interactor.GetExcludedScanlators
|
||||
import eu.kanade.domain.manga.interactor.SetExcludedScanlators
|
||||
import eu.kanade.domain.manga.interactor.SetMangaViewerFlags
|
||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||
import eu.kanade.domain.source.interactor.CreateSourceRepo
|
||||
import eu.kanade.domain.source.interactor.DeleteSourceRepo
|
||||
import eu.kanade.domain.source.interactor.GetEnabledSources
|
||||
import eu.kanade.domain.source.interactor.GetLanguagesWithSources
|
||||
import eu.kanade.domain.source.interactor.GetSourceRepos
|
||||
import eu.kanade.domain.source.interactor.GetSourcesWithFavoriteCount
|
||||
import eu.kanade.domain.source.interactor.SetMigrateSorting
|
||||
import eu.kanade.domain.source.interactor.ToggleLanguage
|
||||
@ -167,5 +170,9 @@ class DomainModule : InjektModule {
|
||||
addFactory { ToggleLanguage(get()) }
|
||||
addFactory { ToggleSource(get()) }
|
||||
addFactory { ToggleSourcePin(get()) }
|
||||
|
||||
addFactory { CreateSourceRepo(get()) }
|
||||
addFactory { DeleteSourceRepo(get()) }
|
||||
addFactory { GetSourceRepos(get()) }
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,26 @@
|
||||
package eu.kanade.domain.source.interactor
|
||||
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
import tachiyomi.core.preference.plusAssign
|
||||
|
||||
class CreateSourceRepo(private val preferences: SourcePreferences) {
|
||||
|
||||
fun await(name: String): Result {
|
||||
// Do not allow invalid formats
|
||||
if (!name.matches(repoRegex) || name.startsWith(OFFICIAL_REPO_BASE_URL)) {
|
||||
return Result.InvalidUrl
|
||||
}
|
||||
|
||||
preferences.extensionRepos() += name.substringBeforeLast("/index.min.json")
|
||||
|
||||
return Result.Success
|
||||
}
|
||||
|
||||
sealed interface Result {
|
||||
data object InvalidUrl : Result
|
||||
data object Success : Result
|
||||
}
|
||||
}
|
||||
|
||||
const val OFFICIAL_REPO_BASE_URL = "https://raw.githubusercontent.com/tachiyomiorg/tachiyomi-extensions/repo"
|
||||
private val repoRegex = """^https://.*/index\.min\.json$""".toRegex()
|
@ -0,0 +1,11 @@
|
||||
package eu.kanade.domain.source.interactor
|
||||
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
import tachiyomi.core.preference.minusAssign
|
||||
|
||||
class DeleteSourceRepo(private val preferences: SourcePreferences) {
|
||||
|
||||
fun await(repo: String) {
|
||||
preferences.extensionRepos() -= repo
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package eu.kanade.domain.source.interactor
|
||||
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
class GetSourceRepos(private val preferences: SourcePreferences) {
|
||||
|
||||
fun subscribe(): Flow<List<String>> {
|
||||
return preferences.extensionRepos().changes()
|
||||
.map { it.sortedWith(String.CASE_INSENSITIVE_ORDER) }
|
||||
}
|
||||
}
|
@ -38,6 +38,8 @@ class SourcePreferences(
|
||||
SetMigrateSorting.Direction.ASCENDING,
|
||||
)
|
||||
|
||||
fun extensionRepos() = preferenceStore.getStringSet("extension_repos", emptySet())
|
||||
|
||||
fun extensionUpdatesCount() = preferenceStore.getInt("ext_updates_count", 0)
|
||||
|
||||
fun trustedSignatures() = preferenceStore.getStringSet(Preference.appStateKey("trusted_signatures"), emptySet())
|
||||
|
@ -16,7 +16,6 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.HelpOutline
|
||||
import androidx.compose.material.icons.outlined.History
|
||||
import androidx.compose.material.icons.outlined.Settings
|
||||
import androidx.compose.material3.AlertDialog
|
||||
@ -38,6 +37,7 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
@ -67,7 +67,6 @@ fun ExtensionDetailsScreen(
|
||||
state: ExtensionDetailsScreenModel.State,
|
||||
onClickSourcePreferences: (sourceId: Long) -> Unit,
|
||||
onClickWhatsNew: () -> Unit,
|
||||
onClickReadme: () -> Unit,
|
||||
onClickEnableAll: () -> Unit,
|
||||
onClickDisableAll: () -> Unit,
|
||||
onClickClearCookies: () -> Unit,
|
||||
@ -91,13 +90,6 @@ fun ExtensionDetailsScreen(
|
||||
onClick = onClickWhatsNew,
|
||||
),
|
||||
)
|
||||
add(
|
||||
AppBar.Action(
|
||||
title = stringResource(MR.strings.action_faq_and_guides),
|
||||
icon = Icons.AutoMirrored.Outlined.HelpOutline,
|
||||
onClick = onClickReadme,
|
||||
),
|
||||
)
|
||||
}
|
||||
addAll(
|
||||
listOf(
|
||||
@ -125,7 +117,7 @@ fun ExtensionDetailsScreen(
|
||||
) { paddingValues ->
|
||||
if (state.extension == null) {
|
||||
EmptyScreen(
|
||||
stringRes = MR.strings.empty_screen,
|
||||
MR.strings.empty_screen,
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
)
|
||||
return@Scaffold
|
||||
@ -158,6 +150,21 @@ private fun ExtensionDetails(
|
||||
contentPadding = contentPadding,
|
||||
) {
|
||||
when {
|
||||
extension.isRepoSource ->
|
||||
item {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
WarningBanner(
|
||||
MR.strings.repo_extension_message,
|
||||
modifier = Modifier.clickable {
|
||||
extension.repoUrl ?: return@clickable
|
||||
uriHandler.openUri(
|
||||
extension.repoUrl
|
||||
.replace("https://raw.githubusercontent.com", "https://github.com")
|
||||
.removeSuffix("/repo/"),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
extension.isUnofficial ->
|
||||
item {
|
||||
WarningBanner(MR.strings.unofficial_extension_message)
|
||||
|
@ -28,6 +28,7 @@ import androidx.compose.ui.focus.focusRequester
|
||||
import eu.kanade.core.preference.asToggleableState
|
||||
import eu.kanade.presentation.category.visualName
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.delay
|
||||
import tachiyomi.core.preference.CheckboxState
|
||||
import tachiyomi.domain.category.model.Category
|
||||
@ -40,12 +41,12 @@ import kotlin.time.Duration.Companion.seconds
|
||||
fun CategoryCreateDialog(
|
||||
onDismissRequest: () -> Unit,
|
||||
onCreate: (String) -> Unit,
|
||||
categories: ImmutableList<Category>,
|
||||
categories: ImmutableList<String>,
|
||||
) {
|
||||
var name by remember { mutableStateOf("") }
|
||||
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val nameAlreadyExists = remember(name) { categories.anyWithName(name) }
|
||||
val nameAlreadyExists = remember(name) { categories.contains(name) }
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
@ -70,10 +71,13 @@ fun CategoryCreateDialog(
|
||||
},
|
||||
text = {
|
||||
OutlinedTextField(
|
||||
modifier = Modifier.focusRequester(focusRequester),
|
||||
modifier = Modifier
|
||||
.focusRequester(focusRequester),
|
||||
value = name,
|
||||
onValueChange = { name = it },
|
||||
label = { Text(text = stringResource(MR.strings.name)) },
|
||||
label = {
|
||||
Text(text = stringResource(MR.strings.name))
|
||||
},
|
||||
supportingText = {
|
||||
val msgRes = if (name.isNotEmpty() && nameAlreadyExists) {
|
||||
MR.strings.error_category_exists
|
||||
@ -99,14 +103,14 @@ fun CategoryCreateDialog(
|
||||
fun CategoryRenameDialog(
|
||||
onDismissRequest: () -> Unit,
|
||||
onRename: (String) -> Unit,
|
||||
categories: ImmutableList<Category>,
|
||||
category: Category,
|
||||
categories: ImmutableList<String>,
|
||||
category: String,
|
||||
) {
|
||||
var name by remember { mutableStateOf(category.name) }
|
||||
var name by remember { mutableStateOf(category) }
|
||||
var valueHasChanged by remember { mutableStateOf(false) }
|
||||
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val nameAlreadyExists = remember(name) { categories.anyWithName(name) }
|
||||
val nameAlreadyExists = remember(name) { categories.contains(name) }
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
@ -163,7 +167,7 @@ fun CategoryRenameDialog(
|
||||
fun CategoryDeleteDialog(
|
||||
onDismissRequest: () -> Unit,
|
||||
onDelete: () -> Unit,
|
||||
category: Category,
|
||||
category: String,
|
||||
) {
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
@ -184,7 +188,7 @@ fun CategoryDeleteDialog(
|
||||
Text(text = stringResource(MR.strings.delete_category))
|
||||
},
|
||||
text = {
|
||||
Text(text = stringResource(MR.strings.delete_category_confirmation, category.name))
|
||||
Text(text = stringResource(MR.strings.delete_category_confirmation, category))
|
||||
},
|
||||
)
|
||||
}
|
||||
@ -220,7 +224,7 @@ fun CategorySortAlphabeticallyDialog(
|
||||
|
||||
@Composable
|
||||
fun ChangeCategoryDialog(
|
||||
initialSelection: List<CheckboxState<Category>>,
|
||||
initialSelection: ImmutableList<CheckboxState<Category>>,
|
||||
onDismissRequest: () -> Unit,
|
||||
onEditCategories: () -> Unit,
|
||||
onConfirm: (List<Long>, List<Long>) -> Unit,
|
||||
@ -292,7 +296,7 @@ fun ChangeCategoryDialog(
|
||||
if (index != -1) {
|
||||
val mutableList = selection.toMutableList()
|
||||
mutableList[index] = it.next()
|
||||
selection = mutableList.toList()
|
||||
selection = mutableList.toList().toImmutableList()
|
||||
}
|
||||
}
|
||||
Row(
|
||||
@ -326,7 +330,3 @@ fun ChangeCategoryDialog(
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun List<Category>.anyWithName(name: String): Boolean {
|
||||
return any { name == it.name }
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ fun CategoryListItem(
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(imageVector = Icons.AutoMirrored.Outlined.Label, contentDescription = "")
|
||||
Icon(imageVector = Icons.AutoMirrored.Outlined.Label, contentDescription = null)
|
||||
Text(
|
||||
text = category.name,
|
||||
modifier = Modifier
|
||||
@ -61,13 +61,13 @@ fun CategoryListItem(
|
||||
onClick = { onMoveUp(category) },
|
||||
enabled = canMoveUp,
|
||||
) {
|
||||
Icon(imageVector = Icons.Outlined.ArrowDropUp, contentDescription = "")
|
||||
Icon(imageVector = Icons.Outlined.ArrowDropUp, contentDescription = null)
|
||||
}
|
||||
IconButton(
|
||||
onClick = { onMoveDown(category) },
|
||||
enabled = canMoveDown,
|
||||
) {
|
||||
Icon(imageVector = Icons.Outlined.ArrowDropDown, contentDescription = "")
|
||||
Icon(imageVector = Icons.Outlined.ArrowDropDown, contentDescription = null)
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
IconButton(onClick = onRename) {
|
||||
|
@ -78,12 +78,13 @@ import tachiyomi.presentation.core.i18n.stringResource
|
||||
import tachiyomi.presentation.core.util.isScrolledToEnd
|
||||
import tachiyomi.presentation.core.util.isScrollingUp
|
||||
import tachiyomi.source.local.isLocal
|
||||
import java.time.Instant
|
||||
|
||||
@Composable
|
||||
fun MangaScreen(
|
||||
state: MangaScreenModel.State.Success,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
fetchInterval: Int?,
|
||||
nextUpdate: Instant?,
|
||||
isTabletUi: Boolean,
|
||||
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
||||
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
||||
@ -138,7 +139,7 @@ fun MangaScreen(
|
||||
MangaScreenSmallImpl(
|
||||
state = state,
|
||||
snackbarHostState = snackbarHostState,
|
||||
fetchInterval = fetchInterval,
|
||||
nextUpdate = nextUpdate,
|
||||
chapterSwipeStartAction = chapterSwipeStartAction,
|
||||
chapterSwipeEndAction = chapterSwipeEndAction,
|
||||
onBackClicked = onBackClicked,
|
||||
@ -175,7 +176,7 @@ fun MangaScreen(
|
||||
snackbarHostState = snackbarHostState,
|
||||
chapterSwipeStartAction = chapterSwipeStartAction,
|
||||
chapterSwipeEndAction = chapterSwipeEndAction,
|
||||
fetchInterval = fetchInterval,
|
||||
nextUpdate = nextUpdate,
|
||||
onBackClicked = onBackClicked,
|
||||
onChapterClicked = onChapterClicked,
|
||||
onDownloadChapter = onDownloadChapter,
|
||||
@ -211,7 +212,7 @@ fun MangaScreen(
|
||||
private fun MangaScreenSmallImpl(
|
||||
state: MangaScreenModel.State.Success,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
fetchInterval: Int?,
|
||||
nextUpdate: Instant?,
|
||||
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
||||
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
||||
onBackClicked: () -> Unit,
|
||||
@ -272,10 +273,7 @@ private fun MangaScreenSmallImpl(
|
||||
onBackClicked()
|
||||
}
|
||||
}
|
||||
BackHandler(
|
||||
enabled = isAnySelected,
|
||||
onBack = { onAllChapterSelected(false) },
|
||||
)
|
||||
BackHandler(onBack = internalOnBackPressed)
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
@ -402,7 +400,7 @@ private fun MangaScreenSmallImpl(
|
||||
MangaActionRow(
|
||||
favorite = state.manga.favorite,
|
||||
trackingCount = state.trackingCount,
|
||||
fetchInterval = fetchInterval,
|
||||
nextUpdate = nextUpdate,
|
||||
isUserIntervalMode = state.manga.fetchInterval < 0,
|
||||
onAddToLibraryClicked = onAddToLibraryClicked,
|
||||
onWebViewClicked = onWebViewClicked,
|
||||
@ -462,7 +460,7 @@ private fun MangaScreenSmallImpl(
|
||||
fun MangaScreenLargeImpl(
|
||||
state: MangaScreenModel.State.Success,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
fetchInterval: Int?,
|
||||
nextUpdate: Instant?,
|
||||
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
||||
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
||||
onBackClicked: () -> Unit,
|
||||
@ -529,10 +527,7 @@ fun MangaScreenLargeImpl(
|
||||
onBackClicked()
|
||||
}
|
||||
}
|
||||
BackHandler(
|
||||
enabled = isAnySelected,
|
||||
onBack = { onAllChapterSelected(false) },
|
||||
)
|
||||
BackHandler(onBack = internalOnBackPressed)
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
@ -641,7 +636,7 @@ fun MangaScreenLargeImpl(
|
||||
MangaActionRow(
|
||||
favorite = state.manga.favorite,
|
||||
trackingCount = state.trackingCount,
|
||||
fetchInterval = fetchInterval,
|
||||
nextUpdate = nextUpdate,
|
||||
isUserIntervalMode = state.manga.fetchInterval < 0,
|
||||
onAddToLibraryClicked = onAddToLibraryClicked,
|
||||
onWebViewClicked = onWebViewClicked,
|
||||
|
@ -3,10 +3,6 @@ package eu.kanade.presentation.manga.components
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.os.Build
|
||||
import androidx.activity.compose.PredictiveBackHandler
|
||||
import androidx.compose.animation.core.LinearOutSlowInEasing
|
||||
import androidx.compose.animation.core.animate
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@ -29,18 +25,15 @@ import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.lerp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
@ -55,13 +48,11 @@ import eu.kanade.presentation.components.DropdownMenu
|
||||
import eu.kanade.presentation.manga.EditCoverAction
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderPageImageView
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import soup.compose.material.motion.MotionConstants
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.material.Scaffold
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import tachiyomi.presentation.core.util.clickableNoIndication
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
|
||||
@Composable
|
||||
fun MangaCoverDialog(
|
||||
@ -160,32 +151,10 @@ fun MangaCoverDialog(
|
||||
val statusBarPaddingPx = with(LocalDensity.current) { contentPadding.calculateTopPadding().roundToPx() }
|
||||
val bottomPaddingPx = with(LocalDensity.current) { contentPadding.calculateBottomPadding().roundToPx() }
|
||||
|
||||
var scale by remember { mutableFloatStateOf(1f) }
|
||||
PredictiveBackHandler { progress ->
|
||||
try {
|
||||
progress.collect { backEvent ->
|
||||
scale = lerp(1f, 0.8f, LinearOutSlowInEasing.transform(backEvent.progress))
|
||||
}
|
||||
onDismissRequest()
|
||||
} catch (e: CancellationException) {
|
||||
animate(
|
||||
initialValue = scale,
|
||||
targetValue = 1f,
|
||||
animationSpec = tween(durationMillis = MotionConstants.DefaultMotionDuration),
|
||||
) { value, _ ->
|
||||
scale = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.clickableNoIndication(onClick = onDismissRequest)
|
||||
.graphicsLayer {
|
||||
scaleX = scale
|
||||
scaleY = scale
|
||||
},
|
||||
.clickableNoIndication(onClick = onDismissRequest),
|
||||
) {
|
||||
AndroidView(
|
||||
factory = {
|
||||
|
@ -2,8 +2,11 @@ package eu.kanade.presentation.manga.components
|
||||
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
@ -16,10 +19,13 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.tachiyomi.util.system.isDevFlavor
|
||||
import eu.kanade.tachiyomi.util.system.isPreviewBuildType
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import tachiyomi.domain.manga.interactor.FetchInterval
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.WheelTextPicker
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
import tachiyomi.presentation.core.i18n.pluralStringResource
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import java.time.Instant
|
||||
@ -59,57 +65,71 @@ fun DeleteChaptersDialog(
|
||||
@Composable
|
||||
fun SetIntervalDialog(
|
||||
interval: Int,
|
||||
nextUpdate: Long,
|
||||
nextUpdate: Instant?,
|
||||
onDismissRequest: () -> Unit,
|
||||
onValueChanged: (Int) -> Unit,
|
||||
onValueChanged: ((Int) -> Unit)? = null,
|
||||
) {
|
||||
var selectedInterval by rememberSaveable { mutableIntStateOf(if (interval < 0) -interval else 0) }
|
||||
|
||||
val nextUpdateDays = remember(nextUpdate) {
|
||||
val now = Instant.now()
|
||||
val nextUpdateInstant = Instant.ofEpochMilli(nextUpdate)
|
||||
|
||||
now.until(nextUpdateInstant, ChronoUnit.DAYS)
|
||||
return@remember if (nextUpdate != null) {
|
||||
val now = Instant.now()
|
||||
now.until(nextUpdate, ChronoUnit.DAYS).toInt().coerceAtLeast(0)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
title = { Text(stringResource(MR.strings.manga_modify_calculated_interval_title)) },
|
||||
title = { Text(stringResource(MR.strings.pref_library_update_smart_update)) },
|
||||
text = {
|
||||
Column {
|
||||
if (nextUpdateDays >= 0) {
|
||||
if (nextUpdateDays != null && nextUpdateDays >= 0) {
|
||||
Text(
|
||||
stringResource(
|
||||
MR.strings.manga_interval_expected_update,
|
||||
pluralStringResource(
|
||||
MR.plurals.day,
|
||||
count = nextUpdateDays.toInt(),
|
||||
count = nextUpdateDays,
|
||||
nextUpdateDays,
|
||||
),
|
||||
pluralStringResource(
|
||||
MR.plurals.day,
|
||||
count = interval,
|
||||
interval,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(MaterialTheme.padding.small))
|
||||
}
|
||||
|
||||
BoxWithConstraints(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
val size = DpSize(width = maxWidth / 2, height = 128.dp)
|
||||
val items = (0..FetchInterval.MAX_INTERVAL)
|
||||
.map {
|
||||
if (it == 0) {
|
||||
stringResource(MR.strings.label_default)
|
||||
} else {
|
||||
it.toString()
|
||||
// TODO: selecting "1" then doesn't allow for future changes unless defaulting first?
|
||||
if (onValueChanged != null && (isDevFlavor || isPreviewBuildType)) {
|
||||
Text(stringResource(MR.strings.manga_interval_custom_amount))
|
||||
|
||||
BoxWithConstraints(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
val size = DpSize(width = maxWidth / 2, height = 128.dp)
|
||||
val items = (0..FetchInterval.MAX_INTERVAL)
|
||||
.map {
|
||||
if (it == 0) {
|
||||
stringResource(MR.strings.label_default)
|
||||
} else {
|
||||
it.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
.toImmutableList()
|
||||
WheelTextPicker(
|
||||
items = items,
|
||||
size = size,
|
||||
startIndex = selectedInterval,
|
||||
onSelectionChanged = { selectedInterval = it },
|
||||
)
|
||||
.toImmutableList()
|
||||
WheelTextPicker(
|
||||
items = items,
|
||||
size = size,
|
||||
startIndex = selectedInterval,
|
||||
onSelectionChanged = { selectedInterval = it },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -120,7 +140,7 @@ fun SetIntervalDialog(
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = {
|
||||
onValueChanged(selectedInterval)
|
||||
onValueChanged?.invoke(selectedInterval)
|
||||
onDismissRequest()
|
||||
}) {
|
||||
Text(text = stringResource(MR.strings.action_ok))
|
||||
|
@ -86,7 +86,8 @@ import tachiyomi.presentation.core.i18n.pluralStringResource
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import tachiyomi.presentation.core.util.clickableNoIndication
|
||||
import tachiyomi.presentation.core.util.secondaryItemAlpha
|
||||
import kotlin.math.absoluteValue
|
||||
import java.time.Instant
|
||||
import java.time.temporal.ChronoUnit
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
private val whitespaceLineRegex = Regex("[\\r\\n]{2,}", setOf(RegexOption.MULTILINE))
|
||||
@ -165,7 +166,7 @@ fun MangaInfoBox(
|
||||
fun MangaActionRow(
|
||||
favorite: Boolean,
|
||||
trackingCount: Int,
|
||||
fetchInterval: Int?,
|
||||
nextUpdate: Instant?,
|
||||
isUserIntervalMode: Boolean,
|
||||
onAddToLibraryClicked: () -> Unit,
|
||||
onWebViewClicked: (() -> Unit)?,
|
||||
@ -177,6 +178,16 @@ fun MangaActionRow(
|
||||
) {
|
||||
val defaultActionButtonColor = MaterialTheme.colorScheme.onSurface.copy(alpha = .38f)
|
||||
|
||||
// TODO: show something better when using custom interval
|
||||
val nextUpdateDays = remember(nextUpdate) {
|
||||
return@remember if (nextUpdate != null) {
|
||||
val now = Instant.now()
|
||||
now.until(nextUpdate, ChronoUnit.DAYS).toInt().coerceAtLeast(0)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
Row(modifier = modifier.padding(start = 16.dp, top = 8.dp, end = 16.dp)) {
|
||||
MangaActionButton(
|
||||
title = if (favorite) {
|
||||
@ -189,18 +200,20 @@ fun MangaActionRow(
|
||||
onClick = onAddToLibraryClicked,
|
||||
onLongClick = onEditCategory,
|
||||
)
|
||||
if (onEditIntervalClicked != null && fetchInterval != null) {
|
||||
MangaActionButton(
|
||||
title = pluralStringResource(
|
||||
MangaActionButton(
|
||||
title = if (nextUpdateDays != null) {
|
||||
pluralStringResource(
|
||||
MR.plurals.day,
|
||||
count = fetchInterval.absoluteValue,
|
||||
fetchInterval.absoluteValue,
|
||||
),
|
||||
icon = Icons.Default.HourglassEmpty,
|
||||
color = if (isUserIntervalMode) MaterialTheme.colorScheme.primary else defaultActionButtonColor,
|
||||
onClick = onEditIntervalClicked,
|
||||
)
|
||||
}
|
||||
count = nextUpdateDays,
|
||||
nextUpdateDays,
|
||||
)
|
||||
} else {
|
||||
stringResource(MR.strings.not_applicable)
|
||||
},
|
||||
icon = Icons.Default.HourglassEmpty,
|
||||
color = if (isUserIntervalMode) MaterialTheme.colorScheme.primary else defaultActionButtonColor,
|
||||
onClick = { onEditIntervalClicked?.invoke() },
|
||||
)
|
||||
MangaActionButton(
|
||||
title = if (trackingCount == 0) {
|
||||
stringResource(MR.strings.manga_tracking_tab)
|
||||
|
@ -17,7 +17,7 @@ import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import com.halilibo.richtext.markdown.Markdown
|
||||
import com.halilibo.richtext.ui.RichTextStyle
|
||||
import com.halilibo.richtext.ui.material3.Material3RichText
|
||||
import com.halilibo.richtext.ui.material3.RichText
|
||||
import com.halilibo.richtext.ui.string.RichTextStringStyle
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import tachiyomi.i18n.MR
|
||||
@ -42,7 +42,7 @@ fun NewUpdateScreen(
|
||||
rejectText = stringResource(MR.strings.action_not_now),
|
||||
onRejectClick = onRejectUpdate,
|
||||
) {
|
||||
Material3RichText(
|
||||
RichText(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = MaterialTheme.padding.large),
|
||||
|
@ -41,6 +41,7 @@ internal class GuidesStep(
|
||||
}
|
||||
|
||||
HorizontalDivider(
|
||||
modifier = Modifier.padding(vertical = 8.dp),
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
)
|
||||
|
||||
|
@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@ -14,6 +15,7 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.presentation.more.settings.screen.SettingsDataScreen
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
@ -38,6 +40,8 @@ internal class StorageStep : OnboardingStep {
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val context = LocalContext.current
|
||||
val handler = LocalUriHandler.current
|
||||
|
||||
val pickStorageLocation = SettingsDataScreen.storageLocationPicker(storagePref)
|
||||
|
||||
Column(
|
||||
@ -64,6 +68,19 @@ internal class StorageStep : OnboardingStep {
|
||||
) {
|
||||
Text(stringResource(MR.strings.onboarding_storage_action_select))
|
||||
}
|
||||
|
||||
HorizontalDivider(
|
||||
modifier = Modifier.padding(vertical = 8.dp),
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
)
|
||||
|
||||
Text(stringResource(MR.strings.onboarding_storage_help_info, stringResource(MR.strings.app_name)))
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = { handler.openUri("https://tachiyomi.org/docs/faq/storage") },
|
||||
) {
|
||||
Text(stringResource(MR.strings.onboarding_storage_help_action))
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
|
@ -1,34 +1,26 @@
|
||||
package eu.kanade.presentation.more.settings.screen
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.domain.ui.UiPreferences
|
||||
import eu.kanade.domain.ui.model.TabletUiMode
|
||||
import eu.kanade.domain.ui.model.ThemeMode
|
||||
import eu.kanade.domain.ui.model.setAppCompatDelegateThemeMode
|
||||
import eu.kanade.presentation.more.settings.Preference
|
||||
import eu.kanade.presentation.more.settings.screen.appearance.AppLanguageScreen
|
||||
import eu.kanade.presentation.more.settings.widget.AppThemeModePreferenceWidget
|
||||
import eu.kanade.presentation.more.settings.widget.AppThemePreferenceWidget
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.collections.immutable.ImmutableMap
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableMap
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
import tachiyomi.core.i18n.stringResource
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import tachiyomi.presentation.core.util.collectAsState
|
||||
@ -107,11 +99,8 @@ object SettingsAppearanceScreen : SearchableSettings {
|
||||
uiPreferences: UiPreferences,
|
||||
): Preference.PreferenceGroup {
|
||||
val context = LocalContext.current
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
|
||||
val langs = remember { getLangs(context) }
|
||||
var currentLanguage by remember {
|
||||
mutableStateOf(AppCompatDelegate.getApplicationLocales().get(0)?.toLanguageTag() ?: "")
|
||||
}
|
||||
val now = remember { Instant.now().toEpochMilli() }
|
||||
|
||||
val dateFormat by uiPreferences.dateFormat().collectAsState()
|
||||
@ -119,26 +108,12 @@ object SettingsAppearanceScreen : SearchableSettings {
|
||||
UiPreferences.dateFormat(dateFormat).format(now)
|
||||
}
|
||||
|
||||
LaunchedEffect(currentLanguage) {
|
||||
val locale = if (currentLanguage.isEmpty()) {
|
||||
LocaleListCompat.getEmptyLocaleList()
|
||||
} else {
|
||||
LocaleListCompat.forLanguageTags(currentLanguage)
|
||||
}
|
||||
AppCompatDelegate.setApplicationLocales(locale)
|
||||
}
|
||||
|
||||
return Preference.PreferenceGroup(
|
||||
title = stringResource(MR.strings.pref_category_display),
|
||||
preferenceItems = persistentListOf(
|
||||
Preference.PreferenceItem.BasicListPreference(
|
||||
value = currentLanguage,
|
||||
Preference.PreferenceItem.TextPreference(
|
||||
title = stringResource(MR.strings.pref_app_language),
|
||||
entries = langs,
|
||||
onValueChanged = { newValue ->
|
||||
currentLanguage = newValue
|
||||
true
|
||||
},
|
||||
onClick = { navigator.push(AppLanguageScreen()) },
|
||||
),
|
||||
Preference.PreferenceItem.ListPreference(
|
||||
pref = uiPreferences.tabletUiMode(),
|
||||
@ -173,30 +148,6 @@ object SettingsAppearanceScreen : SearchableSettings {
|
||||
),
|
||||
)
|
||||
}
|
||||
private fun getLangs(context: Context): ImmutableMap<String, String> {
|
||||
val langs = mutableListOf<Pair<String, String>>()
|
||||
val parser = context.resources.getXml(R.xml.locales_config)
|
||||
var eventType = parser.eventType
|
||||
while (eventType != XmlPullParser.END_DOCUMENT) {
|
||||
if (eventType == XmlPullParser.START_TAG && parser.name == "locale") {
|
||||
for (i in 0..<parser.attributeCount) {
|
||||
if (parser.getAttributeName(i) == "name") {
|
||||
val langTag = parser.getAttributeValue(i)
|
||||
val displayName = LocaleHelper.getDisplayName(langTag)
|
||||
if (displayName.isNotEmpty()) {
|
||||
langs.add(Pair(langTag, displayName))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
eventType = parser.next()
|
||||
}
|
||||
|
||||
langs.sortBy { it.second }
|
||||
langs.add(0, Pair("", context.stringResource(MR.strings.label_default)))
|
||||
|
||||
return langs.toMap().toImmutableMap()
|
||||
}
|
||||
}
|
||||
|
||||
private val DateFormats = listOf(
|
||||
|
@ -2,16 +2,22 @@ package eu.kanade.presentation.more.settings.screen
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
import eu.kanade.presentation.more.settings.Preference
|
||||
import eu.kanade.presentation.more.settings.screen.browse.ExtensionReposScreen
|
||||
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.authenticate
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import tachiyomi.core.i18n.stringResource
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.i18n.pluralStringResource
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import tachiyomi.presentation.core.util.collectAsState
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
@ -24,7 +30,11 @@ object SettingsBrowseScreen : SearchableSettings {
|
||||
@Composable
|
||||
override fun getPreferences(): List<Preference> {
|
||||
val context = LocalContext.current
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
|
||||
val sourcePreferences = remember { Injekt.get<SourcePreferences>() }
|
||||
val reposCount by sourcePreferences.extensionRepos().collectAsState()
|
||||
|
||||
return listOf(
|
||||
Preference.PreferenceGroup(
|
||||
title = stringResource(MR.strings.label_sources),
|
||||
@ -33,6 +43,13 @@ object SettingsBrowseScreen : SearchableSettings {
|
||||
pref = sourcePreferences.hideInLibraryItems(),
|
||||
title = stringResource(MR.strings.pref_hide_in_library_items),
|
||||
),
|
||||
Preference.PreferenceItem.TextPreference(
|
||||
title = stringResource(MR.strings.label_extension_repos),
|
||||
subtitle = pluralStringResource(MR.plurals.num_repos, reposCount.size, reposCount.size),
|
||||
onClick = {
|
||||
navigator.push(ExtensionReposScreen())
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Preference.PreferenceGroup(
|
||||
|
@ -198,7 +198,7 @@ object SettingsLibraryScreen : SearchableSettings {
|
||||
),
|
||||
Preference.PreferenceItem.MultiSelectListPreference(
|
||||
pref = libraryPreferences.autoUpdateMangaRestrictions(),
|
||||
title = stringResource(MR.strings.pref_library_update_manga_restriction),
|
||||
title = stringResource(MR.strings.pref_library_update_smart_update),
|
||||
entries = persistentMapOf(
|
||||
MANGA_HAS_UNREAD to stringResource(MR.strings.pref_update_only_completely_read),
|
||||
MANGA_NON_READ to stringResource(MR.strings.pref_update_only_started),
|
||||
|
@ -0,0 +1,128 @@
|
||||
package eu.kanade.presentation.more.settings.screen.appearance
|
||||
|
||||
import android.content.Context
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.presentation.components.AppBar
|
||||
import eu.kanade.presentation.util.Screen
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
import tachiyomi.core.i18n.stringResource
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.material.Scaffold
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import java.util.Locale
|
||||
|
||||
class AppLanguageScreen : Screen() {
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val context = LocalContext.current
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
|
||||
val langs = remember { getLangs(context) }
|
||||
var currentLanguage by remember {
|
||||
mutableStateOf(AppCompatDelegate.getApplicationLocales().get(0)?.toLanguageTag() ?: "")
|
||||
}
|
||||
|
||||
LaunchedEffect(currentLanguage) {
|
||||
val locale = if (currentLanguage.isEmpty()) {
|
||||
LocaleListCompat.getEmptyLocaleList()
|
||||
} else {
|
||||
LocaleListCompat.forLanguageTags(currentLanguage)
|
||||
}
|
||||
AppCompatDelegate.setApplicationLocales(locale)
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = { scrollBehavior ->
|
||||
AppBar(
|
||||
title = stringResource(MR.strings.pref_app_language),
|
||||
navigateUp = navigator::pop,
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
},
|
||||
) { contentPadding ->
|
||||
LazyColumn(
|
||||
modifier = Modifier.padding(contentPadding),
|
||||
) {
|
||||
items(langs) {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable {
|
||||
currentLanguage = it.langTag
|
||||
},
|
||||
headlineContent = { Text(it.displayName) },
|
||||
supportingContent = {
|
||||
it.localizedDisplayName?.let {
|
||||
Text(it)
|
||||
}
|
||||
},
|
||||
trailingContent = {
|
||||
if (currentLanguage == it.langTag) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Check,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getLangs(context: Context): ImmutableList<Language> {
|
||||
val langs = mutableListOf<Language>()
|
||||
val parser = context.resources.getXml(R.xml.locales_config)
|
||||
var eventType = parser.eventType
|
||||
while (eventType != XmlPullParser.END_DOCUMENT) {
|
||||
if (eventType == XmlPullParser.START_TAG && parser.name == "locale") {
|
||||
for (i in 0..<parser.attributeCount) {
|
||||
if (parser.getAttributeName(i) == "name") {
|
||||
val langTag = parser.getAttributeValue(i)
|
||||
val displayName = LocaleHelper.getDisplayName(langTag)
|
||||
if (displayName.isNotEmpty()) {
|
||||
langs.add(Language(langTag, displayName, Locale.forLanguageTag(langTag).displayName))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
eventType = parser.next()
|
||||
}
|
||||
|
||||
langs.sortBy { it.displayName }
|
||||
langs.add(0, Language("", context.stringResource(MR.strings.label_default), null))
|
||||
|
||||
return langs.toImmutableList()
|
||||
}
|
||||
|
||||
private data class Language(
|
||||
val langTag: String,
|
||||
val displayName: String,
|
||||
val localizedDisplayName: String?,
|
||||
)
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package eu.kanade.presentation.more.settings.screen.browse
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.presentation.more.settings.screen.browse.components.ExtensionRepoCreateDialog
|
||||
import eu.kanade.presentation.more.settings.screen.browse.components.ExtensionRepoDeleteDialog
|
||||
import eu.kanade.presentation.more.settings.screen.browse.components.ExtensionReposScreen
|
||||
import eu.kanade.presentation.util.Screen
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import tachiyomi.presentation.core.screens.LoadingScreen
|
||||
|
||||
class ExtensionReposScreen : Screen() {
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val context = LocalContext.current
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
val screenModel = rememberScreenModel { ExtensionReposScreenModel() }
|
||||
|
||||
val state by screenModel.state.collectAsState()
|
||||
|
||||
if (state is RepoScreenState.Loading) {
|
||||
LoadingScreen()
|
||||
return
|
||||
}
|
||||
|
||||
val successState = state as RepoScreenState.Success
|
||||
|
||||
ExtensionReposScreen(
|
||||
state = successState,
|
||||
onClickCreate = { screenModel.showDialog(RepoDialog.Create) },
|
||||
onClickDelete = { screenModel.showDialog(RepoDialog.Delete(it)) },
|
||||
navigateUp = navigator::pop,
|
||||
)
|
||||
|
||||
when (val dialog = successState.dialog) {
|
||||
null -> {}
|
||||
RepoDialog.Create -> {
|
||||
ExtensionRepoCreateDialog(
|
||||
onDismissRequest = screenModel::dismissDialog,
|
||||
onCreate = { screenModel.createRepo(it) },
|
||||
categories = successState.repos,
|
||||
)
|
||||
}
|
||||
is RepoDialog.Delete -> {
|
||||
ExtensionRepoDeleteDialog(
|
||||
onDismissRequest = screenModel::dismissDialog,
|
||||
onDelete = { screenModel.deleteRepo(dialog.repo) },
|
||||
repo = dialog.repo,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
screenModel.events.collectLatest { event ->
|
||||
if (event is RepoEvent.LocalizedMessage) {
|
||||
context.toast(event.stringRes)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
package eu.kanade.presentation.more.settings.screen.browse
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import cafe.adriel.voyager.core.model.StateScreenModel
|
||||
import cafe.adriel.voyager.core.model.screenModelScope
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import eu.kanade.domain.source.interactor.CreateSourceRepo
|
||||
import eu.kanade.domain.source.interactor.DeleteSourceRepo
|
||||
import eu.kanade.domain.source.interactor.GetSourceRepos
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.receiveAsFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import tachiyomi.core.util.lang.launchIO
|
||||
import tachiyomi.i18n.MR
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class ExtensionReposScreenModel(
|
||||
private val getSourceRepos: GetSourceRepos = Injekt.get(),
|
||||
private val createSourceRepo: CreateSourceRepo = Injekt.get(),
|
||||
private val deleteSourceRepo: DeleteSourceRepo = Injekt.get(),
|
||||
) : StateScreenModel<RepoScreenState>(RepoScreenState.Loading) {
|
||||
|
||||
private val _events: Channel<RepoEvent> = Channel(Int.MAX_VALUE)
|
||||
val events = _events.receiveAsFlow()
|
||||
|
||||
init {
|
||||
screenModelScope.launchIO {
|
||||
getSourceRepos.subscribe()
|
||||
.collectLatest { repos ->
|
||||
mutableState.update {
|
||||
RepoScreenState.Success(
|
||||
repos = repos.toImmutableList(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and adds a new repo to the database.
|
||||
*
|
||||
* @param name The name of the repo to create.
|
||||
*/
|
||||
fun createRepo(name: String) {
|
||||
screenModelScope.launchIO {
|
||||
when (createSourceRepo.await(name)) {
|
||||
is CreateSourceRepo.Result.InvalidUrl -> _events.send(RepoEvent.InvalidUrl)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the given repo from the database.
|
||||
*
|
||||
* @param repo The repo to delete.
|
||||
*/
|
||||
fun deleteRepo(repo: String) {
|
||||
screenModelScope.launchIO {
|
||||
deleteSourceRepo.await(repo)
|
||||
}
|
||||
}
|
||||
|
||||
fun showDialog(dialog: RepoDialog) {
|
||||
mutableState.update {
|
||||
when (it) {
|
||||
RepoScreenState.Loading -> it
|
||||
is RepoScreenState.Success -> it.copy(dialog = dialog)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun dismissDialog() {
|
||||
mutableState.update {
|
||||
when (it) {
|
||||
RepoScreenState.Loading -> it
|
||||
is RepoScreenState.Success -> it.copy(dialog = null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class RepoEvent {
|
||||
sealed class LocalizedMessage(val stringRes: StringResource) : RepoEvent()
|
||||
data object InvalidUrl : LocalizedMessage(MR.strings.invalid_repo_name)
|
||||
}
|
||||
|
||||
sealed class RepoDialog {
|
||||
data object Create : RepoDialog()
|
||||
data class Delete(val repo: String) : RepoDialog()
|
||||
}
|
||||
|
||||
sealed class RepoScreenState {
|
||||
|
||||
@Immutable
|
||||
data object Loading : RepoScreenState()
|
||||
|
||||
@Immutable
|
||||
data class Success(
|
||||
val repos: ImmutableList<String>,
|
||||
val dialog: RepoDialog? = null,
|
||||
) : RepoScreenState() {
|
||||
|
||||
val isEmpty: Boolean
|
||||
get() = repos.isEmpty()
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
package eu.kanade.presentation.more.settings.screen.browse.components
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.Label
|
||||
import androidx.compose.material.icons.outlined.Delete
|
||||
import androidx.compose.material3.ElevatedCard
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
|
||||
@Composable
|
||||
fun ExtensionReposContent(
|
||||
repos: ImmutableList<String>,
|
||||
lazyListState: LazyListState,
|
||||
paddingValues: PaddingValues,
|
||||
onClickDelete: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
LazyColumn(
|
||||
state = lazyListState,
|
||||
contentPadding = paddingValues,
|
||||
verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
|
||||
modifier = modifier,
|
||||
) {
|
||||
items(repos) { repo ->
|
||||
ExtensionRepoListItem(
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
repo = repo,
|
||||
onDelete = { onClickDelete(repo) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ExtensionRepoListItem(
|
||||
repo: String,
|
||||
onDelete: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
ElevatedCard(
|
||||
modifier = modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
start = MaterialTheme.padding.medium,
|
||||
top = MaterialTheme.padding.medium,
|
||||
end = MaterialTheme.padding.medium,
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(imageVector = Icons.AutoMirrored.Outlined.Label, contentDescription = null)
|
||||
Text(text = repo, modifier = Modifier.padding(start = MaterialTheme.padding.medium))
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End,
|
||||
) {
|
||||
IconButton(onClick = onDelete) {
|
||||
Icon(imageVector = Icons.Outlined.Delete, contentDescription = null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
package eu.kanade.presentation.more.settings.screen.browse.components
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.coroutines.delay
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@Composable
|
||||
fun ExtensionRepoCreateDialog(
|
||||
onDismissRequest: () -> Unit,
|
||||
onCreate: (String) -> Unit,
|
||||
categories: ImmutableList<String>,
|
||||
) {
|
||||
var name by remember { mutableStateOf("") }
|
||||
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val nameAlreadyExists = remember(name) { categories.contains(name) }
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
enabled = name.isNotEmpty() && !nameAlreadyExists,
|
||||
onClick = {
|
||||
onCreate(name)
|
||||
onDismissRequest()
|
||||
},
|
||||
) {
|
||||
Text(text = stringResource(MR.strings.action_add))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismissRequest) {
|
||||
Text(text = stringResource(MR.strings.action_cancel))
|
||||
}
|
||||
},
|
||||
title = {
|
||||
Text(text = stringResource(MR.strings.action_add_repo))
|
||||
},
|
||||
text = {
|
||||
Column {
|
||||
Text(text = stringResource(MR.strings.action_add_repo_message))
|
||||
|
||||
OutlinedTextField(
|
||||
modifier = Modifier
|
||||
.focusRequester(focusRequester),
|
||||
value = name,
|
||||
onValueChange = { name = it },
|
||||
label = {
|
||||
Text(text = stringResource(MR.strings.label_add_repo_input))
|
||||
},
|
||||
supportingText = {
|
||||
val msgRes = if (name.isNotEmpty() && nameAlreadyExists) {
|
||||
MR.strings.error_repo_exists
|
||||
} else {
|
||||
MR.strings.information_required_plain
|
||||
}
|
||||
Text(text = stringResource(msgRes))
|
||||
},
|
||||
isError = name.isNotEmpty() && nameAlreadyExists,
|
||||
singleLine = true,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
LaunchedEffect(focusRequester) {
|
||||
// TODO: https://issuetracker.google.com/issues/204502668
|
||||
delay(0.1.seconds)
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ExtensionRepoDeleteDialog(
|
||||
onDismissRequest: () -> Unit,
|
||||
onDelete: () -> Unit,
|
||||
repo: String,
|
||||
) {
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
confirmButton = {
|
||||
TextButton(onClick = {
|
||||
onDelete()
|
||||
onDismissRequest()
|
||||
}) {
|
||||
Text(text = stringResource(MR.strings.action_ok))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismissRequest) {
|
||||
Text(text = stringResource(MR.strings.action_cancel))
|
||||
}
|
||||
},
|
||||
title = {
|
||||
Text(text = stringResource(MR.strings.action_delete_repo))
|
||||
},
|
||||
text = {
|
||||
Text(text = stringResource(MR.strings.delete_repo_confirmation, repo))
|
||||
},
|
||||
)
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
@file:JvmName("ExtensionReposScreenKt")
|
||||
|
||||
package eu.kanade.presentation.more.settings.screen.browse.components
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import eu.kanade.presentation.category.components.CategoryFloatingActionButton
|
||||
import eu.kanade.presentation.components.AppBar
|
||||
import eu.kanade.presentation.more.settings.screen.browse.RepoScreenState
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.material.Scaffold
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
import tachiyomi.presentation.core.components.material.topSmallPaddingValues
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import tachiyomi.presentation.core.screens.EmptyScreen
|
||||
import tachiyomi.presentation.core.util.plus
|
||||
|
||||
@Composable
|
||||
fun ExtensionReposScreen(
|
||||
state: RepoScreenState.Success,
|
||||
onClickCreate: () -> Unit,
|
||||
onClickDelete: (String) -> Unit,
|
||||
navigateUp: () -> Unit,
|
||||
) {
|
||||
val lazyListState = rememberLazyListState()
|
||||
Scaffold(
|
||||
topBar = { scrollBehavior ->
|
||||
AppBar(
|
||||
navigateUp = navigateUp,
|
||||
title = stringResource(MR.strings.label_extension_repos),
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
CategoryFloatingActionButton(
|
||||
lazyListState = lazyListState,
|
||||
onCreate = onClickCreate,
|
||||
)
|
||||
},
|
||||
) { paddingValues ->
|
||||
if (state.isEmpty) {
|
||||
EmptyScreen(
|
||||
MR.strings.information_empty_repos,
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
)
|
||||
return@Scaffold
|
||||
}
|
||||
|
||||
ExtensionReposContent(
|
||||
repos = state.repos,
|
||||
lazyListState = lazyListState,
|
||||
paddingValues = paddingValues + topSmallPaddingValues +
|
||||
PaddingValues(horizontal = MaterialTheme.padding.medium),
|
||||
onClickDelete = onClickDelete,
|
||||
)
|
||||
}
|
||||
}
|
@ -1,54 +1,13 @@
|
||||
package eu.kanade.presentation.util
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.activity.BackEventCompat
|
||||
import androidx.activity.compose.PredictiveBackHandler
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.AnimatedContentTransitionScope
|
||||
import androidx.compose.animation.ContentTransform
|
||||
import androidx.compose.animation.EnterTransition
|
||||
import androidx.compose.animation.ExitTransition
|
||||
import androidx.compose.animation.core.FastOutSlowInEasing
|
||||
import androidx.compose.animation.core.LinearOutSlowInEasing
|
||||
import androidx.compose.animation.core.animate
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.ProvidableCompositionLocal
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.movableContentOf
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.drawWithCache
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Rect
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.BlurEffect
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.ColorMatrix
|
||||
import androidx.compose.ui.graphics.Paint
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.graphics.TransformOrigin
|
||||
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.toSize
|
||||
import androidx.compose.ui.util.lerp
|
||||
import androidx.compose.ui.zIndex
|
||||
import cafe.adriel.voyager.core.model.ScreenModel
|
||||
import cafe.adriel.voyager.core.model.ScreenModelStore
|
||||
import cafe.adriel.voyager.core.screen.Screen
|
||||
@ -57,25 +16,14 @@ import cafe.adriel.voyager.core.screen.uniqueScreenKey
|
||||
import cafe.adriel.voyager.core.stack.StackEvent
|
||||
import cafe.adriel.voyager.navigator.Navigator
|
||||
import cafe.adriel.voyager.transitions.ScreenTransitionContent
|
||||
import eu.kanade.tachiyomi.util.view.getWindowRadius
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.onCompletion
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.plus
|
||||
import soup.compose.material.motion.MotionConstants
|
||||
import soup.compose.material.motion.animation.materialSharedAxisX
|
||||
import soup.compose.material.motion.animation.rememberSlideDistance
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.sin
|
||||
|
||||
/**
|
||||
* For invoking back press to the parent activity
|
||||
@ -109,299 +57,17 @@ interface AssistContentScreen {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DefaultNavigatorScreenTransition(
|
||||
navigator: Navigator,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
val view = LocalView.current
|
||||
val handler = remember {
|
||||
OnBackHandler(
|
||||
scope = scope,
|
||||
windowCornerRadius = view.getWindowRadius(),
|
||||
onBackPressed = navigator::pop,
|
||||
)
|
||||
}
|
||||
PredictiveBackHandler(enabled = navigator.canPop) { progress ->
|
||||
progress
|
||||
.onStart { handler.reset() }
|
||||
.onCompletion { e ->
|
||||
if (e == null) {
|
||||
handler.onBackConfirmed()
|
||||
} else {
|
||||
handler.onBackCancelled()
|
||||
}
|
||||
}
|
||||
.collect(handler::onBackEvent)
|
||||
}
|
||||
|
||||
Box(modifier = modifier.onSizeChanged { handler.updateContainerSize(it.toSize()) }) {
|
||||
val currentSceneEntry = navigator.lastItem
|
||||
val showPrev by remember {
|
||||
derivedStateOf { handler.scale < 1f || handler.translationY != 0f }
|
||||
}
|
||||
val visibleItems = remember(currentSceneEntry, showPrev) {
|
||||
if (showPrev) {
|
||||
val prevSceneEntry = navigator.items.getOrNull(navigator.size - 2)
|
||||
listOfNotNull(currentSceneEntry, prevSceneEntry)
|
||||
} else {
|
||||
listOfNotNull(currentSceneEntry)
|
||||
}
|
||||
}
|
||||
|
||||
val slideDistance = rememberSlideDistance()
|
||||
|
||||
val screenContent = remember {
|
||||
movableContentOf<Screen> { screen ->
|
||||
navigator.saveableState("transition", screen) {
|
||||
screen.Content()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
visibleItems.forEachIndexed { index, backStackEntry ->
|
||||
val isPrev = index == 1 && visibleItems.size > 1
|
||||
if (!isPrev) {
|
||||
AnimatedContent(
|
||||
targetState = backStackEntry,
|
||||
transitionSpec = {
|
||||
val forward = navigator.lastEvent != StackEvent.Pop
|
||||
if (!forward && !handler.isReady) {
|
||||
// Pop screen without animation when predictive back is in use
|
||||
EnterTransition.None togetherWith ExitTransition.None
|
||||
} else {
|
||||
materialSharedAxisX(
|
||||
forward = forward,
|
||||
slideDistance = slideDistance,
|
||||
)
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.zIndex(1f)
|
||||
.graphicsLayer {
|
||||
this.alpha = handler.alpha
|
||||
this.transformOrigin = TransformOrigin(
|
||||
pivotFractionX = if (handler.swipeEdge == BackEventCompat.EDGE_LEFT) 0.8f else 0.2f,
|
||||
pivotFractionY = 0.5f,
|
||||
)
|
||||
this.scaleX = handler.scale
|
||||
this.scaleY = handler.scale
|
||||
this.translationY = handler.translationY
|
||||
this.clip = true
|
||||
this.shape = if (showPrev) {
|
||||
RoundedCornerShape(handler.windowCornerRadius.toFloat())
|
||||
} else {
|
||||
RectangleShape
|
||||
}
|
||||
}
|
||||
.then(
|
||||
if (showPrev) {
|
||||
Modifier.pointerInput(Unit) {
|
||||
// Animated content should not be interactive
|
||||
}
|
||||
} else {
|
||||
Modifier
|
||||
},
|
||||
),
|
||||
content = {
|
||||
if (visibleItems.size == 2 && visibleItems.getOrNull(1) == it) {
|
||||
// Avoid drawing previous screen
|
||||
return@AnimatedContent
|
||||
}
|
||||
screenContent(it)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.zIndex(0f)
|
||||
.drawWithCache {
|
||||
val bounds = Rect(Offset.Zero, size)
|
||||
val matrix = ColorMatrix().apply {
|
||||
// Reduce saturation and brightness
|
||||
setToSaturation(lerp(1f, 0.95f, handler.alpha))
|
||||
set(0, 4, lerp(0f, -25f, handler.alpha))
|
||||
set(1, 4, lerp(0f, -25f, handler.alpha))
|
||||
set(2, 4, lerp(0f, -25f, handler.alpha))
|
||||
}
|
||||
val paint = Paint().apply { colorFilter = ColorFilter.colorMatrix(matrix) }
|
||||
onDrawWithContent {
|
||||
drawIntoCanvas {
|
||||
it.saveLayer(bounds, paint)
|
||||
drawContent()
|
||||
it.restore()
|
||||
}
|
||||
}
|
||||
}
|
||||
.graphicsLayer {
|
||||
val blurRadius = 5.dp.toPx() * handler.alpha
|
||||
renderEffect = if (blurRadius > 0f) {
|
||||
BlurEffect(blurRadius, blurRadius)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
.pointerInput(Unit) {
|
||||
// bg content should not be interactive
|
||||
},
|
||||
content = { screenContent(backStackEntry) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(currentSceneEntry) {
|
||||
// Reset *after* the screen is popped successfully
|
||||
// so that the correct transition is applied
|
||||
handler.setReady()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Stable
|
||||
private class OnBackHandler(
|
||||
private val scope: CoroutineScope,
|
||||
val windowCornerRadius: Int,
|
||||
private val onBackPressed: () -> Unit,
|
||||
) {
|
||||
|
||||
var isReady = true
|
||||
private set
|
||||
|
||||
var alpha by mutableFloatStateOf(1f)
|
||||
private set
|
||||
|
||||
var scale by mutableFloatStateOf(1f)
|
||||
private set
|
||||
|
||||
var translationY by mutableFloatStateOf(0f)
|
||||
private set
|
||||
|
||||
var swipeEdge by mutableIntStateOf(BackEventCompat.EDGE_LEFT)
|
||||
private set
|
||||
|
||||
private var containerSize = Size.Zero
|
||||
private var startPointY = Float.NaN
|
||||
|
||||
var isPredictiveBack by mutableStateOf(false)
|
||||
private set
|
||||
|
||||
private var animationJob: Job? = null
|
||||
set(value) {
|
||||
isReady = false
|
||||
field = value
|
||||
}
|
||||
|
||||
fun updateContainerSize(size: Size) {
|
||||
containerSize = size
|
||||
}
|
||||
|
||||
fun setReady() {
|
||||
reset()
|
||||
animationJob?.cancel()
|
||||
animationJob = null
|
||||
isReady = true
|
||||
isPredictiveBack = false
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
startPointY = Float.NaN
|
||||
}
|
||||
|
||||
fun onBackEvent(backEvent: BackEventCompat) {
|
||||
if (!isReady) return
|
||||
isPredictiveBack = true
|
||||
swipeEdge = backEvent.swipeEdge
|
||||
|
||||
val progress = LinearOutSlowInEasing.transform(backEvent.progress)
|
||||
scale = lerp(1f, 0.85f, progress)
|
||||
|
||||
if (startPointY.isNaN()) {
|
||||
startPointY = backEvent.touchY
|
||||
}
|
||||
val deltaYRatio = (backEvent.touchY - startPointY) / containerSize.height
|
||||
val translateYDistance = containerSize.height / 20
|
||||
translationY = sin(deltaYRatio * PI * 0.5).toFloat() * translateYDistance * progress
|
||||
}
|
||||
|
||||
fun onBackConfirmed() {
|
||||
if (!isReady) return
|
||||
if (isPredictiveBack) {
|
||||
// Continue predictive animation and pop the screen
|
||||
val animationSpec = tween<Float>(
|
||||
durationMillis = MotionConstants.DefaultMotionDuration,
|
||||
easing = FastOutSlowInEasing,
|
||||
fun DefaultNavigatorScreenTransition(navigator: Navigator) {
|
||||
val slideDistance = rememberSlideDistance()
|
||||
ScreenTransition(
|
||||
navigator = navigator,
|
||||
transition = {
|
||||
materialSharedAxisX(
|
||||
forward = navigator.lastEvent != StackEvent.Pop,
|
||||
slideDistance = slideDistance,
|
||||
)
|
||||
animationJob = scope.launch {
|
||||
try {
|
||||
listOf(
|
||||
async {
|
||||
animate(
|
||||
initialValue = alpha,
|
||||
targetValue = 0f,
|
||||
animationSpec = animationSpec,
|
||||
) { value, _ ->
|
||||
alpha = value
|
||||
}
|
||||
},
|
||||
async {
|
||||
animate(
|
||||
initialValue = scale,
|
||||
targetValue = scale - 0.05f,
|
||||
animationSpec = animationSpec,
|
||||
) { value, _ ->
|
||||
scale = value
|
||||
}
|
||||
},
|
||||
).awaitAll()
|
||||
} catch (e: CancellationException) {
|
||||
// no-op
|
||||
} finally {
|
||||
onBackPressed()
|
||||
alpha = 1f
|
||||
translationY = 0f
|
||||
scale = 1f
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Pop right away and use default transition
|
||||
onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
fun onBackCancelled() {
|
||||
// Reset states
|
||||
isPredictiveBack = false
|
||||
animationJob = scope.launch {
|
||||
listOf(
|
||||
async {
|
||||
animate(
|
||||
initialValue = scale,
|
||||
targetValue = 1f,
|
||||
) { value, _ ->
|
||||
scale = value
|
||||
}
|
||||
},
|
||||
async {
|
||||
animate(
|
||||
initialValue = alpha,
|
||||
targetValue = 1f,
|
||||
) { value, _ ->
|
||||
alpha = value
|
||||
}
|
||||
},
|
||||
async {
|
||||
animate(
|
||||
initialValue = translationY,
|
||||
targetValue = 0f,
|
||||
) { value, _ ->
|
||||
translationY = value
|
||||
}
|
||||
},
|
||||
).awaitAll()
|
||||
|
||||
isReady = true
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
@ -405,6 +405,11 @@ object Migrations {
|
||||
// Deleting old download cache index files, but might as well clear it all out
|
||||
context.cacheDir.deleteRecursively()
|
||||
}
|
||||
if (oldVersion < 114) {
|
||||
sourcePreferences.extensionRepos().getAndSet {
|
||||
it.map { "https://raw.githubusercontent.com/$it/repo" }.toSet()
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@ import com.google.api.services.drive.DriveScopes
|
||||
import com.google.api.services.drive.model.File
|
||||
import eu.kanade.tachiyomi.R
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
@ -32,6 +33,7 @@ import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStreamReader
|
||||
import java.time.Instant
|
||||
import java.util.zip.GZIPInputStream
|
||||
import java.util.zip.GZIPOutputStream
|
||||
|
||||
@ -57,12 +59,50 @@ class GoogleDriveSyncService(context: Context, json: Json, syncPreferences: Sync
|
||||
|
||||
private val remoteFileName = "tachiyomi_sync_data.gz"
|
||||
|
||||
private val lockFileName = "tachiyomi_sync.lock"
|
||||
|
||||
private val googleDriveService = GoogleDriveService(context)
|
||||
|
||||
override suspend fun beforeSync() = googleDriveService.refreshToken()
|
||||
override suspend fun beforeSync() {
|
||||
googleDriveService.refreshToken()
|
||||
val drive = googleDriveService.driveService ?: throw Exception("Google Drive service not initialized")
|
||||
|
||||
var backoff = 2000L // Start with 2 seconds
|
||||
|
||||
while (true) {
|
||||
val lockFiles = findLockFile(drive) // Fetch the current list of lock files
|
||||
|
||||
when {
|
||||
lockFiles.isEmpty() -> {
|
||||
// No lock file exists, try to create a new one
|
||||
createLockFile(drive)
|
||||
}
|
||||
lockFiles.size == 1 -> {
|
||||
// Exactly one lock file exists
|
||||
val lockFile = lockFiles.first()
|
||||
val createdTime = Instant.parse(lockFile.createdTime.toString())
|
||||
val ageMinutes = java.time.Duration.between(createdTime, Instant.now()).toMinutes()
|
||||
if (ageMinutes <= 3) {
|
||||
// Lock file is new and presumably held by this process, break the loop to proceed
|
||||
break
|
||||
} else {
|
||||
// Lock file is old, delete and attempt to create a new one
|
||||
deleteLockFile(drive)
|
||||
createLockFile(drive)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// More than one lock file exists, likely due to a race condition
|
||||
delay(backoff) // Apply backoff strategy
|
||||
backoff = (backoff * 2).coerceAtMost(32000L) // Max backoff of 32 seconds
|
||||
}
|
||||
}
|
||||
// The loop continues until it can confirm that there's exactly one new lock file.
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun pullSyncData(): SyncData? {
|
||||
val drive = googleDriveService.googleDriveService
|
||||
val drive = googleDriveService.driveService
|
||||
|
||||
// Check if the Google Drive service is initialized
|
||||
if (drive == null) {
|
||||
@ -89,27 +129,11 @@ class GoogleDriveSyncService(context: Context, json: Json, syncPreferences: Sync
|
||||
|
||||
override suspend fun pushSyncData(syncData: SyncData) {
|
||||
val jsonData = json.encodeToString(syncData)
|
||||
val drive = googleDriveService.driveService ?: throw Exception(context.getString(R.string.google_drive_not_signed_in))
|
||||
|
||||
val drive = googleDriveService.googleDriveService
|
||||
|
||||
// Check if the Google Drive service is initialized
|
||||
if (drive == null) {
|
||||
logcat(LogPriority.ERROR) { "Google Drive service not initialized" }
|
||||
throw Exception(context.getString(R.string.google_drive_not_signed_in))
|
||||
}
|
||||
|
||||
// delete file if exists
|
||||
val fileList = getFileList(drive)
|
||||
if (fileList.isNotEmpty()) {
|
||||
drive.files().delete(fileList[0].id).execute()
|
||||
}
|
||||
|
||||
val fileMetadata = File()
|
||||
fileMetadata.name = remoteFileName
|
||||
fileMetadata.mimeType = "application/gzip"
|
||||
|
||||
val byteArrayOutputStream = ByteArrayOutputStream()
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
val gzipOutputStream = GZIPOutputStream(byteArrayOutputStream)
|
||||
gzipOutputStream.write(jsonData.toByteArray(Charsets.UTF_8))
|
||||
@ -117,24 +141,100 @@ class GoogleDriveSyncService(context: Context, json: Json, syncPreferences: Sync
|
||||
}
|
||||
|
||||
val byteArrayContent = ByteArrayContent("application/octet-stream", byteArrayOutputStream.toByteArray())
|
||||
val uploadedFile = drive.files().create(fileMetadata, byteArrayContent)
|
||||
.setFields("id")
|
||||
.execute()
|
||||
|
||||
logcat(LogPriority.DEBUG) { "Created sync data file in Google Drive with file ID: ${uploadedFile.id}" }
|
||||
try {
|
||||
if (fileList.isNotEmpty()) {
|
||||
// File exists, so update it
|
||||
val fileId = fileList[0].id
|
||||
drive.files().update(fileId, null, byteArrayContent).execute()
|
||||
logcat(LogPriority.DEBUG) { "Updated existing sync data file in Google Drive with file ID: $fileId" }
|
||||
} else {
|
||||
// File doesn't exist, so create it
|
||||
val fileMetadata = File().apply {
|
||||
name = remoteFileName
|
||||
mimeType = "application/gzip"
|
||||
}
|
||||
val uploadedFile = drive.files().create(fileMetadata, byteArrayContent)
|
||||
.setFields("id")
|
||||
.execute()
|
||||
|
||||
logcat(
|
||||
LogPriority.DEBUG,
|
||||
) { "Created new sync data file in Google Drive with file ID: ${uploadedFile.id}" }
|
||||
}
|
||||
|
||||
// Data has been successfully pushed or updated, delete the lock file
|
||||
deleteLockFile(drive)
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR) { "Failed to push or update sync data: ${e.message}" }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFileList(drive: Drive): MutableList<File> {
|
||||
// Search for the existing file by name
|
||||
val query = "mimeType='application/gzip' and trashed = false and name = '$remoteFileName'"
|
||||
val fileList = drive.files().list().setQ(query).execute().files
|
||||
Log.d("GoogleDrive", "File list: $fileList")
|
||||
try {
|
||||
// Search for the existing file by name
|
||||
val query = "mimeType='application/gzip' and trashed = false and name = '$remoteFileName'"
|
||||
val fileList = drive.files().list().setQ(query).execute().files
|
||||
Log.d("GoogleDrive", "File list: $fileList")
|
||||
|
||||
return fileList
|
||||
return fileList
|
||||
} catch (e: Exception) {
|
||||
Log.e("GoogleDrive", "Error no sync data found: ${e.message}")
|
||||
return mutableListOf()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createLockFile(drive: Drive) {
|
||||
try {
|
||||
val fileMetadata = File()
|
||||
fileMetadata.name = lockFileName
|
||||
fileMetadata.mimeType = "text/plain"
|
||||
|
||||
// Create an empty content to upload as the lock file
|
||||
val emptyContent = ByteArrayContent.fromString("text/plain", "")
|
||||
|
||||
val file = drive.files().create(fileMetadata, emptyContent)
|
||||
.setFields("id, name, createdTime")
|
||||
.execute()
|
||||
|
||||
Log.d("GoogleDrive", "Created lock file with ID: ${file.id}")
|
||||
} catch (e: Exception) {
|
||||
Log.e("GoogleDrive", "Error creating lock file: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun findLockFile(drive: Drive): MutableList<File> {
|
||||
try {
|
||||
val query = "mimeType='text/plain' and trashed = false and name = '$lockFileName'"
|
||||
val fileList = drive.files().list().setQ(query).setFields("files(id, name, createdTime)").execute().files
|
||||
Log.d("GoogleDrive", "Lock file search result: $fileList")
|
||||
return fileList
|
||||
} catch (e: Exception) {
|
||||
Log.e("GoogleDrive", "Error finding lock file: ${e.message}")
|
||||
return mutableListOf()
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteLockFile(drive: Drive) {
|
||||
try {
|
||||
val lockFiles = findLockFile(drive)
|
||||
|
||||
if (lockFiles.isNotEmpty()) {
|
||||
for (file in lockFiles) {
|
||||
drive.files().delete(file.id).execute()
|
||||
Log.d("GoogleDrive", "Deleted lock file with ID: ${file.id}")
|
||||
}
|
||||
} else {
|
||||
Log.d("GoogleDrive", "No lock file found to delete.")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("GoogleDrive", "Error deleting lock file: ${e.message}")
|
||||
throw Exception(context.getString(R.string.error_deleting_google_drive_lock_file))
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun deleteSyncDataFromGoogleDrive(): DeleteSyncDataStatus {
|
||||
val drive = googleDriveService.googleDriveService
|
||||
val drive = googleDriveService.driveService
|
||||
|
||||
if (drive == null) {
|
||||
logcat(LogPriority.ERROR) { "Google Drive service not initialized" }
|
||||
@ -160,7 +260,7 @@ class GoogleDriveSyncService(context: Context, json: Json, syncPreferences: Sync
|
||||
}
|
||||
|
||||
class GoogleDriveService(private val context: Context) {
|
||||
var googleDriveService: Drive? = null
|
||||
var driveService: Drive? = null
|
||||
companion object {
|
||||
const val REDIRECT_URI = "eu.kanade.google.oauth:/oauth2redirect"
|
||||
}
|
||||
@ -179,7 +279,7 @@ class GoogleDriveService(private val context: Context) {
|
||||
val refreshToken = syncPreferences.googleDriveRefreshToken().get()
|
||||
|
||||
if (accessToken == "" || refreshToken == "") {
|
||||
googleDriveService = null
|
||||
driveService = null
|
||||
return
|
||||
}
|
||||
|
||||
@ -296,7 +396,7 @@ class GoogleDriveService(private val context: Context) {
|
||||
credential.accessToken = accessToken
|
||||
credential.refreshToken = refreshToken
|
||||
|
||||
googleDriveService = Drive.Builder(
|
||||
driveService = Drive.Builder(
|
||||
NetHttpTransport(),
|
||||
jsonFactory,
|
||||
credential,
|
||||
|
@ -3,7 +3,11 @@ package eu.kanade.tachiyomi.data.sync.service
|
||||
import android.content.Context
|
||||
import eu.kanade.tachiyomi.data.sync.SyncNotifier
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.PATCH
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import logcat.LogPriority
|
||||
@ -22,6 +26,108 @@ class SyncYomiSyncService(
|
||||
syncPreferences: SyncPreferences,
|
||||
private val notifier: SyncNotifier,
|
||||
) : SyncService(context, json, syncPreferences) {
|
||||
|
||||
@Serializable
|
||||
enum class SyncStatus {
|
||||
@SerialName("pending")
|
||||
Pending,
|
||||
|
||||
@SerialName("syncing")
|
||||
Syncing,
|
||||
|
||||
@SerialName("success")
|
||||
Success,
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class LockFile(
|
||||
@SerialName("id")
|
||||
val id: Int?,
|
||||
@SerialName("user_api_key")
|
||||
val userApiKey: String?,
|
||||
@SerialName("acquired_by")
|
||||
val acquiredBy: String?,
|
||||
@SerialName("last_synced")
|
||||
val lastSynced: String?,
|
||||
@SerialName("status")
|
||||
val status: SyncStatus,
|
||||
@SerialName("acquired_at")
|
||||
val acquiredAt: String?,
|
||||
@SerialName("expires_at")
|
||||
val expiresAt: String?,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class LockfileCreateRequest(
|
||||
@SerialName("acquired_by")
|
||||
val acquiredBy: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class LockfilePatchRequest(
|
||||
@SerialName("user_api_key")
|
||||
val userApiKey: String,
|
||||
@SerialName("acquired_by")
|
||||
val acquiredBy: String,
|
||||
)
|
||||
|
||||
override suspend fun beforeSync() {
|
||||
val host = syncPreferences.syncHost().get()
|
||||
val apiKey = syncPreferences.syncAPIKey().get()
|
||||
val lockFileApi = "$host/api/sync/lock"
|
||||
val deviceId = syncPreferences.uniqueDeviceID()
|
||||
val client = OkHttpClient()
|
||||
val headers = Headers.Builder().add("X-API-Token", apiKey).build()
|
||||
val json = Json { ignoreUnknownKeys = true }
|
||||
|
||||
val createLockfileRequest = LockfileCreateRequest(deviceId)
|
||||
val createLockfileJson = json.encodeToString(createLockfileRequest)
|
||||
|
||||
val patchRequest = LockfilePatchRequest(apiKey, deviceId)
|
||||
val patchJson = json.encodeToString(patchRequest)
|
||||
|
||||
val lockFileRequest = GET(
|
||||
url = lockFileApi,
|
||||
headers = headers,
|
||||
)
|
||||
|
||||
val lockFileCreate = POST(
|
||||
url = lockFileApi,
|
||||
headers = headers,
|
||||
body = createLockfileJson.toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull()),
|
||||
)
|
||||
|
||||
val lockFileUpdate = PATCH(
|
||||
url = lockFileApi,
|
||||
headers = headers,
|
||||
body = patchJson.toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull()),
|
||||
)
|
||||
|
||||
// create lock file first
|
||||
client.newCall(lockFileCreate).execute()
|
||||
// update lock file acquired_by
|
||||
client.newCall(lockFileUpdate).execute()
|
||||
|
||||
var backoff = 2000L // Start with 2 seconds
|
||||
val maxBackoff = 32000L // Maximum backoff time e.g., 32 seconds
|
||||
var lockFile: LockFile
|
||||
do {
|
||||
val response = client.newCall(lockFileRequest).execute()
|
||||
val responseBody = response.body.string()
|
||||
lockFile = json.decodeFromString<LockFile>(responseBody)
|
||||
logcat(LogPriority.DEBUG) { "SyncYomi lock file status: ${lockFile.status}" }
|
||||
|
||||
if (lockFile.status != SyncStatus.Success) {
|
||||
logcat(LogPriority.DEBUG) { "Lock file not ready, retrying in $backoff ms..." }
|
||||
delay(backoff)
|
||||
backoff = (backoff * 2).coerceAtMost(maxBackoff)
|
||||
}
|
||||
} while (lockFile.status != SyncStatus.Success)
|
||||
|
||||
// update lock file acquired_by
|
||||
client.newCall(lockFileUpdate).execute()
|
||||
}
|
||||
|
||||
override suspend fun pullSyncData(): SyncData? {
|
||||
val host = syncPreferences.syncHost().get()
|
||||
val apiKey = syncPreferences.syncAPIKey().get()
|
||||
|
@ -28,6 +28,7 @@ import nl.adaptivity.xmlutil.XmlDeclMode
|
||||
import nl.adaptivity.xmlutil.core.XmlVersion
|
||||
import nl.adaptivity.xmlutil.serialization.XML
|
||||
import tachiyomi.core.storage.AndroidStorageFolderProvider
|
||||
import tachiyomi.core.storage.UniFileTempFileManager
|
||||
import tachiyomi.data.AndroidDatabaseHandler
|
||||
import tachiyomi.data.Database
|
||||
import tachiyomi.data.DatabaseHandler
|
||||
@ -112,6 +113,8 @@ class AppModule(val app: Application) : InjektModule {
|
||||
ProtoBuf
|
||||
}
|
||||
|
||||
addSingletonFactory { UniFileTempFileManager(app) }
|
||||
|
||||
addSingletonFactory { ChapterCache(app, get()) }
|
||||
addSingletonFactory { CoverCache(app) }
|
||||
|
||||
|
@ -3,7 +3,7 @@ package eu.kanade.tachiyomi.extension
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi
|
||||
import eu.kanade.tachiyomi.extension.api.ExtensionApi
|
||||
import eu.kanade.tachiyomi.extension.api.ExtensionUpdateNotifier
|
||||
import eu.kanade.tachiyomi.extension.model.Extension
|
||||
import eu.kanade.tachiyomi.extension.model.InstallStep
|
||||
@ -49,7 +49,7 @@ class ExtensionManager(
|
||||
/**
|
||||
* API where all the available extensions can be found.
|
||||
*/
|
||||
private val api = ExtensionGithubApi()
|
||||
private val api = ExtensionApi()
|
||||
|
||||
/**
|
||||
* The installer which installs, updates and uninstalls the extensions.
|
||||
@ -258,7 +258,6 @@ class ExtensionManager(
|
||||
val untrustedSignatures = _untrustedExtensionsFlow.value.map { it.signatureHash }.toSet()
|
||||
if (signature !in untrustedSignatures) return
|
||||
|
||||
ExtensionLoader.trustedSignatures += signature
|
||||
preferences.trustedSignatures() += signature
|
||||
|
||||
val nowTrustedExtensions = _untrustedExtensionsFlow.value.filter { it.signatureHash == signature }
|
||||
|
@ -1,6 +1,8 @@
|
||||
package eu.kanade.tachiyomi.extension.api
|
||||
|
||||
import android.content.Context
|
||||
import eu.kanade.domain.source.interactor.OFFICIAL_REPO_BASE_URL
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
import eu.kanade.tachiyomi.extension.ExtensionManager
|
||||
import eu.kanade.tachiyomi.extension.model.Extension
|
||||
import eu.kanade.tachiyomi.extension.model.LoadResult
|
||||
@ -20,10 +22,11 @@ import uy.kohesive.injekt.injectLazy
|
||||
import java.time.Instant
|
||||
import kotlin.time.Duration.Companion.days
|
||||
|
||||
internal class ExtensionGithubApi {
|
||||
internal class ExtensionApi {
|
||||
|
||||
private val networkService: NetworkHelper by injectLazy()
|
||||
private val preferenceStore: PreferenceStore by injectLazy()
|
||||
private val sourcePreferences: SourcePreferences by injectLazy()
|
||||
private val extensionManager: ExtensionManager by injectLazy()
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
@ -31,39 +34,16 @@ internal class ExtensionGithubApi {
|
||||
preferenceStore.getLong(Preference.appStateKey("last_ext_check"), 0)
|
||||
}
|
||||
|
||||
private var requiresFallbackSource = false
|
||||
|
||||
suspend fun findExtensions(): List<Extension.Available> {
|
||||
return withIOContext {
|
||||
val githubResponse = if (requiresFallbackSource) {
|
||||
null
|
||||
} else {
|
||||
try {
|
||||
networkService.client
|
||||
.newCall(GET("${REPO_URL_PREFIX}index.min.json"))
|
||||
.awaitSuccess()
|
||||
} catch (e: Throwable) {
|
||||
logcat(LogPriority.ERROR, e) { "Failed to get extensions from GitHub" }
|
||||
requiresFallbackSource = true
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
val response = githubResponse ?: run {
|
||||
networkService.client
|
||||
.newCall(GET("${FALLBACK_REPO_URL_PREFIX}index.min.json"))
|
||||
.awaitSuccess()
|
||||
}
|
||||
|
||||
val extensions = with(json) {
|
||||
response
|
||||
.parseAs<List<ExtensionJsonObject>>()
|
||||
.toExtensions()
|
||||
val extensions = buildList {
|
||||
addAll(getExtensions(OFFICIAL_REPO_BASE_URL, true))
|
||||
sourcePreferences.extensionRepos().get().map { addAll(getExtensions(it, false)) }
|
||||
}
|
||||
|
||||
// Sanity check - a small number of extensions probably means something broke
|
||||
// with the repo generator
|
||||
if (extensions.size < 100) {
|
||||
if (extensions.size < 50) {
|
||||
throw Exception()
|
||||
}
|
||||
|
||||
@ -71,6 +51,26 @@ internal class ExtensionGithubApi {
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getExtensions(
|
||||
repoBaseUrl: String,
|
||||
isOfficialRepo: Boolean,
|
||||
): List<Extension.Available> {
|
||||
return try {
|
||||
val response = networkService.client
|
||||
.newCall(GET("$repoBaseUrl/index.min.json"))
|
||||
.awaitSuccess()
|
||||
|
||||
with(json) {
|
||||
response
|
||||
.parseAs<List<ExtensionJsonObject>>()
|
||||
.toExtensions(repoBaseUrl, isRepoSource = !isOfficialRepo)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
logcat(LogPriority.ERROR, e) { "Failed to get extensions from $repoBaseUrl" }
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun checkForUpdates(
|
||||
context: Context,
|
||||
fromAvailableExtensionList: Boolean = false,
|
||||
@ -111,7 +111,10 @@ internal class ExtensionGithubApi {
|
||||
return extensionsWithUpdate
|
||||
}
|
||||
|
||||
private fun List<ExtensionJsonObject>.toExtensions(): List<Extension.Available> {
|
||||
private fun List<ExtensionJsonObject>.toExtensions(
|
||||
repoUrl: String,
|
||||
isRepoSource: Boolean,
|
||||
): List<Extension.Available> {
|
||||
return this
|
||||
.filter {
|
||||
val libVersion = it.extractLibVersion()
|
||||
@ -126,25 +129,17 @@ internal class ExtensionGithubApi {
|
||||
libVersion = it.extractLibVersion(),
|
||||
lang = it.lang,
|
||||
isNsfw = it.nsfw == 1,
|
||||
hasReadme = it.hasReadme == 1,
|
||||
hasChangelog = it.hasChangelog == 1,
|
||||
sources = it.sources?.map(extensionSourceMapper).orEmpty(),
|
||||
apkName = it.apk,
|
||||
iconUrl = "${getUrlPrefix()}icon/${it.pkg}.png",
|
||||
iconUrl = "$repoUrl/icon/${it.pkg}.png",
|
||||
repoUrl = repoUrl,
|
||||
isRepoSource = isRepoSource,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun getApkUrl(extension: Extension.Available): String {
|
||||
return "${getUrlPrefix()}apk/${extension.apkName}"
|
||||
}
|
||||
|
||||
private fun getUrlPrefix(): String {
|
||||
return if (requiresFallbackSource) {
|
||||
FALLBACK_REPO_URL_PREFIX
|
||||
} else {
|
||||
REPO_URL_PREFIX
|
||||
}
|
||||
return "${extension.repoUrl}/apk/${extension.apkName}"
|
||||
}
|
||||
|
||||
private fun ExtensionJsonObject.extractLibVersion(): Double {
|
||||
@ -152,9 +147,6 @@ internal class ExtensionGithubApi {
|
||||
}
|
||||
}
|
||||
|
||||
private const val REPO_URL_PREFIX = "https://raw.githubusercontent.com/tachiyomiorg/tachiyomi-extensions/repo/"
|
||||
private const val FALLBACK_REPO_URL_PREFIX = "https://gcore.jsdelivr.net/gh/tachiyomiorg/tachiyomi-extensions@repo/"
|
||||
|
||||
@Serializable
|
||||
private data class ExtensionJsonObject(
|
||||
val name: String,
|
||||
@ -164,8 +156,6 @@ private data class ExtensionJsonObject(
|
||||
val code: Long,
|
||||
val version: String,
|
||||
val nsfw: Int,
|
||||
val hasReadme: Int = 0,
|
||||
val hasChangelog: Int = 0,
|
||||
val sources: List<ExtensionSourceJsonObject>?,
|
||||
)
|
||||
|
@ -13,8 +13,6 @@ sealed class Extension {
|
||||
abstract val libVersion: Double
|
||||
abstract val lang: String?
|
||||
abstract val isNsfw: Boolean
|
||||
abstract val hasReadme: Boolean
|
||||
abstract val hasChangelog: Boolean
|
||||
|
||||
data class Installed(
|
||||
override val name: String,
|
||||
@ -24,8 +22,6 @@ sealed class Extension {
|
||||
override val libVersion: Double,
|
||||
override val lang: String,
|
||||
override val isNsfw: Boolean,
|
||||
override val hasReadme: Boolean,
|
||||
override val hasChangelog: Boolean,
|
||||
val pkgFactory: String?,
|
||||
val sources: List<Source>,
|
||||
val icon: Drawable?,
|
||||
@ -33,6 +29,8 @@ sealed class Extension {
|
||||
val isObsolete: Boolean = false,
|
||||
val isUnofficial: Boolean = false,
|
||||
val isShared: Boolean,
|
||||
val repoUrl: String? = null,
|
||||
val isRepoSource: Boolean = false,
|
||||
) : Extension()
|
||||
|
||||
data class Available(
|
||||
@ -43,11 +41,11 @@ sealed class Extension {
|
||||
override val libVersion: Double,
|
||||
override val lang: String,
|
||||
override val isNsfw: Boolean,
|
||||
override val hasReadme: Boolean,
|
||||
override val hasChangelog: Boolean,
|
||||
val sources: List<Source>,
|
||||
val apkName: String,
|
||||
val iconUrl: String,
|
||||
val repoUrl: String,
|
||||
val isRepoSource: Boolean,
|
||||
) : Extension() {
|
||||
|
||||
data class Source(
|
||||
@ -75,7 +73,5 @@ sealed class Extension {
|
||||
val signatureHash: String,
|
||||
override val lang: String? = null,
|
||||
override val isNsfw: Boolean = false,
|
||||
override val hasReadme: Boolean = false,
|
||||
override val hasChangelog: Boolean = false,
|
||||
) : Extension()
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.SourceFactory
|
||||
import eu.kanade.tachiyomi.util.lang.Hash
|
||||
import eu.kanade.tachiyomi.util.storage.copyAndSetReadOnlyTo
|
||||
import eu.kanade.tachiyomi.util.system.isDevFlavor
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.runBlocking
|
||||
@ -62,11 +63,6 @@ internal object ExtensionLoader {
|
||||
// inorichi's key
|
||||
private const val officialSignature = "7ce04da7773d41b489f4693a366c36bcd0a11fc39b547168553c285bd7348e23"
|
||||
|
||||
/**
|
||||
* List of the trusted signatures.
|
||||
*/
|
||||
var trustedSignatures = mutableSetOf(officialSignature) + preferences.trustedSignatures().get()
|
||||
|
||||
private const val PRIVATE_EXTENSION_EXTENSION = "ext"
|
||||
|
||||
private fun getPrivateExtensionDir(context: Context) = File(context.filesDir, "exts")
|
||||
@ -123,6 +119,12 @@ internal object ExtensionLoader {
|
||||
* @param context The application context.
|
||||
*/
|
||||
fun loadExtensions(context: Context): List<LoadResult> {
|
||||
// Always make users trust unknown extensions on cold starts in non-dev builds
|
||||
// due to inherent security risks
|
||||
if (!isDevFlavor) {
|
||||
preferences.trustedSignatures().delete()
|
||||
}
|
||||
|
||||
val pkgManager = context.packageManager
|
||||
|
||||
val installedPkgs = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
@ -329,8 +331,6 @@ internal object ExtensionLoader {
|
||||
libVersion = libVersion,
|
||||
lang = lang,
|
||||
isNsfw = isNsfw,
|
||||
hasReadme = hasReadme,
|
||||
hasChangelog = hasChangelog,
|
||||
sources = sources,
|
||||
pkgFactory = appInfo.metaData.getString(METADATA_SOURCE_FACTORY),
|
||||
isUnofficial = !isOfficiallySigned(signatures),
|
||||
@ -394,6 +394,11 @@ internal object ExtensionLoader {
|
||||
}
|
||||
|
||||
private fun hasTrustedSignature(signatures: List<String>): Boolean {
|
||||
if (officialSignature in signatures) {
|
||||
return true
|
||||
}
|
||||
|
||||
val trustedSignatures = preferences.trustedSignatures().get()
|
||||
return trustedSignatures.any { signatures.contains(it) }
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,6 @@ data class ExtensionDetailsScreen(
|
||||
state = state,
|
||||
onClickSourcePreferences = { navigator.push(SourcePreferencesScreen(it)) },
|
||||
onClickWhatsNew = { uriHandler.openUri(screenModel.getChangelogUrl()) },
|
||||
onClickReadme = { uriHandler.openUri(screenModel.getReadmeUrl()) },
|
||||
onClickEnableAll = { screenModel.toggleSources(true) },
|
||||
onClickDisableAll = { screenModel.toggleSources(false) },
|
||||
onClickClearCookies = screenModel::clearCookies,
|
||||
|
@ -31,8 +31,6 @@ import uy.kohesive.injekt.api.get
|
||||
|
||||
private const val URL_EXTENSION_COMMITS =
|
||||
"https://github.com/tachiyomiorg/tachiyomi-extensions/commits/master"
|
||||
private const val URL_EXTENSION_BLOB =
|
||||
"https://github.com/tachiyomiorg/tachiyomi-extensions/blob/master"
|
||||
|
||||
class ExtensionDetailsScreenModel(
|
||||
pkgName: String,
|
||||
@ -93,26 +91,11 @@ class ExtensionDetailsScreenModel(
|
||||
|
||||
val pkgName = extension.pkgName.substringAfter("eu.kanade.tachiyomi.extension.")
|
||||
val pkgFactory = extension.pkgFactory
|
||||
if (extension.hasChangelog) {
|
||||
return createUrl(URL_EXTENSION_BLOB, pkgName, pkgFactory, "/CHANGELOG.md")
|
||||
}
|
||||
|
||||
// Falling back on GitHub commit history because there is no explicit changelog in extension
|
||||
return createUrl(URL_EXTENSION_COMMITS, pkgName, pkgFactory)
|
||||
}
|
||||
|
||||
fun getReadmeUrl(): String {
|
||||
val extension = state.value.extension ?: return ""
|
||||
|
||||
if (!extension.hasReadme) {
|
||||
return "https://tachiyomi.org/docs/faq/browse/extensions"
|
||||
}
|
||||
|
||||
val pkgName = extension.pkgName.substringAfter("eu.kanade.tachiyomi.extension.")
|
||||
val pkgFactory = extension.pkgFactory
|
||||
return createUrl(URL_EXTENSION_BLOB, pkgName, pkgFactory, "/README.md")
|
||||
}
|
||||
|
||||
fun clearCookies() {
|
||||
val extension = state.value.extension ?: return
|
||||
|
||||
|
@ -24,6 +24,8 @@ import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.util.removeCovers
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
@ -265,7 +267,10 @@ class BrowseSourceScreenModel(
|
||||
else -> {
|
||||
val preselectedIds = getCategories.await(manga.id).map { it.id }
|
||||
setDialog(
|
||||
Dialog.ChangeMangaCategory(manga, categories.mapAsCheckboxState { it.id in preselectedIds }),
|
||||
Dialog.ChangeMangaCategory(
|
||||
manga,
|
||||
categories.mapAsCheckboxState { it.id in preselectedIds }.toImmutableList(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -338,7 +343,7 @@ class BrowseSourceScreenModel(
|
||||
data class AddDuplicateManga(val manga: Manga, val duplicate: Manga) : Dialog
|
||||
data class ChangeMangaCategory(
|
||||
val manga: Manga,
|
||||
val initialSelection: List<CheckboxState.State<Category>>,
|
||||
val initialSelection: ImmutableList<CheckboxState.State<Category>>,
|
||||
) : Dialog
|
||||
data class Migrate(val newManga: Manga) : Dialog
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.util.fastMap
|
||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
@ -15,6 +16,7 @@ import eu.kanade.presentation.category.components.CategoryRenameDialog
|
||||
import eu.kanade.presentation.category.components.CategorySortAlphabeticallyDialog
|
||||
import eu.kanade.presentation.util.Screen
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import tachiyomi.presentation.core.screens.LoadingScreen
|
||||
|
||||
@ -52,22 +54,22 @@ class CategoryScreen : Screen() {
|
||||
CategoryCreateDialog(
|
||||
onDismissRequest = screenModel::dismissDialog,
|
||||
onCreate = screenModel::createCategory,
|
||||
categories = successState.categories,
|
||||
categories = successState.categories.fastMap { it.name }.toImmutableList(),
|
||||
)
|
||||
}
|
||||
is CategoryDialog.Rename -> {
|
||||
CategoryRenameDialog(
|
||||
onDismissRequest = screenModel::dismissDialog,
|
||||
onRename = { screenModel.renameCategory(dialog.category, it) },
|
||||
categories = successState.categories,
|
||||
category = dialog.category,
|
||||
categories = successState.categories.fastMap { it.name }.toImmutableList(),
|
||||
category = dialog.category.name,
|
||||
)
|
||||
}
|
||||
is CategoryDialog.Delete -> {
|
||||
CategoryDeleteDialog(
|
||||
onDismissRequest = screenModel::dismissDialog,
|
||||
onDelete = { screenModel.deleteCategory(dialog.category.id) },
|
||||
category = dialog.category,
|
||||
category = dialog.category.name,
|
||||
)
|
||||
}
|
||||
is CategoryDialog.SortAlphabetically -> {
|
||||
|
@ -1,11 +1,8 @@
|
||||
package eu.kanade.tachiyomi.ui.home
|
||||
|
||||
import androidx.activity.compose.PredictiveBackHandler
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.LinearOutSlowInEasing
|
||||
import androidx.compose.animation.core.animate
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.animation.togetherWith
|
||||
@ -26,20 +23,13 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.TransformOrigin
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import androidx.compose.ui.util.lerp
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import cafe.adriel.voyager.navigator.tab.LocalTabNavigator
|
||||
@ -59,7 +49,6 @@ import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.receiveAsFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import soup.compose.material.motion.MotionConstants
|
||||
import soup.compose.material.motion.animation.materialFadeThroughIn
|
||||
import soup.compose.material.motion.animation.materialFadeThroughOut
|
||||
import tachiyomi.domain.library.service.LibraryPreferences
|
||||
@ -70,7 +59,6 @@ import tachiyomi.presentation.core.components.material.Scaffold
|
||||
import tachiyomi.presentation.core.i18n.pluralStringResource
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
|
||||
object HomeScreen : Screen() {
|
||||
|
||||
@ -92,8 +80,6 @@ object HomeScreen : Screen() {
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
var scale by remember { mutableFloatStateOf(1f) }
|
||||
|
||||
TabNavigator(
|
||||
tab = LibraryTab,
|
||||
key = TabNavigatorKey,
|
||||
@ -132,11 +118,6 @@ object HomeScreen : Screen() {
|
||||
) { contentPadding ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.graphicsLayer {
|
||||
scaleX = scale
|
||||
scaleY = scale
|
||||
transformOrigin = TransformOrigin(0.5f, 1f)
|
||||
}
|
||||
.padding(contentPadding)
|
||||
.consumeWindowInsets(contentPadding),
|
||||
) {
|
||||
@ -157,30 +138,10 @@ object HomeScreen : Screen() {
|
||||
}
|
||||
|
||||
val goToLibraryTab = { tabNavigator.current = LibraryTab }
|
||||
|
||||
var handlingBack by remember { mutableStateOf(false) }
|
||||
PredictiveBackHandler(enabled = handlingBack || tabNavigator.current != LibraryTab) { progress ->
|
||||
handlingBack = true
|
||||
val currentTab = tabNavigator.current
|
||||
try {
|
||||
progress.collect { backEvent ->
|
||||
scale = lerp(1f, 0.92f, LinearOutSlowInEasing.transform(backEvent.progress))
|
||||
tabNavigator.current = if (backEvent.progress > 0.25f) tabs[0] else currentTab
|
||||
}
|
||||
goToLibraryTab()
|
||||
} catch (e: CancellationException) {
|
||||
tabNavigator.current = currentTab
|
||||
} finally {
|
||||
animate(
|
||||
initialValue = scale,
|
||||
targetValue = 1f,
|
||||
animationSpec = tween(durationMillis = MotionConstants.DefaultMotionDuration),
|
||||
) { value, _ ->
|
||||
scale = value
|
||||
}
|
||||
handlingBack = false
|
||||
}
|
||||
}
|
||||
BackHandler(
|
||||
enabled = tabNavigator.current != LibraryTab,
|
||||
onBack = goToLibraryTab,
|
||||
)
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
launch {
|
||||
|
@ -28,9 +28,11 @@ import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.util.chapter.getNextUnread
|
||||
import eu.kanade.tachiyomi.util.removeCovers
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.mutate
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.combine
|
||||
@ -661,13 +663,15 @@ class LibraryScreenModel(
|
||||
val common = getCommonCategories(mangaList)
|
||||
// Get indexes of the mix categories to preselect.
|
||||
val mix = getMixCategories(mangaList)
|
||||
val preselected = categories.map {
|
||||
when (it) {
|
||||
in common -> CheckboxState.State.Checked(it)
|
||||
in mix -> CheckboxState.TriState.Exclude(it)
|
||||
else -> CheckboxState.State.None(it)
|
||||
val preselected = categories
|
||||
.map {
|
||||
when (it) {
|
||||
in common -> CheckboxState.State.Checked(it)
|
||||
in mix -> CheckboxState.TriState.Exclude(it)
|
||||
else -> CheckboxState.State.None(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
.toImmutableList()
|
||||
mutableState.update { it.copy(dialog = Dialog.ChangeCategory(mangaList, preselected)) }
|
||||
}
|
||||
}
|
||||
@ -683,7 +687,10 @@ class LibraryScreenModel(
|
||||
|
||||
sealed interface Dialog {
|
||||
data object SettingsSheet : Dialog
|
||||
data class ChangeCategory(val manga: List<Manga>, val initialSelection: List<CheckboxState<Category>>) : Dialog
|
||||
data class ChangeCategory(
|
||||
val manga: List<Manga>,
|
||||
val initialSelection: ImmutableList<CheckboxState<Category>>,
|
||||
) : Dialog
|
||||
data class DeleteManga(val manga: List<Manga>) : Dialog
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||
@ -64,7 +65,7 @@ import eu.kanade.tachiyomi.data.download.DownloadCache
|
||||
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
|
||||
import eu.kanade.tachiyomi.data.updater.AppUpdateChecker
|
||||
import eu.kanade.tachiyomi.data.updater.RELEASE_URL
|
||||
import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi
|
||||
import eu.kanade.tachiyomi.extension.api.ExtensionApi
|
||||
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
|
||||
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreen
|
||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchScreen
|
||||
@ -222,13 +223,14 @@ class MainActivity : BaseActivity() {
|
||||
contentWindowInsets = scaffoldInsets,
|
||||
) { contentPadding ->
|
||||
// Consume insets already used by app state banners
|
||||
// Shows current screen
|
||||
DefaultNavigatorScreenTransition(
|
||||
navigator = navigator,
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(contentPadding)
|
||||
.consumeWindowInsets(contentPadding),
|
||||
)
|
||||
) {
|
||||
// Shows current screen
|
||||
DefaultNavigatorScreenTransition(navigator = navigator)
|
||||
}
|
||||
}
|
||||
|
||||
// Pop source-related screens when incognito mode is turned off
|
||||
@ -335,7 +337,7 @@ class MainActivity : BaseActivity() {
|
||||
// Extensions updates
|
||||
LaunchedEffect(Unit) {
|
||||
try {
|
||||
ExtensionGithubApi().checkForUpdates(context)
|
||||
ExtensionApi().checkForUpdates(context)
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR, e)
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ class MangaScreen(
|
||||
MangaScreen(
|
||||
state = successState,
|
||||
snackbarHostState = screenModel.snackbarHostState,
|
||||
fetchInterval = successState.manga.fetchInterval,
|
||||
nextUpdate = successState.manga.expectedNextUpdate,
|
||||
isTabletUi = isTabletUi(),
|
||||
chapterSwipeStartAction = screenModel.chapterSwipeStartAction,
|
||||
chapterSwipeEndAction = screenModel.chapterSwipeEndAction,
|
||||
@ -146,7 +146,7 @@ class MangaScreen(
|
||||
onDownloadActionClicked = screenModel::runDownloadAction.takeIf { !successState.source.isLocalOrStub() },
|
||||
onEditCategoryClicked = screenModel::showChangeCategoryDialog.takeIf { successState.manga.favorite },
|
||||
onEditFetchIntervalClicked = screenModel::showSetFetchIntervalDialog.takeIf {
|
||||
screenModel.isUpdateIntervalEnabled && successState.manga.favorite
|
||||
successState.manga.favorite
|
||||
},
|
||||
onMigrateClicked = {
|
||||
navigator.push(MigrateSearchScreen(successState.manga.id))
|
||||
@ -243,9 +243,10 @@ class MangaScreen(
|
||||
is MangaScreenModel.Dialog.SetFetchInterval -> {
|
||||
SetIntervalDialog(
|
||||
interval = dialog.manga.fetchInterval,
|
||||
nextUpdate = dialog.manga.nextUpdate,
|
||||
nextUpdate = dialog.manga.expectedNextUpdate,
|
||||
onDismissRequest = onDismissRequest,
|
||||
onValueChanged = { screenModel.setFetchInterval(dialog.manga, it) },
|
||||
onValueChanged = { interval: Int -> screenModel.setFetchInterval(dialog.manga, interval) }
|
||||
.takeIf { screenModel.isUpdateIntervalEnabled },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,8 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||
import eu.kanade.tachiyomi.util.chapter.getNextUnread
|
||||
import eu.kanade.tachiyomi.util.removeCovers
|
||||
import eu.kanade.tachiyomi.util.shouldDownloadNewChapters
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.flow.catch
|
||||
@ -360,7 +362,7 @@ class MangaScreenModel(
|
||||
successState.copy(
|
||||
dialog = Dialog.ChangeCategory(
|
||||
manga = manga,
|
||||
initialSelection = categories.mapAsCheckboxState { it.id in selection },
|
||||
initialSelection = categories.mapAsCheckboxState { it.id in selection }.toImmutableList(),
|
||||
),
|
||||
)
|
||||
}
|
||||
@ -992,7 +994,10 @@ class MangaScreenModel(
|
||||
// Track sheet - end
|
||||
|
||||
sealed interface Dialog {
|
||||
data class ChangeCategory(val manga: Manga, val initialSelection: List<CheckboxState<Category>>) : Dialog
|
||||
data class ChangeCategory(
|
||||
val manga: Manga,
|
||||
val initialSelection: ImmutableList<CheckboxState<Category>>,
|
||||
) : Dialog
|
||||
data class DeleteChapters(val chapters: List<Chapter>) : Dialog
|
||||
data class DuplicateManga(val manga: Manga, val duplicate: Manga) : Dialog
|
||||
data class SetFetchInterval(val manga: Manga) : Dialog
|
||||
|
@ -56,6 +56,7 @@ import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.core.preference.toggle
|
||||
import tachiyomi.core.storage.UniFileTempFileManager
|
||||
import tachiyomi.core.util.lang.launchIO
|
||||
import tachiyomi.core.util.lang.launchNonCancellable
|
||||
import tachiyomi.core.util.lang.withIOContext
|
||||
@ -87,6 +88,7 @@ class ReaderViewModel @JvmOverloads constructor(
|
||||
private val sourceManager: SourceManager = Injekt.get(),
|
||||
private val downloadManager: DownloadManager = Injekt.get(),
|
||||
private val downloadProvider: DownloadProvider = Injekt.get(),
|
||||
private val tempFileManager: UniFileTempFileManager = Injekt.get(),
|
||||
private val imageSaver: ImageSaver = Injekt.get(),
|
||||
preferences: BasePreferences = Injekt.get(),
|
||||
val readerPreferences: ReaderPreferences = Injekt.get(),
|
||||
@ -272,7 +274,7 @@ class ReaderViewModel @JvmOverloads constructor(
|
||||
|
||||
val context = Injekt.get<Application>()
|
||||
val source = sourceManager.getOrStub(manga.source)
|
||||
loader = ChapterLoader(context, downloadManager, downloadProvider, manga, source)
|
||||
loader = ChapterLoader(context, downloadManager, downloadProvider, tempFileManager, manga, source)
|
||||
|
||||
loadChapter(loader!!, chapterList.first { chapterId == it.chapter.id })
|
||||
Result.success(true)
|
||||
@ -922,6 +924,7 @@ class ReaderViewModel @JvmOverloads constructor(
|
||||
private fun deletePendingChapters() {
|
||||
viewModelScope.launchNonCancellable {
|
||||
downloadManager.deletePendingChapters()
|
||||
tempFileManager.deleteTempFiles()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
|
||||
import tachiyomi.core.i18n.stringResource
|
||||
import tachiyomi.core.storage.toTempFile
|
||||
import tachiyomi.core.storage.UniFileTempFileManager
|
||||
import tachiyomi.core.util.lang.withIOContext
|
||||
import tachiyomi.core.util.system.logcat
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
@ -24,6 +24,7 @@ class ChapterLoader(
|
||||
private val context: Context,
|
||||
private val downloadManager: DownloadManager,
|
||||
private val downloadProvider: DownloadProvider,
|
||||
private val tempFileManager: UniFileTempFileManager,
|
||||
private val manga: Manga,
|
||||
private val source: Source,
|
||||
) {
|
||||
@ -85,17 +86,24 @@ class ChapterLoader(
|
||||
skipCache = true,
|
||||
)
|
||||
return when {
|
||||
isDownloaded -> DownloadPageLoader(chapter, manga, source, downloadManager, downloadProvider)
|
||||
isDownloaded -> DownloadPageLoader(
|
||||
chapter,
|
||||
manga,
|
||||
source,
|
||||
downloadManager,
|
||||
downloadProvider,
|
||||
tempFileManager,
|
||||
)
|
||||
source is LocalSource -> source.getFormat(chapter.chapter).let { format ->
|
||||
when (format) {
|
||||
is Format.Directory -> DirectoryPageLoader(format.file)
|
||||
is Format.Zip -> ZipPageLoader(format.file.toTempFile(context))
|
||||
is Format.Zip -> ZipPageLoader(tempFileManager.createTempFile(format.file))
|
||||
is Format.Rar -> try {
|
||||
RarPageLoader(format.file.toTempFile(context))
|
||||
RarPageLoader(tempFileManager.createTempFile(format.file))
|
||||
} catch (e: UnsupportedRarV5Exception) {
|
||||
error(context.stringResource(MR.strings.loader_rar5_error))
|
||||
}
|
||||
is Format.Epub -> EpubPageLoader(format.file.toTempFile(context))
|
||||
is Format.Epub -> EpubPageLoader(tempFileManager.createTempFile(format.file))
|
||||
}
|
||||
}
|
||||
source is HttpSource -> HttpPageLoader(chapter, source)
|
||||
|
@ -10,7 +10,7 @@ import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
|
||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
||||
import tachiyomi.core.storage.toTempFile
|
||||
import tachiyomi.core.storage.UniFileTempFileManager
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
@ -23,6 +23,7 @@ internal class DownloadPageLoader(
|
||||
private val source: Source,
|
||||
private val downloadManager: DownloadManager,
|
||||
private val downloadProvider: DownloadProvider,
|
||||
private val tempFileManager: UniFileTempFileManager,
|
||||
) : PageLoader() {
|
||||
|
||||
private val context: Application by injectLazy()
|
||||
@ -46,8 +47,8 @@ internal class DownloadPageLoader(
|
||||
zipPageLoader?.recycle()
|
||||
}
|
||||
|
||||
private suspend fun getPagesFromArchive(chapterPath: UniFile): List<ReaderPage> {
|
||||
val loader = ZipPageLoader(chapterPath.toTempFile(context)).also { zipPageLoader = it }
|
||||
private suspend fun getPagesFromArchive(file: UniFile): List<ReaderPage> {
|
||||
val loader = ZipPageLoader(tempFileManager.createTempFile(file)).also { zipPageLoader = it }
|
||||
return loader.getPages()
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.io.PipedInputStream
|
||||
import java.io.PipedOutputStream
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
/**
|
||||
* Loader used to load a chapter from a .rar or .cbr file.
|
||||
@ -20,13 +21,18 @@ internal class RarPageLoader(file: File) : PageLoader() {
|
||||
|
||||
override var isLocal: Boolean = true
|
||||
|
||||
/**
|
||||
* Pool for copying compressed files to an input stream.
|
||||
*/
|
||||
private val pool = Executors.newFixedThreadPool(1)
|
||||
|
||||
override suspend fun getPages(): List<ReaderPage> {
|
||||
return rar.fileHeaders.asSequence()
|
||||
.filter { !it.isDirectory && ImageUtil.isImage(it.fileName) { rar.getInputStream(it) } }
|
||||
.sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) }
|
||||
.mapIndexed { i, header ->
|
||||
ReaderPage(i).apply {
|
||||
stream = { getStream(rar, header) }
|
||||
stream = { getStream(header) }
|
||||
status = Page.State.READY
|
||||
}
|
||||
}
|
||||
@ -40,15 +46,16 @@ internal class RarPageLoader(file: File) : PageLoader() {
|
||||
override fun recycle() {
|
||||
super.recycle()
|
||||
rar.close()
|
||||
pool.shutdown()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an input stream for the given [header].
|
||||
*/
|
||||
private fun getStream(rar: Archive, header: FileHeader): InputStream {
|
||||
private fun getStream(header: FileHeader): InputStream {
|
||||
val pipeIn = PipedInputStream()
|
||||
val pipeOut = PipedOutputStream(pipeIn)
|
||||
synchronized(this) {
|
||||
pool.execute {
|
||||
try {
|
||||
pipeOut.use {
|
||||
rar.extractFile(header, it)
|
||||
|
@ -235,7 +235,7 @@ class PagerPageHolder(
|
||||
*/
|
||||
private fun setError() {
|
||||
progressIndicator.hide()
|
||||
showErrorLayout(withOpenInWebView = false)
|
||||
showErrorLayout()
|
||||
}
|
||||
|
||||
override fun onImageLoaded() {
|
||||
@ -248,8 +248,7 @@ class PagerPageHolder(
|
||||
*/
|
||||
override fun onImageLoadError() {
|
||||
super.onImageLoadError()
|
||||
progressIndicator.hide()
|
||||
showErrorLayout(withOpenInWebView = true)
|
||||
setError()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -260,23 +259,27 @@ class PagerPageHolder(
|
||||
viewer.activity.hideMenu()
|
||||
}
|
||||
|
||||
private fun showErrorLayout(withOpenInWebView: Boolean): ReaderErrorBinding {
|
||||
private fun showErrorLayout(): ReaderErrorBinding {
|
||||
if (errorLayout == null) {
|
||||
errorLayout = ReaderErrorBinding.inflate(LayoutInflater.from(context), this, true)
|
||||
errorLayout?.actionRetry?.viewer = viewer
|
||||
errorLayout?.actionRetry?.setOnClickListener {
|
||||
page.chapter.pageLoader?.retryPage(page)
|
||||
}
|
||||
val imageUrl = page.imageUrl
|
||||
if (imageUrl.orEmpty().startsWith("http", true)) {
|
||||
}
|
||||
|
||||
val imageUrl = page.imageUrl
|
||||
errorLayout?.actionOpenInWebView?.isVisible = imageUrl != null
|
||||
if (imageUrl != null) {
|
||||
if (imageUrl.startsWith("http", true)) {
|
||||
errorLayout?.actionOpenInWebView?.viewer = viewer
|
||||
errorLayout?.actionOpenInWebView?.setOnClickListener {
|
||||
val intent = WebViewActivity.newIntent(context, imageUrl!!)
|
||||
val intent = WebViewActivity.newIntent(context, imageUrl)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
errorLayout?.actionOpenInWebView?.isVisible = withOpenInWebView
|
||||
|
||||
errorLayout?.root?.isVisible = true
|
||||
return errorLayout!!
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ class WebtoonPageHolder(
|
||||
refreshLayoutParams()
|
||||
|
||||
frame.onImageLoaded = { onImageDecoded() }
|
||||
frame.onImageLoadError = { onImageDecodeError() }
|
||||
frame.onImageLoadError = { setError() }
|
||||
frame.onScaleChanged = { viewer.activity.hideMenu() }
|
||||
}
|
||||
|
||||
@ -240,7 +240,7 @@ class WebtoonPageHolder(
|
||||
*/
|
||||
private fun setError() {
|
||||
progressContainer.isVisible = false
|
||||
initErrorLayout(withOpenInWebView = false)
|
||||
initErrorLayout()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -251,14 +251,6 @@ class WebtoonPageHolder(
|
||||
removeErrorLayout()
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the image fails to decode.
|
||||
*/
|
||||
private fun onImageDecodeError() {
|
||||
progressContainer.isVisible = false
|
||||
initErrorLayout(withOpenInWebView = true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new progress bar.
|
||||
*/
|
||||
@ -278,22 +270,26 @@ class WebtoonPageHolder(
|
||||
/**
|
||||
* Initializes a button to retry pages.
|
||||
*/
|
||||
private fun initErrorLayout(withOpenInWebView: Boolean): ReaderErrorBinding {
|
||||
private fun initErrorLayout(): ReaderErrorBinding {
|
||||
if (errorLayout == null) {
|
||||
errorLayout = ReaderErrorBinding.inflate(LayoutInflater.from(context), frame, true)
|
||||
errorLayout?.root?.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, (parentHeight * 0.8).toInt())
|
||||
errorLayout?.actionRetry?.setOnClickListener {
|
||||
page?.let { it.chapter.pageLoader?.retryPage(it) }
|
||||
}
|
||||
val imageUrl = page?.imageUrl
|
||||
if (imageUrl.orEmpty().startsWith("http", true)) {
|
||||
}
|
||||
|
||||
val imageUrl = page?.imageUrl
|
||||
errorLayout?.actionOpenInWebView?.isVisible = imageUrl != null
|
||||
if (imageUrl != null) {
|
||||
if (imageUrl.startsWith("http", true)) {
|
||||
errorLayout?.actionOpenInWebView?.setOnClickListener {
|
||||
val intent = WebViewActivity.newIntent(context, imageUrl!!)
|
||||
val intent = WebViewActivity.newIntent(context, imageUrl)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
errorLayout?.actionOpenInWebView?.isVisible = withOpenInWebView
|
||||
|
||||
return errorLayout!!
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,6 @@ class SettingsScreen(
|
||||
Destination.Tracking.id -> SettingsTrackingScreen
|
||||
else -> SettingsMainScreen
|
||||
},
|
||||
onBackPressed = null,
|
||||
content = {
|
||||
val pop: () -> Unit = {
|
||||
if (it.canPop) {
|
||||
@ -62,7 +61,6 @@ class SettingsScreen(
|
||||
Destination.Tracking.id -> SettingsTrackingScreen
|
||||
else -> SettingsAppearanceScreen
|
||||
},
|
||||
onBackPressed = null,
|
||||
) {
|
||||
val insets = WindowInsets.systemBars.only(WindowInsetsSides.Horizontal)
|
||||
TwoPanelBox(
|
||||
|
@ -4,11 +4,9 @@ package eu.kanade.tachiyomi.util.view
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Rect
|
||||
import android.os.Build
|
||||
import android.view.Gravity
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.RoundedCorner
|
||||
import android.view.View
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
@ -97,22 +95,3 @@ fun View?.isVisibleOnScreen(): Boolean {
|
||||
Rect(0, 0, Resources.getSystem().displayMetrics.widthPixels, Resources.getSystem().displayMetrics.heightPixels)
|
||||
return actualPosition.intersect(screen)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns window radius (in pixel) applied to this view
|
||||
*/
|
||||
fun View.getWindowRadius(): Int {
|
||||
val rad = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
val windowInsets = rootWindowInsets
|
||||
listOfNotNull(
|
||||
windowInsets.getRoundedCorner(RoundedCorner.POSITION_TOP_LEFT),
|
||||
windowInsets.getRoundedCorner(RoundedCorner.POSITION_TOP_RIGHT),
|
||||
windowInsets.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT),
|
||||
windowInsets.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT),
|
||||
)
|
||||
.minOfOrNull { it.radius }
|
||||
} else {
|
||||
null
|
||||
}
|
||||
return rad ?: 0
|
||||
}
|
||||
|
@ -128,14 +128,13 @@ fun OkHttpClient.Builder.dohQuad101() = dns(
|
||||
/*
|
||||
* Mullvad DoH
|
||||
* without ad blocking option
|
||||
* Source : https://mullvad.net/en/help/dns-over-https-and-dns-over-tls/
|
||||
* Source: https://mullvad.net/en/help/dns-over-https-and-dns-over-tls
|
||||
*/
|
||||
fun OkHttpClient.Builder.dohMullvad() = dns(
|
||||
DnsOverHttps.Builder().client(build())
|
||||
.url("https://doh.mullvad.net/dns-query".toHttpUrl())
|
||||
.url(" https://dns.mullvad.net/dns-query".toHttpUrl())
|
||||
.bootstrapDnsHosts(
|
||||
InetAddress.getByName("194.242.2.2"),
|
||||
InetAddress.getByName("193.19.108.2"),
|
||||
InetAddress.getByName("2a07:e340::2"),
|
||||
)
|
||||
.build(),
|
||||
@ -144,7 +143,7 @@ fun OkHttpClient.Builder.dohMullvad() = dns(
|
||||
/*
|
||||
* Control D
|
||||
* unfiltered option
|
||||
* Source : https://controld.com/free-dns/?
|
||||
* Source: https://controld.com/free-dns/?
|
||||
*/
|
||||
fun OkHttpClient.Builder.dohControlD() = dns(
|
||||
DnsOverHttps.Builder().client(build())
|
||||
|
@ -19,7 +19,7 @@ class NetworkPreferences(
|
||||
fun defaultUserAgent(): Preference<String> {
|
||||
return preferenceStore.getString(
|
||||
"default_user_agent",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/118.0",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/121.0",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -63,6 +63,19 @@ fun PUT(
|
||||
.cacheControl(cache)
|
||||
.build()
|
||||
}
|
||||
fun PATCH(
|
||||
url: String,
|
||||
headers: Headers = DEFAULT_HEADERS,
|
||||
body: RequestBody = DEFAULT_BODY,
|
||||
cache: CacheControl = DEFAULT_CACHE_CONTROL,
|
||||
): Request {
|
||||
return Request.Builder()
|
||||
.url(url)
|
||||
.patch(body)
|
||||
.headers(headers)
|
||||
.cacheControl(cache)
|
||||
.build()
|
||||
}
|
||||
|
||||
fun DELETE(
|
||||
url: String,
|
||||
|
@ -15,7 +15,7 @@ import kotlin.coroutines.resume
|
||||
object WebViewUtil {
|
||||
const val SPOOF_PACKAGE_NAME = "org.chromium.chrome"
|
||||
|
||||
const val MINIMUM_WEBVIEW_VERSION = 114
|
||||
const val MINIMUM_WEBVIEW_VERSION = 118
|
||||
|
||||
/**
|
||||
* Uses the WebView's user agent string to create something similar to what Chrome on Android
|
||||
|
@ -1,11 +1,6 @@
|
||||
package tachiyomi.core.storage
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.FileUtils
|
||||
import com.hippo.unifile.UniFile
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.File
|
||||
|
||||
val UniFile.extension: String?
|
||||
get() = name?.substringAfterLast('.')
|
||||
@ -15,27 +10,3 @@ val UniFile.nameWithoutExtension: String?
|
||||
|
||||
val UniFile.displayablePath: String
|
||||
get() = filePath ?: uri.toString()
|
||||
|
||||
fun UniFile.toTempFile(context: Context): File {
|
||||
val inputStream = context.contentResolver.openInputStream(uri)!!
|
||||
val tempFile = File.createTempFile(
|
||||
nameWithoutExtension.orEmpty().padEnd(3), // Prefix must be 3+ chars
|
||||
null,
|
||||
)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
FileUtils.copy(inputStream, tempFile.outputStream())
|
||||
} else {
|
||||
BufferedOutputStream(tempFile.outputStream()).use { tmpOut ->
|
||||
inputStream.use { input ->
|
||||
val buffer = ByteArray(8192)
|
||||
var count: Int
|
||||
while (input.read(buffer).also { count = it } > 0) {
|
||||
tmpOut.write(buffer, 0, count)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tempFile
|
||||
}
|
||||
|
@ -0,0 +1,46 @@
|
||||
package tachiyomi.core.storage
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.FileUtils
|
||||
import com.hippo.unifile.UniFile
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.File
|
||||
|
||||
class UniFileTempFileManager(
|
||||
private val context: Context,
|
||||
) {
|
||||
|
||||
private val dir = File(context.externalCacheDir, "tmp")
|
||||
|
||||
fun createTempFile(file: UniFile): File {
|
||||
dir.mkdirs()
|
||||
|
||||
val inputStream = context.contentResolver.openInputStream(file.uri)!!
|
||||
val tempFile = File.createTempFile(
|
||||
file.nameWithoutExtension.orEmpty().padEnd(3), // Prefix must be 3+ chars
|
||||
null,
|
||||
dir,
|
||||
)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
FileUtils.copy(inputStream, tempFile.outputStream())
|
||||
} else {
|
||||
BufferedOutputStream(tempFile.outputStream()).use { tmpOut ->
|
||||
inputStream.use { input ->
|
||||
val buffer = ByteArray(8192)
|
||||
var count: Int
|
||||
while (input.read(buffer).also { count = it } > 0) {
|
||||
tmpOut.write(buffer, 0, count)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tempFile
|
||||
}
|
||||
|
||||
fun deleteTempFiles() {
|
||||
dir.deleteRecursively()
|
||||
}
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
package tachiyomi.domain.manga.model
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.source.model.UpdateStrategy
|
||||
import tachiyomi.core.preference.TriState
|
||||
import java.io.Serializable
|
||||
import java.time.Instant
|
||||
|
||||
data class Manga(
|
||||
val id: Long,
|
||||
@ -29,6 +31,11 @@ data class Manga(
|
||||
val favoriteModifiedAt: Long?,
|
||||
) : Serializable {
|
||||
|
||||
val expectedNextUpdate: Instant?
|
||||
get() = nextUpdate
|
||||
.takeIf { status != SManga.COMPLETED.toLong() }
|
||||
?.let { Instant.ofEpochMilli(it) }
|
||||
|
||||
val sorting: Long
|
||||
get() = chapterFlags and CHAPTER_SORTING_MASK
|
||||
|
||||
|
@ -2,6 +2,7 @@ package tachiyomi.domain.sync
|
||||
|
||||
import tachiyomi.core.preference.Preference
|
||||
import tachiyomi.core.preference.PreferenceStore
|
||||
import java.util.UUID
|
||||
|
||||
class SyncPreferences(
|
||||
private val preferenceStore: PreferenceStore,
|
||||
@ -43,6 +44,19 @@ class SyncPreferences(
|
||||
"",
|
||||
)
|
||||
|
||||
fun uniqueDeviceID(): String {
|
||||
val uniqueIDPreference = preferenceStore.getString("unique_device_id", "")
|
||||
|
||||
// Retrieve the current value of the preference
|
||||
var uniqueID = uniqueIDPreference.get()
|
||||
if (uniqueID.isBlank()) {
|
||||
uniqueID = UUID.randomUUID().toString()
|
||||
uniqueIDPreference.set(uniqueID)
|
||||
}
|
||||
|
||||
return uniqueID
|
||||
}
|
||||
|
||||
fun syncFlags() = preferenceStore.getInt("sync_flags", Flags.Defaults)
|
||||
|
||||
fun isSyncEnabled(): Boolean {
|
||||
|
@ -1,5 +1,5 @@
|
||||
[versions]
|
||||
agp_version = "8.2.0"
|
||||
agp_version = "8.2.1"
|
||||
lifecycle_version = "2.6.2"
|
||||
paging_version = "3.2.1"
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
[versions]
|
||||
aboutlib_version = "10.10.0"
|
||||
acra = "5.11.3"
|
||||
leakcanary = "2.12"
|
||||
leakcanary = "2.13"
|
||||
moko = "0.23.0"
|
||||
okhttp_version = "5.0.0-alpha.12"
|
||||
richtext = "0.17.0"
|
||||
richtext = "0.20.0"
|
||||
shizuku_version = "12.2.0"
|
||||
sqldelight = "2.0.0"
|
||||
sqlite = "2.4.0"
|
||||
|
@ -557,7 +557,6 @@
|
||||
<string name="library_errors_help">للحصول على المساعده في إصلاح أخطاء تحديث المكتبة، أنظر هنا%1$s</string>
|
||||
<string name="save_chapter_as_cbz">احفظ كأرشيف CBZ</string>
|
||||
<string name="privacy_policy">سياسة الخصوصية</string>
|
||||
<string name="pref_library_update_manga_restriction">تخطي تحديث الإدخالات</string>
|
||||
<string name="pref_update_only_completely_read">فيها فصول لم تُقرأ</string>
|
||||
<string name="ext_installer_legacy">قياسي</string>
|
||||
<string name="pref_auto_clear_chapter_cache">امسح ملفات التخزين المؤقت عند فتح التطبيق</string>
|
||||
@ -566,7 +565,6 @@
|
||||
<string name="extension_api_error">فشل الحصول على قائمة الملحقات</string>
|
||||
<string name="ext_installer_shizuku_unavailable_dialog">ثبِّت «شيزوكو» وشغِّله لتستخدمه مثبِّت إضافات.</string>
|
||||
<string name="download_queue_size_warning">تحذير: يمكن أن تؤدِّي التنزيلات كبيرة الحجم والعدد إلى إبطاء المصادر، وقد يُحظر Tachiyomi منها بسبب ذلك. اضغط لمعرفة المزيد۔</string>
|
||||
<string name="action_faq_and_guides">الاسئله الشائعه والأدلة</string>
|
||||
<string name="action_show_manga">إظهار الدخول</string>
|
||||
<string name="action_display_cover_only_grid">شبكة بالاغلفة</string>
|
||||
<string name="skipped_reason_not_started">تُخُطِّيت بسبب عدم وجود فصول قُرئت</string>
|
||||
@ -575,12 +573,10 @@
|
||||
<string name="skipped_reason_completed">تُخُطِّيت لأنها تمِّت</string>
|
||||
<string name="pref_navigate_pan">حرِّك الصور الواسعة</string>
|
||||
<string name="pref_landscape_zoom">كبِّر الصور العريضة تلقائيًّا</string>
|
||||
<string name="channel_skipped">متجاوَز</string>
|
||||
<string name="error_saving_picture">خطأ أثناء حفظ الصورة</string>
|
||||
<string name="disabled_nav">معطل</string>
|
||||
<string name="rotation_reverse_portrait">رأسي بالعكس</string>
|
||||
<string name="notification_update_error">فشل %1$d تحديث أو تحديثات</string>
|
||||
<string name="notification_update_skipped">تُخُطِّي %1$d تحديث أو تحديثات</string>
|
||||
<string name="learn_more">اضغط لقراءة المزيد</string>
|
||||
<string name="update_check_fdroid_migration_info">نسخة جديدة متاحة من الإصدارات الرسمية. انقر لمعرفة كيفية التحويل من إصدارات F-Droid غير الرسمية.</string>
|
||||
<string name="action_move_to_top_all_for_series">نقل سلسلة الفصول للأعلى</string>
|
||||
@ -718,7 +714,6 @@
|
||||
<string name="action_filter_interval_dropped">ربما أُلغيت؟ ٢٠ التماسًا متأخرًا وشهران</string>
|
||||
<string name="manga_display_interval_title">قدِّر كلَّ</string>
|
||||
<string name="pref_update_only_in_release_period">ليس ضمن مدة الإصدار المتوقعة</string>
|
||||
<string name="manga_modify_calculated_interval_title">خصِّص المدة</string>
|
||||
<string name="skipped_reason_not_in_release_period">تُخُطِّيت بسبب عدم توقع صدور اليوم</string>
|
||||
<string name="manga_display_modified_interval_title">عيِّن التحديث كلَّ</string>
|
||||
<string name="intervals_header">المدة</string>
|
||||
|
@ -80,4 +80,9 @@
|
||||
<item quantity="one">Extension update available</item>
|
||||
<item quantity="other">%d extension updates available</item>
|
||||
</plurals>
|
||||
|
||||
<plurals name="num_repos">
|
||||
<item quantity="one">%d repo</item>
|
||||
<item quantity="other">%d repos</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
@ -164,7 +164,6 @@
|
||||
<string name="action_webview_forward">Forward</string>
|
||||
<string name="action_webview_refresh">Refresh</string>
|
||||
<string name="action_start_downloading_now">Start downloading now</string>
|
||||
<string name="action_faq_and_guides">FAQ and Guides</string>
|
||||
<string name="action_not_now">Not now</string>
|
||||
|
||||
<!-- Operations -->
|
||||
@ -184,6 +183,8 @@
|
||||
<string name="onboarding_storage_info">Select a folder where %1$s will store chapter downloads, backups, and more.\n\nA dedicated folder is recommended.\n\nSelected folder: %2$s</string>
|
||||
<string name="onboarding_storage_action_select">Select a folder</string>
|
||||
<string name="onboarding_storage_selection_required">A folder must be selected</string>
|
||||
<string name="onboarding_storage_help_info">Updating from an older version and not sure what to select? Refer to the storage guide for more information.</string>
|
||||
<string name="onboarding_storage_help_action">Storage guide</string>
|
||||
<string name="onboarding_permission_install_apps">Install apps permission</string>
|
||||
<string name="onboarding_permission_install_apps_description">To install source extensions.</string>
|
||||
<string name="onboarding_permission_notifications">Notification permission</string>
|
||||
@ -192,7 +193,7 @@
|
||||
<string name="onboarding_permission_ignore_battery_opts_description">Avoid interruptions to long-running library updates, downloads, and backup restores.</string>
|
||||
<string name="onboarding_permission_action_grant">Grant</string>
|
||||
<string name="onboarding_guides_new_user">New to %s? We recommend checking out the getting started guide.</string>
|
||||
<string name="onboarding_guides_returning_user">Already used %s before?</string>
|
||||
<string name="onboarding_guides_returning_user">Reinstalling %s?</string>
|
||||
|
||||
<!-- Preferences -->
|
||||
<!-- Subsections -->
|
||||
@ -281,12 +282,12 @@
|
||||
<string name="charging">When charging</string>
|
||||
<string name="restrictions">Restrictions: %s</string>
|
||||
|
||||
<string name="pref_library_update_manga_restriction">Skip updating entries</string>
|
||||
<string name="pref_update_only_completely_read">With unread chapter(s)</string>
|
||||
<string name="pref_update_only_non_completed">With \"Completed\" status</string>
|
||||
<string name="pref_update_only_started">That haven\'t been started</string>
|
||||
<string name="pref_library_update_smart_update">Smart update</string>
|
||||
<string name="pref_update_only_completely_read">Skip entries with unread chapter(s)</string>
|
||||
<string name="pref_update_only_non_completed">Skip entries with \"Completed\" status</string>
|
||||
<string name="pref_update_only_started">Skip unstarted entries</string>
|
||||
<string name="pref_update_only_in_release_period">Predict next release time</string>
|
||||
<string name="pref_library_update_show_tab_badge">Show unread count on Updates icon</string>
|
||||
<string name="pref_update_only_in_release_period">Outside expected release period</string>
|
||||
|
||||
<string name="pref_library_update_refresh_metadata">Automatically refresh metadata</string>
|
||||
<string name="pref_library_update_refresh_metadata_summary">Check for new cover and details when updating library</string>
|
||||
@ -323,7 +324,7 @@
|
||||
<string name="ext_uninstall">Uninstall</string>
|
||||
<string name="ext_app_info">App info</string>
|
||||
<string name="untrusted_extension">Untrusted extension</string>
|
||||
<string name="untrusted_extension_message">This extension was signed with an untrusted certificate and wasn\'t activated.\n\nA malicious extension could read any stored login credentials or execute arbitrary code.\n\nBy trusting this certificate you accept these risks.</string>
|
||||
<string name="untrusted_extension_message">This extension was signed by any unknown author and wasn\'t loaded.\n\nMalicious extensions can read any stored login credentials or execute arbitrary code.\n\nBy trusting this extension\'s certificate, you accept these risks.</string>
|
||||
<string name="obsolete_extension_message">This extension is no longer available. It may not function properly and can cause issues with the app. Uninstalling it is recommended.</string>
|
||||
<string name="unofficial_extension_message">This extension is not from the official list.</string>
|
||||
<string name="extension_api_error">Failed to get extensions list</string>
|
||||
@ -342,6 +343,18 @@
|
||||
<string name="ext_installer_shizuku_stopped">Shizuku is not running</string>
|
||||
<string name="ext_installer_shizuku_unavailable_dialog">Install and start Shizuku to use Shizuku as extension installer.</string>
|
||||
|
||||
<!-- Extension repos -->
|
||||
<string name="label_extension_repos">Extension repos</string>
|
||||
<string name="information_empty_repos">You have no repos set.</string>
|
||||
<string name="action_add_repo">Add repo</string>
|
||||
<string name="label_add_repo_input">Repo URL</string>
|
||||
<string name="action_add_repo_message">Add additional repos to Tachiyomi. This should be a URL that ends with \"index.min.json\".</string>
|
||||
<string name="error_repo_exists">This repo already exists!</string>
|
||||
<string name="action_delete_repo">Delete repo</string>
|
||||
<string name="invalid_repo_name">Invalid repo URL</string>
|
||||
<string name="delete_repo_confirmation">Do you wish to delete the repo \"%s\"?</string>
|
||||
<string name="repo_extension_message">This extension is from an external repo. Tap to view the repo.</string>
|
||||
|
||||
<!-- Reader section -->
|
||||
<string name="pref_fullscreen">Fullscreen</string>
|
||||
<string name="pref_show_navigation_mode">Show tap zones overlay</string>
|
||||
@ -566,6 +579,7 @@
|
||||
<string name="google_drive_login_success">Logged in to Google Drive</string>
|
||||
<string name="google_drive_login_failed">Failed to log in to Google Drive: %s</string>
|
||||
<string name="google_drive_not_signed_in">Not signed in to Google Drive</string>
|
||||
<string name="error_deleting_google_drive_lock_file">Error Deleting Google Drive Lock File</string>
|
||||
<string name="pref_purge_confirmation_title">Purge confirmation</string>
|
||||
<string name="pref_purge_confirmation_message">Purging sync data will delete all your sync data from Google Drive. Are you sure you want to continue?</string>
|
||||
<string name="pref_sync_options">Create sync triggers</string>
|
||||
@ -714,9 +728,10 @@
|
||||
<string name="display_mode_chapter">Chapter %1$s</string>
|
||||
<string name="manga_display_interval_title">Estimate every</string>
|
||||
<string name="manga_display_modified_interval_title">Set to update every</string>
|
||||
<string name="manga_interval_header">Next update</string>
|
||||
<!-- "... around 2 days" -->
|
||||
<string name="manga_interval_expected_update">Next update expected in around %s</string>
|
||||
<string name="manga_modify_calculated_interval_title">Customize interval</string>
|
||||
<string name="manga_interval_expected_update">Next update expected in around %1$s, checking around every %2$s</string>
|
||||
<string name="manga_interval_custom_amount">Custom update frequency:</string>
|
||||
<string name="chapter_downloading_progress">Downloading (%1$d/%2$d)</string>
|
||||
<string name="chapter_error">Error</string>
|
||||
<string name="chapter_paused">Paused</string>
|
||||
|
@ -18,4 +18,10 @@
|
||||
<item quantity="many">%1$d дзён таму</item>
|
||||
<item quantity="other">%1$d дзён таму</item>
|
||||
</plurals>
|
||||
<plurals name="next_unread_chapters">
|
||||
<item quantity="one">Наступная непрачытаная глава</item>
|
||||
<item quantity="few">Наступныя %d непрачытаныя главы</item>
|
||||
<item quantity="many">Наступныя %d непрачытаных глав</item>
|
||||
<item quantity="other">Наступныя %d непрачытаных глав</item>
|
||||
</plurals>
|
||||
</resources>
|
@ -327,12 +327,10 @@
|
||||
<string name="ext_app_info">Аб дадатку</string>
|
||||
<string name="action_sort_count">Колькасць мангі</string>
|
||||
<string name="action_display_language_badge">Мова</string>
|
||||
<string name="action_faq_and_guides">Часта задаваемые пытанні і кіраўніцтва</string>
|
||||
<string name="network_not_metered">Толькі ў безлемітнай сетцы</string>
|
||||
<string name="ext_info_language">Мова</string>
|
||||
<string name="theme_lavender">Лаванда</string>
|
||||
<string name="pref_app_language">Мова дадатку</string>
|
||||
<string name="pref_library_update_manga_restriction">Прапусціць абнаўленні</string>
|
||||
<string name="pref_update_only_completely_read">Ёсць непрачытаныя главы</string>
|
||||
<string name="ext_installer_shizuku_stopped">Shizuku не працуе</string>
|
||||
<string name="action_close">Закрыць</string>
|
||||
|
@ -556,20 +556,17 @@
|
||||
<string name="privacy_policy">Политика за поверителност</string>
|
||||
<string name="pref_update_only_completely_read">С непрочетени глави</string>
|
||||
<string name="ext_update_all">Обнови всички</string>
|
||||
<string name="pref_library_update_manga_restriction">Ограничения за актуализиране на библиотеката</string>
|
||||
<string name="extension_api_error">Неуспешно получаване на списък с разширения</string>
|
||||
<string name="update_72hour">На всеки 3 дена</string>
|
||||
<string name="connected_to_wifi">Само през Wi-Fi</string>
|
||||
<string name="download_queue_size_warning">Предупреждение: големите масови изтегляния могат да доведат до забавяне на източниците и/или блокиране на Tachiyomi. Натиснете тук, за да научите повече.</string>
|
||||
<string name="action_show_manga">Покажи манга</string>
|
||||
<string name="action_faq_and_guides">Често задавани въпроси и ръководства</string>
|
||||
<string name="action_display_cover_only_grid">Решетка само с корици</string>
|
||||
<string name="delete_category_confirmation">Искате ли да изтриете категорията \"%s\"\?</string>
|
||||
<string name="internal_error">InternalError: Провери записите за крашове за повече информация</string>
|
||||
<string name="error_saving_picture">Грешка при запазването на изображението</string>
|
||||
<string name="update_already_running">Обновява се</string>
|
||||
<string name="not_installed">Не е инсталиран</string>
|
||||
<string name="channel_skipped">Пропуснати</string>
|
||||
<string name="rotation_reverse_portrait">Обърнат портрет</string>
|
||||
<string name="ext_info_language">Език</string>
|
||||
<string name="crash_screen_title">Случи се неочаквана грешка</string>
|
||||
@ -607,7 +604,6 @@
|
||||
<string name="action_sort_last_manga_update">Последно обновяване</string>
|
||||
<string name="pref_clear_webview_data">Изчисти данните на WebView</string>
|
||||
<string name="empty_screen">Това е неудобно</string>
|
||||
<string name="notification_update_skipped">%1$d пропуснати обновление/я</string>
|
||||
<string name="cancelled">Отменена</string>
|
||||
<string name="library_errors_help">За помощ при оправянето на грешки с обновленията, виж %1$s</string>
|
||||
<string name="theme_tidalwave">Приливна вълна</string>
|
||||
|
@ -560,7 +560,6 @@
|
||||
<string name="privacy_policy">গোপনীয়তা নীতি</string>
|
||||
<string name="extension_api_error">এক্সটেনশন তালিকা পেতে ব্যার্থ হলো</string>
|
||||
<string name="skipped_reason_completed">এড়িয়ে যাওয়া হয়েছে কারণ সিরিজটি সম্পূর্ণ</string>
|
||||
<string name="notification_update_skipped">%1$d আপডেট(গুলি) এড়িয়ে গেছে</string>
|
||||
<string name="cancelled">বাতিল করা হয়েছে</string>
|
||||
<string name="on_hiatus">বিরতিতে রয়েছে</string>
|
||||
<string name="notification_update_error">%1$d আপডেট(গুলি) ব্যর্থ হয়েছে</string>
|
||||
@ -583,14 +582,11 @@
|
||||
<string name="on_hold_list">স্থগিত তালিকা</string>
|
||||
<string name="auto_download_while_reading">পড়ার সময় সয়ংক্রিয়ভাবে ডাউনলোড</string>
|
||||
<string name="pref_update_only_completely_read">অপঠিত অধ্যায়সহ</string>
|
||||
<string name="pref_library_update_manga_restriction">শিরোনামসমুহ হালনাগাদ এড়িয়ে যান</string>
|
||||
<string name="channel_skipped">এড়িয়ে যাওয়া</string>
|
||||
<string name="pref_update_only_started">যেগুলো শুরু করা হয়নি</string>
|
||||
<string name="webview_data_deleted">ওয়েবভিউ ডাটা মুছা হয়েছে</string>
|
||||
<string name="split_tall_images_summary">পঠন কর্মক্ষমতা উন্নত করে</string>
|
||||
<string name="pref_clear_webview_data">ওয়েবভিইউ ডাটা মুছুন</string>
|
||||
<string name="multi_lang">বহু</string>
|
||||
<string name="action_faq_and_guides">FAQ এবং নির্দেশনা</string>
|
||||
<string name="pref_library_summary">বিভাগসমূহ, সার্বজনীন হালনাগাদ</string>
|
||||
<string name="pref_app_language">অ্যাপ ভাষা</string>
|
||||
<string name="pref_appearance_summary">থিম,সময় ও তারিখের ধরন</string>
|
||||
@ -625,4 +621,27 @@
|
||||
<string name="action_bar_up_description">নেভিগেট আপ</string>
|
||||
<string name="label_data_storage">ডাটা অন স্টোরেজ</string>
|
||||
<string name="action_filter_interval_custom">কাস্টমাইজড আনার ব্যবধান</string>
|
||||
<string name="onboarding_storage_action_select">ফোল্ডার নির্বাচন করুন</string>
|
||||
<string name="pref_onboarding_guide">অনবর্ডিং গাইড</string>
|
||||
<string name="onboarding_action_finish">শুরু করুন</string>
|
||||
<string name="action_apply">এপ্লাই</string>
|
||||
<string name="action_filter_interval_passed">চেক করার সময় পার হয়েছে</string>
|
||||
<string name="onboarding_storage_selection_required">একটি ফোল্ডার অবশ্যই ঠিক করতে হবে</string>
|
||||
<string name="onboarding_permission_notifications">নোটিফিকেশনের পারমিশন</string>
|
||||
<string name="action_revert_to_default">সকল সেটিং ডিফল্ট করুন</string>
|
||||
<string name="onboarding_permission_install_apps">অ্যাপের ইন্সটল করার পারমিশন</string>
|
||||
<string name="action_sort_category">ক্যাটাগরি ফিল্টার করুন</string>
|
||||
<string name="onboarding_heading">স্বাগতম</string>
|
||||
<string name="action_move_to_bottom_all_for_series">নিচের দিকে সরান</string>
|
||||
<string name="onboarding_action_skip">বাদ দিন</string>
|
||||
<string name="onboarding_action_next">পরবর্তী</string>
|
||||
<string name="onboarding_permission_install_apps_description">সোর্স এক্সটেনশন ইনস্টল করার জন্য</string>
|
||||
<string name="onboarding_description">প্রথমে কিছু ঠিক করে নিন , পরবর্তীতে আপনি চাইলে সেটিং থেকে সবকিছু পরিবর্তন করতে পারবেন।</string>
|
||||
<string name="internal_error">ইন্টারনাল ইরর: বাকি ইনফরমেশন পেতে ক্যাশ লগ দেখুন</string>
|
||||
<string name="skipped_reason_not_started">স্কিপ করা হয়েছে কারণ আগে কোন চ্যাপ্টার পড়া হয়নি</string>
|
||||
<string name="action_sort_tracker_score">ট্রেকার স্কোর</string>
|
||||
<string name="sort_category_confirmation">আপনি কি বর্ণানুক্রমিকভাবে ফিল্টার করতে চান</string>
|
||||
<string name="action_ok">ঠিক আছে</string>
|
||||
<string name="action_sort_next_updated">পরবর্তী আপডেটের সম্ভাব্য সময়</string>
|
||||
<string name="action_filter_interval_long">প্রতিমাসে রিফ্রেস করুন (২৮ দিন)</string>
|
||||
</resources>
|
@ -167,7 +167,7 @@
|
||||
<string name="creating_backup">S’està creant la còpia de seguretat</string>
|
||||
<string name="pref_clear_chapter_cache">Buida la memòria cau de capítols</string>
|
||||
<string name="used_cache">Ús: %1$s</string>
|
||||
<string name="cache_deleted">S’ha buidat la memòria cau. S’han suprimit %1$d fitxers</string>
|
||||
<string name="cache_deleted">S’ha buidat la memòria cau i s’han suprimit %1$d fitxers</string>
|
||||
<string name="cache_delete_error">S’ha produït un error en netejar</string>
|
||||
<string name="pref_clear_cookies">Esborra les galetes</string>
|
||||
<string name="cookies_cleared">S’han esborrat les galetes</string>
|
||||
@ -376,7 +376,7 @@
|
||||
<string name="label_data">Dades</string>
|
||||
<string name="backup_restore_missing_sources">Manquen fonts:</string>
|
||||
<string name="invalid_backup_file_missing_manga">La còpia de seguretat no conté cap element de la biblioteca.</string>
|
||||
<string name="invalid_backup_file">Fitxer de còpia de seguretat invàlid</string>
|
||||
<string name="invalid_backup_file">Fitxer de còpia de seguretat invàlid:</string>
|
||||
<string name="pref_library_update_refresh_metadata_summary">Comprova si hi ha noves portades o detalls en actualitzar la biblioteca</string>
|
||||
<string name="pref_library_update_refresh_metadata">Refresca les metadades automàticament</string>
|
||||
<string name="action_display_comfortable_grid">Graella confortable</string>
|
||||
@ -457,9 +457,7 @@
|
||||
<string name="pref_dual_page_split">Divideix les pàgines amples</string>
|
||||
<string name="pref_dual_page_invert_summary">Si la ubicació de les pàgines amples dividides no encaixa amb la direcció de lectura</string>
|
||||
<string name="pref_dual_page_invert">Inverteix la ubicació de la divisió de pàgines</string>
|
||||
<string name="backup_restore_content_full">Es restauraran les dades del fitxer de còpia de seguretat.
|
||||
\n
|
||||
\nCaldrà que instal·leu les extensions que manquin i que després inicieu la sessió als serveis de seguiment per a utilitzar-los.</string>
|
||||
<string name="backup_restore_content_full">Caldrà que instal·leu les extensions que manquin i que després inicieu la sessió als serveis de seguiment per a utilitzar-los.</string>
|
||||
<string name="update_check_eol">Aquesta versió d’Android ja no està suportada</string>
|
||||
<string name="error_no_match">No s’ha trobat cap coincidència</string>
|
||||
<string name="source_unsupported">La font no està suportada</string>
|
||||
@ -553,8 +551,6 @@
|
||||
<string name="cancelled">Cancel·lada</string>
|
||||
<string name="library_errors_help">Per a obtenir ajuda per a resoldre errors d’actualització de la biblioteca, vegeu %1$s</string>
|
||||
<string name="extension_api_error">No s’ha pogut obtenir la llista d’extensions</string>
|
||||
<string name="action_faq_and_guides">PMF i guies</string>
|
||||
<string name="pref_library_update_manga_restriction">Omet les actualitzacions dels elements</string>
|
||||
<string name="pref_update_only_completely_read">Amb capítols no llegits</string>
|
||||
<string name="save_chapter_as_cbz">Desa com a arxiu CBZ</string>
|
||||
<string name="backup_info">També hauríeu de desar còpies de les còpies de seguretat en altres llocs. Les còpies de seguretat poden contenir dades sensibles, incloent-hi les contrasenyes desades; aneu amb compte si les compartiu.</string>
|
||||
@ -576,9 +572,7 @@
|
||||
<string name="skipped_reason_not_started">S’ha omès perquè no hi ha cap capítol llegit</string>
|
||||
<string name="skipped_reason_not_caught_up">S’ha omès perquè hi ha capítols no llegits</string>
|
||||
<string name="learn_more">Premeu per a obtenir més informació</string>
|
||||
<string name="channel_skipped">Omesa</string>
|
||||
<string name="notification_update_error">Han fallat %1$d actualitzacions</string>
|
||||
<string name="notification_update_skipped">S’han omès %1$d actualitzacions</string>
|
||||
<string name="rotation_reverse_portrait">Vertical inversa</string>
|
||||
<string name="action_move_to_top_all_for_series">Mou la sèrie a dalt de tot</string>
|
||||
<string name="disabled_nav">Desactivat</string>
|
||||
@ -721,7 +715,6 @@
|
||||
<string name="intervals_header">Intervals</string>
|
||||
<string name="manga_display_interval_title">Estima cada</string>
|
||||
<string name="manga_display_modified_interval_title">S’actualitzarà cada</string>
|
||||
<string name="manga_modify_calculated_interval_title">Personalitza l’interval</string>
|
||||
<string name="track_delete_title">Voleu deixar de seguir %s\?</string>
|
||||
<string name="track_delete_remote_text">Elimina també de %s</string>
|
||||
<string name="track_delete_text">Se n’eliminarà el seguiment local.</string>
|
||||
@ -757,7 +750,7 @@
|
||||
<string name="last_auto_backup_info">Darrera còpia de seguretat automàtica: %s</string>
|
||||
<string name="no_scanlators_found">No s’ha trobat cap scanlator</string>
|
||||
<string name="scanlator">Scanlator</string>
|
||||
<string name="pref_flash_page">Centelleig blanc en canviar de pàgina</string>
|
||||
<string name="pref_flash_page">Centelleig en canviar de pàgina</string>
|
||||
<string name="pref_storage_usage">Ús de l’emmagatzematge</string>
|
||||
<string name="action_sort_tracker_score">Puntuació del servei de seguiment</string>
|
||||
<string name="label_data_storage">Dades i emmagatzematge</string>
|
||||
@ -784,4 +777,16 @@
|
||||
\nÉs recomanable fer servir una carpeta dedicada.
|
||||
\n
|
||||
\nCarpeta seleccionada: %2$s</string>
|
||||
<string name="onboarding_permission_notifications">Permís de notificacions</string>
|
||||
<string name="onboarding_permission_install_apps">Permís per a instal·lar aplicacions</string>
|
||||
<string name="onboarding_permission_ignore_battery_opts_description">Eviteu interrupcions en actualitzacions de la biblioteca, baixades i restauracions de còpies de seguretat que tinguin una llarga durada.</string>
|
||||
<string name="onboarding_permission_ignore_battery_opts">Ús de la bateria en segon pla</string>
|
||||
<string name="onboarding_permission_install_apps_description">Per a instal·lar extensions de fonts.</string>
|
||||
<string name="onboarding_permission_notifications_description">Rebeu notificacions quan hi hagi actualitzacions de la biblioteca i més.</string>
|
||||
<string name="onboarding_permission_action_grant">Permet</string>
|
||||
<string name="available_disk_space_info">Disponible: %1$s / Total: %2$s</string>
|
||||
<string name="manga_interval_expected_update">La pròxima actualització es farà pels volts de %s</string>
|
||||
<string name="invalid_backup_file_error">Error complet:</string>
|
||||
<string name="ext_permission_install_apps_warning">Calen permisos per a instal·lar extensions. Premeu aquí per a concedir-les.</string>
|
||||
<string name="private_settings">Inclou-hi configuració sensible (per exemple, testimonis d’autenticació dels serveis de seguiment)</string>
|
||||
</resources>
|
@ -144,7 +144,6 @@
|
||||
<string name="update_weekly">Kada semana</string>
|
||||
<string name="charging">Sa dihang nag-charge</string>
|
||||
<string name="restrictions">Mga pagdili: %s</string>
|
||||
<string name="pref_library_update_manga_restriction">Laktawan ang pag-update sa mga titulo</string>
|
||||
<string name="pref_update_only_completely_read">Uban sa wala pa mabasa nga (mga) kapitulo</string>
|
||||
<string name="pref_update_only_started">Wala pa kana nagsugod</string>
|
||||
<string name="pref_library_update_refresh_metadata">Awtomatikong i-refresh ang metadata</string>
|
||||
@ -156,7 +155,6 @@
|
||||
<string name="action_move_to_top">Ibalhin sa ibabaw</string>
|
||||
<string name="action_move_to_top_all_for_series">Ibalhin ang serye sa ibabaw</string>
|
||||
<string name="action_show_errors">I-tap aron makita ang mga detalye</string>
|
||||
<string name="action_faq_and_guides">FAQ ug mga Giya</string>
|
||||
<string name="action_asc">Pagsaka</string>
|
||||
<string name="action_share">Ipaambit</string>
|
||||
<string name="action_open_log">Bukas nga log</string>
|
||||
|
@ -390,8 +390,8 @@
|
||||
<string name="secure_screen_summary">Secure screen skryje obsah aplikace při přepínání aplikací a blokuje tvorbu snímků obrazovky</string>
|
||||
<string name="secure_screen">Chráněná obrazovka</string>
|
||||
<string name="lock_with_biometrics">Vyžadovat odemknutí</string>
|
||||
<string name="theme_dark">Zapnuto</string>
|
||||
<string name="theme_light">Vypnuto</string>
|
||||
<string name="theme_dark">Tmavý</string>
|
||||
<string name="theme_light">Světlý</string>
|
||||
<string name="pref_category_theme">Vzhled</string>
|
||||
<string name="action_move_to_top">Přesunout nahoru</string>
|
||||
<string name="action_desc">Sestupně</string>
|
||||
@ -558,22 +558,18 @@
|
||||
<string name="privacy_policy">Zásady ochrany osobních údajů</string>
|
||||
<string name="clear_database_source_item_count">%1$d neknihovní záznamy v databázi</string>
|
||||
<string name="pref_true_color_summary">Snižuje pruhování barev, ale může mít vliv na výkon</string>
|
||||
<string name="pref_library_update_manga_restriction">Přeskočit aktualizování položek</string>
|
||||
<string name="pref_update_only_completely_read">S nepřečtenými kapitolami</string>
|
||||
<string name="database_clean">Nic k vyčištění</string>
|
||||
<string name="save_chapter_as_cbz">Uložit jako CBZ archiv</string>
|
||||
<string name="library_errors_help">Nápovědu k tomu, jak opravit chyby při aktualizaci knihovny, viz %1$s</string>
|
||||
<string name="action_faq_and_guides">FAQ a Návody</string>
|
||||
<string name="publishing_finished">Zveřejnění dokončeno</string>
|
||||
<string name="cancelled">Zrušeno</string>
|
||||
<string name="action_show_manga">Zobrazit položku</string>
|
||||
<string name="action_display_cover_only_grid">Mřížka jen s přebaly</string>
|
||||
<string name="pref_update_only_started">Které nebyly rozečteny</string>
|
||||
<string name="notification_update_error">%1$d aktualizace(í) selhalo</string>
|
||||
<string name="notification_update_skipped">%1$d aktualizace(í) přeskočena(y)</string>
|
||||
<string name="on_hiatus">Má pauzu</string>
|
||||
<string name="skipped_reason_not_caught_up">Přeskočeno, protože obsahuje nepřečtené kapitoly</string>
|
||||
<string name="channel_skipped">Přeskočeno</string>
|
||||
<string name="action_move_to_top_all_for_series">Přesunout sérii na začátek</string>
|
||||
<string name="learn_more">Klepnutím se dozvíte více</string>
|
||||
<string name="skipped_reason_not_started">Přeskočeno, protože nebyly přečteny žádné kapitoly</string>
|
||||
@ -710,7 +706,6 @@
|
||||
<string name="pref_chapter_swipe_start">Přejetí prstem doleva</string>
|
||||
<string name="pref_double_tap_zoom">Přiblížení dvojitým klepnutím</string>
|
||||
<string name="pref_library_columns_per_row">%d na řádek</string>
|
||||
<string name="manga_modify_calculated_interval_title">Přizpůsobit interval</string>
|
||||
<string name="action_filter_interval_passed">Uplynulé kontrolní období</string>
|
||||
<string name="action_filter_interval_custom">Přizpůsobený interval načítání</string>
|
||||
<string name="action_filter_interval_long">Načíst měsíčně (28 dní)</string>
|
||||
@ -761,4 +756,37 @@
|
||||
<string name="label_data_storage">Data a úložiště</string>
|
||||
<string name="file_null_uri_error">Nevybrán žádný soubor</string>
|
||||
<string name="exclude_scanlators">Vynechat překladatele</string>
|
||||
<string name="onboarding_storage_action_select">Vybrat složku</string>
|
||||
<string name="onboarding_heading">Vítejte!</string>
|
||||
<string name="action_menu_overflow_description">Více možností</string>
|
||||
<string name="onboarding_action_skip">Přeskočit</string>
|
||||
<string name="onboarding_action_next">Další</string>
|
||||
<string name="pref_storage_location">Umístění úložiště</string>
|
||||
<string name="pref_onboarding_guide">Nástupní průvodce</string>
|
||||
<string name="pref_storage_location_info">Používá se pro automatické zálohování, stahování kapitol a lokální zdroj.</string>
|
||||
<string name="onboarding_guides_new_user">Jste v %s noví? Doporučujeme se podívat se na průvodce pro začátečníky.</string>
|
||||
<string name="onboarding_action_finish">Začínáme</string>
|
||||
<string name="onboarding_storage_selection_required">Složka musí být vybrána</string>
|
||||
<string name="onboarding_permission_notifications">Povolení oznámení</string>
|
||||
<string name="onboarding_permission_install_apps">Povolení stahovat aplikace</string>
|
||||
<string name="available_disk_space_info">Dostupné: %1$s / Celkem: %2$s</string>
|
||||
<string name="onboarding_guides_returning_user">Už jste použili %s dříve?</string>
|
||||
<string name="selected">Vybráno</string>
|
||||
<string name="not_selected">Nevybráno</string>
|
||||
<string name="onboarding_permission_ignore_battery_opts_description">Vyhněte se přerušením dlouhých aktualizací knihovny, stahování a obnovení záloh.</string>
|
||||
<string name="onboarding_permission_ignore_battery_opts">Využití baterie na pozadí</string>
|
||||
<string name="onboarding_permission_install_apps_description">K nainstalování rozšíření zdrojů.</string>
|
||||
<string name="action_bar_up_description">Navigovat nahoru</string>
|
||||
<string name="onboarding_description">Pojďme nastavit nějaké věci. Vždy můžete tyto změny později změnit v nastavení.</string>
|
||||
<string name="action_sort_tracker_score">Skóre sledovače</string>
|
||||
<string name="no_location_set">Není nastaveno umístění úložiště</string>
|
||||
<string name="onboarding_permission_notifications_description">Buďte upozorněni na aktualizaci knihovny atd.</string>
|
||||
<string name="ext_permission_install_apps_warning">K instalaci rozšíření je nutné povolení. Klepněte zde pro udělení povolení.</string>
|
||||
<string name="private_settings">Zahrnout citlivé nastavení (např. přihlašovací tokeny sledovačů)</string>
|
||||
<string name="onboarding_permission_action_grant">Udělit</string>
|
||||
<string name="onboarding_storage_info">Vyberte složku, kde %1$s bude ukládat stahování kapitol, zálohy a další.
|
||||
\n
|
||||
\nJe doporučena vyhrazená složka.
|
||||
\n
|
||||
\nVybraná složka: %2$s</string>
|
||||
</resources>
|
@ -490,7 +490,6 @@
|
||||
<string name="action_display_cover_only_grid">Йатсӑр сетке</string>
|
||||
<string name="action_move_to_top_all_for_series">Серилӗхе пуҫламӑша куҫар</string>
|
||||
<string name="confirm_lock_change">Улшӑнӑва ҫирӗплетме есӗлӗхе ҫирӗплет</string>
|
||||
<string name="action_faq_and_guides">Кӑтартусем тата ыйту-хурав</string>
|
||||
<string name="action_show_manga">Ҫырава кӑтарт</string>
|
||||
<string name="action_display_language_badge">Чӗлхе</string>
|
||||
<string name="action_search_hint">Шыра…</string>
|
||||
@ -524,7 +523,6 @@
|
||||
<string name="pref_browse_summary">Ҫӑл куҫсем, хушмасем, пӗтӗмӗшле шырав</string>
|
||||
<string name="theme_greenapple">Симӗс пан улми</string>
|
||||
<string name="theme_yotsuba">Йутсупа</string>
|
||||
<string name="pref_library_update_manga_restriction">Серилӗхсене ҫӗнетнине сиктер</string>
|
||||
<string name="network_not_metered">Чараксӑр тетел урлӑ ҫеҫ</string>
|
||||
<string name="pref_update_only_started">Серилӗхе пуҫламан</string>
|
||||
<string name="theme_tako">Такку</string>
|
||||
|
@ -112,7 +112,6 @@
|
||||
<string name="action_webview_back">Tilbage</string>
|
||||
<string name="action_webview_refresh">Genindlæs</string>
|
||||
<string name="action_start_downloading_now">Start download nu</string>
|
||||
<string name="action_faq_and_guides">FAQ og guides</string>
|
||||
<string name="loading">Loader…</string>
|
||||
<string name="app_not_available">App ikke tilgængelig</string>
|
||||
<string name="pref_category_general">Generelt</string>
|
||||
@ -170,7 +169,6 @@
|
||||
<string name="update_weekly">Ugentligt</string>
|
||||
<string name="charging">Kun under opladning</string>
|
||||
<string name="restrictions">Begrænsninger: %s</string>
|
||||
<string name="pref_library_update_manga_restriction">Spring over opdatering af titler</string>
|
||||
<string name="pref_update_only_completely_read">Med ulæst kapitler</string>
|
||||
<string name="pref_update_only_non_completed">Med \"Færdig\" status</string>
|
||||
<string name="pref_update_only_started">Der ikke er blevet startet</string>
|
||||
@ -275,7 +273,6 @@
|
||||
<string name="pref_always_show_chapter_transition">Vis altid kapitelovergang</string>
|
||||
<string name="action_create">Opret</string>
|
||||
<string name="pref_restore_backup">Gendan sikkerhedskopi</string>
|
||||
<string name="channel_skipped">Sprunget over</string>
|
||||
<string name="used_cache">Brugt: %1$s</string>
|
||||
<string name="pref_lowest">Laveste</string>
|
||||
<string name="rotation_type">Rotation</string>
|
||||
|
@ -140,7 +140,7 @@
|
||||
<string name="creating_backup">Datensicherung wird erstellt</string>
|
||||
<string name="pref_clear_chapter_cache">Kapitel-Zwischenspeicher leeren</string>
|
||||
<string name="used_cache">Belegt: %1$s</string>
|
||||
<string name="cache_deleted">Zwischenspeicher geleert. %1$d Dateien wurden gelöscht</string>
|
||||
<string name="cache_deleted">Zwischenspeicher geleert, %1$d Dateien gelöscht</string>
|
||||
<string name="cache_delete_error">Fehler während dem Leeren aufgetreten</string>
|
||||
<string name="pref_clear_cookies">Cookies löschen</string>
|
||||
<string name="cookies_cleared">Cookies gelöscht</string>
|
||||
@ -560,13 +560,11 @@
|
||||
<string name="extension_api_error">Herunterladen der Erweiterungsliste ist fehlgeschlagen</string>
|
||||
<string name="privacy_policy">Datenschutzbestimmungen</string>
|
||||
<string name="pref_update_only_completely_read">Mit ungelesenen Kapiteln</string>
|
||||
<string name="pref_library_update_manga_restriction">Aktualisierung der Einträge überspringen</string>
|
||||
<string name="library_errors_help">Für Hilfe zum Beheben von Fehlern bei Bibliotheksaktualisierungen, siehe %1$s</string>
|
||||
<string name="save_chapter_as_cbz">Als CBZ-Archiv speichern</string>
|
||||
<string name="cancelled">Abgebrochen</string>
|
||||
<string name="on_hiatus">Unterbrochen</string>
|
||||
<string name="publishing_finished">Veröffentlichung abgeschlossen</string>
|
||||
<string name="action_faq_and_guides">Häufige Fragen und Leitfäden</string>
|
||||
<string name="action_show_manga">Eintrag anzeigen</string>
|
||||
<string name="action_display_cover_only_grid">Nur-Cover-Kacheln</string>
|
||||
<string name="pref_landscape_zoom">Bei breiten Bildern automatisch hineinzoomen</string>
|
||||
@ -576,8 +574,6 @@
|
||||
<string name="skipped_reason_not_caught_up">Übersprungen, weil es ungelesene Kapitel gibt</string>
|
||||
<string name="skipped_reason_not_started">Übersprungen, weil keine Kapitel gelesen wurden</string>
|
||||
<string name="notification_update_error">%1$d Aktualisierung(en) fehlgeschlagen</string>
|
||||
<string name="notification_update_skipped">%1$d Aktualisierung(en) übersprungen</string>
|
||||
<string name="channel_skipped">Übersprungen</string>
|
||||
<string name="learn_more">Antippen, um mehr zu erfahren</string>
|
||||
<string name="rotation_reverse_portrait">Umgekehrtes Hochformat</string>
|
||||
<string name="action_move_to_top_all_for_series">Serie nach oben verschieben</string>
|
||||
@ -716,7 +712,6 @@
|
||||
<string name="action_filter_interval_long">Monatlich abrufen (28 Tage)</string>
|
||||
<string name="action_sort_next_updated">Nächste erwartete Aktualisierung</string>
|
||||
<string name="pref_update_only_in_release_period">Außerhalb des erwarteten Veröffentlichungszeitraums</string>
|
||||
<string name="manga_modify_calculated_interval_title">Intervall anpassen</string>
|
||||
<string name="skipped_reason_not_in_release_period">Übersprungen, da heute keine Veröffentlichung erwartet wurde</string>
|
||||
<string name="manga_display_interval_title">Schätzt alle</string>
|
||||
<string name="manga_display_modified_interval_title">Aktualisiert alle</string>
|
||||
@ -752,7 +747,7 @@
|
||||
<string name="relative_time_span_never">Nie</string>
|
||||
<string name="pref_flash_page_summ">Reduziert Ghosting auf E-Papier-Displays</string>
|
||||
<string name="last_auto_backup_info">Zuletzt automatisch gesichert: %s</string>
|
||||
<string name="pref_flash_page">Bei Umblättern weiß aufleuchten</string>
|
||||
<string name="pref_flash_page">Bei Umblättern aufleuchten</string>
|
||||
<string name="label_data_storage">Daten und Speicher</string>
|
||||
<string name="action_create">Erstellen</string>
|
||||
<string name="action_apply">Anwenden</string>
|
||||
@ -784,4 +779,14 @@
|
||||
\nEin dedizierter Ordner wird empfohlen.
|
||||
\n
|
||||
\nAusgewählter Ordner: %2$s</string>
|
||||
<string name="onboarding_permission_notifications">Berechtigung für Benachrichtigungen</string>
|
||||
<string name="onboarding_permission_install_apps">Berechtigung zum Installieren von Apps</string>
|
||||
<string name="available_disk_space_info">Verfügbar: %1$s / Insgesamt: %2$s</string>
|
||||
<string name="onboarding_permission_ignore_battery_opts_description">Verhindere Unterbrechungen bei lang anhaltenden Bibliotheksaktualisierungen, Downloads und Sicherungswiederherstellungen.</string>
|
||||
<string name="onboarding_permission_ignore_battery_opts">Akkunutzung im Hintergrund</string>
|
||||
<string name="onboarding_permission_install_apps_description">Zum Installieren von Quellenerweiterungen.</string>
|
||||
<string name="onboarding_permission_notifications_description">Erhalte Benachrichtigungen für Bibliotheksaktualisierungen und mehr.</string>
|
||||
<string name="ext_permission_install_apps_warning">Für das Installieren von Erweiterungen sind Berechtigungen erforderlich. Tippe hier, um sie zu gewähren.</string>
|
||||
<string name="private_settings">Sensible Einstellungen einbeziehen (z. B. Login-Token für Tracker)</string>
|
||||
<string name="onboarding_permission_action_grant">Zulassen</string>
|
||||
</resources>
|
@ -167,7 +167,7 @@
|
||||
<string name="creating_backup">Δημιουργία αντιγράφων ασφαλείας</string>
|
||||
<string name="pref_clear_chapter_cache">Καθάρισμα προσωρινής μνήμης κεφαλαίου</string>
|
||||
<string name="used_cache">Χρησιμοποιήθηκε: %1$s</string>
|
||||
<string name="cache_deleted">Η κρυφή μνήμη διαγράφηκε. %1$d αρχεία έχουν διαγραφεί</string>
|
||||
<string name="cache_deleted">Η προσωρινή μνήμη διαγράφηκε, %1$d αρχεία διαγράφηκαν</string>
|
||||
<string name="cache_delete_error">Παρουσιάστηκε σφάλμα κατά την εκκαθάριση</string>
|
||||
<string name="pref_clear_cookies">Διαγραφή cookies</string>
|
||||
<string name="cookies_cleared">Τα cookies διαγράφηκαν</string>
|
||||
@ -559,14 +559,12 @@
|
||||
<string name="database_clean">Τίποτα προς εκκαθάριση</string>
|
||||
<string name="extension_api_error">Απέτυχε η λήψη λίστας επεκτάσεων</string>
|
||||
<string name="privacy_policy">Πολιτική απορρήτου</string>
|
||||
<string name="pref_library_update_manga_restriction">Παράλειψη ενημέρωσης καταχωρήσεων</string>
|
||||
<string name="pref_update_only_completely_read">Με αδιάβαστο(α) κεφάλαιο(α)</string>
|
||||
<string name="library_errors_help">Για βοήθεια σχετικά με τον τρόπο διόρθωσης σφαλμάτων ενημέρωσης βιβλιοθήκης, ανατρέξτε στο %1$s</string>
|
||||
<string name="save_chapter_as_cbz">Αποθήκευση ως αρχείο CBZ</string>
|
||||
<string name="cancelled">Ακυρώθηκε</string>
|
||||
<string name="on_hiatus">Σε αναστολή</string>
|
||||
<string name="publishing_finished">Η έκδοση ολοκληρώθηκε</string>
|
||||
<string name="action_faq_and_guides">Συχνές ερωτήσεις και οδηγοί</string>
|
||||
<string name="action_show_manga">Εμφάνιση καταχώρισης</string>
|
||||
<string name="pref_landscape_zoom">Αυτόματο ζουμ σε ευρείες εικόνες</string>
|
||||
<string name="action_display_cover_only_grid">Πλέγμα μόνο με εξώφυλλα</string>
|
||||
@ -577,8 +575,6 @@
|
||||
<string name="skipped_reason_not_started">Παραλείπεται επειδή δεν έχουν διαβαστεί κεφάλαια</string>
|
||||
<string name="notification_update_error">%1$d ενημέρωση(ες) απέτυχε(-αν)</string>
|
||||
<string name="learn_more">Πατήστε για να μάθετε περισσότερα</string>
|
||||
<string name="channel_skipped">Παραλήφθηκαν</string>
|
||||
<string name="notification_update_skipped">%1$d ενημέρωση(εις) παραλείφθηκε(-αν)</string>
|
||||
<string name="rotation_reverse_portrait">Αντίστροφο πορτρέτο</string>
|
||||
<string name="action_move_to_top_all_for_series">Μετακίνηση σειράς προς τα πάνω</string>
|
||||
<string name="disabled_nav">Απενεργοποιημένο</string>
|
||||
@ -721,7 +717,6 @@
|
||||
<string name="manga_display_modified_interval_title">Ρύθμιση για ενημέρωση κάθε</string>
|
||||
<string name="action_filter_interval_long">Ανάκτηση μηνιαίως (28 ημέρες)</string>
|
||||
<string name="action_sort_next_updated">Επόμενη αναμενόμενη ενημέρωση</string>
|
||||
<string name="manga_modify_calculated_interval_title">Προσαρμογή διαστήματος</string>
|
||||
<string name="skipped_reason_not_in_release_period">Παραλείφθηκε επειδή δεν αναμενόταν κυκλοφορία σήμερα</string>
|
||||
<string name="action_ok">Εντάξει</string>
|
||||
<string name="track_delete_title">Κατάργηση παρακολούθησης %s;</string>
|
||||
@ -784,4 +779,14 @@
|
||||
\nΣυνιστάται ένας αποκλειστικός φάκελος.
|
||||
\n
|
||||
\nΕπιλεγμένος φάκελος: %2$s</string>
|
||||
<string name="onboarding_permission_notifications">Άδεια ειδοποιήσεων</string>
|
||||
<string name="onboarding_permission_install_apps">Άδεια εγκατάστασης εφαρμογών</string>
|
||||
<string name="onboarding_permission_ignore_battery_opts_description">Αποφύγετε διακοπές σε μακροχρόνιες ενημερώσεις της βιβλιοθήκης, λήψεις και επαναφορές αντιγράφων ασφαλείας.</string>
|
||||
<string name="onboarding_permission_ignore_battery_opts">Χρήση μπαταρίας στο παρασκήνιο</string>
|
||||
<string name="onboarding_permission_install_apps_description">Για εγκατάσταση επεκτάσεων πηγών.</string>
|
||||
<string name="onboarding_permission_notifications_description">Ειδοποιηθείτε για ενημερώσεις της βιβλιοθήκης και άλλων.</string>
|
||||
<string name="onboarding_permission_action_grant">Παραχώρηση</string>
|
||||
<string name="available_disk_space_info">Διαθέσιμο: %1$s / Σύνολο: %2$s</string>
|
||||
<string name="ext_permission_install_apps_warning">Απαιτούνται δικαιώματα για την εγκατάσταση επεκτάσεων. Πατήστε εδώ για παραχώρηση.</string>
|
||||
<string name="private_settings">Συμπεριλάβετε ευαίσθητες ρυθμίσεις (π.χ. διακριτικά σύνδεσης παρακολούθησης)</string>
|
||||
</resources>
|
@ -80,4 +80,9 @@
|
||||
<item quantity="many">%d días</item>
|
||||
<item quantity="other">%d días</item>
|
||||
</plurals>
|
||||
<plurals name="num_repos">
|
||||
<item quantity="one">%d repositorio</item>
|
||||
<item quantity="many">%d repositorios</item>
|
||||
<item quantity="other">%d repositorios</item>
|
||||
</plurals>
|
||||
</resources>
|
@ -38,7 +38,7 @@
|
||||
<string name="action_install">Instalar</string>
|
||||
<string name="loading">Cargando…</string>
|
||||
<string name="pref_category_general">General</string>
|
||||
<string name="pref_category_reader">Lector</string>
|
||||
<string name="pref_category_reader">Visor</string>
|
||||
<string name="pref_category_downloads">Descargas</string>
|
||||
<string name="pref_category_tracking">Seguimiento</string>
|
||||
<string name="pref_category_advanced">Avanzado</string>
|
||||
@ -54,7 +54,7 @@
|
||||
<string name="update_48hour">Cada 2 días</string>
|
||||
<string name="pref_library_update_restriction">Restricciones de actualización automática del dispositivo</string>
|
||||
<string name="charging">Mientras se carga la batería</string>
|
||||
<string name="pref_update_only_non_completed">Marcadas como completadas</string>
|
||||
<string name="pref_update_only_non_completed">Omitir entradas marcadas como \"Completado\"</string>
|
||||
<string name="pref_auto_update_manga_sync">Actualizar progreso al terminar un capítulo</string>
|
||||
<string name="pref_fullscreen">Pantalla completa</string>
|
||||
<string name="pref_page_transitions">Transiciones de página animadas</string>
|
||||
@ -67,8 +67,8 @@
|
||||
<string name="white_background">Blanco</string>
|
||||
<string name="black_background">Negro</string>
|
||||
<string name="pref_viewer_type">Sentido de lectura normal</string>
|
||||
<string name="left_to_right_viewer">Por páginas (de izquierda a derecha)</string>
|
||||
<string name="right_to_left_viewer">Por páginas (de derecha a izquierda)</string>
|
||||
<string name="left_to_right_viewer">Por páginas, de izquierda a derecha</string>
|
||||
<string name="right_to_left_viewer">Por páginas, de derecha a izquierda</string>
|
||||
<string name="vertical_viewer">Por páginas, de arriba abajo</string>
|
||||
<string name="webtoon_viewer">Tira vertical continua</string>
|
||||
<string name="pref_image_scale_type">Método de ampliación</string>
|
||||
@ -92,7 +92,7 @@
|
||||
<string name="services">Servicios de seguimiento</string>
|
||||
<string name="pref_clear_chapter_cache">Limpiar la caché de capítulos</string>
|
||||
<string name="used_cache">Usado: %1$s</string>
|
||||
<string name="cache_deleted">Se vació la caché. Se han eliminado %1$d archivos</string>
|
||||
<string name="cache_deleted">Se vació la caché. Se han borrado %1$d archivos</string>
|
||||
<string name="cache_delete_error">Se produjo un error al limpiar</string>
|
||||
<string name="pref_clear_cookies">Borrar cookies</string>
|
||||
<string name="cookies_cleared">Cookies borradas</string>
|
||||
@ -252,11 +252,11 @@
|
||||
<string name="transition_no_previous">No hay ningún capítulo anterior</string>
|
||||
<string name="migrate">Migrar</string>
|
||||
<string name="copy">Copiar</string>
|
||||
<string name="untrusted_extension_message">Esta extensión está firmada por una fuente sin certificar y por lo tanto no se ha activado.
|
||||
<string name="untrusted_extension_message">Esta extensión fue firmada por un autor desconocido y no fue cargada.
|
||||
\n
|
||||
\nUna extensión maliciosa puede leer credenciales de inicio guardadas o ejecutar cualquier tipo de código en tu dispositivo.
|
||||
\nLas extensiones maliciosas pueden leer cualquier credencial de inicio de sesión almacenada o ejecutar código arbitrario.
|
||||
\n
|
||||
\nAl confiar en este certificado aceptas estos riesgos.</string>
|
||||
\nAl confiar en el certificado de esta extensión, aceptas estos riesgos.</string>
|
||||
<string name="double_tap_anim_speed_0">Sin animación</string>
|
||||
<string name="manga_added_library">Añadido a biblioteca</string>
|
||||
<string name="manga_removed_library">Quitado de biblioteca</string>
|
||||
@ -302,12 +302,12 @@
|
||||
<string name="action_sort_latest_chapter">Por capítulo más reciente</string>
|
||||
<string name="action_view_chapters">Ver capítulos</string>
|
||||
<string name="action_cancel_all">Cancelar todo</string>
|
||||
<string name="theme_light">Claro</string>
|
||||
<string name="theme_dark">Oscuro</string>
|
||||
<string name="theme_system">Sistema</string>
|
||||
<string name="theme_light">Colores claros</string>
|
||||
<string name="theme_dark">Colores oscuros</string>
|
||||
<string name="theme_system">Colores del sistema</string>
|
||||
<string name="pref_manage_notifications">Gestionar notificaciones</string>
|
||||
<string name="pref_category_security">Seguridad y privacidad</string>
|
||||
<string name="lock_with_biometrics">Requiere desbloqueo</string>
|
||||
<string name="lock_with_biometrics">Desbloqueo biométrico</string>
|
||||
<string name="lock_when_idle">Bloquear por inactividad</string>
|
||||
<string name="lock_always">Siempre</string>
|
||||
<string name="lock_never">Nunca</string>
|
||||
@ -317,7 +317,7 @@
|
||||
<string name="information_cloudflare_bypass_failure">Fallo al evitar Cloudflare</string>
|
||||
<string name="information_webview_outdated">Actualiza la aplicación WebView para mejorar la compatibilidad</string>
|
||||
<string name="channel_new_chapters">Nuevos capítulos</string>
|
||||
<string name="secure_screen_summary">Pantalla segura oculta el contenido al cambiar de aplicación y bloquea las capturas de pantalla</string>
|
||||
<string name="secure_screen_summary">Activando «pantalla segura» ocultas la vista previa del contenido de la ventana al cambiar de aplicación y se bloquean las capturas de pantalla</string>
|
||||
<string name="pref_category_display">Visualización</string>
|
||||
<string name="notification_chapters_single">Capítulo %1$s</string>
|
||||
<string name="notification_chapters_single_and_more">Capítulo %1$s y %2$d más</string>
|
||||
@ -376,7 +376,7 @@
|
||||
<string name="label_data">Datos</string>
|
||||
<string name="backup_restore_missing_sources">Fuentes que faltan:</string>
|
||||
<string name="invalid_backup_file_missing_manga">La copia de seguridad no contiene ningun elemento; la biblioteca está vacía.</string>
|
||||
<string name="invalid_backup_file">El archivo de copia de respaldo no es correcto</string>
|
||||
<string name="invalid_backup_file">El archivo de copia de seguridad es inválido:</string>
|
||||
<string name="pref_library_update_refresh_metadata_summary">Comprueba si hay una nueva portada, información y descripción al actualizar la biblioteca</string>
|
||||
<string name="pref_library_update_refresh_metadata">Actualizar automáticamente los metadatos</string>
|
||||
<string name="action_display_comfortable_grid">Cuadrícula amplia</string>
|
||||
@ -386,7 +386,7 @@
|
||||
<string name="page_list_empty_error">No se encontraron páginas</string>
|
||||
<string name="action_disable_all">Deshabilitar todo</string>
|
||||
<string name="action_enable_all">Habilitar todo</string>
|
||||
<string name="pref_show_reading_mode_summary">Mostrar brevemente el modo actual al abrir el lector</string>
|
||||
<string name="pref_show_reading_mode_summary">Mostrar brevemente el modo actual al abrir el visor</string>
|
||||
<string name="pref_show_reading_mode">Mostrar modo de lectura</string>
|
||||
<string name="action_start">Empezar</string>
|
||||
<string name="loader_not_implemented_error">No se ha encontrado la fuente</string>
|
||||
@ -453,9 +453,7 @@
|
||||
<string name="action_order_by_upload_date">Por fecha de subida</string>
|
||||
<string name="action_filter_tracked">En seguimiento</string>
|
||||
<string name="right_and_left_nav">Derecha e izquierda</string>
|
||||
<string name="backup_restore_content_full">Se restaurarán los datos del archivo de respaldo.
|
||||
\n
|
||||
\nTendrás que instalar las extensiones que falten e iniciar sesión en los servicios de seguimiento para poder usarlos.</string>
|
||||
<string name="backup_restore_content_full">Tendrás que instalar las extensiones que falten e iniciar sesión en los servicios de seguimiento para poder usarlos.</string>
|
||||
<string name="update_check_eol">Esta versión de Android ya no es compatible</string>
|
||||
<string name="clipboard_copy_error">No se pudo copiar al portapapeles</string>
|
||||
<string name="pref_dns_over_https">DNS sobre HTTPS (DoH)</string>
|
||||
@ -470,7 +468,7 @@
|
||||
<string name="pref_dual_page_invert_summary">Si la partición de la página ancha no coincide con la dirección de lectura</string>
|
||||
<string name="pref_dual_page_invert">Dividir la página partida en la dirección contraria</string>
|
||||
<string name="pref_dual_page_split">Partir en dos las páginas anchas</string>
|
||||
<string name="pref_show_navigation_mode_summary">Muestra una vista previa de las zonas al abrir el lector</string>
|
||||
<string name="pref_show_navigation_mode_summary">Muestra una vista previa de las zonas al abrir el visor</string>
|
||||
<string name="pref_show_navigation_mode">Ver superposición con zonas de toque</string>
|
||||
<string name="exclude">Excluir: %s</string>
|
||||
<string name="include">Incluir: %s</string>
|
||||
@ -497,7 +495,7 @@
|
||||
<string name="pref_app_theme">Tema de la aplicación</string>
|
||||
<string name="action_start_downloading_now">Empezar a descargar ahora</string>
|
||||
<string name="cancel_all_for_series">Cancelar todo para esta serie</string>
|
||||
<string name="action_display_local_badge">Fuente local</string>
|
||||
<string name="action_display_local_badge">Contenido local</string>
|
||||
<string name="about_dont_kill_my_app">Algunos fabricantes tienen restricciones adicionales para aplicaciones que detienen los servicios en segundo plano. Esta página web tiene más información sobre cómo solucionarlo.</string>
|
||||
<string name="information_empty_category_dialog">Todavía no existen categorías.</string>
|
||||
<string name="error_no_match">No se han encontrado coincidencias</string>
|
||||
@ -559,26 +557,22 @@
|
||||
<string name="clear_database_source_item_count">%1$d entradas que no pertenecen a la biblioteca en la base de datos</string>
|
||||
<string name="extension_api_error">No se pudo descargar el listado de extensiones</string>
|
||||
<string name="privacy_policy">Política de privacidad</string>
|
||||
<string name="pref_update_only_completely_read">Con capítulos sin leer</string>
|
||||
<string name="pref_library_update_manga_restriction">Omitir actualizaciones</string>
|
||||
<string name="pref_update_only_completely_read">Omitir entradas con capítulos no leídos</string>
|
||||
<string name="library_errors_help">Si necesitas ayuda para resolver los errores de actualización de la biblioteca mira en %1$s</string>
|
||||
<string name="save_chapter_as_cbz">Guardar como archivo CBZ</string>
|
||||
<string name="on_hiatus">En pausa</string>
|
||||
<string name="publishing_finished">Serie terminada</string>
|
||||
<string name="cancelled">Cancelada</string>
|
||||
<string name="action_faq_and_guides">Guías y soluciones</string>
|
||||
<string name="action_show_manga">Mostrar el elemento</string>
|
||||
<string name="pref_navigate_pan">Desplazarse por el resto de la página antes de cambiar</string>
|
||||
<string name="action_display_cover_only_grid">Cuadrícula sólo de portadas</string>
|
||||
<string name="pref_landscape_zoom">Acercar la vista en horizontal</string>
|
||||
<string name="pref_update_only_started">Sin empezar</string>
|
||||
<string name="pref_update_only_started">Omitir entradas sin lectura iniciada</string>
|
||||
<string name="skipped_reason_completed">Omitido, ya que su publicación ha terminado</string>
|
||||
<string name="skipped_reason_not_caught_up">Omitido porque hay capítulos sin leer</string>
|
||||
<string name="skipped_reason_not_started">Omitido porque no hay capítulos leídos</string>
|
||||
<string name="learn_more">Ver más detalles</string>
|
||||
<string name="channel_skipped">Omitidas</string>
|
||||
<string name="notification_update_error">Actualizaciones fallidas: %1$d</string>
|
||||
<string name="notification_update_skipped">Actualizaciones omitidas: %1$d</string>
|
||||
<string name="rotation_reverse_portrait">En vertical, al revés</string>
|
||||
<string name="action_move_to_top_all_for_series">Mover al primer puesto</string>
|
||||
<string name="disabled_nav">Desactivado</string>
|
||||
@ -593,12 +587,12 @@
|
||||
<string name="source_empty_screen">No se ha encontrado ninguna fuente</string>
|
||||
<string name="action_sort_last_manga_update">Última comprobación de actualizaciones</string>
|
||||
<string name="action_sort_unread_count">Capítulos restantes</string>
|
||||
<string name="split_tall_images_summary">Mejora el rendimiento del lector dividiendo páginas descargadas mucho más altas que anchas</string>
|
||||
<string name="split_tall_images_summary">Mejora el rendimiento del visor, dividiendo páginas descargadas mucho más altas que anchas</string>
|
||||
<string name="download_notifier_split_page_not_found">No se ha encontrado la página %d al dividir</string>
|
||||
<string name="download_notifier_split_page_path_not_found">La ruta al archivo de la página %d no se encuentra</string>
|
||||
<string name="pref_reset_viewer_flags_summary">Restablece el modo de lectura y orientación en toda la biblioteca</string>
|
||||
<string name="pref_reset_viewer_flags_success">Se han restablecido los ajustes del visor</string>
|
||||
<string name="pref_reset_viewer_flags">Restablecer los ajustes del lector en cada serie</string>
|
||||
<string name="pref_reset_viewer_flags">Restablecer los ajustes del visor en cada serie</string>
|
||||
<string name="pref_reset_viewer_flags_error">No se pudieron restablecer los ajustes del visor</string>
|
||||
<string name="empty_screen">Houston, tenemos un problema</string>
|
||||
<string name="ext_info_version">Versión</string>
|
||||
@ -712,14 +706,13 @@
|
||||
<string name="pref_library_columns_per_row">%d por fila</string>
|
||||
<string name="action_filter_interval_late">Tras 10 o más días</string>
|
||||
<string name="action_sort_next_updated">Próxima actualización prevista</string>
|
||||
<string name="pref_update_only_in_release_period">Fuera del período de publicación esperado</string>
|
||||
<string name="pref_update_only_in_release_period">Prever la fecha del próximo lanzamiento</string>
|
||||
<string name="intervals_header">Intervalos</string>
|
||||
<string name="action_filter_interval_passed">Ha pasado el período de comprobación</string>
|
||||
<string name="manga_display_interval_title">Estimar cada</string>
|
||||
<string name="manga_modify_calculated_interval_title">Personalizar intervalo</string>
|
||||
<string name="action_filter_interval_dropped">¿Abandonado\? Tras más de 20 días y 2 meses</string>
|
||||
<string name="manga_display_modified_interval_title">Forzar actualización cada</string>
|
||||
<string name="skipped_reason_not_in_release_period">No se ha comprobado ninguna actualización hoy al no esperar ningún cambio</string>
|
||||
<string name="skipped_reason_not_in_release_period">No se ha comprobado ninguna actualización hoy al no prever ninguna</string>
|
||||
<string name="action_set_interval">Establecer intervalo</string>
|
||||
<string name="action_filter_interval_custom">Intervalo de descarga personalizado</string>
|
||||
<string name="action_filter_interval_long">Comprobar de forma mensual (28 días)</string>
|
||||
@ -759,38 +752,54 @@
|
||||
<string name="action_apply">Aplicar</string>
|
||||
<string name="action_revert_to_default">Volver a la configuración predeterminada</string>
|
||||
<string name="action_create">Crear</string>
|
||||
<string name="no_scanlators_found">Sin escanducciones</string>
|
||||
<string name="scanlator">Escanductor</string>
|
||||
<string name="exclude_scanlators">Excluir escanducciones</string>
|
||||
<string name="no_scanlators_found">No se ha encontrado ningún equipo de traducción</string>
|
||||
<string name="scanlator">Equipo de traducción</string>
|
||||
<string name="exclude_scanlators">Excluir equipo de traducción</string>
|
||||
<string name="selected">Seleccionado</string>
|
||||
<string name="not_selected">Sin seleccionar</string>
|
||||
<string name="action_menu_overflow_description">Más opciones</string>
|
||||
<string name="action_bar_up_description">Subir un nivel</string>
|
||||
<string name="pref_storage_location">Ubicación del almacenamiento</string>
|
||||
<string name="pref_storage_location_info">Se utiliza para las copias de seguridad automáticas, las descargas de capítulos y la fuente local.</string>
|
||||
<string name="onboarding_storage_action_select">Seleccionar una carpeta</string>
|
||||
<string name="pref_onboarding_guide">Guía de incorporación</string>
|
||||
<string name="onboarding_guides_new_user">¿Nuevo en %s? Recomendamos consultar la guía de introducción.</string>
|
||||
<string name="pref_storage_location_info">Se utiliza para las copias de seguridad automáticas, poder descargar capítulos y abrir los que ya tengas en tu dispositivo.</string>
|
||||
<string name="onboarding_storage_action_select">Elige una carpeta</string>
|
||||
<string name="pref_onboarding_guide">Guía para principiantes</string>
|
||||
<string name="onboarding_guides_new_user">¿Es la primera vez que instalas %s? Te recomendamos leer la guía de introducción.</string>
|
||||
<string name="onboarding_action_finish">Comenzar</string>
|
||||
<string name="onboarding_storage_selection_required">Debe seleccionarse una carpeta</string>
|
||||
<string name="onboarding_heading">Bienvenido!</string>
|
||||
<string name="onboarding_guides_returning_user">¿Ya usaste %s antes?</string>
|
||||
<string name="onboarding_storage_selection_required">Tienes que elegir una carpeta</string>
|
||||
<string name="onboarding_heading">Te damos la bienvenida</string>
|
||||
<string name="onboarding_guides_returning_user">¿No es la primera vez que instalas %s?</string>
|
||||
<string name="onboarding_action_skip">Saltar</string>
|
||||
<string name="onboarding_action_next">Siguiente</string>
|
||||
<string name="onboarding_description">Vamos a configurar algunas cosas primero. Siempre puedes volver a cambiarlas más tarde en la configuración.</string>
|
||||
<string name="no_location_set">No se ha establecido una ubicación de almacenamiento</string>
|
||||
<string name="onboarding_storage_info">Selecciona una carpeta donde %1$s almacenará las descargas de capítulos, copias de seguridad y más.
|
||||
<string name="onboarding_description">Lo primero de todo es poner las cosas a tu gusto. Siempre puedes volver a cambiarlas más tarde en los ajustes.</string>
|
||||
<string name="no_location_set">Todavía no has proporcionado una ubicación de almacenamiento</string>
|
||||
<string name="onboarding_storage_info">Selecciona una carpeta donde %1$s almacenará las descargas de capítulos, copias de seguridad y otras cosas.
|
||||
\n
|
||||
\nSe recomienda una carpeta dedicada.
|
||||
\nTe recomendamos que sea solo para %1$s.
|
||||
\n
|
||||
\nCarpeta seleccionada: %2$s</string>
|
||||
<string name="onboarding_permission_install_apps">Permiso para instalar aplicaciones</string>
|
||||
<string name="onboarding_permission_type_optional">Opcional</string>
|
||||
<string name="onboarding_permission_type_required">Requerido</string>
|
||||
<string name="onboarding_permission_notifications">Permiso de notificación</string>
|
||||
<string name="onboarding_permission_ignore_battery_opts_description">Evitar interrupciones en las actualizaciones largas de la biblioteca, descargas y restauraciones de copias de seguridad.</string>
|
||||
<string name="onboarding_permission_ignore_battery_opts_description">Previene cortes y retrasos al procesar tareas en segundo plano que tarden un poco; como al buscar y descargar contenido nuevo, así como al restaurar copias de respaldo.</string>
|
||||
<string name="onboarding_permission_ignore_battery_opts">Uso de batería en segundo plano</string>
|
||||
<string name="onboarding_permission_install_apps_description">Para instalar las extensiones de fuentes.</string>
|
||||
<string name="onboarding_permission_notifications_description">Recibe notificaciones sobre actualizaciones de la biblioteca y más.</string>
|
||||
<string name="onboarding_permission_action_grant">Permitir</string>
|
||||
<string name="onboarding_permission_install_apps_description">Para instalar extensiones que te permiten buscar y descargar contenido.</string>
|
||||
<string name="onboarding_permission_notifications_description">Recibe notificaciones cuando aparece contenido nuevo en tu biblioteca y otras cosas.</string>
|
||||
<string name="onboarding_permission_action_grant">Conceder</string>
|
||||
<string name="available_disk_space_info">Disponible: %1$s / Total: %2$s</string>
|
||||
<string name="ext_permission_install_apps_warning">Toca aquí para conceder los permisos necesarios para instalar extensiones.</string>
|
||||
<string name="private_settings">Incluir datos privados, como las claves de inicio de sesión en plataformas de seguimiento</string>
|
||||
<string name="invalid_backup_file_error">Error completo:</string>
|
||||
<string name="manga_interval_expected_update">Próxima actualización prevista en torno a %1$s, revisando cada %2$s</string>
|
||||
<string name="repo_extension_message">Esta extensión proviene de un repositorio externo. Toca para ver el repositorio.</string>
|
||||
<string name="manga_interval_custom_amount">Frecuencia de actualización personalizada:</string>
|
||||
<string name="error_repo_exists">¡Este repositorio ya existe!</string>
|
||||
<string name="pref_library_update_smart_update">Actualización inteligente</string>
|
||||
<string name="invalid_repo_name">URL de repositorio inválida</string>
|
||||
<string name="action_add_repo_message">Agregar repositorios adicionales a Tachiyomi. La URL debe terminar con \"index.min.json\".</string>
|
||||
<string name="delete_repo_confirmation">¿Deseas eliminar el repositorio \"%s\"?</string>
|
||||
<string name="action_delete_repo">Eliminar repositorio</string>
|
||||
<string name="manga_interval_header">Próxima actualización</string>
|
||||
<string name="action_add_repo">Agregar repositorio</string>
|
||||
<string name="information_empty_repos">No tienes repositorios agregados.</string>
|
||||
<string name="label_extension_repos">Repositorios de extensiones</string>
|
||||
<string name="label_add_repo_input">URL del repositorio</string>
|
||||
</resources>
|
@ -357,7 +357,6 @@
|
||||
<string name="snack_add_to_library">Manga liburutegira gehitu\?</string>
|
||||
<string name="file_select_backup">Hautatu babeskopia fitxategia</string>
|
||||
<string name="notification_cover_update_failed">Ezin izan da azala eguneratu</string>
|
||||
<string name="pref_library_update_manga_restriction">Saltatu eguneraketak</string>
|
||||
<string name="action_sort_date_added">Gehiketa data</string>
|
||||
<string name="action_search">Bilatu</string>
|
||||
<string name="action_search_settings">Bilatu ezarpenak</string>
|
||||
@ -566,7 +565,6 @@
|
||||
<string name="pref_dual_page_invert">Alderantzikatu orrialde bikoitzeko zatiketa</string>
|
||||
<string name="pref_dual_page_invert_summary">Orrialde bikoitzeko zatiketaren kokapena irakurketa noranzkoarekin bat ez badator</string>
|
||||
<string name="pref_library_update_restriction">Eguneraketa automatikoentzako gailuaren murrizketak</string>
|
||||
<string name="action_faq_and_guides">Ohiko galderak eta gidak</string>
|
||||
<string name="action_show_manga">Manga erakutsi</string>
|
||||
<string name="skipped_reason_not_caught_up">Saltatu egin da irakurri gabeko kapituluak daudelako</string>
|
||||
<string name="pref_update_only_started">Kapitulurik irakurri gabe</string>
|
||||
@ -590,4 +588,21 @@
|
||||
<string name="action_not_now">Orain ez</string>
|
||||
<string name="delete_downloaded">Ezabatu deskargatuak</string>
|
||||
<string name="pref_library_columns_per_row">%d ilara bakoitzeko</string>
|
||||
<string name="onboarding_storage_action_select">Aukeratu karpeta bat</string>
|
||||
<string name="action_apply">Ezarri</string>
|
||||
<string name="onboarding_storage_selection_required">Karpeta bat aukeratu behar da</string>
|
||||
<string name="onboarding_permission_notifications">Notifikazioetarako baimena</string>
|
||||
<string name="onboarding_permission_install_apps">Aplikazioak instalatzeko baimena</string>
|
||||
<string name="action_sort_category">Kategoriak ordenatu</string>
|
||||
<string name="onboarding_heading">Ongi etorri!</string>
|
||||
<string name="action_update_category">Kategoria eguneratu</string>
|
||||
<string name="action_menu_overflow_description">Aukera gehiago</string>
|
||||
<string name="onboarding_guides_returning_user">%s erabili duzu dagoeneko?</string>
|
||||
<string name="selected">Aukeratuak</string>
|
||||
<string name="not_selected">Aukeratu gabeak</string>
|
||||
<string name="onboarding_action_next">Hurrengoa</string>
|
||||
<string name="action_bar_up_description">Nabigatu gora</string>
|
||||
<string name="sort_category_confirmation">Kategoriak alfabetikoki ordenatu nahi?</string>
|
||||
<string name="action_display_show_continue_reading_button">Jarraitu irakurtzen botoia</string>
|
||||
<string name="action_ok">OK</string>
|
||||
</resources>
|
@ -465,7 +465,6 @@
|
||||
<string name="pref_navigate_pan">هنگام ضربه زدن تصاویر را عریض کنید</string>
|
||||
<string name="pref_landscape_zoom">بزرگنمایی تصویر چشم انداز</string>
|
||||
<string name="update_check_fdroid_migration_info">نسخه جدیدی از نسخههای رسمی موجود است. برای یادگیری نحوه انتقال از نسخههای غیررسمی اف دروید، ضربه بزنید.</string>
|
||||
<string name="channel_skipped">رد شده</string>
|
||||
<string name="action_sort_last_manga_update">بررسی آخرین به روزرسانی</string>
|
||||
<string name="action_sort_unread_count">تعداد نخواندهها</string>
|
||||
<string name="skipped_reason_completed">به دلیل کاملبودن سری، رد شد</string>
|
||||
@ -478,7 +477,6 @@
|
||||
<string name="action_order_by_chapter_number">بر اساس شمارهی قسمت</string>
|
||||
<string name="action_asc">صعودی</string>
|
||||
<string name="action_display_language_badge">زبان</string>
|
||||
<string name="action_faq_and_guides">پرسش و پاسخهای متداول و راهنماییها</string>
|
||||
<string name="action_show_manga">نمایش ورودی</string>
|
||||
<string name="cancel_all_for_series">لغو همه برای این سری</string>
|
||||
<string name="pref_dark_theme_pure_black">حالت تاریک مشکی خالص</string>
|
||||
@ -498,7 +496,6 @@
|
||||
<string name="pref_update_only_started">که شروع نشدهاند</string>
|
||||
<string name="restrictions">محدودیتها: %s</string>
|
||||
<string name="pref_update_only_completely_read">با قسمت(های) خواندهنشده</string>
|
||||
<string name="pref_library_update_manga_restriction">از بهروزرسانی ورودیها صرفنظر کنید</string>
|
||||
<string name="relative_time_today">امروز</string>
|
||||
<string name="update_72hour">هر 3 روز</string>
|
||||
<string name="connected_to_wifi">فقط روی Wi-Fi</string>
|
||||
@ -615,7 +612,6 @@
|
||||
<string name="download_queue_size_warning">هشدار: حجم زیاد بارگیری ممکن است باعث اهسته تر شدن سرعت ویا مسدود کردن Tachiyomi از منبع شود. برای اطلاعات بیشتر لمس کنید.</string>
|
||||
<string name="skipped_reason_not_always_update">به دلیل این که این مجموعه نیازی به به روز رسانی نداشت رد شد</string>
|
||||
<string name="skipped_reason_not_caught_up">به دلیل وجود چپتر های خوانده نشده رد شد</string>
|
||||
<string name="notification_update_skipped">%1$d بزور رسانی رد شد</string>
|
||||
<string name="skipped_reason_not_started">به دلیل این که هیچ چپتری خوانده نشده بود رد شد</string>
|
||||
<string name="notification_update_error">%1$d بروز رسانی ناموفق</string>
|
||||
<string name="pref_relative_format_summary">\"%1$s\" به جای \"%2$s\"</string>
|
||||
|
@ -520,8 +520,6 @@
|
||||
<string name="label_warning">Varoitus</string>
|
||||
<string name="confirm_lock_change">Tunnistaudu vahvistaaksesi muutokset</string>
|
||||
<string name="action_display_language_badge">Kieli</string>
|
||||
<string name="action_faq_and_guides">Usein kysytyt kysymykset ja oppaat</string>
|
||||
<string name="pref_library_update_manga_restriction">Ohita päivitys</string>
|
||||
<string name="action_move_to_top_all_for_series">Siirrä sarja päällimmäiseksi</string>
|
||||
<string name="theme_tako">Tako</string>
|
||||
<string name="update_72hour">Joka 3. Päivä</string>
|
||||
@ -610,14 +608,12 @@
|
||||
<string name="pref_verbose_logging">Yksityiskohtainen kirjaaminen</string>
|
||||
<string name="cover_saved">Kansikuva tallennettu</string>
|
||||
<string name="error_saving_cover">Virhe tallentaessa kansikuvaa</string>
|
||||
<string name="notification_update_skipped">%1$d päivitys(tä) ohitettu</string>
|
||||
<string name="loader_rar5_error">RARv5-muoto ei ole tuettu</string>
|
||||
<string name="skipped_reason_not_caught_up">Ohitettu, koska sarjassa on luettomia lukuja</string>
|
||||
<string name="skipped_reason_not_always_update">Ohitettu, koska sarja ei vaadi päivityksiä</string>
|
||||
<string name="remove_manga">Olet poistamassa \"%s\" kirjastostasi</string>
|
||||
<string name="update_check_fdroid_migration_info">Uusi versio on saatavilla viralliselta alustalta. Napauta saadaksesi tietää, miten siirtyä pois epävirallisesta F-Droid versiosta.</string>
|
||||
<string name="pref_verbose_logging_summary">Kirjoittaa yksityiskohtaiset lokit järjestelmälokiin (heikentää sovelluksen suorituskykyä)</string>
|
||||
<string name="channel_skipped">Ohitettu</string>
|
||||
<string name="backup_info">Varmuuskopioita kannattaa säilyttää myös muissa paikoissa.</string>
|
||||
<string name="wish_list">Toivelista</string>
|
||||
<string name="cant_open_last_read_chapter">Viimeksi luettua lukua ei voitu avata</string>
|
||||
|
@ -65,7 +65,7 @@
|
||||
<string name="action_sort_alpha">Pa-alpabeto</string>
|
||||
<string name="action_filter_empty">Tanggalin ang pansala</string>
|
||||
<string name="action_filter_unread">Hindi Nabasa</string>
|
||||
<string name="action_filter_bookmarked">Tinandaan</string>
|
||||
<string name="action_filter_bookmarked">Na-bookmark</string>
|
||||
<string name="action_filter">Pansala</string>
|
||||
<string name="action_menu">Menu</string>
|
||||
<string name="action_settings">Mga setting</string>
|
||||
@ -152,7 +152,7 @@
|
||||
<string name="black_background">Itim</string>
|
||||
<string name="gray_background">Abo</string>
|
||||
<string name="white_background">Puti</string>
|
||||
<string name="pref_reader_theme">Likurang kulay</string>
|
||||
<string name="pref_reader_theme">Kulay ng background</string>
|
||||
<string name="pref_read_with_long_tap">Ipakita ang mga aksyon sa mahabang tap</string>
|
||||
<string name="pref_read_with_volume_keys_inverted">Baligtarin ang pindutan ng volume</string>
|
||||
<string name="pref_read_with_volume_keys">Pindutan ng volume</string>
|
||||
@ -412,7 +412,7 @@
|
||||
<string name="channel_errors">Mga error</string>
|
||||
<string name="channel_progress">Takbo</string>
|
||||
<string name="backup_restore_missing_trackers">Di naka-login sa mga tracker:</string>
|
||||
<string name="pref_remove_bookmarked_chapters">Burahin din ang may pananda</string>
|
||||
<string name="pref_remove_bookmarked_chapters">Payagan ang pagtanggal ng mga naka-bookmark na kabanata</string>
|
||||
<string name="pref_category_delete_chapters">Magbura ng kabanata</string>
|
||||
<string name="parental_controls_info">Hindi nito naiiwasan ang pagpapakita ng mga NSFW (18+) sa mga di-opisyal o posibleng maling naka-flag na extension sa loob ng app.</string>
|
||||
<string name="ext_nsfw_warning">Posibleng may NSFW (18+) content ang mga source mula sa extension na ito</string>
|
||||
@ -451,7 +451,7 @@
|
||||
<string name="action_asc">Pataas</string>
|
||||
<string name="action_order_by_chapter_number">Kabanata</string>
|
||||
<string name="action_order_by_upload_date">Petsa in-upload</string>
|
||||
<string name="action_filter_tracked">Sinundan</string>
|
||||
<string name="action_filter_tracked">Sinusubaybayan</string>
|
||||
<string name="right_and_left_nav">Kaliwa at Kanan</string>
|
||||
<string name="pref_dual_page_split">Hatiin ang mga malalapad na pahina</string>
|
||||
<string name="action_display_show_number_of_items">Ipakita ang bilang ng mga item</string>
|
||||
@ -561,12 +561,10 @@
|
||||
<string name="privacy_policy">Patakaran sa Pagkapribado</string>
|
||||
<string name="library_errors_help">Para sa tulong sa pag-aayos ng mga error sa pag-update ng aklatan, tingnan ang %1$s</string>
|
||||
<string name="pref_update_only_completely_read">May di pa nababasang kabanata</string>
|
||||
<string name="pref_library_update_manga_restriction">Laktawan ang pag-update</string>
|
||||
<string name="save_chapter_as_cbz">I-save bilang CBZ archive</string>
|
||||
<string name="publishing_finished">Tapos na\'ng mailathala</string>
|
||||
<string name="on_hiatus">Naka-hiatus</string>
|
||||
<string name="cancelled">Kinansela</string>
|
||||
<string name="action_faq_and_guides">Mga Madalas Itanong (FAQ) at Gabay</string>
|
||||
<string name="action_show_manga">Ipakita ang entry</string>
|
||||
<string name="action_display_cover_only_grid">Pabalat lang</string>
|
||||
<string name="skipped_reason_completed">Nilaktawan dahil kumpleto na ang serye</string>
|
||||
@ -576,10 +574,8 @@
|
||||
<string name="pref_landscape_zoom">Awtomatikong mag-zoom sa mga malalawak na larawan</string>
|
||||
<string name="pref_navigate_pan">I-pan ang mga malalapad na larawan</string>
|
||||
<string name="learn_more">Matuto pa</string>
|
||||
<string name="channel_skipped">Nilaktawan</string>
|
||||
<string name="notification_update_error">Nabigo ang %1$d (na) update</string>
|
||||
<string name="rotation_reverse_portrait">Baligtad na patayo</string>
|
||||
<string name="notification_update_skipped">Nilaktawan ang %1$d (na) update</string>
|
||||
<string name="action_move_to_top_all_for_series">Ilagay sa taas ang serye</string>
|
||||
<string name="disabled_nav">Nakasara</string>
|
||||
<string name="empty_backup_error">Walang entry sa Aklatan na maiba-backup</string>
|
||||
@ -721,7 +717,6 @@
|
||||
<string name="manga_display_modified_interval_title">Itakdang i-update bawat</string>
|
||||
<string name="pref_update_only_in_release_period">Sa labas ng inaasahang release period</string>
|
||||
<string name="intervals_header">Mga pagitan</string>
|
||||
<string name="manga_modify_calculated_interval_title">Ayusin ang interval</string>
|
||||
<string name="skipped_reason_not_in_release_period">Nilaktawan dahil walang inaasahang release ngayong araw</string>
|
||||
<string name="has_results">May mga resulta</string>
|
||||
<string name="delete_downloaded">Tanggalin ang na-download</string>
|
||||
@ -784,4 +779,16 @@
|
||||
\nInirerekomenda ang isang nakalaang folder.
|
||||
\n
|
||||
\nNapiling folder: %2$s</string>
|
||||
<string name="onboarding_permission_notifications">Pahintulot sa pag-abiso</string>
|
||||
<string name="onboarding_permission_install_apps">Pahintulot sa pag-install ng mga app</string>
|
||||
<string name="onboarding_permission_ignore_battery_opts_description">Ma-iwasan ang mga hadlang sa mahahabang pag-update ng aklatan, pag-download, at pag-restore ng mga backup.</string>
|
||||
<string name="onboarding_permission_ignore_battery_opts">Paggamit ng baterya sa background</string>
|
||||
<string name="onboarding_permission_install_apps_description">Para mag-install ng mga extension ng source.</string>
|
||||
<string name="onboarding_permission_notifications_description">Maabisuhan para sa mga update sa aklatan at higit pa.</string>
|
||||
<string name="onboarding_permission_action_grant">Payagan</string>
|
||||
<string name="available_disk_space_info">Na magagamit: %1$s / Kabuuan: %2$s</string>
|
||||
<string name="manga_interval_expected_update">Inaasahan ang susunod na update sa humigit-kumulang %s</string>
|
||||
<string name="invalid_backup_file_error">Buong error:</string>
|
||||
<string name="ext_permission_install_apps_warning">Kinakailangan ng permiso para mag-install ng mga extension. I-tap upang mapayagan ito.</string>
|
||||
<string name="private_settings">Kasali ang mga sensitibong setting (hal., mga tracker login token)</string>
|
||||
</resources>
|
@ -114,7 +114,7 @@
|
||||
<string name="pref_download_new">Télécharger les nouveaux chapitres</string>
|
||||
<string name="services">Services</string>
|
||||
<string name="used_cache">Utilisé : %1$s</string>
|
||||
<string name="cache_deleted">Cache effacé. %1$d fichiers ont été supprimés</string>
|
||||
<string name="cache_deleted">Cache effacé, %1$d fichiers supprimés</string>
|
||||
<string name="cache_delete_error">Une erreur est survenue lors de l\'effacement du cache</string>
|
||||
<string name="pref_clear_cookies">Effacer les cookies</string>
|
||||
<string name="cookies_cleared">Cookies effacés</string>
|
||||
@ -301,9 +301,9 @@
|
||||
<string name="action_sort_latest_chapter">Dernier chapitre</string>
|
||||
<string name="action_view_chapters">Voir les chapitres</string>
|
||||
<string name="action_cancel_all">Tout annuler</string>
|
||||
<string name="theme_light">Désactivé</string>
|
||||
<string name="theme_dark">Activé</string>
|
||||
<string name="theme_system">Par défaut du système</string>
|
||||
<string name="theme_light">Clair</string>
|
||||
<string name="theme_dark">Sombre</string>
|
||||
<string name="theme_system">Système</string>
|
||||
<string name="pref_manage_notifications">Notifications</string>
|
||||
<string name="pref_category_security">Sécurité et confidentialité</string>
|
||||
<string name="lock_with_biometrics">Nécessite un déverrouillage</string>
|
||||
@ -457,9 +457,7 @@
|
||||
<string name="pref_dual_page_split">Diviser les pages larges</string>
|
||||
<string name="pref_dual_page_invert_summary">Si l\'emplacement des pages larges divisées ne correspond pas au sens de lecture</string>
|
||||
<string name="pref_dual_page_invert">Inverser le placement des pages divisées</string>
|
||||
<string name="backup_restore_content_full">Les données du fichier de sauvegarde seront restaurées.
|
||||
\n
|
||||
\nVous devrez installer les extensions manquantes et vous connecter ensuite aux services de suivi pour les utiliser.</string>
|
||||
<string name="backup_restore_content_full">Vous devrez installer les extensions manquantes et vous connecter ensuite aux services de suivi pour les utiliser.</string>
|
||||
<string name="nav_zone_prev">Précédent</string>
|
||||
<string name="pref_dns_over_https">DNS over HTTPS (DoH)</string>
|
||||
<string name="nav_zone_right">Droite</string>
|
||||
@ -559,14 +557,12 @@
|
||||
<string name="database_clean">Rien à effacer</string>
|
||||
<string name="extension_api_error">Échec de la récupération de la liste des extensions</string>
|
||||
<string name="privacy_policy">Politique de confidentialité</string>
|
||||
<string name="pref_library_update_manga_restriction">Ignorer la mise à jour</string>
|
||||
<string name="pref_update_only_completely_read">Avec des chapitres non lus</string>
|
||||
<string name="save_chapter_as_cbz">Enregistrer comme archive CBZ</string>
|
||||
<string name="library_errors_help">Pour savoir comment corriger les erreurs de mise à jour de la bibliothèque, voir %1$s</string>
|
||||
<string name="on_hiatus">En pause</string>
|
||||
<string name="publishing_finished">Publication terminée</string>
|
||||
<string name="cancelled">Annulé</string>
|
||||
<string name="action_faq_and_guides">FAQ et guides</string>
|
||||
<string name="action_show_manga">Afficher le titre</string>
|
||||
<string name="action_display_cover_only_grid">Grille avec seulement la couverture</string>
|
||||
<string name="pref_navigate_pan">Panoramique des images larges</string>
|
||||
@ -576,9 +572,7 @@
|
||||
<string name="skipped_reason_not_started">Sauté car aucun chapitre n\'est lu</string>
|
||||
<string name="skipped_reason_not_caught_up">Sauté car il y a des chapitres non lus</string>
|
||||
<string name="learn_more">Appuyez pour en savoir plus</string>
|
||||
<string name="notification_update_skipped">%1$d mise(s) à jour ignorée(s)</string>
|
||||
<string name="notification_update_error">Échec de %1$d mise(s) à jour</string>
|
||||
<string name="channel_skipped">Ignoré</string>
|
||||
<string name="rotation_reverse_portrait">Portrait inversé</string>
|
||||
<string name="action_move_to_top_all_for_series">Déplacer la série vers le haut</string>
|
||||
<string name="disabled_nav">Désactivé</string>
|
||||
@ -732,7 +726,6 @@
|
||||
<string name="manga_display_interval_title">Estimer chaque</string>
|
||||
<string name="exception_http">HTTP %d, consulter le site Web dans WebView</string>
|
||||
<string name="manga_display_modified_interval_title">Configurer pour mettre à jour tous les</string>
|
||||
<string name="manga_modify_calculated_interval_title">Personnaliser l\'intervalle</string>
|
||||
<string name="action_move_to_bottom_all_for_series">Déplacer la série vers le bas</string>
|
||||
<string name="track_delete_remote_text">Supprimez également de %s</string>
|
||||
<string name="exception_unknown_host">Impossible de joindre %s</string>
|
||||
@ -745,11 +738,24 @@
|
||||
<string name="selected">Sélectionné</string>
|
||||
<string name="not_selected">Pas sélectionné(e)</string>
|
||||
<string name="scanlator">Scanlator</string>
|
||||
<string name="pref_flash_page">Faire l\'écran clignoter sur le changement de page</string>
|
||||
<string name="pref_flash_page">Flash lors du changement de page</string>
|
||||
<string name="action_bar_up_description">Naviguer vers le haut</string>
|
||||
<string name="action_sort_tracker_score">Score du service de suivi</string>
|
||||
<string name="label_data_storage">Données et stockage</string>
|
||||
<string name="sort_category_confirmation">Voulez-vous trier les catégories par ordre alphabétique ?</string>
|
||||
<string name="track_activity_name">Se connecter au service de suivi</string>
|
||||
<string name="pref_relative_format_summary">« %1$s » au lieu de « %2$s »</string>
|
||||
<string name="pref_storage_location">Emplacement de stockage</string>
|
||||
<string name="pref_flash_page_summ">Réduire le ghosting sur les écrans e-ink</string>
|
||||
<string name="pref_storage_location_info">Utilisé pour les backups automatiques, téléchargements des chapitres et la source locale.</string>
|
||||
<string name="onboarding_storage_selection_required">Un dossier doit être sélectionné</string>
|
||||
<string name="available_disk_space_info">Disponible : %1$s / Total : %2$s</string>
|
||||
<string name="onboarding_heading">Bienvenue !</string>
|
||||
<string name="onboarding_action_skip">Passer</string>
|
||||
<string name="pref_storage_usage">Utilisation du stockage</string>
|
||||
<string name="onboarding_action_next">Suivant</string>
|
||||
<string name="no_location_set">Aucun emplacement de stockage défini</string>
|
||||
<string name="ext_permission_install_apps_warning">Des permissions sont nécessaires pour installer des extensions. Appuyer ici pour les accorder.</string>
|
||||
<string name="onboarding_permission_action_grant">Accorder</string>
|
||||
<string name="pref_relative_format">Durées relatives</string>
|
||||
</resources>
|
@ -418,7 +418,6 @@
|
||||
<string name="tracking_guide">Guía de seguemento</string>
|
||||
<string name="on_hiatus">En pausa</string>
|
||||
<string name="action_order_by_chapter_number">Por número de capítulo</string>
|
||||
<string name="action_faq_and_guides">Preguntas frecuentes e guías</string>
|
||||
<string name="theme_tako">Tako</string>
|
||||
<string name="theme_yotsuba">Yotsuba</string>
|
||||
<string name="theme_monet">Dinámico</string>
|
||||
@ -482,7 +481,6 @@
|
||||
<string name="cancel_all_for_series">Cancelar todo para esta serie</string>
|
||||
<string name="action_order_by_upload_date">Por data de subida</string>
|
||||
<string name="action_show_errors">Preme para ver os detalles do erro</string>
|
||||
<string name="pref_library_update_manga_restriction">Omitir actualizacións</string>
|
||||
<string name="restrictions">Restricións: %s</string>
|
||||
<string name="categorized_display_settings">Opcións de ordenación e visualización por categoría</string>
|
||||
<string name="pref_dual_page_split">Separar as páxinas anchas</string>
|
||||
@ -629,7 +627,6 @@
|
||||
<string name="show_chapter_number">Número do capítulo</string>
|
||||
<string name="download_notifier_split_page_path_not_found">Non se atopou a ruta do ficheiro da páxina %d</string>
|
||||
<string name="channel_complete">Rematada</string>
|
||||
<string name="channel_skipped">Omitidas</string>
|
||||
<string name="channel_new_chapters">Actualizacións de capítulos</string>
|
||||
<string name="information_webview_required">Tachiyomi necesita WebView</string>
|
||||
<string name="download_notifier_split_page_not_found">Non se atopou a páxina %d ao separar</string>
|
||||
@ -686,7 +683,6 @@
|
||||
<string name="seconds_short">%dseg</string>
|
||||
<string name="label_tracked_titles">Elementos en seguimento</string>
|
||||
<string name="label_mean_score">Puntuación media</string>
|
||||
<string name="notification_update_skipped">Actualizacións omitidas: %1$d</string>
|
||||
<string name="skipped_reason_not_started">Omitiuse porque non hai capítulos lidos</string>
|
||||
<string name="notification_chapters_single_and_more">Capítulo %1$s e %2$d máis</string>
|
||||
<string name="notification_chapters_multiple">Capítulos %1$s</string>
|
||||
|
@ -379,9 +379,7 @@
|
||||
<string name="pref_show_reading_mode">הראה מצב קריאה</string>
|
||||
<string name="ext_unofficial">בלתי רשמי</string>
|
||||
<string name="ext_app_info">פרטי האפליקצייה</string>
|
||||
<string name="pref_library_update_manga_restriction">דלג על עדכוני פריטים</string>
|
||||
<string name="extension_api_error">נכשל בקבלת רשימת ההרחבות</string>
|
||||
<string name="action_faq_and_guides">שו\"ת ומדריכים</string>
|
||||
<string name="ext_nsfw_short">18+</string>
|
||||
<string name="none">אין</string>
|
||||
<string name="update_72hour">כל 3 ימים</string>
|
||||
@ -527,7 +525,6 @@
|
||||
<string name="action_sort_unread_count">לא נקראו</string>
|
||||
<string name="categorized_display_settings">הגדרות מיון לכל קטגוריה בנפרד</string>
|
||||
<string name="source_empty_screen">מקור לא נמצא</string>
|
||||
<string name="notification_update_skipped">%1$d עדכונים דולגו</string>
|
||||
<string name="update_check_eol">גרסת האנדרואיד הזאת כבר לא נתמכת</string>
|
||||
<string name="information_webview_required">WebView נצרך ל-Tachiyomi</string>
|
||||
<string name="action_display_cover_only_grid">רשת רק של הכריכות</string>
|
||||
@ -539,7 +536,6 @@
|
||||
<string name="skipped_reason_not_caught_up">דולג בגלל שיש פרקים שלא נקראו</string>
|
||||
<string name="skipped_reason_not_started">דולג בגלל שאין פרקים שנקראו</string>
|
||||
<string name="action_close">סגור</string>
|
||||
<string name="channel_skipped">דולגו</string>
|
||||
<string name="pref_true_color_summary">מפחית פסים, אך עשוי להשפיע על הביצועים</string>
|
||||
<string name="channel_errors">שגיאות</string>
|
||||
<string name="pref_reset_viewer_flags">אפס את הגדרות מצב הקריאה של כל סדרה בנפרד</string>
|
||||
@ -588,7 +584,6 @@
|
||||
<string name="action_filter_interval_custom">קבע מרווח זמן מותאם מראש לטעינה</string>
|
||||
<string name="action_filter_interval_long">טען חודשית (28 ימים)</string>
|
||||
<string name="unknown_title">כותרת לא מוכרת</string>
|
||||
<string name="manga_modify_calculated_interval_title">בחר מרווח זמן מותאם אישית</string>
|
||||
<string name="download_notifier_downloader_title">מנהל ההורדות</string>
|
||||
<string name="exception_http">HTTP %d, בדוק באתר ב-WebView</string>
|
||||
<string name="exception_offline">אין חיבור אינטרנט</string>
|
||||
|
@ -558,16 +558,13 @@
|
||||
<string name="pref_auto_clear_chapter_cache">ऐप लॉन्च पर चैप्टर कैशे साफ़ करें</string>
|
||||
<string name="database_clean">साफ़ करने के लिए कुछ नहीं है</string>
|
||||
<string name="pref_update_only_completely_read">अपठित अध्याय हैं</string>
|
||||
<string name="pref_library_update_manga_restriction">शीर्षक अद्यतन न करें</string>
|
||||
<string name="save_chapter_as_cbz">CBZ आर्कैव के रूप में सहेजें</string>
|
||||
<string name="privacy_policy">गोपनीयता नीति</string>
|
||||
<string name="library_errors_help">लाइब्रेरी अपडेट त्रुटियों को ठीक करने के तरीके पर सहायता के लिए, %1$s देखें</string>
|
||||
<string name="extension_api_error">एक्सटेंशन सूची प्राप्त करने में विफल</string>
|
||||
<string name="action_faq_and_guides">सामान्य प्रश्नोत्तर और मार्गदर्शन</string>
|
||||
<string name="cancelled">रद्द किया गया</string>
|
||||
<string name="on_hiatus">अंतराल पर है</string>
|
||||
<string name="publishing_finished">प्रकाशन समाप्त</string>
|
||||
<string name="channel_skipped">छोड़े गए</string>
|
||||
<string name="learn_more">अधिक जानकारी के लिए दबाए</string>
|
||||
<string name="skipped_reason_completed">छोड़े गए क्योंकि श्रृंखला पूरी हो गई है</string>
|
||||
<string name="skipped_reason_not_caught_up">छोड़ दिया गया क्योंकि वहाँ अपठित अध्याय हैं</string>
|
||||
@ -578,7 +575,6 @@
|
||||
<string name="disabled_nav">अक्षम</string>
|
||||
<string name="rotation_reverse_portrait">रिवर्स पोर्ट्रेट</string>
|
||||
<string name="notification_update_error">%1$d अपडेट विफल</string>
|
||||
<string name="notification_update_skipped">%1$d अपडेट छोड़े गए</string>
|
||||
<string name="pref_navigate_pan">चौड़ी छवियां पैन करें</string>
|
||||
<string name="pref_landscape_zoom">ज़ूम लैंडस्केप इमेज</string>
|
||||
<string name="action_move_to_top_all_for_series">श्रृंखला को शीर्ष पर ले जाएं</string>
|
||||
|
@ -47,9 +47,9 @@
|
||||
<string name="pref_category_security">Sigurnost i privatnost</string>
|
||||
<string name="pref_manage_notifications">Upravljaj obavijestima</string>
|
||||
<string name="pref_date_format">Format datuma</string>
|
||||
<string name="theme_system">Slijedi sustav</string>
|
||||
<string name="theme_dark">Uključeno</string>
|
||||
<string name="theme_light">Isključeno</string>
|
||||
<string name="theme_system">Sustav</string>
|
||||
<string name="theme_dark">Tamna</string>
|
||||
<string name="theme_light">Svijetla</string>
|
||||
<string name="pref_category_about">Informacije</string>
|
||||
<string name="pref_category_advanced">Napredno</string>
|
||||
<string name="pref_category_tracking">Praćenje</string>
|
||||
@ -345,7 +345,7 @@
|
||||
<string name="cookies_cleared">Kolačići su izbrisani</string>
|
||||
<string name="pref_clear_cookies">Ukloni kolačiće</string>
|
||||
<string name="cache_delete_error">Došlo je do greške prilikom brisanja</string>
|
||||
<string name="cache_deleted">Predmemorija je prazna. Broj izbrisanih datoteka: %1$d</string>
|
||||
<string name="cache_deleted">Predmemorija je izrisana. Broj izbrisanih datoteka: %1$d</string>
|
||||
<string name="used_cache">Korišteno: %1$s</string>
|
||||
<string name="pref_clear_chapter_cache">Isprazni predmemoriju poglavlja</string>
|
||||
<string name="restoring_backup_canceled">Obnavljanje prekinuto</string>
|
||||
@ -519,7 +519,7 @@
|
||||
<string name="pref_dark_theme_pure_black">Potpuno crna tamna tema</string>
|
||||
<string name="theme_strawberrydaiquiri">Strawberry Daiquiri</string>
|
||||
<string name="pref_category_appearance">Izgled</string>
|
||||
<string name="getting_started_guide">Početni vodič</string>
|
||||
<string name="getting_started_guide">Vodič za pokretanje</string>
|
||||
<string name="confirm_lock_change">Ovjeri za potvrditi promjenu</string>
|
||||
<string name="label_default">Zadano</string>
|
||||
<string name="restore_miui_warning">Spremanje sigurnosne kopije i obnavljanje možda neće ispravno raditi, ako MIUI optimizacija nije aktivirana.</string>
|
||||
@ -553,7 +553,6 @@
|
||||
<string name="enhanced_tracking_info">Pruža poboljšane značajke za određene izvore. Unosi se automatski prate kada se dodaju u biblioteku.</string>
|
||||
<string name="action_track">Prati</string>
|
||||
<string name="privacy_policy">Politika privatnosti</string>
|
||||
<string name="pref_library_update_manga_restriction">Preskoči ažuriranje unosa</string>
|
||||
<string name="extension_api_error">Neuspjelo preuzimanje popisa proširenja</string>
|
||||
<string name="ext_installer_pref">Instalacijski program</string>
|
||||
<string name="ext_installer_legacy">Stari način</string>
|
||||
@ -566,7 +565,6 @@
|
||||
<string name="on_hiatus">Zaustavljeno</string>
|
||||
<string name="library_errors_help">Za pomoć o tome kako popraviti greške aktualiziranja biblioteke, pogledaj %1$s</string>
|
||||
<string name="save_chapter_as_cbz">Spremi kao CBZ arhivu</string>
|
||||
<string name="action_faq_and_guides">ČPP i vodiči</string>
|
||||
<string name="action_show_manga">Prikaži unos</string>
|
||||
<string name="action_display_cover_only_grid">Samo naslovnice</string>
|
||||
<string name="pref_navigate_pan">Panoramski prikaz širokih slika</string>
|
||||
@ -577,8 +575,6 @@
|
||||
<string name="skipped_reason_not_caught_up">Preskočeno, jer postoje nepročitana poglavlja</string>
|
||||
<string name="notification_update_error">Nauspjela aktualiziranja: %1$d</string>
|
||||
<string name="learn_more">Dodirni za daljnje informacije</string>
|
||||
<string name="channel_skipped">Preskočeno</string>
|
||||
<string name="notification_update_skipped">Preskočena aktualiziranja: %1$d</string>
|
||||
<string name="rotation_reverse_portrait">Preokrenuto uspravno</string>
|
||||
<string name="action_move_to_top_all_for_series">Pomakni seriju na vrh</string>
|
||||
<string name="disabled_nav">Deaktivirano</string>
|
||||
@ -613,7 +609,7 @@
|
||||
<string name="multi_lang">Višejezičnost</string>
|
||||
<string name="missing_storage_permission">Dozvole za spremanje nisu odobrena</string>
|
||||
<string name="theme_tidalwave">Tsunami</string>
|
||||
<string name="invalid_location">Nevažeća lokacija: %s</string>
|
||||
<string name="invalid_location">Nevažeće mjesto: %s</string>
|
||||
<string name="pref_advanced_summary">Zapisnici iznenadnog gašenja aplikacije, optimizacije baterije</string>
|
||||
<string name="label_started">Započeto</string>
|
||||
<string name="label_local">Lokalno</string>
|
||||
@ -714,7 +710,6 @@
|
||||
<string name="action_filter_interval_custom">Prilagođeni interval dohvaćanja</string>
|
||||
<string name="action_filter_interval_long">Mjesečno dohvaćanje (28 dana)</string>
|
||||
<string name="manga_display_interval_title">Procijeni svakih</string>
|
||||
<string name="manga_modify_calculated_interval_title">Prilagodi interval</string>
|
||||
<string name="action_sort_next_updated">Sljedeće očekivano aktualiziranje</string>
|
||||
<string name="manga_display_modified_interval_title">Postavi za aktualiziranje svakih</string>
|
||||
<string name="action_ok">U redu</string>
|
||||
@ -768,4 +763,27 @@
|
||||
<string name="selected">Odabrano</string>
|
||||
<string name="not_selected">Neodabrano</string>
|
||||
<string name="action_bar_up_description">Navigiraj prema gore</string>
|
||||
<string name="onboarding_storage_action_select">Odaberi mapu</string>
|
||||
<string name="onboarding_action_finish">Započni</string>
|
||||
<string name="onboarding_storage_selection_required">Moraš odabrati jednu mapu</string>
|
||||
<string name="onboarding_permission_notifications">Dozvola za obavijesti</string>
|
||||
<string name="onboarding_permission_install_apps">Dozvola za instaliranje aplikacija</string>
|
||||
<string name="onboarding_action_skip">Preskoči</string>
|
||||
<string name="onboarding_action_next">Dalje</string>
|
||||
<string name="onboarding_permission_install_apps_description">Za instaliranje izvornih proširenja.</string>
|
||||
<string name="onboarding_permission_notifications_description">Primaj obavijesti o aktualiziranju biblioteke i više.</string>
|
||||
<string name="onboarding_permission_action_grant">Odobri</string>
|
||||
<string name="onboarding_heading">Dobro došao, dobro došla!</string>
|
||||
<string name="pref_onboarding_guide">Vodič za početno pokretanje</string>
|
||||
<string name="onboarding_guides_new_user">Koristiš %s po prvi puta? Preporučujemo da pogledaš vodič za pokretanje.</string>
|
||||
<string name="onboarding_guides_returning_user">Već si koristio/la %s?</string>
|
||||
<string name="onboarding_permission_ignore_battery_opts_description">Izbjegni prekide pri dugotrajnim aktualiziranjima biblioteke, preuzimanjima i obnavljanja sigurnosnih kopija.</string>
|
||||
<string name="onboarding_permission_ignore_battery_opts">Upotreba baterije u pozadini</string>
|
||||
<string name="onboarding_description">Hajdemo najprije postaviti neke stvari. Ako želiš možeš ih kasnije promijeniti u postavkama.</string>
|
||||
<string name="no_location_set">Nije postavljeno mjesto za spremanje podataka</string>
|
||||
<string name="onboarding_storage_info">Odaberi mapu u koju će %1$s spremati preuzimanja poglavlja, sigurnosne kopije i drugo.
|
||||
\n
|
||||
\nPreporučujemo koristiti zasebnu mapu.
|
||||
\n
|
||||
\nOdabrana mapa: %2$s</string>
|
||||
</resources>
|
@ -419,7 +419,6 @@
|
||||
<string name="date">Dátum</string>
|
||||
<string name="clear_history_confirmation">Biztos benne\? Minden előzmény elvész.</string>
|
||||
<string name="delete_category">Kategória törlése</string>
|
||||
<string name="action_faq_and_guides">Gyakori kérdések és Útmutatók</string>
|
||||
<string name="pref_app_language">Alkalmazás nyelve</string>
|
||||
<string name="unread">Olvasatlan</string>
|
||||
<string name="set_chapter_settings_as_default">Beállítás alapértelmezettként</string>
|
||||
@ -435,7 +434,6 @@
|
||||
<string name="delete_category_confirmation">Törölni akarja a %s kategóriát \?</string>
|
||||
<string name="internal_error">InternalError: Nézze meg a hibaüzenetet további információért</string>
|
||||
<string name="network_not_metered">Csak nem-lemért hálózatokon</string>
|
||||
<string name="pref_library_update_manga_restriction">Frissülő elemek kihagyása</string>
|
||||
<string name="backup_choice">Mit tartalmazzon a biztonsági mentés\?</string>
|
||||
<string name="restore_miui_warning">Biztonsági mentés/helyreállítás nem biztos,hogy működik ha a MIUI Optimalizáció ki van kapcsolva.</string>
|
||||
<string name="restore_in_progress">Helyreállítás folyamatban van</string>
|
||||
@ -474,7 +472,6 @@
|
||||
<string name="snack_categories_deleted">Kategóriák törölve</string>
|
||||
<string name="file_select_cover">Válasszon képet a fedlapnak</string>
|
||||
<string name="update_check_no_new_updates">Nem található új frissítés</string>
|
||||
<string name="channel_skipped">Kihagyott</string>
|
||||
<string name="ext_info_version">Verzió</string>
|
||||
<string name="ext_info_language">Nyelv</string>
|
||||
<string name="ext_info_age_rating">Korhatár</string>
|
||||
@ -512,7 +509,6 @@
|
||||
<string name="backup_in_progress">Biztonsági mentés már folyamatban van</string>
|
||||
<string name="not_installed">Nincs telepítve</string>
|
||||
<string name="download_queue_size_warning">Figyelmeztetés: a tömeges letöltések a források lelassulásához és/vagy a Tachiyomi leállásához vezethetnek. Koppintson további információért.</string>
|
||||
<string name="notification_update_skipped">%1$d frissítés kihagyva</string>
|
||||
<string name="learn_more">Koppintson további információért</string>
|
||||
<string name="notification_cover_update_failed">Nem sikerült a fedlap frissítése</string>
|
||||
<string name="notification_first_add_to_library">Először adja a mangát a könyvtárhoz</string>
|
||||
@ -726,7 +722,6 @@
|
||||
<string name="action_menu_overflow_description">További lehetőségek</string>
|
||||
<string name="library_sync_complete">Könyvtár szinkronizálása elkészült</string>
|
||||
<string name="last_auto_backup_info">Utolsó automatikus biztonsági mentés: %s</string>
|
||||
<string name="manga_modify_calculated_interval_title">Intervallum testreszabása</string>
|
||||
<string name="selected">Kiválasztott</string>
|
||||
<string name="no_scanlators_found">Nincs szkennelő-fordító találat</string>
|
||||
<string name="not_selected">Nem kiválasztott</string>
|
||||
|
@ -123,9 +123,9 @@
|
||||
<string name="disabled">Tidak Aktif</string>
|
||||
<string name="last_read_chapter">Bab terakhir yang dibaca</string>
|
||||
<string name="second_to_last">Bab kedua dari terakhir dibaca</string>
|
||||
<string name="third_to_last">Bab ketiga dari terakhir dibaca</string>
|
||||
<string name="fourth_to_last">Bab keempat dari terakhir dibaca</string>
|
||||
<string name="fifth_to_last">Bab kelima dari terakhir dibaca</string>
|
||||
<string name="third_to_last">Chapter ketiga dari terakhir dibaca</string>
|
||||
<string name="fourth_to_last">Chapter keempat dari terakhir dibaca</string>
|
||||
<string name="fifth_to_last">Chapter kelima dari terakhir dibaca</string>
|
||||
<string name="pref_download_new">Unduh bab baru</string>
|
||||
<string name="services">Pelacakan</string>
|
||||
<string name="pref_create_backup">Buat cadangan</string>
|
||||
@ -140,7 +140,7 @@
|
||||
<string name="creating_backup">Membuat cadangan</string>
|
||||
<string name="pref_clear_chapter_cache">Hapus cache bab</string>
|
||||
<string name="used_cache">Terpakai: %1$s</string>
|
||||
<string name="cache_deleted">Cache dibersihkan %1$d berkas telah dihapus</string>
|
||||
<string name="cache_deleted">Cache dibersihkan, %1$d file dihapus</string>
|
||||
<string name="cache_delete_error">Terjadi kesalahan saat membersihkan</string>
|
||||
<string name="pref_clear_cookies">Hapus cookies</string>
|
||||
<string name="cookies_cleared">Cookies dihapus</string>
|
||||
@ -293,9 +293,9 @@
|
||||
<string name="action_sort_latest_chapter">Bab terbaru</string>
|
||||
<string name="action_view_chapters">Lihat bab</string>
|
||||
<string name="action_cancel_all">Batalkan semua</string>
|
||||
<string name="theme_light">Mati</string>
|
||||
<string name="theme_dark">Nyala</string>
|
||||
<string name="theme_system">Ikuti sistem</string>
|
||||
<string name="theme_light">Terang</string>
|
||||
<string name="theme_dark">Gelap</string>
|
||||
<string name="theme_system">Sistem</string>
|
||||
<string name="pref_manage_notifications">Kelola notifikasi</string>
|
||||
<string name="pref_category_security">Keamanan dan privasi</string>
|
||||
<string name="action_menu">Menu</string>
|
||||
@ -413,7 +413,7 @@
|
||||
<string name="ext_nsfw_warning">Sumber dari ekstensi ini mungkin berisi konten DEWASA (18+)</string>
|
||||
<string name="ext_nsfw_short">18+</string>
|
||||
<string name="pref_remove_bookmarked_chapters">Hapus bab yang ditandai</string>
|
||||
<string name="pref_category_delete_chapters">Hapus bab</string>
|
||||
<string name="pref_category_delete_chapters">Hapus chapter</string>
|
||||
<string name="parental_controls_info">Hal ini tidak mencegah ekstensi yang tidak resmi atau berpotensi salah ditandai untuk menampilkan konten NSFW (18+) di dalam aplikasi.</string>
|
||||
<string name="no_chapters_error">Tidak ada bab yang ditemukan</string>
|
||||
<string name="chapter_settings_updated">Pengaturan bab bawaan diperbarui</string>
|
||||
@ -458,7 +458,7 @@
|
||||
<string name="nav_zone_right">Kanan</string>
|
||||
<string name="nav_zone_left">Kiri</string>
|
||||
<string name="nav_zone_next">Lanjut</string>
|
||||
<string name="nav_zone_prev">Sblm</string>
|
||||
<string name="nav_zone_prev">Sebelum</string>
|
||||
<string name="automatic_background">Otomatis</string>
|
||||
<string name="pref_create_folder_per_manga">Menyimpan halaman ke dalam folder terpisah</string>
|
||||
<string name="pref_create_folder_per_manga_summary">Membuat folder menurut judul entri</string>
|
||||
@ -509,7 +509,7 @@
|
||||
<string name="enhanced_tracking_info">Menyediakan fitur yang disempurnakan untuk sumber tertentu. Entri secara otomatis dilacak ketika ditambahkan ke perpustakaan Anda.</string>
|
||||
<string name="enhanced_services">Pelacak yang ditingkatkan</string>
|
||||
<string name="pref_dark_theme_pure_black">Mode gelap hitam pekat</string>
|
||||
<string name="theme_yotsuba">yotsuba</string>
|
||||
<string name="theme_yotsuba">Yotsuba</string>
|
||||
<string name="theme_yinyang">Yin dan Yang</string>
|
||||
<string name="theme_tako">Tako</string>
|
||||
<string name="theme_strawberrydaiquiri">Stroberi Daiquiri</string>
|
||||
@ -528,7 +528,7 @@
|
||||
<string name="getting_started_guide">Panduan awal mulai</string>
|
||||
<string name="action_track">Lacak</string>
|
||||
<string name="theme_tealturquoise">Teal & Pirus</string>
|
||||
<string name="pref_category_appearance">Penampilan</string>
|
||||
<string name="pref_category_appearance">Tampilan</string>
|
||||
<string name="confirm_lock_change">Autentikasi untuk mengonfirmasi perubahan</string>
|
||||
<string name="label_default">Bawaan</string>
|
||||
<string name="pref_remove_exclude_categories">Kategori yang dikecualikan</string>
|
||||
@ -558,7 +558,6 @@
|
||||
<string name="database_clean">Tidak ada yang perlu dibersihkan</string>
|
||||
<string name="clear_database_source_item_count">%1$d entri non-perpustakaan dalam database</string>
|
||||
<string name="extension_api_error">Gagal mendapatkan daftar ekstensi</string>
|
||||
<string name="pref_library_update_manga_restriction">Lewati pembaruan entri</string>
|
||||
<string name="pref_update_only_completely_read">Dengan bab yang belum dibaca</string>
|
||||
<string name="privacy_policy">Kebijakan privasi</string>
|
||||
<string name="publishing_finished">Penerbitan selesai</string>
|
||||
@ -566,7 +565,6 @@
|
||||
<string name="on_hiatus">Sedang Hiatus</string>
|
||||
<string name="library_errors_help">Untuk bantuan cara memperbaiki kesalahan pembaruan pustaka, lihat %1$s</string>
|
||||
<string name="save_chapter_as_cbz">Simpan sebagai arsip CBZ</string>
|
||||
<string name="action_faq_and_guides">Tanya Jawab dan Panduan</string>
|
||||
<string name="action_show_manga">Lihat entri</string>
|
||||
<string name="action_display_cover_only_grid">Grid sampul saja</string>
|
||||
<string name="pref_update_only_started">Yang belum dibaca</string>
|
||||
@ -575,9 +573,7 @@
|
||||
<string name="skipped_reason_not_started">Dilewati karena tidak ada bab yang dibaca</string>
|
||||
<string name="pref_landscape_zoom">Secara otomatis melebarkan gambar</string>
|
||||
<string name="pref_navigate_pan">Geser gambar lebar</string>
|
||||
<string name="notification_update_skipped">%1$d pembaruan terlewat</string>
|
||||
<string name="learn_more">Pelajari lebih lanjut</string>
|
||||
<string name="channel_skipped">Terlewat</string>
|
||||
<string name="rotation_reverse_portrait">Layar tegak terbalik</string>
|
||||
<string name="notification_update_error">%1$d pembaruan gagal</string>
|
||||
<string name="action_move_to_top_all_for_series">Pindahkan seri ke atas</string>
|
||||
@ -641,10 +637,10 @@
|
||||
<string name="action_search_hint">Cari…</string>
|
||||
<string name="pref_browse_summary">Sumber, ekstensi, pencarian global</string>
|
||||
<string name="pref_reader_summary">Mode membaca, tampilan, navigasi</string>
|
||||
<string name="pref_tracking_summary">Sinkronisasi kemajuan satu arah, sinkronisasi yang ditingkatkan</string>
|
||||
<string name="pref_tracking_summary">Sinkronisasi progres searah, sinkronisasi yang ditingkatkan</string>
|
||||
<string name="crash_screen_description">%s mengalami kesalahan tak terduga. Kami menyarankan Anda membagi log kerusakan di saluran dukungan kami di Discord.</string>
|
||||
<string name="pref_downloads_summary">Unduh otomatis, unduh di depan</string>
|
||||
<string name="pref_security_summary">Kunci aplikasi, layar aman</string>
|
||||
<string name="pref_downloads_summary">Unduh otomatis, unduh terlebih dahulu</string>
|
||||
<string name="pref_security_summary">Kunci aplikasi, amankan layar</string>
|
||||
<string name="pref_appearance_summary">Tema, format tanggal & waktu</string>
|
||||
<string name="pref_library_summary">Kategori, pembaruan global, geser chapter</string>
|
||||
<string name="pref_backup_summary">Pencadangan manual & otomatis, ruang penyimpanan</string>
|
||||
@ -695,7 +691,7 @@
|
||||
<string name="confirm_add_duplicate_manga">Kamu memiliki entri di pustaka dengan nama yang sama.
|
||||
\n
|
||||
\nApakah kamu ingin melanjutkan\?</string>
|
||||
<string name="track_error">%1$seror%2$s</string>
|
||||
<string name="track_error">%1$s eror: %2$s</string>
|
||||
<string name="information_required_plain">*dibutuhkan</string>
|
||||
<string name="pref_hide_in_library_items">Semua catatan tersembunyi sudah ada di perpustakaan</string>
|
||||
<string name="action_copy_to_clipboard">Salin ke papan klip</string>
|
||||
@ -714,7 +710,6 @@
|
||||
<string name="action_filter_interval_custom">Interval pengambilan disesuaikan</string>
|
||||
<string name="action_filter_interval_long">Ambil bulanan (28 hari)</string>
|
||||
<string name="action_filter_interval_late">Cek 10+ terlambat</string>
|
||||
<string name="manga_modify_calculated_interval_title">Sesuaikan Interval</string>
|
||||
<string name="skipped_reason_not_in_release_period">Dilewati karena tidak ada rilis yang diharapkan hari ini</string>
|
||||
<string name="action_filter_interval_dropped">Berkurang\? Akhir 20+ dan 2 bulan</string>
|
||||
<string name="action_filter_interval_passed">Melewati periode pemeriksaan</string>
|
||||
@ -768,4 +763,27 @@
|
||||
<string name="action_bar_up_description">Navigasi ke atas</string>
|
||||
<string name="pref_storage_location">Lokasi penyimpanan</string>
|
||||
<string name="pref_storage_location_info">Digunakan untuk pencadangan otomatis, pengunduhan bab, dan sumber lokal.</string>
|
||||
<string name="onboarding_storage_action_select">Pilih direktori</string>
|
||||
<string name="pref_onboarding_guide">Panduan awal</string>
|
||||
<string name="onboarding_guides_new_user">Baru mengenal %s? Kami menyarankan Anda membaca panduan awal.</string>
|
||||
<string name="onboarding_action_finish">Selesai</string>
|
||||
<string name="onboarding_storage_selection_required">Harap pilih direktori terlebih dahulu</string>
|
||||
<string name="onboarding_permission_notifications">Izin notifikasi</string>
|
||||
<string name="onboarding_permission_install_apps">Izin pasang aplikasi</string>
|
||||
<string name="onboarding_heading">Selamat Datang!</string>
|
||||
<string name="onboarding_guides_returning_user">Sudah pernah menggunakan %s sebelumnya?</string>
|
||||
<string name="onboarding_action_skip">Lewati</string>
|
||||
<string name="onboarding_permission_ignore_battery_opts_description">Hindari gangguan pada pembaruan pustaka, pengunduhan, dan pemulihan cadangan yang berlangsung lama.</string>
|
||||
<string name="onboarding_action_next">Selanjutnya</string>
|
||||
<string name="onboarding_permission_ignore_battery_opts">Penggunaan baterai di latar belakang</string>
|
||||
<string name="onboarding_permission_install_apps_description">Untuk memasang ekstensi.</string>
|
||||
<string name="onboarding_description">Mari kita atur beberapa hal terlebih dahulu. Anda juga selalu dapat mengubahnya di menu pengaturan nanti.</string>
|
||||
<string name="no_location_set">Lokasi penyimpanan belum ditentukan</string>
|
||||
<string name="onboarding_permission_notifications_description">Dapatkan notifikasi untuk pembaruan pustaka dan sebagainya.</string>
|
||||
<string name="onboarding_permission_action_grant">Izinkan</string>
|
||||
<string name="onboarding_storage_info">Pilih direktori untuk menyimpan konten dari %1$s, termasuk unduhan chapter, data backup, dan lainnya.
|
||||
\n
|
||||
\nSebaiknya menggunakan direktori terpisah.
|
||||
\n
|
||||
\nDirektori yang dipilih: %2$s</string>
|
||||
</resources>
|
@ -114,7 +114,7 @@
|
||||
<string name="services">Servizi di tracking</string>
|
||||
<string name="pref_clear_chapter_cache">Cancella cache capitoli</string>
|
||||
<string name="used_cache">Usati: %1$s</string>
|
||||
<string name="cache_deleted">Cache cancellata. %1$d file sono stati cancellati</string>
|
||||
<string name="cache_deleted">Cache cancellat, %1$d file cancellati</string>
|
||||
<string name="cache_delete_error">Errore durante la cancellazione</string>
|
||||
<string name="pref_clear_cookies">Cancella cookie</string>
|
||||
<string name="cookies_cleared">Cookie cancellati</string>
|
||||
@ -301,9 +301,9 @@
|
||||
<string name="action_sort_latest_chapter">Ultimo capitolo</string>
|
||||
<string name="action_view_chapters">Visualizza capitoli</string>
|
||||
<string name="action_cancel_all">Annulla tutto</string>
|
||||
<string name="theme_light">Disattivo</string>
|
||||
<string name="theme_dark">Attivo</string>
|
||||
<string name="theme_system">Usa il tema di sistema</string>
|
||||
<string name="theme_light">Chiaro</string>
|
||||
<string name="theme_dark">Scuro</string>
|
||||
<string name="theme_system">Sistema</string>
|
||||
<string name="pref_manage_notifications">Gestisci le notifiche</string>
|
||||
<string name="pref_category_security">Sicurezza e privacy</string>
|
||||
<string name="lock_with_biometrics">Richiedi sblocco</string>
|
||||
@ -560,13 +560,11 @@
|
||||
<string name="extension_api_error">Impossibile ottenere l\'elenco estensioni</string>
|
||||
<string name="privacy_policy">Politica sulla privacy</string>
|
||||
<string name="pref_update_only_completely_read">Con capitoli non letti</string>
|
||||
<string name="pref_library_update_manga_restriction">Salta l\'aggiornamento delle voci</string>
|
||||
<string name="library_errors_help">Per un aiuto su come risolvere gli errori di aggiornamento della libreria vedi %1$s</string>
|
||||
<string name="save_chapter_as_cbz">Salva come archivio CBZ</string>
|
||||
<string name="cancelled">Cancellata</string>
|
||||
<string name="on_hiatus">In pausa</string>
|
||||
<string name="publishing_finished">Pubblicazione terminata</string>
|
||||
<string name="action_faq_and_guides">Domande frequenti e guide</string>
|
||||
<string name="action_show_manga">Mostra voce</string>
|
||||
<string name="pref_navigate_pan">Scorri le immagini larghe</string>
|
||||
<string name="pref_landscape_zoom">Ingrandisci automaticamente le immagini larghe</string>
|
||||
@ -575,9 +573,7 @@
|
||||
<string name="skipped_reason_completed">Saltato perché la serie è completa</string>
|
||||
<string name="skipped_reason_not_caught_up">Saltato perché ci sono capitoli non letti</string>
|
||||
<string name="skipped_reason_not_started">Saltato perché non ci sono capitoli letti</string>
|
||||
<string name="channel_skipped">Saltato</string>
|
||||
<string name="notification_update_error">%1$d aggiornamento(i) fallito(i)</string>
|
||||
<string name="notification_update_skipped">%1$d aggiornamento(i) saltato(i)</string>
|
||||
<string name="learn_more">Tocca per approfondire</string>
|
||||
<string name="rotation_reverse_portrait">Verticale inverso</string>
|
||||
<string name="action_move_to_top_all_for_series">Sposta la serie in cima</string>
|
||||
@ -714,7 +710,6 @@
|
||||
<string name="action_filter_interval_long">Recupera mensilmente (28 giorni)</string>
|
||||
<string name="action_filter_interval_dropped">Abbandonato\? In ritardo tra 20 giorni e 2 mesi</string>
|
||||
<string name="pref_update_only_in_release_period">Fuori dal periodo di rilascio previsto</string>
|
||||
<string name="manga_modify_calculated_interval_title">Personalizza intervallo</string>
|
||||
<string name="intervals_header">Intervalli</string>
|
||||
<string name="manga_display_interval_title">Stima ogni</string>
|
||||
<string name="action_filter_interval_custom">Intervallo di recupero personalizzato</string>
|
||||
@ -750,4 +745,48 @@
|
||||
<string name="sort_category_confirmation">Vuoi ordinare le categorie alfabeticamente\?</string>
|
||||
<string name="file_null_uri_error">Il selettore di file ha restituito file all\'app</string>
|
||||
<string name="label_data_storage">Dati e archiviazione</string>
|
||||
<string name="onboarding_storage_action_select">Scegli una cartella</string>
|
||||
<string name="onboarding_action_finish">Iniziamo</string>
|
||||
<string name="action_apply">Applica</string>
|
||||
<string name="onboarding_storage_selection_required">Devi selezionare una cartella</string>
|
||||
<string name="onboarding_permission_notifications">Permessi di notifica</string>
|
||||
<string name="action_revert_to_default">Reimposta predefiniti</string>
|
||||
<string name="onboarding_permission_install_apps">Permessi per installare app</string>
|
||||
<string name="onboarding_heading">Ti diamo il benvenuto!</string>
|
||||
<string name="action_menu_overflow_description">Altre opzioni</string>
|
||||
<string name="selected">Selezionato</string>
|
||||
<string name="not_selected">Non selezionato</string>
|
||||
<string name="scanlator">Scanlator</string>
|
||||
<string name="onboarding_action_skip">Salta</string>
|
||||
<string name="onboarding_action_next">Prossimo</string>
|
||||
<string name="onboarding_permission_ignore_battery_opts">Uso della batteria in secondo piano</string>
|
||||
<string name="onboarding_permission_install_apps_description">Per installare le estensioni delle fonti.</string>
|
||||
<string name="action_bar_up_description">Navigare su</string>
|
||||
<string name="onboarding_description">Per prima cosa impostiamo alcune cose. Puoi sempre cambiarle successivamente nelle impostazioni.</string>
|
||||
<string name="onboarding_permission_notifications_description">Ricevi notifiche per aggiornamenti della libreria e altro.</string>
|
||||
<string name="ext_permission_install_apps_warning">È necessaria l\'autorizzazione per installare le estensioni. Tocca qui per concederla.</string>
|
||||
<string name="onboarding_storage_info">Selezione una cartella dove %1$s salverà i capitoli scaricati, i backup e altro.
|
||||
\n
|
||||
\nÈ raccomandato usare una cartella dedicata.
|
||||
\n
|
||||
\nCartella selezionata: %2$s</string>
|
||||
<string name="pref_storage_location">Posizione di archiviazione</string>
|
||||
<string name="action_create">Crea</string>
|
||||
<string name="relative_time_span_never">Mai</string>
|
||||
<string name="pref_flash_page_summ">Riduce il ghosting sugli schermi e-ink</string>
|
||||
<string name="pref_onboarding_guide">Guida di benvenuto</string>
|
||||
<string name="pref_storage_location_info">Usato per i backup automatici, download capitoli e fonti locali.</string>
|
||||
<string name="onboarding_guides_new_user">Nuovo su %s? Ti consigliamo di dare un\'occhiata alla guida introduttiva.</string>
|
||||
<string name="available_disk_space_info">Disponibile: %1$s / Totale: %2$s</string>
|
||||
<string name="last_auto_backup_info">Ultimo backup automatico: %s</string>
|
||||
<string name="onboarding_guides_returning_user">Hai già usato %s in precedenza?</string>
|
||||
<string name="no_scanlators_found">Nessuno scanlator trovato</string>
|
||||
<string name="pref_flash_page">Lampeggia al cambio pagina</string>
|
||||
<string name="onboarding_permission_ignore_battery_opts_description">Evita interruzioni a lunghi aggiornamenti libreria, download e ripristino di backup.</string>
|
||||
<string name="pref_storage_usage">Archiviazione utilizzata</string>
|
||||
<string name="action_sort_tracker_score">Punteggio del tracker</string>
|
||||
<string name="no_location_set">Cartella di archiviazione non impostata</string>
|
||||
<string name="private_settings">Includi impostazioni sensibili (es. token di login dei tracker)</string>
|
||||
<string name="onboarding_permission_action_grant">Consenti</string>
|
||||
<string name="exclude_scanlators">Escludi scanlator</string>
|
||||
</resources>
|
@ -158,11 +158,11 @@
|
||||
<string name="action_open_in_web_view">WebView で開く</string>
|
||||
<string name="pref_category_reader">ビューア</string>
|
||||
<string name="pref_category_tracking">同期</string>
|
||||
<string name="untrusted_extension_message">この拡張機能は信頼できない証明書でサインされているため、有効にされていません。
|
||||
<string name="untrusted_extension_message">この拡張機能は不明の作成者によりサインされているため、ロードされていません。
|
||||
\n
|
||||
\n悪意のある拡張機能は保存されているすべてのログイン情報を読み取ることや、任意コード実行をすることができます。
|
||||
\n
|
||||
\nこれらのリスクを受け入れ、この証明書を信頼しますか?</string>
|
||||
\nこれらのリスクを受け入れ、この拡張機能の証明書を信頼しますか?</string>
|
||||
<string name="pref_show_page_number">ページ数を表示</string>
|
||||
<string name="pref_keep_screen_on">画面を常にON</string>
|
||||
<string name="scale_type_fit_height">高さに合わせる</string>
|
||||
@ -178,7 +178,7 @@
|
||||
<string name="restoring_backup">バックアップを復元中</string>
|
||||
<string name="creating_backup">バックアップを作成中</string>
|
||||
<string name="used_cache">サイズ: %1$s</string>
|
||||
<string name="cache_deleted">キャッシュを削除しました。%1$d件のファイルは削除されました</string>
|
||||
<string name="cache_deleted">キャッシュがクリアされ、%1$d 個のファイルが削除されました</string>
|
||||
<string name="pref_clear_cookies">Cookiesを削除</string>
|
||||
<string name="cookies_cleared">Cookiesを削除しました</string>
|
||||
<string name="clear_database_confirmation">データベースをクリアしてもよろしいですか?ライブラリにない項目の読んだ章と読書進捗はすべて失われます</string>
|
||||
@ -331,7 +331,7 @@
|
||||
<string name="restore_duration">%02d分%02d秒</string>
|
||||
<string name="backup_restore_missing_sources">ソースがありません:</string>
|
||||
<string name="invalid_backup_file_missing_manga">バックアップにはライブラリの項目が含まれません。</string>
|
||||
<string name="invalid_backup_file">バックアップファイルは無効です</string>
|
||||
<string name="invalid_backup_file">無効なバックアップファイル:</string>
|
||||
<string name="tracking_info">一方同期の外部追跡サービスにある章の読書進捗を更新します。個別の項目の「同期」ボタンで追跡サービスを設定してください。</string>
|
||||
<string name="pref_webtoon_side_padding">余白</string>
|
||||
<string name="pref_category_reading">読書中</string>
|
||||
@ -457,9 +457,7 @@
|
||||
<string name="pref_dual_page_invert_summary">分割された幅広いページの配置が読む方向と一致しない場合は有効にしてください</string>
|
||||
<string name="pref_dual_page_invert">分割ページの配置を反転</string>
|
||||
<string name="pref_dual_page_split">幅広いページの分割</string>
|
||||
<string name="backup_restore_content_full">バックアップファイルからデータを復旧します。
|
||||
\n
|
||||
\n後で手動で必要な拡張機能をインストールし、使用したい追跡サービスにログインしてください。</string>
|
||||
<string name="backup_restore_content_full">後で手動で必要な拡張機能をインストールし、使用したい追跡サービスにログインしてください。</string>
|
||||
<string name="nav_zone_right">右</string>
|
||||
<string name="nav_zone_left">左</string>
|
||||
<string name="nav_zone_next">次へ</string>
|
||||
@ -559,14 +557,12 @@
|
||||
<string name="database_clean">消去できるものはありませんでした</string>
|
||||
<string name="extension_api_error">拡張機能リストを取得できませんでした</string>
|
||||
<string name="privacy_policy">プライバシーポリシー</string>
|
||||
<string name="pref_library_update_manga_restriction">項目更新のスキップ</string>
|
||||
<string name="pref_update_only_completely_read">未読の章あり</string>
|
||||
<string name="library_errors_help">ライブラリ更新エラーの修正については、%1$sをご覧ください</string>
|
||||
<string name="save_chapter_as_cbz">CBZアーカイブとして保存</string>
|
||||
<string name="publishing_finished">完結済み</string>
|
||||
<string name="cancelled">キャンセル済み</string>
|
||||
<string name="on_hiatus">休載中</string>
|
||||
<string name="action_faq_and_guides">よくあるご質問とガイド</string>
|
||||
<string name="action_show_manga">項目を表示</string>
|
||||
<string name="action_display_cover_only_grid">表紙グリッド</string>
|
||||
<string name="pref_update_only_started">読み始めていない</string>
|
||||
@ -575,9 +571,7 @@
|
||||
<string name="skipped_reason_completed">完結済みの為スキップしました</string>
|
||||
<string name="skipped_reason_not_started">読了した章がない為スキップしました</string>
|
||||
<string name="pref_navigate_pan">幅広い画像を左右に移動</string>
|
||||
<string name="notification_update_skipped">%1$d件の更新がスキップされました</string>
|
||||
<string name="learn_more">タップでもっと詳しく</string>
|
||||
<string name="channel_skipped">スキップ済み</string>
|
||||
<string name="notification_update_error">%1$d件の更新に失敗しました</string>
|
||||
<string name="rotation_reverse_portrait">縦向き (反転)</string>
|
||||
<string name="action_move_to_top_all_for_series">シリーズをトップに移動</string>
|
||||
@ -717,7 +711,6 @@
|
||||
<string name="pref_update_only_in_release_period">更新予定時間外</string>
|
||||
<string name="intervals_header">間隔</string>
|
||||
<string name="skipped_reason_not_in_release_period">今日、連載更新が予想されていないためスキップしました</string>
|
||||
<string name="manga_modify_calculated_interval_title">間隔を設定</string>
|
||||
<string name="has_results">結果あり</string>
|
||||
<string name="track_delete_title">%s の追跡を削除しますか\?</string>
|
||||
<string name="manga_display_interval_title">毎に評価</string>
|
||||
@ -787,10 +780,12 @@
|
||||
<string name="onboarding_permission_notifications">通知の許可</string>
|
||||
<string name="onboarding_permission_install_apps">アプリのインストールの許可</string>
|
||||
<string name="onboarding_permission_ignore_battery_opts_description">時間のかかるライブラリ更新、ダウンロードやバックアップの復元などへの中断を防ぎます。</string>
|
||||
<string name="onboarding_permission_type_optional">任意</string>
|
||||
<string name="onboarding_permission_ignore_battery_opts">バックグラウンドでのバッテリー使用量</string>
|
||||
<string name="onboarding_permission_install_apps_description">ソース拡張機能をインストールするために必要です。</string>
|
||||
<string name="onboarding_permission_notifications_description">ライブラリ更新などの通知を送信します。</string>
|
||||
<string name="onboarding_permission_type_required">必須</string>
|
||||
<string name="onboarding_permission_action_grant">許可</string>
|
||||
<string name="available_disk_space_info">空き領域:%1$s/総計:%2$s</string>
|
||||
<string name="manga_interval_expected_update">次のアップデートは%sに実行する予定です</string>
|
||||
<string name="invalid_backup_file_error">詳細なエラー情報:</string>
|
||||
<string name="ext_permission_install_apps_warning">拡張機能をインストールするためには権限が必要です。タップで許可してください。</string>
|
||||
</resources>
|
@ -175,7 +175,6 @@
|
||||
<string name="pref_app_theme">Tema aplikasi</string>
|
||||
<string name="theme_monet">Dinamis</string>
|
||||
<string name="theme_greenapple">Ijo apel</string>
|
||||
<string name="action_faq_and_guides">Takon jawab lan panduan</string>
|
||||
<string name="parental_controls_info">Iki ora nyegah ekstensi kang ora resmi utawa salah dilabeli kanggo nampilake konten NSFW (18+) ing aplikasi.</string>
|
||||
<string name="relative_time_today">Dinten puniki</string>
|
||||
<string name="update_never">Mati</string>
|
||||
@ -246,7 +245,6 @@
|
||||
<string name="lock_with_biometrics">Mbutuhake mbukak kunci</string>
|
||||
<string name="pref_library_update_refresh_metadata">Nganyari metadata otomatis</string>
|
||||
<string name="network_not_metered">Jaringan gak kebates tok</string>
|
||||
<string name="pref_library_update_manga_restriction">Lewati updatean judul</string>
|
||||
<string name="pref_app_language">Bohoso aplikasi</string>
|
||||
<string name="untrusted_extension_message">Ekstensi iki ditandatangani nganggo sertifikat sing ora dipercaya lan ora diaktifake.
|
||||
\n
|
||||
|
@ -422,7 +422,6 @@
|
||||
<string name="pref_app_theme">აპლიკაციის თემა</string>
|
||||
<string name="label_warning">გაფრთხილება</string>
|
||||
<string name="confirm_lock_change">აუთენტიკაცია ცვლილების დასადასტურებლად</string>
|
||||
<string name="action_faq_and_guides">ხდკ და სახელმძღვანელოები</string>
|
||||
<string name="pref_category_appearance">გარეგნობა</string>
|
||||
<string name="label_default">ნაგულისხმევი</string>
|
||||
<string name="action_show_errors">დააჭირე დეტალების სანახავად</string>
|
||||
|
@ -159,7 +159,6 @@
|
||||
<string name="action_webview_forward">Алға</string>
|
||||
<string name="action_webview_refresh">Жаңарту</string>
|
||||
<string name="action_start_downloading_now">Жүктеуді қазір бастау</string>
|
||||
<string name="action_faq_and_guides">ЖҚС пен Нұсқаулықтар</string>
|
||||
<string name="loading">Жүктелуде…</string>
|
||||
<string name="app_not_available">Қолданба қолжетімсіз</string>
|
||||
<string name="pref_category_general">Жалпы</string>
|
||||
@ -231,7 +230,6 @@
|
||||
<string name="pref_crop_borders">Кесу жиектері</string>
|
||||
<string name="extension_api_error">Кеңейтулер тізімін алу сәтсіз өтті</string>
|
||||
<string name="pref_cutout_short">Мазмұнды кесу аймағында көрсету</string>
|
||||
<string name="pref_library_update_manga_restriction">Жазбаларды жаңартуды өткізіп жіберу</string>
|
||||
<string name="pref_update_only_started">Бұл басталған жоқ</string>
|
||||
<string name="pref_library_update_refresh_metadata_summary">Кітапхананы жаңарту кезінде жаңа мұқаба мен мәліметтерді тексеру</string>
|
||||
<string name="ext_updates_pending">Жаңартулар күтілуде</string>
|
||||
@ -518,7 +516,6 @@
|
||||
<string name="clear_history_confirmation">Сенімдісіз бе\? Бар тарих жоғалады.</string>
|
||||
<string name="source_empty_screen">Дереккөз табылмады</string>
|
||||
<string name="notification_update_error">%1$d жаңарту сәтсіз өтті</string>
|
||||
<string name="notification_update_skipped">%1$d жаңарту өткізіп жіберілді</string>
|
||||
<string name="notification_cover_update_failed">Мұқаба жаңартылмады</string>
|
||||
<string name="update_check_notification_update_available">Жаңа нұсқа қолжетімді!</string>
|
||||
<string name="download_notifier_split_page_not_found">%d бет табылмады, бөлінгенде</string>
|
||||
@ -589,7 +586,6 @@
|
||||
<string name="update_check_eol">Бұл Android нұсқасы қолжетімсіз</string>
|
||||
<string name="download_notifier_unknown_error">Белгісіз қателіктің кесірінен тарау жүктеп алынбады</string>
|
||||
<string name="download_notifier_split_page_path_not_found">Бет файлының жолы %d табылмады</string>
|
||||
<string name="channel_skipped">Өткізуп жіберілді</string>
|
||||
<string name="channel_new_chapters">Тарау жаңартулары</string>
|
||||
<string name="channel_app_updates">Қолданба жаңартулары</string>
|
||||
<string name="channel_ext_updates">Кеңейту жаңартулары</string>
|
||||
|
@ -5,7 +5,7 @@
|
||||
<string name="chapters">ភាគ</string>
|
||||
<string name="label_warning">បំរាម</string>
|
||||
<string name="action_filter_unread">មិនទាន់អាន</string>
|
||||
<string name="action_sort_last_read">ម៊េងហ្គាដែលបានអានចុងក្រោយបង្អស់</string>
|
||||
<string name="action_sort_last_read">អានចុងក្រោយគេ</string>
|
||||
<string name="action_previous_chapter">ភាគមុន</string>
|
||||
<string name="track">ការតាមដានការអាន</string>
|
||||
<string name="label_sources">ប្រភព</string>
|
||||
@ -22,28 +22,28 @@
|
||||
<string name="label_extensions">ប្រភពម៊េងហ្គា</string>
|
||||
<string name="label_help">ជំនួយ</string>
|
||||
<string name="label_default">លំនាំដើម</string>
|
||||
<string name="action_filter">ហ្វីលធឺ</string>
|
||||
<string name="action_filter_bookmarked">បានបញ្ចូលទៅក្នុងបណ្ណាល័យ</string>
|
||||
<string name="action_filter_tracked">បានដាក់ការតាមដានការអាន</string>
|
||||
<string name="action_filter_empty">ដកហ្វីលធឺចេញ</string>
|
||||
<string name="action_sort_count">ចំនួនម៊េងហ្គាទាំងអស់</string>
|
||||
<string name="action_sort_total">ចំនួនភាគទាំងអស់</string>
|
||||
<string name="action_sort_chapter_fetch_date">ថ្ងៃខែដែលបានកែប្រែ</string>
|
||||
<string name="action_sort_date_added">បានបញ្ចូលតាមកាលបរិច្ឆេទ</string>
|
||||
<string name="action_filter">តម្រង</string>
|
||||
<string name="action_filter_bookmarked">បានចំណាំ</string>
|
||||
<string name="action_filter_tracked">បានតាមដានការអាន</string>
|
||||
<string name="action_filter_empty">ជម្រុះតម្រងចេញ</string>
|
||||
<string name="action_sort_count">ចំនួនត្លុកសរុប</string>
|
||||
<string name="action_sort_total">ចំនួនភាគសរុប</string>
|
||||
<string name="action_sort_chapter_fetch_date">ថ្ងៃខែដែលបានឆែកមើល</string>
|
||||
<string name="action_sort_date_added">តាមកាលបរិច្ឆេទដែលបានបញ្ចូល</string>
|
||||
<string name="action_global_search">ស្វែងរកជាសកល</string>
|
||||
<string name="action_select_inverse">ជ្រើសរើសផ្ទុយ</string>
|
||||
<string name="action_mark_as_read">បញ្ជាក់ថាបានអាន</string>
|
||||
<string name="action_download">ទាញយក</string>
|
||||
<string name="action_bookmark">ចំណាំភាគនេះទុក</string>
|
||||
<string name="action_rename_category">ប្ដូរឈ្មោះថ្នាក់</string>
|
||||
<string name="action_move_category">ទទួលយកថ្នាក់</string>
|
||||
<string name="action_edit_cover">ផ្លាស់ប្ដូរក្របម៊េងហ្គា</string>
|
||||
<string name="action_rename_category">ប្ដូរឈ្មោះថ្នាក់ក្រម</string>
|
||||
<string name="action_move_category">កំណត់</string>
|
||||
<string name="action_edit_cover">ផ្លាស់ប្ដូរកម្របត្លុក</string>
|
||||
<string name="action_view_chapters">មើលភាគ</string>
|
||||
<string name="action_pause">ឈប់បណ្ណោះអាសន្ន</string>
|
||||
<string name="action_retry">ម្ដងទៀត</string>
|
||||
<string name="action_remove">លុប</string>
|
||||
<string name="action_pause">ផ្អាក</string>
|
||||
<string name="action_retry">ព្យាយាមម្ដងទៀត</string>
|
||||
<string name="action_remove">ដកចេញ</string>
|
||||
<string name="action_start">ចាប់ផ្ដើម</string>
|
||||
<string name="action_resume">បន្ត</string>
|
||||
<string name="action_resume">បន្តវិញ</string>
|
||||
<string name="action_display_mode">ម៊ូដនៃការបង្ហាញ</string>
|
||||
<string name="action_display">ការបង្ហាញ</string>
|
||||
<string name="action_display_grid">ជ្រុងCompact</string>
|
||||
@ -73,10 +73,10 @@
|
||||
<string name="label_extension_info">អំពីប្រភពម៊េងហ្គា</string>
|
||||
<string name="information_empty_library">បណ្ណាល័យរបស់អ្នកទទេស្អាត</string>
|
||||
<string name="information_empty_category">អ្នកពុំមានការទម្រៀបម៊េងហ្គាឡើយ។ ចុចប៊ូតុងសញ្ញាបូកដើម្បីទម្រៀបម៊េងហ្គាសម្រាប់បណ្ណាល័យរបស់អ្នក។</string>
|
||||
<string name="manga">ម៊េងហ្គា</string>
|
||||
<string name="confirm_lock_change">Authenticateដើម្បីបញ្ចាក់ពីការកែប្រែ</string>
|
||||
<string name="manga">បណ្តុំក្នុងបណ្ណាល័យ</string>
|
||||
<string name="confirm_lock_change">ផ្ទៀងផ្ទាត់ភាពត្រឹមត្រូវដើម្បីបញ្ជាក់ការផ្លាស់ប្តូរ</string>
|
||||
<string name="action_settings">ការកំណត់</string>
|
||||
<string name="action_menu">កន្លែងកំណត់</string>
|
||||
<string name="action_menu">មនុយ</string>
|
||||
<string name="action_mark_previous_as_read">បញ្ចាក់ភាគមុនថាបានអាន</string>
|
||||
<string name="action_delete">លុប</string>
|
||||
<string name="action_sort_latest_chapter">ភាគថ្មីបំផុត</string>
|
||||
@ -86,14 +86,14 @@
|
||||
<string name="action_mark_as_unread">បញ្ជាក់ថាមិនទាន់បានអាន</string>
|
||||
<string name="action_update_library">ធ្វើបច្ចុប្បន្នភាពបណ្ណាល័យ</string>
|
||||
<string name="action_enable_all">បើកទាំងអស់</string>
|
||||
<string name="action_add_category">បញ្ចូលថ្នាក់</string>
|
||||
<string name="action_add_category">បន្ថែមថ្នាក់ក្រម</string>
|
||||
<string name="action_disable_all">បិទទាំងអស់</string>
|
||||
<string name="action_edit">ផ្លាស់ប្ដូរ</string>
|
||||
<string name="action_add">បញ្ចូល</string>
|
||||
<string name="action_edit_categories">កែប្រែថ្នាក់</string>
|
||||
<string name="action_edit_categories">កែប្រែ</string>
|
||||
<string name="action_next_chapter">ភាគបន្ទាប់</string>
|
||||
<string name="action_open_in_browser">បើកនៅក្នុងកម្មវិធីរុករក</string>
|
||||
<string name="action_show_manga">បង្ហាញម៊េងហ្គា</string>
|
||||
<string name="action_show_manga">បង្ហាញត្លុក</string>
|
||||
<string name="action_open_in_web_view">បើកនៅក្នុងWebView</string>
|
||||
<string name="action_migrate">ប្ដូរ</string>
|
||||
<string name="action_display_show_number_of_items">បង្ហាញចំនួនលេខនៃធាតុ</string>
|
||||
@ -109,7 +109,6 @@
|
||||
<string name="action_restore">restore</string>
|
||||
<string name="action_webview_back">ត្រឡប់</string>
|
||||
<string name="action_start_downloading_now">ចាប់ផ្ដើមទាញយកឥឡូវនេះ</string>
|
||||
<string name="action_faq_and_guides">FAQនិងមគ្គុទ្ទេសក៍</string>
|
||||
<string name="loading">កំពុងដំណើរការ…</string>
|
||||
<string name="app_not_available">កម្មវិធីមិនដំណើរការ</string>
|
||||
<string name="pref_category_appearance">រូបរាង</string>
|
||||
@ -188,4 +187,29 @@
|
||||
<string name="information_cloudflare_bypass_failure">បរាជ័យក្នុងការឆ្លងកាត់Cloudflare</string>
|
||||
<string name="action_search_hint">ស្វែងរក…</string>
|
||||
<string name="notification_new_chapters">មានភាគថ្មី</string>
|
||||
<string name="action_copy_to_clipboard">ចម្លងទៅក្ដារតម្បៀតខ្ទាស់</string>
|
||||
<string name="label_backup">បម្រុងទុក និងស្ដារឡើងវិញ</string>
|
||||
<string name="delete_downloaded">លុបអ្វីដែលបានទាញហើយ</string>
|
||||
<string name="label_local">ក្នុងទូរស័ព្ទ</string>
|
||||
<string name="action_filter_interval_passed">សូមឆែកមើលកំឡុងពេល</string>
|
||||
<string name="on">បើក</string>
|
||||
<string name="action_sort_category">តម្រៀបតាមថ្នាក់ក្រម</string>
|
||||
<string name="action_update_category">ធ្វើបច្ចុប្បន្នភាពថ្នាក់ក្រម</string>
|
||||
<string name="action_menu_overflow_description">ជម្រើសបន្ថែមទៀត</string>
|
||||
<string name="off">បិទ</string>
|
||||
<string name="delete_category_confirmation">តើអ្នកចង់លុបថ្នាក់ក្រម %s ឬ?</string>
|
||||
<string name="selected">បានជ្រើសយក</string>
|
||||
<string name="not_selected">មិនបានជ្រើសយក</string>
|
||||
<string name="scanlator">អ្នកបកប្រែ</string>
|
||||
<string name="action_bar_up_description">រុករកទៅលើ</string>
|
||||
<string name="label_data_storage">ទិន្នន័យ និងអង្គផ្ទុក</string>
|
||||
<string name="action_remove_everything">ដកអ្វីៗទាំងអស់ចេញ</string>
|
||||
<string name="sort_category_confirmation">តើអ្នកចង់តម្រៀប តាមអក្ខរក្រមទេ?</string>
|
||||
<string name="action_open_random_manga">បើកត្លុកដោយចៃដន្យ</string>
|
||||
<string name="label_started">បានចាប់ផ្តើមហើយ</string>
|
||||
<string name="action_sort_unread_count">ចំនួនមិនទាន់អាន</string>
|
||||
<string name="action_sort_last_manga_update">ពិនិត្យបច្ចុប្បន្នភាពចុងក្រោយ</string>
|
||||
<string name="delete_category">លុបថ្នាក់ក្រម</string>
|
||||
<string name="action_sort_next_updated">ភាគថ្មីរំពឹងទុកនៅ</string>
|
||||
<string name="action_filter_interval_long">ទាញចូលប្រចាំខែ (28 ថ្ងៃ)</string>
|
||||
</resources>
|
@ -385,7 +385,6 @@
|
||||
<string name="pref_category_library">서재</string>
|
||||
<string name="add_to_library">서재에 추가</string>
|
||||
<string name="save_chapter_as_cbz">CBZ 파일로 저장</string>
|
||||
<string name="pref_library_update_manga_restriction">항목 업데이트 건너뛰기</string>
|
||||
<string name="secure_screen_summary">화면 보안을 켜면 앱을 전환할 때 내용이 숨겨지며 스크린샷을 찍을 수 없습니다</string>
|
||||
<string name="in_library">서재에 추가됨</string>
|
||||
<string name="action_migrate">데이터 이전</string>
|
||||
@ -431,7 +430,6 @@
|
||||
<string name="database_clean">지울 것이 없습니다</string>
|
||||
<string name="learn_more">터치하여 자세히 알아보기</string>
|
||||
<string name="chapter_settings_updated">기본 회차 설정을 업데이트 했습니다</string>
|
||||
<string name="channel_skipped">건너뜀</string>
|
||||
<string name="channel_new_chapters">회차 업데이트</string>
|
||||
<string name="include">포함: %s</string>
|
||||
<string name="label_background_activity">백그라운드 활동</string>
|
||||
@ -474,7 +472,6 @@
|
||||
<string name="notification_size_warning">대규모 업데이트는 소스에 피해를 입히고 업데이트가 느려지고 배터리 사용량이 증가할 수 있습니다. 탭하여 자세히 알아보기.</string>
|
||||
<string name="notification_chapters_single">%1$s화</string>
|
||||
<string name="notification_update_error">%1$d개의 업데이트가 실패했습니다</string>
|
||||
<string name="notification_update_skipped">%1$d개의 업데이트를 건너 뛰었습니다</string>
|
||||
<string name="library_errors_help">서재 업데이트 오류를 해결하려면 %1$s를 참조하세요</string>
|
||||
<string name="skipped_reason_completed">완결된 만화를 건너 뛰었습니다</string>
|
||||
<string name="skipped_reason_not_started">읽지 않은 만화를 건너 뛰었습니다</string>
|
||||
@ -562,7 +559,6 @@
|
||||
<string name="cancel_all_for_series">이 만화의 항목을 모두 취소</string>
|
||||
<string name="action_webview_forward">앞으로</string>
|
||||
<string name="pref_create_folder_per_manga">각각의 폴더에 페이지 저장</string>
|
||||
<string name="action_faq_and_guides">FAQ 및 설명서</string>
|
||||
<string name="pref_update_only_started">읽지 않음</string>
|
||||
<string name="action_move_to_top_all_for_series">이 시리즈를 맨 위로 이동</string>
|
||||
<string name="action_webview_refresh">새로고침</string>
|
||||
@ -714,7 +710,6 @@
|
||||
<string name="action_filter_interval_custom">사용자 지정 가져오기 간격</string>
|
||||
<string name="action_filter_interval_long">매월 가져오기 (28일)</string>
|
||||
<string name="intervals_header">간격</string>
|
||||
<string name="manga_modify_calculated_interval_title">간격 설정</string>
|
||||
<string name="delete_downloaded">다운로드 삭제</string>
|
||||
<string name="has_results">결과가 있는 것만 보기</string>
|
||||
<string name="track_delete_text">이렇게 하면 로컬에서 동기화가 제거됩니다.</string>
|
||||
|
@ -295,8 +295,6 @@
|
||||
<string name="ext_installer_shizuku_unavailable_dialog">Instaliuoti ir paleisti Shizuku, kad galėtumėte naudoti Shizuku kaip papildinių instaliuotoją.</string>
|
||||
<string name="pref_show_navigation_mode">Rodyti lietimui jautrias zonas</string>
|
||||
<string name="label_warning">Įspėjimas</string>
|
||||
<string name="action_faq_and_guides">DUK ir vadovai</string>
|
||||
<string name="pref_library_update_manga_restriction">Praleisti įrašų atnaujinimą</string>
|
||||
<string name="update_72hour">Kas 3 dienas</string>
|
||||
<string name="connected_to_wifi">Kai naudojamas Wi-Fi</string>
|
||||
<string name="ext_updates_pending">Yra naujinių</string>
|
||||
@ -372,7 +370,6 @@
|
||||
<string name="migration_dialog_what_to_include">Pasirinkite duomenis, kuriuos norite įtraukti</string>
|
||||
<string name="copy">Kopijuoti</string>
|
||||
<string name="notification_new_chapters">Atrasti nauji skyriai</string>
|
||||
<string name="notification_update_skipped">Praleistas %1$d naujinys (-iai)</string>
|
||||
<string name="skipped_reason_not_caught_up">Praleista, nes yra neperskaitytų skyrių</string>
|
||||
<string name="update_check_notification_download_in_progress">Atsiunčiama…</string>
|
||||
<string name="file_select_cover">Pasirinkti viršelio vaizdą</string>
|
||||
@ -497,7 +494,6 @@
|
||||
<string name="download_notifier_split_page_path_not_found">Nepavyko rasti puslapio failo kelio %d</string>
|
||||
<string name="channel_common">Bendras</string>
|
||||
<string name="channel_complete">Baigtas</string>
|
||||
<string name="channel_skipped">Praleistas</string>
|
||||
<string name="download_notifier_download_paused">Atsisiuntimas pristabdytas</string>
|
||||
<string name="channel_new_chapters">Skyriaus atnaujinimai</string>
|
||||
<string name="spen_next_page">Kitas puslapis</string>
|
||||
|
@ -204,8 +204,6 @@
|
||||
<string name="update_never">Izslēgts</string>
|
||||
<string name="action_display_language_badge">Valoda</string>
|
||||
<string name="action_sort_chapter_fetch_date">Nodaļas ieneses datums</string>
|
||||
<string name="pref_library_update_manga_restriction">Izlaist ierakstu atjaunināšanu</string>
|
||||
<string name="action_faq_and_guides">Biežāk uzdotie jautājumi un rokasgrāmatas</string>
|
||||
<string name="action_close">Aizvērt</string>
|
||||
<string name="pref_category_library_update">Globāls atjauninājums</string>
|
||||
<string name="parental_controls_info">Šis neliedz neoficiāliem vai, iespējams, nepareizi atzīmētiem paplašinājumiem parādīties NSFW (18+) saturu lietotnē.</string>
|
||||
@ -508,12 +506,10 @@
|
||||
<string name="viewer">Lasīšanas režīms</string>
|
||||
<string name="no_next_chapter">Nākošā nodaļa nav atrasta</string>
|
||||
<string name="empty_screen">Nu, šis ir neveikli</string>
|
||||
<string name="notification_update_skipped">%1$d atjauninājums(-i) izlaists(-i)</string>
|
||||
<string name="update_check_open">Atvērt vietnē GitHub</string>
|
||||
<string name="update_check_eol">Šī Android versija vairs netiek atbalstīta</string>
|
||||
<string name="update_check_notification_download_in_progress">Lejupielādē…</string>
|
||||
<string name="channel_progress">Progress</string>
|
||||
<string name="channel_skipped">Izlaists</string>
|
||||
<string name="decode_image_error">Attēlu nevarēja ielādēt</string>
|
||||
<string name="confirm_set_image_as_cover">Vai izmantot šo attēlu kā vāku\?</string>
|
||||
<string name="pref_category_for_this_series">Priekš šīs sērijas</string>
|
||||
@ -736,7 +732,6 @@
|
||||
<string name="action_filter_interval_dropped">Pametāt\? Par vēlu +20 un 2 mēnešus</string>
|
||||
<string name="pref_chapter_swipe">Vilkt nodaļas</string>
|
||||
<string name="manga_display_modified_interval_title">Atjaunināt katru</string>
|
||||
<string name="manga_modify_calculated_interval_title">Pielāgot intervālu</string>
|
||||
<string name="exception_http">HTTP %d, pārbaudiet vietni iekš WebView</string>
|
||||
<string name="exception_offline">Nav interneta savienojuma</string>
|
||||
<string name="exception_unknown_host">Nevarēja sasniegt %s</string>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user