chore: merge upstream changes.

Signed-off-by: KaiserBh <kaiserbh@proton.me>
This commit is contained in:
KaiserBh 2023-12-05 23:53:20 +11:00
commit 4b9ccb9b75
No known key found for this signature in database
GPG Key ID: 14D73B142042BBA9
141 changed files with 745 additions and 932 deletions

View File

@ -28,7 +28,7 @@ jobs:
uses: actions/dependency-review-action@v3
- name: Set up JDK
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: 17
distribution: adopt

View File

@ -23,7 +23,7 @@ jobs:
uses: gradle/wrapper-validation-action@v1
- name: Set up JDK
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: 17
distribution: adopt

View File

@ -22,7 +22,7 @@ android {
defaultConfig {
applicationId = "eu.kanade.tachiyomi"
versionCode = 110
versionCode = 111
versionName = "0.14.7"
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
@ -196,7 +196,6 @@ dependencies {
// RxJava
implementation(libs.rxjava)
implementation(libs.flowreactivenetwork)
// Networking
implementation(libs.bundles.okhttp)

View File

@ -7,6 +7,9 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!-- Storage -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- For background jobs -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
@ -22,6 +25,8 @@
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.READ_APP_SPECIFIC_LOCALES" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<!-- Remove permission from Firebase dependency -->
<uses-permission android:name="com.google.android.gms.permission.AD_ID"
tools:node="remove" />
@ -36,6 +41,8 @@
android:largeHeap="true"
android:localeConfig="@xml/locales_config"
android:networkSecurityConfig="@xml/network_security_config"
android:preserveLegacyExternalStorage="true"
android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Tachiyomi">
@ -151,10 +158,6 @@
android:name=".data.notification.NotificationReceiver"
android:exported="false" />
<service
android:name=".data.download.DownloadService"
android:exported="false" />
<service
android:name=".extension.util.ExtensionInstallService"
android:exported="false" />
@ -168,6 +171,11 @@
android:value="true" />
</service>
<service
android:name="androidx.work.impl.foreground.SystemForegroundService"
android:foregroundServiceType="dataSync"
tools:node="merge" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"

View File

@ -190,6 +190,7 @@ private fun ExtensionDetails(
key = { it.source.id },
) { source ->
SourceSwitchPreference(
modifier = Modifier.animateItemPlacement(),
source = source,
onClickSourcePreferences = onClickSourcePreferences,
onClickSource = onClickSource,

View File

@ -58,6 +58,7 @@ private fun ExtensionFilterContent(
) {
items(state.languages) { language ->
SwitchPreferenceWidget(
modifier = Modifier.animateItemPlacement(),
title = LocaleHelper.getSourceDisplayName(language, context),
checked = language in state.enabledLanguages,
onCheckedChanged = { onClickLang(language) },

View File

@ -148,12 +148,14 @@ private fun ExtensionContent(
}
ExtensionHeader(
textRes = header.textRes,
modifier = Modifier.animateItemPlacement(),
action = action,
)
}
is ExtensionUiModel.Header.Text -> {
ExtensionHeader(
text = header.text,
modifier = Modifier.animateItemPlacement(),
)
}
}
@ -165,6 +167,7 @@ private fun ExtensionContent(
key = { "extension-${it.hashCode()}" },
) { item ->
ExtensionItem(
modifier = Modifier.animateItemPlacement(),
item = item,
onClickItem = {
when (it) {

View File

@ -132,6 +132,7 @@ private fun MigrateSourceList(
key = { (source, _) -> "migrate-${source.id}" },
) { (source, count) ->
MigrateSourceItem(
modifier = Modifier.animateItemPlacement(),
source = source,
count = count,
onClickItem = { onClickItem(source) },

View File

@ -68,6 +68,7 @@ private fun SourcesFilterContent(
contentType = "source-filter-header",
) {
SourcesFilterHeader(
modifier = Modifier.animateItemPlacement(),
language = language,
enabled = enabled,
onClickItem = onClickLanguage,
@ -80,6 +81,7 @@ private fun SourcesFilterContent(
contentType = { "source-filter-item" },
) { source ->
SourcesFilterItem(
modifier = Modifier.animateItemPlacement(),
source = source,
enabled = "${source.id}" !in state.disabledSources,
onClickItem = onClickSource,

View File

@ -74,10 +74,12 @@ fun SourcesScreen(
when (model) {
is SourceUiModel.Header -> {
SourceHeader(
modifier = Modifier.animateItemPlacement(),
language = model.language,
)
}
is SourceUiModel.Item -> SourceItem(
modifier = Modifier.animateItemPlacement(),
source = model.source,
onClickItem = onClickItem,
onLongClickItem = onLongClickItem,

View File

@ -107,6 +107,7 @@ private fun CategoryContent(
key = { _, category -> "category-${category.id}" },
) { index, category ->
CategoryListItem(
modifier = Modifier.animateItemPlacement(),
category = category,
canMoveUp = index != 0,
canMoveDown = index != categories.lastIndex,

View File

@ -123,6 +123,7 @@ private fun HistoryScreenContent(
when (item) {
is HistoryUiModel.Header -> {
RelativeDateHeader(
modifier = Modifier.animateItemPlacement(),
date = item.date,
relativeTime = relativeTime,
dateFormat = dateFormat,
@ -131,6 +132,7 @@ private fun HistoryScreenContent(
is HistoryUiModel.Item -> {
val value = item.item
HistoryItem(
modifier = Modifier.animateItemPlacement(),
history = value,
onClickCover = { onClickCover(value) },
onClickResume = { onClickResume(value) },

View File

@ -114,7 +114,8 @@ object SettingsDataScreen : SearchableSettings {
return Preference.PreferenceItem.TextPreference(
title = stringResource(MR.strings.pref_storage_location),
subtitle = remember(storageDir) {
(UniFile.fromUri(context, storageDir.toUri())?.filePath)
val file = UniFile.fromUri(context, storageDir.toUri())
file?.filePath ?: file?.uri?.toString()
} ?: stringResource(MR.strings.invalid_location, storageDir),
onClick = {
try {

View File

@ -13,7 +13,6 @@ import eu.kanade.presentation.more.settings.Preference
import eu.kanade.presentation.more.settings.PreferenceScaffold
import eu.kanade.presentation.more.settings.screen.about.AboutScreen
import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.system.DeviceUtil
import eu.kanade.tachiyomi.util.system.WebViewUtil
import kotlinx.coroutines.guava.await

View File

@ -18,17 +18,18 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEach
import androidx.lifecycle.asFlow
import androidx.work.WorkInfo
import androidx.work.WorkQuery
import cafe.adriel.voyager.core.model.ScreenModel
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.domain.ui.UiPreferences
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.AppBarActions
import eu.kanade.presentation.util.Screen
import eu.kanade.presentation.util.ioCoroutineScope
import eu.kanade.tachiyomi.util.lang.toDateTimestampString
import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.workManager
import kotlinx.collections.immutable.persistentListOf
@ -39,6 +40,9 @@ import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.plus
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.Date
class WorkerInfoScreen : Screen() {
@ -116,22 +120,19 @@ class WorkerInfoScreen : Screen() {
private val workManager = context.workManager
val finished = workManager
.getWorkInfosLiveData(
.getWorkInfosFlow(
WorkQuery.fromStates(WorkInfo.State.SUCCEEDED, WorkInfo.State.FAILED, WorkInfo.State.CANCELLED),
)
.asFlow()
.map(::constructString)
.stateIn(ioCoroutineScope, SharingStarted.WhileSubscribed(), "")
val running = workManager
.getWorkInfosLiveData(WorkQuery.fromStates(WorkInfo.State.RUNNING))
.asFlow()
.getWorkInfosFlow(WorkQuery.fromStates(WorkInfo.State.RUNNING))
.map(::constructString)
.stateIn(ioCoroutineScope, SharingStarted.WhileSubscribed(), "")
val enqueued = workManager
.getWorkInfosLiveData(WorkQuery.fromStates(WorkInfo.State.ENQUEUED))
.asFlow()
.getWorkInfosFlow(WorkQuery.fromStates(WorkInfo.State.ENQUEUED))
.map(::constructString)
.stateIn(ioCoroutineScope, SharingStarted.WhileSubscribed(), "")
@ -146,6 +147,16 @@ class WorkerInfoScreen : Screen() {
appendLine(" - $it")
}
appendLine("State: ${workInfo.state}")
if (workInfo.state == WorkInfo.State.ENQUEUED) {
appendLine(
"Next scheduled run: ${Date(workInfo.nextScheduleTimeMillis).toDateTimestampString(
UiPreferences.dateFormat(
Injekt.get<UiPreferences>().dateFormat().get(),
),
)}",
)
appendLine("Attempt #${workInfo.runAttemptCount + 1}")
}
appendLine()
}
}

View File

@ -1,27 +0,0 @@
package eu.kanade.presentation.reader
import androidx.annotation.IntRange
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import kotlin.math.abs
@Composable
fun BrightnessOverlay(
@IntRange(from = -100, to = 100) value: Int,
) {
if (value >= 0) return
Canvas(
modifier = Modifier
.fillMaxSize()
.graphicsLayer {
alpha = abs(value) / 100f
},
) {
drawRect(Color.Black)
}
}

View File

@ -11,8 +11,6 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.tooling.preview.PreviewLightDark
import dev.icerock.moko.resources.StringResource
import eu.kanade.domain.manga.model.readerOrientation
@ -72,7 +70,7 @@ private fun DialogContent(
selected = mode
},
modifier = Modifier.fillMaxWidth(),
imageVector = ImageVector.vectorResource(mode.iconRes),
imageVector = mode.icon,
title = stringResource(mode.stringRes),
)
}

View File

@ -0,0 +1,49 @@
package eu.kanade.presentation.reader
import androidx.annotation.ColorInt
import androidx.annotation.IntRange
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import kotlin.math.abs
@Composable
fun ReaderContentOverlay(
@IntRange(from = -100, to = 100) brightness: Int,
@ColorInt color: Int?,
colorBlendMode: BlendMode?,
modifier: Modifier = Modifier,
) {
if (brightness < 0) {
val brightnessAlpha = remember(brightness) {
abs(brightness) / 100f
}
Canvas(
modifier = modifier
.fillMaxSize()
.graphicsLayer {
alpha = brightnessAlpha
},
) {
drawRect(Color.Black)
}
}
if (color != null) {
Canvas(
modifier = modifier
.fillMaxSize(),
) {
drawRect(
color = Color(color),
blendMode = colorBlendMode ?: BlendMode.SrcOver,
)
}
}
}

View File

@ -49,7 +49,7 @@ fun BottomReaderBar(
IconButton(onClick = onClickOrientation) {
Icon(
painter = painterResource(orientation.iconRes),
imageVector = orientation.icon,
contentDescription = stringResource(MR.strings.rotation_type),
)
}

View File

@ -1,6 +1,5 @@
package eu.kanade.presentation.reader.settings
import android.os.Build
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.material3.FilterChip
import androidx.compose.material3.Text
@ -10,6 +9,7 @@ import androidx.core.graphics.alpha
import androidx.core.graphics.blue
import androidx.core.graphics.green
import androidx.core.graphics.red
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences.Companion.ColorFilterMode
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
import tachiyomi.core.preference.getAndSet
import tachiyomi.i18n.MR
@ -21,25 +21,6 @@ import tachiyomi.presentation.core.util.collectAsState
@Composable
internal fun ColumnScope.ColorFilterPage(screenModel: ReaderSettingsScreenModel) {
val colorFilterModes = buildList {
addAll(
listOf(
MR.strings.label_default,
MR.strings.filter_mode_multiply,
MR.strings.filter_mode_screen,
),
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
addAll(
listOf(
MR.strings.filter_mode_overlay,
MR.strings.filter_mode_lighten,
MR.strings.filter_mode_darken,
),
)
}
}.map { stringResource(it) }
val customBrightness by screenModel.preferences.customBrightness().collectAsState()
CheckboxItem(
label = stringResource(MR.strings.pref_custom_brightness),
@ -118,11 +99,11 @@ internal fun ColumnScope.ColorFilterPage(screenModel: ReaderSettingsScreenModel)
val colorFilterMode by screenModel.preferences.colorFilterMode().collectAsState()
SettingsChipRow(MR.strings.pref_color_filter_mode) {
colorFilterModes.mapIndexed { index, it ->
ColorFilterMode.mapIndexed { index, it ->
FilterChip(
selected = colorFilterMode == index,
onClick = { screenModel.preferences.colorFilterMode().set(index) },
label = { Text(it) },
label = { Text(stringResource(it.first)) },
)
}
}

View File

@ -193,6 +193,7 @@ fun TrackerSearch(
type = it.publishing_type.toLowerCase(Locale.current).capitalize(Locale.current),
startDate = it.start_date,
status = it.publishing_status.toLowerCase(Locale.current).capitalize(Locale.current),
score = it.score,
description = it.summary.trim(),
selected = it == selected,
onClick = { onSelectedChange(it) },
@ -218,6 +219,7 @@ private fun SearchResultItem(
type: String,
startDate: String,
status: String,
score: Float,
description: String,
selected: Boolean,
onClick: () -> Unit,
@ -279,6 +281,12 @@ private fun SearchResultItem(
text = status,
)
}
if (score != -1f) {
SearchResultItemDetails(
title = stringResource(MR.strings.score),
text = score.toString(),
)
}
}
}
if (description.isNotBlank()) {

View File

@ -53,6 +53,7 @@ internal fun LazyListScope.updatesLastUpdatedItem(
item(key = "updates-lastUpdated") {
Box(
modifier = Modifier
.animateItemPlacement()
.padding(horizontal = MaterialTheme.padding.medium, vertical = MaterialTheme.padding.small),
) {
Text(
@ -89,12 +90,14 @@ internal fun LazyListScope.updatesUiItems(
when (item) {
is UpdatesUiModel.Header -> {
ListGroupHeader(
modifier = Modifier.animateItemPlacement(),
text = item.date,
)
}
is UpdatesUiModel.Item -> {
val updatesItem = item.item
UpdatesUiItem(
modifier = Modifier.animateItemPlacement(),
update = updatesItem.update,
selected = updatesItem.selected,
readProgress = updatesItem.update.lastPageRead

View File

@ -416,6 +416,11 @@ object Migrations {
newKey = { Preference.appStateKey(it) },
)
}
if (oldVersion < 111) {
File(context.cacheDir, "dl_index_cache")
.takeIf { it.exists() }
?.delete()
}
return true
}

View File

@ -17,6 +17,7 @@ import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.util.system.cancelNotification
import eu.kanade.tachiyomi.util.system.isRunning
import eu.kanade.tachiyomi.util.system.setForegroundSafely
import eu.kanade.tachiyomi.util.system.workManager
import logcat.LogPriority
import tachiyomi.core.util.system.logcat
@ -39,19 +40,14 @@ class BackupCreateJob(private val context: Context, workerParams: WorkerParamete
if (isAutoBackup && BackupRestoreJob.isRunning(context)) return Result.retry()
val backupPreferences = Injekt.get<BackupPreferences>()
val uri = inputData.getString(LOCATION_URI_KEY)?.toUri()
?: getAutomaticBackupLocation()
?: return Result.failure()
val flags = inputData.getInt(BACKUP_FLAGS_KEY, BackupCreateFlags.AutomaticDefaults)
setForegroundSafely()
try {
setForeground(getForegroundInfo())
} catch (e: IllegalStateException) {
logcat(LogPriority.ERROR, e) { "Not allowed to run on foreground service" }
}
val flags = inputData.getInt(BACKUP_FLAGS_KEY, BackupCreateFlags.AutomaticDefaults)
val backupPreferences = Injekt.get<BackupPreferences>()
return try {
val location = BackupCreator(context).createBackup(uri, flags, isAutoBackup)

View File

@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.data.backup
import android.Manifest
import android.content.Context
import android.net.Uri
import com.hippo.unifile.UniFile
@ -31,7 +30,6 @@ import eu.kanade.tachiyomi.data.backup.models.backupTrackMapper
import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.preferenceKey
import eu.kanade.tachiyomi.source.sourcePreferences
import eu.kanade.tachiyomi.util.system.hasPermission
import kotlinx.serialization.protobuf.ProtoBuf
import logcat.LogPriority
import okio.buffer
@ -73,10 +71,6 @@ class BackupCreator(
* @param isAutoBackup backup called from scheduled backup job
*/
suspend fun createBackup(uri: Uri, flags: Int, isAutoBackup: Boolean): String {
if (!context.hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
throw IllegalStateException(context.stringResource(MR.strings.missing_storage_permission))
}
val databaseManga = getFavorites.await()
val backup = Backup(
backupMangas(databaseManga, flags),

View File

@ -79,11 +79,7 @@ class BackupNotifier(private val context: Context) {
addAction(
R.drawable.ic_share_24dp,
context.stringResource(MR.strings.action_share),
NotificationReceiver.shareBackupPendingBroadcast(
context,
unifile.uri,
Notifications.ID_BACKUP_COMPLETE,
),
NotificationReceiver.shareBackupPendingBroadcast(context, unifile.uri),
)
show(Notifications.ID_BACKUP_COMPLETE)

View File

@ -12,6 +12,7 @@ import androidx.work.workDataOf
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.util.system.cancelNotification
import eu.kanade.tachiyomi.util.system.isRunning
import eu.kanade.tachiyomi.util.system.setForegroundSafely
import eu.kanade.tachiyomi.util.system.workManager
import kotlinx.coroutines.CancellationException
import logcat.LogPriority
@ -29,11 +30,7 @@ class BackupRestoreJob(private val context: Context, workerParams: WorkerParamet
?: return Result.failure()
val sync = inputData.getBoolean(SYNC_KEY, false)
try {
setForeground(getForegroundInfo())
} catch (e: IllegalStateException) {
logcat(LogPriority.ERROR, e) { "Not allowed to run on foreground service" }
}
setForegroundSafely()
return try {
val restorer = BackupRestorer(context, notifier)

View File

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.data.coil
import androidx.core.net.toUri
import coil.ImageLoader
import coil.decode.DataSource
import coil.decode.ImageSource
@ -10,6 +11,7 @@ import coil.fetch.SourceResult
import coil.network.HttpException
import coil.request.Options
import coil.request.Parameters
import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher.Companion.USE_CUSTOM_COVER
import eu.kanade.tachiyomi.network.await
@ -24,6 +26,7 @@ import okio.Path.Companion.toOkioPath
import okio.Source
import okio.buffer
import okio.sink
import okio.source
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.MangaCover
@ -69,8 +72,9 @@ class MangaCoverFetcher(
// diskCacheKey is thumbnail_url
if (url == null) error("No cover specified")
return when (getResourceType(url)) {
Type.URL -> httpLoader()
Type.File -> fileLoader(File(url.substringAfter("file://")))
Type.URI -> fileUriLoader(url)
Type.URL -> httpLoader()
null -> error("Invalid image")
}
}
@ -83,6 +87,18 @@ class MangaCoverFetcher(
)
}
private fun fileUriLoader(uri: String): FetchResult {
val source = UniFile.fromUri(options.context, uri.toUri())!!
.openInputStream()
.source()
.buffer()
return SourceResult(
source = ImageSource(source = source, context = options.context),
mimeType = "image/*",
dataSource = DataSource.DISK,
)
}
private suspend fun httpLoader(): FetchResult {
// Only cache separately if it's a library item
val libraryCoverCacheFile = if (isLibraryManga) {
@ -256,12 +272,15 @@ class MangaCoverFetcher(
cover.isNullOrEmpty() -> null
cover.startsWith("http", true) || cover.startsWith("Custom-", true) -> Type.URL
cover.startsWith("/") || cover.startsWith("file://") -> Type.File
cover.startsWith("content") -> Type.URI
else -> null
}
}
private enum class Type {
File, URL
File,
URI,
URL,
}
class MangaFactory(

View File

@ -94,7 +94,7 @@ class DownloadCache(
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
private val diskCacheFile: File
get() = File(context.cacheDir, "dl_index_cache")
get() = File(context.cacheDir, "dl_index_cache_v2")
private val rootDownloadsDirLock = Mutex()
private var rootDownloadsDir = RootDirectory(provider.downloadsDir)

View File

@ -0,0 +1,117 @@
package eu.kanade.tachiyomi.data.download
import android.content.Context
import android.content.pm.ServiceInfo
import android.os.Build
import androidx.lifecycle.asFlow
import androidx.work.CoroutineWorker
import androidx.work.ExistingWorkPolicy
import androidx.work.ForegroundInfo
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkInfo
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.util.system.isConnectedToWifi
import eu.kanade.tachiyomi.util.system.isOnline
import eu.kanade.tachiyomi.util.system.notificationBuilder
import eu.kanade.tachiyomi.util.system.setForegroundSafely
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import tachiyomi.domain.download.service.DownloadPreferences
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
/**
* This worker is used to manage the downloader. The system can decide to stop the worker, in
* which case the downloader is also stopped. It's also stopped while there's no network available.
*/
class DownloadJob(context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams) {
private val downloadManager: DownloadManager = Injekt.get()
private val downloadPreferences: DownloadPreferences = Injekt.get()
override suspend fun getForegroundInfo(): ForegroundInfo {
val notification = applicationContext.notificationBuilder(Notifications.CHANNEL_DOWNLOADER_PROGRESS) {
setContentTitle(applicationContext.getString(R.string.download_notifier_downloader_title))
setSmallIcon(android.R.drawable.stat_sys_download)
}.build()
return ForegroundInfo(
Notifications.ID_DOWNLOAD_CHAPTER_PROGRESS,
notification,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
} else {
0
},
)
}
override suspend fun doWork(): Result {
var active = checkConnectivity() && downloadManager.downloaderStart()
if (!active) {
return Result.failure()
}
setForegroundSafely()
// Keep the worker running when needed
while (active) {
delay(100)
active = !isStopped && downloadManager.isRunning && checkConnectivity()
}
return Result.success()
}
private fun checkConnectivity(): Boolean {
return with(applicationContext) {
if (isOnline()) {
val noWifi = downloadPreferences.downloadOnlyOverWifi().get() && !isConnectedToWifi()
if (noWifi) {
downloadManager.downloaderStop(
applicationContext.getString(R.string.download_notifier_text_only_wifi),
)
}
!noWifi
} else {
downloadManager.downloaderStop(applicationContext.getString(R.string.download_notifier_no_network))
false
}
}
}
companion object {
private const val TAG = "Downloader"
fun start(context: Context) {
val request = OneTimeWorkRequestBuilder<DownloadJob>()
.addTag(TAG)
.build()
WorkManager.getInstance(context)
.enqueueUniqueWork(TAG, ExistingWorkPolicy.REPLACE, request)
}
fun stop(context: Context) {
WorkManager.getInstance(context)
.cancelUniqueWork(TAG)
}
fun isRunning(context: Context): Boolean {
return WorkManager.getInstance(context)
.getWorkInfosForUniqueWork(TAG)
.get()
.let { list -> list.count { it.state == WorkInfo.State.RUNNING } == 1 }
}
fun isRunningFlow(context: Context): Flow<Boolean> {
return WorkManager.getInstance(context)
.getWorkInfosForUniqueWorkLiveData(TAG)
.asFlow()
.map { list -> list.count { it.state == WorkInfo.State.RUNNING } == 1 }
}
}
}

View File

@ -46,6 +46,9 @@ class DownloadManager(
*/
private val downloader = Downloader(context, provider, cache)
val isRunning: Boolean
get() = downloader.isRunning
/**
* Queue to delay the deletion of a list of chapters until triggered.
*/
@ -59,13 +62,19 @@ class DownloadManager(
fun downloaderStop(reason: String? = null) = downloader.stop(reason)
val isDownloaderRunning
get() = DownloadService.isRunning
get() = DownloadJob.isRunningFlow(context)
/**
* Tells the downloader to begin downloads.
*/
fun startDownloads() {
DownloadService.start(context)
if (downloader.isRunning) return
if (DownloadJob.isRunning(context)) {
downloader.start()
} else {
DownloadJob.start(context)
}
}
/**
@ -94,22 +103,16 @@ class DownloadManager(
return queueState.value.find { it.chapter.id == chapterId }
}
fun startDownloadNow(chapterId: Long?) {
if (chapterId == null) return
val download = getQueuedDownloadOrNull(chapterId)
fun startDownloadNow(chapterId: Long) {
val existingDownload = getQueuedDownloadOrNull(chapterId)
// If not in queue try to start a new download
val toAdd = download ?: runBlocking { Download.fromChapterId(chapterId) } ?: return
val queue = queueState.value.toMutableList()
download?.let { queue.remove(it) }
queue.add(0, toAdd)
reorderQueue(queue)
if (!downloader.isRunning) {
if (DownloadService.isRunning(context)) {
downloader.start()
} else {
DownloadService.start(context)
}
val toAdd = existingDownload ?: runBlocking { Download.fromChapterId(chapterId) } ?: return
queueState.value.toMutableList().apply {
existingDownload?.let { remove(it) }
add(0, toAdd)
reorderQueue(this)
}
startDownloads()
}
/**
@ -143,7 +146,7 @@ class DownloadManager(
addAll(0, downloads)
reorderQueue(this)
}
if (!DownloadService.isRunning(context)) DownloadService.start(context)
if (!DownloadJob.isRunning(context)) startDownloads()
}
/**

View File

@ -1,151 +0,0 @@
package eu.kanade.tachiyomi.data.download
import android.app.Notification
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.IBinder
import android.os.PowerManager
import androidx.core.content.ContextCompat
import dev.icerock.moko.resources.StringResource
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.util.system.acquireWakeLock
import eu.kanade.tachiyomi.util.system.isConnectedToWifi
import eu.kanade.tachiyomi.util.system.isOnline
import eu.kanade.tachiyomi.util.system.isServiceRunning
import eu.kanade.tachiyomi.util.system.notificationBuilder
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import logcat.LogPriority
import ru.beryukhov.reactivenetwork.ReactiveNetwork
import tachiyomi.core.i18n.stringResource
import tachiyomi.core.util.lang.withUIContext
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.download.service.DownloadPreferences
import tachiyomi.i18n.MR
import uy.kohesive.injekt.injectLazy
/**
* This service is used to manage the downloader. The system can decide to stop the service, in
* which case the downloader is also stopped. It's also stopped while there's no network available.
* While the downloader is running, a wake lock will be held.
*/
class DownloadService : Service() {
companion object {
private val _isRunning = MutableStateFlow(false)
val isRunning = _isRunning.asStateFlow()
/**
* Starts this service.
*
* @param context the application context.
*/
fun start(context: Context) {
val intent = Intent(context, DownloadService::class.java)
ContextCompat.startForegroundService(context, intent)
}
/**
* Stops this service.
*
* @param context the application context.
*/
fun stop(context: Context) {
context.stopService(Intent(context, DownloadService::class.java))
}
/**
* Returns the status of the service.
*
* @param context the application context.
* @return true if the service is running, false otherwise.
*/
fun isRunning(context: Context): Boolean {
return context.isServiceRunning(DownloadService::class.java)
}
}
private val downloadManager: DownloadManager by injectLazy()
private val downloadPreferences: DownloadPreferences by injectLazy()
/**
* Wake lock to prevent the device to enter sleep mode.
*/
private lateinit var wakeLock: PowerManager.WakeLock
private lateinit var scope: CoroutineScope
override fun onCreate() {
scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
startForeground(Notifications.ID_DOWNLOAD_CHAPTER_PROGRESS, getPlaceholderNotification())
wakeLock = acquireWakeLock(javaClass.name)
_isRunning.value = true
listenNetworkChanges()
}
override fun onDestroy() {
scope.cancel()
_isRunning.value = false
downloadManager.downloaderStop()
if (wakeLock.isHeld) {
wakeLock.release()
}
}
// Not used
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return START_NOT_STICKY
}
// Not used
override fun onBind(intent: Intent): IBinder? {
return null
}
private fun downloaderStop(string: StringResource) {
downloadManager.downloaderStop(stringResource(string))
}
private fun listenNetworkChanges() {
ReactiveNetwork()
.observeNetworkConnectivity(applicationContext)
.onEach {
withUIContext {
if (isOnline()) {
if (downloadPreferences.downloadOnlyOverWifi().get() && !isConnectedToWifi()) {
downloaderStop(MR.strings.download_notifier_text_only_wifi)
} else {
val started = downloadManager.downloaderStart()
if (!started) stopSelf()
}
} else {
downloaderStop(MR.strings.download_notifier_no_network)
}
}
}
.catch { error ->
withUIContext {
logcat(LogPriority.ERROR, error)
toast(MR.strings.download_queue_error)
stopSelf()
}
}
.launchIn(scope)
}
private fun getPlaceholderNotification(): Notification {
return notificationBuilder(Notifications.CHANNEL_DOWNLOADER_PROGRESS) {
setContentTitle(stringResource(MR.strings.download_notifier_downloader_title))
}.build()
}
}

View File

@ -161,10 +161,7 @@ class Downloader(
isPaused = false
// Prevent recursion when DownloadService.onDestroy() calls downloader.stop()
if (DownloadService.isRunning.value) {
DownloadService.stop(context)
}
DownloadJob.stop(context)
}
/**
@ -310,7 +307,7 @@ class Downloader(
)
}
}
DownloadService.start(context)
DownloadJob.start(context)
}
}
}

View File

@ -1,6 +1,8 @@
package eu.kanade.tachiyomi.data.library
import android.content.Context
import android.content.pm.ServiceInfo
import android.os.Build
import androidx.work.BackoffPolicy
import androidx.work.Constraints
import androidx.work.CoroutineWorker
@ -28,6 +30,7 @@ import eu.kanade.tachiyomi.util.storage.getUriCompat
import eu.kanade.tachiyomi.util.system.createFileInCacheDir
import eu.kanade.tachiyomi.util.system.isConnectedToWifi
import eu.kanade.tachiyomi.util.system.isRunning
import eu.kanade.tachiyomi.util.system.setForegroundSafely
import eu.kanade.tachiyomi.util.system.workManager
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.async
@ -106,11 +109,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
}
}
try {
setForeground(getForegroundInfo())
} catch (e: IllegalStateException) {
logcat(LogPriority.ERROR, e) { "Not allowed to set foreground job" }
}
setForegroundSafely()
libraryPreferences.lastUpdatedTimestamp().set(Date().time)
@ -140,6 +139,11 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
return ForegroundInfo(
Notifications.ID_LIBRARY_PROGRESS,
notifier.progressNotificationBuilder.build(),
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
} else {
0
},
)
}

View File

@ -1,6 +1,8 @@
package eu.kanade.tachiyomi.data.library
import android.content.Context
import android.content.pm.ServiceInfo
import android.os.Build
import androidx.work.CoroutineWorker
import androidx.work.ExistingWorkPolicy
import androidx.work.ForegroundInfo
@ -16,6 +18,7 @@ import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.source.UnmeteredSource
import eu.kanade.tachiyomi.util.prepUpdateCover
import eu.kanade.tachiyomi.util.system.isRunning
import eu.kanade.tachiyomi.util.system.setForegroundSafely
import eu.kanade.tachiyomi.util.system.workManager
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.async
@ -51,11 +54,7 @@ class MetadataUpdateJob(private val context: Context, workerParams: WorkerParame
private var mangaToUpdate: List<LibraryManga> = mutableListOf()
override suspend fun doWork(): Result {
try {
setForeground(getForegroundInfo())
} catch (e: IllegalStateException) {
logcat(LogPriority.ERROR, e) { "Not allowed to set foreground job" }
}
setForegroundSafely()
addMangaToQueue()
@ -82,6 +81,11 @@ class MetadataUpdateJob(private val context: Context, workerParams: WorkerParame
return ForegroundInfo(
Notifications.ID_LIBRARY_PROGRESS,
notifier.progressNotificationBuilder.build(),
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
} else {
0
},
)
}

View File

@ -7,6 +7,7 @@ import android.content.Intent
import android.net.Uri
import android.os.Build
import androidx.core.net.toUri
import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.data.backup.BackupRestoreJob
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
@ -15,7 +16,6 @@ import eu.kanade.tachiyomi.data.updater.AppUpdateDownloadJob
import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.storage.getUriCompat
import eu.kanade.tachiyomi.util.system.cancelNotification
import eu.kanade.tachiyomi.util.system.getParcelableExtraCompat
import eu.kanade.tachiyomi.util.system.notificationManager
@ -36,7 +36,6 @@ import tachiyomi.i18n.MR
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.io.File
import eu.kanade.tachiyomi.BuildConfig.APPLICATION_ID as ID
/**
@ -65,15 +64,13 @@ class NotificationReceiver : BroadcastReceiver() {
ACTION_SHARE_IMAGE ->
shareImage(
context,
intent.getStringExtra(EXTRA_FILE_LOCATION)!!,
intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1),
intent.getStringExtra(EXTRA_URI)!!.toUri(),
)
// Delete image from path and dismiss notification
ACTION_DELETE_IMAGE ->
deleteImage(
context,
intent.getStringExtra(EXTRA_FILE_LOCATION)!!,
intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1),
intent.getStringExtra(EXTRA_URI)!!.toUri(),
)
// Share backup file
ACTION_SHARE_BACKUP ->
@ -81,7 +78,6 @@ class NotificationReceiver : BroadcastReceiver() {
context,
intent.getParcelableExtraCompat(EXTRA_URI)!!,
"application/x-protobuf+gzip",
intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1),
)
ACTION_CANCEL_RESTORE -> cancelRestore(context)
@ -140,12 +136,10 @@ class NotificationReceiver : BroadcastReceiver() {
* Called to start share intent to share image
*
* @param context context of application
* @param path path of file
* @param notificationId id of notification
* @param uri path of file
*/
private fun shareImage(context: Context, path: String, notificationId: Int) {
dismissNotification(context, notificationId)
context.startActivity(File(path).getUriCompat(context).toShareIntent(context))
private fun shareImage(context: Context, uri: Uri) {
context.startActivity(uri.toShareIntent(context))
}
/**
@ -153,10 +147,8 @@ class NotificationReceiver : BroadcastReceiver() {
*
* @param context context of application
* @param path path of file
* @param notificationId id of notification
*/
private fun shareFile(context: Context, uri: Uri, fileMimeType: String, notificationId: Int) {
dismissNotification(context, notificationId)
private fun shareFile(context: Context, uri: Uri, fileMimeType: String) {
context.startActivity(uri.toShareIntent(context, fileMimeType))
}
@ -183,17 +175,11 @@ class NotificationReceiver : BroadcastReceiver() {
/**
* Called to delete image
*
* @param path path of file
* @param notificationId id of notification
* @param uri path of file
*/
private fun deleteImage(context: Context, path: String, notificationId: Int) {
dismissNotification(context, notificationId)
// Delete file
val file = File(path)
file.delete()
DiskUtil.scanMedia(context, file.toUri())
private fun deleteImage(context: Context, uri: Uri) {
UniFile.fromUri(context, uri)?.delete()
DiskUtil.scanMedia(context, uri)
}
/**
@ -423,18 +409,17 @@ class NotificationReceiver : BroadcastReceiver() {
}
/**
* Returns [PendingIntent] that starts a service which cancels the notification and starts a share activity
* Returns [PendingIntent] that starts a share activity
*
* @param context context of application
* @param path location path of file
* @param uri location path of file
* @param notificationId id of notification
* @return [PendingIntent]
*/
internal fun shareImagePendingBroadcast(context: Context, path: String, notificationId: Int): PendingIntent {
internal fun shareImagePendingBroadcast(context: Context, uri: Uri): PendingIntent {
val intent = Intent(context, NotificationReceiver::class.java).apply {
action = ACTION_SHARE_IMAGE
putExtra(EXTRA_FILE_LOCATION, path)
putExtra(EXTRA_NOTIFICATION_ID, notificationId)
putExtra(EXTRA_URI, uri.toString())
}
return PendingIntent.getBroadcast(
context,
@ -448,15 +433,13 @@ class NotificationReceiver : BroadcastReceiver() {
* Returns [PendingIntent] that starts a service which removes an image from disk
*
* @param context context of application
* @param path location path of file
* @param notificationId id of notification
* @param uri location path of file
* @return [PendingIntent]
*/
internal fun deleteImagePendingBroadcast(context: Context, path: String, notificationId: Int): PendingIntent {
internal fun deleteImagePendingBroadcast(context: Context, uri: Uri): PendingIntent {
val intent = Intent(context, NotificationReceiver::class.java).apply {
action = ACTION_DELETE_IMAGE
putExtra(EXTRA_FILE_LOCATION, path)
putExtra(EXTRA_NOTIFICATION_ID, notificationId)
putExtra(EXTRA_URI, uri.toString())
}
return PendingIntent.getBroadcast(
context,
@ -639,14 +622,12 @@ class NotificationReceiver : BroadcastReceiver() {
*
* @param context context of application
* @param uri uri of backup file
* @param notificationId id of notification
* @return [PendingIntent]
*/
internal fun shareBackupPendingBroadcast(context: Context, uri: Uri, notificationId: Int): PendingIntent {
internal fun shareBackupPendingBroadcast(context: Context, uri: Uri): PendingIntent {
val intent = Intent(context, NotificationReceiver::class.java).apply {
action = ACTION_SHARE_BACKUP
putExtra(EXTRA_URI, uri)
putExtra(EXTRA_NOTIFICATION_ID, notificationId)
}
return PendingIntent.getBroadcast(
context,

View File

@ -153,6 +153,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|month
|day
|}
|averageScore
|}
|}
|}
@ -309,6 +310,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
struct["status"]!!.jsonPrimitive.contentOrNull ?: "",
parseDate(struct, "startDate"),
struct["chapters"]!!.jsonPrimitive.intOrNull ?: 0,
struct["averageScore"]?.jsonPrimitive?.intOrNull ?: -1,
)
}

View File

@ -19,6 +19,7 @@ data class ALManga(
val publishing_status: String,
val start_date_fuzzy: Long,
val total_chapters: Int,
val average_score: Int,
) {
fun toTrack() = TrackSearch.create(TrackerManager.ANILIST).apply {
@ -27,6 +28,7 @@ data class ALManga(
total_chapters = this@ALManga.total_chapters
cover_url = image_url_lge
summary = description?.htmlDecode() ?: ""
score = average_score.toFloat()
tracking_url = AnilistApi.mangaUrl(media_id)
publishing_status = this@ALManga.publishing_status
publishing_type = format

View File

@ -11,6 +11,7 @@ import eu.kanade.tachiyomi.network.parseAs
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.floatOrNull
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
@ -108,11 +109,13 @@ class BangumiApi(
} else {
0
}
val rating = obj["rating"]?.jsonObject?.get("score")?.jsonPrimitive?.floatOrNull ?: -1f
return TrackSearch.create(trackId).apply {
media_id = obj["id"]!!.jsonPrimitive.long
title = obj["name_cn"]!!.jsonPrimitive.content
cover_url = coverUrl
summary = obj["name"]!!.jsonPrimitive.content
score = rating
tracking_url = obj["url"]!!.jsonPrimitive.content
total_chapters = totalChapters
}

View File

@ -279,7 +279,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
private const val algoliaAppId = "AWQO5J657S"
private const val algoliaFilter =
"&facetFilters=%5B%22kind%3Amanga%22%5D&attributesToRetrieve=" +
"%5B%22synopsis%22%2C%22canonicalTitle%22%2C%22chapterCount%22%2C%22" +
"%5B%22synopsis%22%2C%22averageRating%22%2C%22canonicalTitle%22%2C%22chapterCount%22%2C%22" +
"posterImage%22%2C%22startDate%22%2C%22subtype%22%2C%22endDate%22%2C%20%22id%22%5D"
fun mangaUrl(remoteId: Long): String {

View File

@ -28,6 +28,7 @@ class KitsuSearchManga(obj: JsonObject) {
null
}
private val synopsis = obj["synopsis"]?.jsonPrimitive?.contentOrNull
private val rating = obj["averageRating"]?.jsonPrimitive?.contentOrNull?.toFloatOrNull()
private var startDate = obj["startDate"]?.jsonPrimitive?.contentOrNull?.let {
val outputDf = SimpleDateFormat("yyyy-MM-dd", Locale.US)
outputDf.format(Date(it.toLong() * 1000))
@ -42,6 +43,7 @@ class KitsuSearchManga(obj: JsonObject) {
cover_url = original ?: ""
summary = synopsis ?: ""
tracking_url = KitsuApi.mangaUrl(media_id)
score = rating ?: -1f
publishing_status = if (endDate == null) {
"Publishing"
} else {

View File

@ -20,7 +20,7 @@ class TrackSearch : Track {
override var total_chapters: Int = 0
override var score: Float = 0f
override var score: Float = -1f
override var status: Int = 0

View File

@ -16,6 +16,7 @@ import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.boolean
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.float
import kotlinx.serialization.json.floatOrNull
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
@ -103,7 +104,7 @@ class MyAnimeListApi(
.appendPath(id.toString())
.appendQueryParameter(
"fields",
"id,title,synopsis,num_chapters,main_picture,status,media_type,start_date",
"id,title,synopsis,num_chapters,mean,main_picture,status,media_type,start_date",
)
.build()
with(json) {
@ -117,6 +118,7 @@ class MyAnimeListApi(
title = obj["title"]!!.jsonPrimitive.content
summary = obj["synopsis"]?.jsonPrimitive?.content ?: ""
total_chapters = obj["num_chapters"]!!.jsonPrimitive.int
score = obj["mean"]?.jsonPrimitive?.floatOrNull ?: -1f
cover_url =
obj["main_picture"]?.jsonObject?.get("large")?.jsonPrimitive?.content
?: ""

View File

@ -107,6 +107,7 @@ class ShikimoriApi(
total_chapters = obj["chapters"]!!.jsonPrimitive.int
cover_url = baseUrl + obj["image"]!!.jsonObject["preview"]!!.jsonPrimitive.content
summary = ""
score = obj["score"]!!.jsonPrimitive.float
tracking_url = baseUrl + obj["url"]!!.jsonPrimitive.content
publishing_status = obj["status"]!!.jsonPrimitive.content
publishing_type = obj["kind"]!!.jsonPrimitive.content

View File

@ -17,13 +17,12 @@ import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.network.newCachelessCallWithProgress
import eu.kanade.tachiyomi.util.storage.getUriCompat
import eu.kanade.tachiyomi.util.storage.saveTo
import eu.kanade.tachiyomi.util.system.setForegroundSafely
import eu.kanade.tachiyomi.util.system.workManager
import logcat.LogPriority
import okhttp3.internal.http2.ErrorCode
import okhttp3.internal.http2.StreamResetException
import tachiyomi.core.i18n.stringResource
import tachiyomi.core.util.lang.withIOContext
import tachiyomi.core.util.system.logcat
import tachiyomi.i18n.MR
import uy.kohesive.injekt.injectLazy
import java.io.File
@ -43,11 +42,7 @@ class AppUpdateDownloadJob(private val context: Context, workerParams: WorkerPar
return Result.failure()
}
try {
setForeground(getForegroundInfo())
} catch (e: IllegalStateException) {
logcat(LogPriority.ERROR, e) { "Not allowed to run on foreground service" }
}
setForegroundSafely()
withIOContext {
downloadApk(title, url)

View File

@ -10,7 +10,6 @@ import eu.kanade.tachiyomi.core.security.SecurityPreferences
import eu.kanade.tachiyomi.ui.security.UnlockActivity
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.isAuthenticationSupported
import eu.kanade.tachiyomi.util.system.overridePendingTransitionCompat
import eu.kanade.tachiyomi.util.view.setSecureScreen
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.launchIn
@ -107,7 +106,7 @@ class SecureActivityDelegateImpl : SecureActivityDelegate, DefaultLifecycleObser
if (activity.isAuthenticationSupported()) {
if (!SecureActivityDelegate.requireUnlock) return
activity.startActivity(Intent(activity, UnlockActivity::class.java))
activity.overridePendingTransitionCompat(0, 0)
activity.overridePendingTransition(0, 0)
} else {
securityPreferences.useAuthenticator().set(false)
}

View File

@ -6,7 +6,6 @@ import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.download.DownloadCache
import tachiyomi.domain.manga.model.Manga
import tachiyomi.i18n.MR
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
data class MigrationFlag(

View File

@ -11,12 +11,14 @@ import eu.kanade.tachiyomi.source.model.Page
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import uy.kohesive.injekt.Injekt
@ -137,8 +139,8 @@ class DownloadQueueScreenModel(
adapter = null
}
val isDownloaderRunning
get() = downloadManager.isDownloaderRunning
val isDownloaderRunning = downloadManager.isDownloaderRunning
.stateIn(screenModelScope, SharingStarted.WhileSubscribed(5000), false)
fun getDownloadStatusFlow() = downloadManager.statusFlow()
fun getDownloadProgressFlow() = downloadManager.progressFlow()

View File

@ -34,17 +34,16 @@ import androidx.core.transition.doOnEnd
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.google.android.material.elevation.SurfaceColors
import com.google.android.material.transition.platform.MaterialContainerTransform
import dev.chrisbanes.insetter.applyInsetter
import eu.kanade.domain.base.BasePreferences
import eu.kanade.presentation.reader.BrightnessOverlay
import eu.kanade.presentation.reader.DisplayRefreshHost
import eu.kanade.presentation.reader.OrientationSelectDialog
import eu.kanade.presentation.reader.PageIndicatorText
import eu.kanade.presentation.reader.ReaderContentOverlay
import eu.kanade.presentation.reader.ReaderPageActionsDialog
import eu.kanade.presentation.reader.ReadingModeSelectDialog
import eu.kanade.presentation.reader.appbars.ReaderAppBars
@ -70,7 +69,6 @@ import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressIndicator
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
import eu.kanade.tachiyomi.util.system.hasDisplayCutout
import eu.kanade.tachiyomi.util.system.isNightMode
import eu.kanade.tachiyomi.util.system.overridePendingTransitionCompat
import eu.kanade.tachiyomi.util.system.toShareIntent
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.view.setComposeContent
@ -90,7 +88,6 @@ import tachiyomi.core.util.lang.launchIO
import tachiyomi.core.util.lang.launchNonCancellable
import tachiyomi.core.util.lang.withUIContext
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.manga.model.Manga
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt
@ -139,7 +136,7 @@ class ReaderActivity : BaseActivity() {
*/
override fun onCreate(savedInstanceState: Bundle?) {
registerSecureActivity(this)
overridePendingTransitionCompat(R.anim.shared_axis_x_push_enter, R.anim.shared_axis_x_push_exit)
overridePendingTransition(R.anim.shared_axis_x_push_enter, R.anim.shared_axis_x_push_exit)
super.onCreate(savedInstanceState)
@ -185,7 +182,7 @@ class ReaderActivity : BaseActivity() {
.map { it.manga }
.distinctUntilChanged()
.filterNotNull()
.onEach(::setManga)
.onEach { updateViewer() }
.launchIn(lifecycleScope)
viewModel.state
@ -270,7 +267,7 @@ class ReaderActivity : BaseActivity() {
override fun finish() {
viewModel.onActivityFinish()
super.finish()
overridePendingTransitionCompat(R.anim.shared_axis_x_pop_enter, R.anim.shared_axis_x_pop_exit)
overridePendingTransition(R.anim.shared_axis_x_pop_enter, R.anim.shared_axis_x_pop_exit)
}
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
@ -332,11 +329,24 @@ class ReaderActivity : BaseActivity() {
val isFullscreen by readerPreferences.fullscreen().collectAsState()
val flashOnPageChange by readerPreferences.flashOnPageChange().collectAsState()
val colorOverlayEnabled by readerPreferences.colorFilter().collectAsState()
val colorOverlay by readerPreferences.colorFilterValue().collectAsState()
val colorOverlayMode by readerPreferences.colorFilterMode().collectAsState()
val colorOverlayBlendMode = remember(colorOverlayMode) {
ReaderPreferences.ColorFilterMode.getOrNull(colorOverlayMode)?.second
}
val cropBorderPaged by readerPreferences.cropBorders().collectAsState()
val cropBorderWebtoon by readerPreferences.cropBordersWebtoon().collectAsState()
val isPagerType = ReadingMode.isPagerType(viewModel.getMangaReadingMode())
val cropEnabled = if (isPagerType) cropBorderPaged else cropBorderWebtoon
ReaderContentOverlay(
brightness = state.brightnessOverlayValue,
color = colorOverlay.takeIf { colorOverlayEnabled },
colorBlendMode = colorOverlayBlendMode,
)
ReaderAppBars(
visible = state.menuVisible,
fullscreen = isFullscreen,
@ -379,10 +389,6 @@ class ReaderActivity : BaseActivity() {
onClickSettings = viewModel::openSettingsDialog,
)
BrightnessOverlay(
value = state.brightnessOverlayValue,
)
if (flashOnPageChange) {
DisplayRefreshHost(
hostState = displayRefreshHost,
@ -479,10 +485,9 @@ class ReaderActivity : BaseActivity() {
}
/**
* Called from the presenter when a manga is ready. Used to instantiate the appropriate viewer
* and the toolbar title.
* Called from the presenter when a manga is ready. Used to instantiate the appropriate viewer.
*/
private fun setManga(manga: Manga) {
private fun updateViewer() {
val prevViewer = viewModel.state.value.viewer
val newViewer = ReadingMode.toViewer(viewModel.getMangaReadingMode(), this)
@ -806,14 +811,6 @@ class ReaderActivity : BaseActivity() {
.onEach(::setCustomBrightness)
.launchIn(lifecycleScope)
readerPreferences.colorFilter().changes()
.onEach(::setColorFilter)
.launchIn(lifecycleScope)
readerPreferences.colorFilterMode().changes()
.onEach { setColorFilter(readerPreferences.colorFilter().get()) }
.launchIn(lifecycleScope)
merge(readerPreferences.grayscale().changes(), readerPreferences.invertedColors().changes())
.onEach { setLayerPaint(readerPreferences.grayscale().get(), readerPreferences.invertedColors().get()) }
.launchIn(lifecycleScope)
@ -885,20 +882,6 @@ class ReaderActivity : BaseActivity() {
}
}
/**
* Sets the color filter overlay according to [enabled].
*/
private fun setColorFilter(enabled: Boolean) {
if (enabled) {
readerPreferences.colorFilterValue().changes()
.sample(100)
.onEach(::setColorFilterValue)
.launchIn(lifecycleScope)
} else {
binding.colorOverlay.isVisible = false
}
}
/**
* Sets the brightness of the screen. Range is [-75, 100].
* From -75 to -1 a semi-transparent black view is overlaid with the minimum brightness.
@ -920,15 +903,6 @@ class ReaderActivity : BaseActivity() {
viewModel.setBrightnessOverlayValue(value)
}
/**
* Sets the color filter [value].
*/
private fun setColorFilterValue(value: Int) {
binding.colorOverlay.isVisible = true
binding.colorOverlay.setFilterColor(value, readerPreferences.colorFilterMode().get())
}
private fun setLayerPaint(grayscale: Boolean, invertedColors: Boolean) {
val paint = if (grayscale || invertedColors) getCombinedPaint(grayscale, invertedColors) else null
binding.viewerContainer.setLayerType(LAYER_TYPE_HARDWARE, paint)

View File

@ -1,36 +0,0 @@
package eu.kanade.tachiyomi.ui.reader
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.PorterDuff
import android.util.AttributeSet
import android.view.View
import androidx.core.graphics.toXfermode
class ReaderColorFilterView(
context: Context,
attrs: AttributeSet? = null,
) : View(context, attrs) {
private val colorFilterPaint: Paint = Paint()
fun setFilterColor(color: Int, filterMode: Int) {
colorFilterPaint.color = color
colorFilterPaint.xfermode = when (filterMode) {
1 -> PorterDuff.Mode.MULTIPLY
2 -> PorterDuff.Mode.SCREEN
3 -> PorterDuff.Mode.OVERLAY
4 -> PorterDuff.Mode.LIGHTEN
5 -> PorterDuff.Mode.DARKEN
else -> PorterDuff.Mode.SRC_OVER
}.toXfermode()
invalidate()
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.drawPaint(colorFilterPaint)
}
}

View File

@ -81,13 +81,13 @@ class SaveImageNotifier(private val context: Context) {
addAction(
R.drawable.ic_share_24dp,
context.stringResource(MR.strings.action_share),
NotificationReceiver.shareImagePendingBroadcast(context, uri.path!!, notificationId),
NotificationReceiver.shareImagePendingBroadcast(context, uri),
)
// Delete action
addAction(
R.drawable.ic_delete_24dp,
context.stringResource(MR.strings.action_delete),
NotificationReceiver.deleteImagePendingBroadcast(context, uri.path!!, notificationId),
NotificationReceiver.deleteImagePendingBroadcast(context, uri),
)
updateNotification()

View File

@ -1,57 +1,62 @@
package eu.kanade.tachiyomi.ui.reader.setting
import android.content.pm.ActivityInfo
import androidx.annotation.DrawableRes
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ScreenLockLandscape
import androidx.compose.material.icons.filled.ScreenLockPortrait
import androidx.compose.material.icons.filled.ScreenRotation
import androidx.compose.material.icons.filled.StayCurrentLandscape
import androidx.compose.material.icons.filled.StayCurrentPortrait
import androidx.compose.ui.graphics.vector.ImageVector
import dev.icerock.moko.resources.StringResource
import eu.kanade.tachiyomi.R
import tachiyomi.i18n.MR
enum class ReaderOrientation(
val flag: Int,
val stringRes: StringResource,
@DrawableRes val iconRes: Int,
val icon: ImageVector,
val flagValue: Int,
) {
DEFAULT(
ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED,
MR.strings.label_default,
R.drawable.ic_screen_rotation_24dp,
Icons.Default.ScreenRotation,
0x00000000,
),
FREE(
ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED,
MR.strings.rotation_free,
R.drawable.ic_screen_rotation_24dp,
Icons.Default.ScreenRotation,
0x00000008,
),
PORTRAIT(
ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT,
MR.strings.rotation_portrait,
R.drawable.ic_stay_current_portrait_24dp,
Icons.Default.StayCurrentPortrait,
0x00000010,
),
LANDSCAPE(
ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE,
MR.strings.rotation_landscape,
R.drawable.ic_stay_current_landscape_24dp,
Icons.Default.StayCurrentLandscape,
0x00000018,
),
LOCKED_PORTRAIT(
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT,
MR.strings.rotation_force_portrait,
R.drawable.ic_screen_lock_portrait_24dp,
Icons.Default.ScreenLockPortrait,
0x00000020,
),
LOCKED_LANDSCAPE(
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE,
MR.strings.rotation_force_landscape,
R.drawable.ic_screen_lock_landscape_24dp,
Icons.Default.ScreenLockLandscape,
0x00000028,
),
REVERSE_PORTRAIT(
ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT,
MR.strings.rotation_reverse_portrait,
R.drawable.ic_stay_current_portrait_24dp,
Icons.Default.StayCurrentPortrait,
0x00000030,
),
;

View File

@ -1,5 +1,7 @@
package eu.kanade.tachiyomi.ui.reader.setting
import android.os.Build
import androidx.compose.ui.graphics.BlendMode
import dev.icerock.moko.resources.StringResource
import tachiyomi.core.preference.PreferenceStore
import tachiyomi.core.preference.getEnum
@ -178,5 +180,24 @@ class ReaderPreferences(
MR.strings.zoom_start_right,
MR.strings.zoom_start_center,
)
val ColorFilterMode = buildList {
addAll(
listOf(
MR.strings.label_default to BlendMode.SrcOver,
MR.strings.filter_mode_multiply to BlendMode.Modulate,
MR.strings.filter_mode_screen to BlendMode.Screen,
),
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
addAll(
listOf(
MR.strings.filter_mode_overlay to BlendMode.Overlay,
MR.strings.filter_mode_lighten to BlendMode.Lighten,
MR.strings.filter_mode_darken to BlendMode.Darken,
),
)
}
}
}
}

View File

@ -13,7 +13,6 @@ import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
import eu.kanade.tachiyomi.util.system.WebViewUtil
import eu.kanade.tachiyomi.util.system.openInBrowser
import eu.kanade.tachiyomi.util.system.overridePendingTransitionCompat
import eu.kanade.tachiyomi.util.system.toShareIntent
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.view.setComposeContent
@ -36,7 +35,7 @@ class WebViewActivity : BaseActivity() {
}
override fun onCreate(savedInstanceState: Bundle?) {
overridePendingTransitionCompat(R.anim.shared_axis_x_push_enter, R.anim.shared_axis_x_push_exit)
overridePendingTransition(R.anim.shared_axis_x_push_enter, R.anim.shared_axis_x_push_exit)
super.onCreate(savedInstanceState)
if (!WebViewUtil.supportsWebView(this)) {
@ -78,7 +77,7 @@ class WebViewActivity : BaseActivity() {
override fun finish() {
super.finish()
overridePendingTransitionCompat(R.anim.shared_axis_x_pop_enter, R.anim.shared_axis_x_pop_exit)
overridePendingTransition(R.anim.shared_axis_x_pop_enter, R.anim.shared_axis_x_pop_exit)
}
private fun shareWebpage(url: String) {

View File

@ -1,14 +0,0 @@
package eu.kanade.tachiyomi.util.system
import android.app.Activity
import android.os.Build
import androidx.annotation.AnimRes
fun Activity.overridePendingTransitionCompat(@AnimRes enterAnim: Int, @AnimRes exitAnim: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
overrideActivityTransition(Activity.OVERRIDE_TRANSITION_OPEN, enterAnim, exitAnim)
} else {
@Suppress("DEPRECATION")
overridePendingTransition(enterAnim, exitAnim)
}
}

View File

@ -1,18 +1,15 @@
package eu.kanade.tachiyomi.util.system
import android.app.ActivityManager
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Build
import android.os.PowerManager
import androidx.appcompat.view.ContextThemeWrapper
import androidx.core.content.PermissionChecker
import androidx.core.content.getSystemService
import androidx.core.net.toUri
import com.hippo.unifile.UniFile
@ -55,39 +52,9 @@ fun Context.copyToClipboard(label: String, content: String) {
}
}
/**
* Checks if the give permission is granted.
*
* @param permission the permission to check.
* @return true if it has permissions.
*/
fun Context.hasPermission(
permission: String,
) = PermissionChecker.checkSelfPermission(this, permission) == PermissionChecker.PERMISSION_GRANTED
val Context.powerManager: PowerManager
get() = getSystemService()!!
/**
* Convenience method to acquire a partial wake lock.
*/
fun Context.acquireWakeLock(tag: String): PowerManager.WakeLock {
val wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "$tag:WakeLock")
wakeLock.acquire()
return wakeLock
}
/**
* Returns true if the given service class is running.
*/
fun Context.isServiceRunning(serviceClass: Class<*>): Boolean {
val className = serviceClass.name
val manager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
@Suppress("DEPRECATION")
return manager.getRunningServices(Integer.MAX_VALUE)
.any { className == it.service.className }
}
fun Context.openInBrowser(url: String, forceDefaultBrowser: Boolean = false) {
this.openInBrowser(url.toUri(), forceDefaultBrowser)
}
@ -200,11 +167,3 @@ fun Context.isInstalledFromFDroid(): Boolean {
// F-Droid builds typically disable the updater
(!BuildConfig.INCLUDE_UPDATER && !isDevFlavor)
}
fun Context.getApplicationIcon(pkgName: String): Drawable? {
return try {
packageManager.getApplicationIcon(pkgName)
} catch (e: PackageManager.NameNotFoundException) {
null
}
}

View File

@ -1,8 +1,12 @@
package eu.kanade.tachiyomi.util.system
import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.WorkInfo
import androidx.work.WorkManager
import kotlinx.coroutines.delay
import logcat.LogPriority
import tachiyomi.core.util.system.logcat
val Context.workManager: WorkManager
get() = WorkManager.getInstance(this)
@ -11,3 +15,21 @@ fun WorkManager.isRunning(tag: String): Boolean {
val list = this.getWorkInfosByTag(tag).get()
return list.any { it.state == WorkInfo.State.RUNNING }
}
/**
* Makes this worker run in the context of a foreground service.
*
* Note that this function is a no-op if the process is subject to foreground
* service restrictions.
*
* Moving to foreground service context requires the worker to run a bit longer,
* allowing Service.startForeground() to be called and avoiding system crash.
*/
suspend fun CoroutineWorker.setForegroundSafely() {
try {
setForeground(getForegroundInfo())
delay(500)
} catch (e: IllegalStateException) {
logcat(LogPriority.ERROR, e) { "Not allowed to set foreground job" }
}
}

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 34% of 12% = ~4% -->
<item android:alpha="0.34" android:color="?attr/colorControlHighlight" />
</selector>

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?attr/colorPrimary" android:state_enabled="true"/>
<item android:alpha="@dimen/material_emphasis_disabled" android:color="?attr/colorOnSurface"/>
</selector>

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="0.24" android:color="?attr/colorPrimary" android:state_enabled="true"/>
<item android:alpha="@dimen/material_emphasis_disabled" android:color="?attr/colorOnSurface"/>
</selector>

View File

@ -1,7 +0,0 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size
android:width="24dp"
android:height="24dp" />
<solid android:color="@android:color/transparent" />
</shape>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M12,2C6.5,2 2,6.5 2,12s4.5,10 10,10s10,-4.5 10,-10S17.5,2 12,2zM17,18H7v-2h10V18zM10.3,14L7,10.7l1.4,-1.4l1.9,1.9l5.3,-5.3L17,7.3L10.3,14z" />
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M21,5L3,5c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2L23,7c0,-1.1 -0.9,-2 -2,-2zM19,17L5,17L5,7h14v10zM10,16h4c0.55,0 1,-0.45 1,-1v-3c0,-0.55 -0.45,-1 -1,-1v-1c0,-1.11 -0.9,-2 -2,-2 -1.11,0 -2,0.9 -2,2v1c-0.55,0 -1,0.45 -1,1v3c0,0.55 0.45,1 1,1zM10.8,10c0,-0.66 0.54,-1.2 1.2,-1.2 0.66,0 1.2,0.54 1.2,1.2v1h-2.4v-1z" />
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M10,16h4c0.55,0 1,-0.45 1,-1v-3c0,-0.55 -0.45,-1 -1,-1v-1c0,-1.11 -0.9,-2 -2,-2 -1.11,0 -2,0.9 -2,2v1c-0.55,0 -1,0.45 -1,1v3c0,0.55 0.45,1 1,1zM10.8,10c0,-0.66 0.54,-1.2 1.2,-1.2 0.66,0 1.2,0.54 1.2,1.2v1h-2.4v-1zM17,1L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2L19,3c0,-1.1 -0.9,-2 -2,-2zM17,19L7,19L7,5h10v14z" />
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M16.48,2.52c3.27,1.55 5.61,4.72 5.97,8.48h1.5C23.44,4.84 18.29,0 12,0l-0.66,0.03 3.81,3.81 1.33,-1.32zM10.23,1.75c-0.59,-0.59 -1.54,-0.59 -2.12,0L1.75,8.11c-0.59,0.59 -0.59,1.54 0,2.12l12.02,12.02c0.59,0.59 1.54,0.59 2.12,0l6.36,-6.36c0.59,-0.59 0.59,-1.54 0,-2.12L10.23,1.75zM14.83,21.19L2.81,9.17l6.36,-6.36 12.02,12.02 -6.36,6.36zM7.52,21.48C4.25,19.94 1.91,16.76 1.55,13L0.05,13C0.56,19.16 5.71,24 12,24l0.66,-0.03 -3.81,-3.81 -1.33,1.32z" />
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M1.01,7L1,17c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2V7c0,-1.1 -0.9,-2 -2,-2H3c-1.1,0 -1.99,0.9 -1.99,2zM19,7v10H5V7h14z" />
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M17,1.01L7,1c-1.1,0 -1.99,0.9 -1.99,2v18c0,1.1 0.89,2 1.99,2h10c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19H7V5h10v14z" />
</vector>

View File

@ -1,7 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#FFF" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM11,19.93c-3.95,-0.49 -7,-3.85 -7,-7.93 0,-0.62 0.08,-1.21 0.21,-1.79L9,15v1c0,1.1 0.9,2 2,2v1.93zM17.9,17.39c-0.26,-0.81 -1,-1.39 -1.9,-1.39h-1v-3c0,-0.55 -0.45,-1 -1,-1L8,12v-2h2c0.55,0 1,-0.45 1,-1L11,7h2c1.1,0 2,-0.9 2,-2v-0.41c2.93,1.19 5,4.06 5,7.41 0,2.08 -0.8,3.97 -2.1,5.39z"/>
</vector>

View File

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:shape="rectangle">
<solid android:color="@android:color/transparent"/>
</shape>
</item>
<item
android:gravity="bottom">
<shape>
<size android:height="1dp" />
<solid android:color="?attr/colorSurfaceVariant" />
</shape>
</item>
</layer-list>

View File

@ -21,12 +21,6 @@
</FrameLayout>
<eu.kanade.tachiyomi.ui.reader.ReaderColorFilterView
android:id="@+id/color_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
<eu.kanade.tachiyomi.ui.reader.ReaderNavigationOverlayView
android:id="@+id/navigation_overlay"
android:layout_width="match_parent"

View File

@ -1,3 +0,0 @@
<resources>
<dimen name="screen_edge_margin">24dp</dimen>
</resources>

View File

@ -1,6 +1,4 @@
<resources>
<dimen name="screen_edge_margin">16dp</dimen>
<dimen name="appwidget_background_radius">16dp</dimen>
<dimen name="appwidget_inner_radius">12dp</dimen>
</resources>

View File

@ -1,5 +1,5 @@
[versions]
agp_version = "8.1.4"
agp_version = "8.2.0"
lifecycle_version = "2.6.2"
paging_version = "3.2.1"
@ -20,14 +20,14 @@ lifecycle-common = { module = "androidx.lifecycle:lifecycle-common", version.ref
lifecycle-process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "lifecycle_version" }
lifecycle-runtimektx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycle_version" }
workmanager = "androidx.work:work-runtime-ktx:2.8.1"
workmanager = "androidx.work:work-runtime:2.9.0"
paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "paging_version" }
paging-compose = { module = "androidx.paging:paging-compose", version.ref = "paging_version" }
benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.1"
test-ext = "androidx.test.ext:junit-ktx:1.2.0-alpha01"
test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-alpha01"
benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.2"
test-ext = "androidx.test.ext:junit-ktx:1.2.0-alpha02"
test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-alpha02"
test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0-alpha05"
[bundles]

View File

@ -1,6 +1,6 @@
[versions]
compiler = "1.5.4"
compose-bom = "2023.12.00-alpha02"
compiler = "1.5.5"
compose-bom = "2023.12.00-alpha03"
accompanist = "0.33.2-alpha"
[libraries]

View File

@ -15,7 +15,6 @@ android-shortcut-gradle = "com.github.zellius:android-shortcut-gradle-plugin:0.1
google-services-gradle = "com.google.gms:google-services:4.4.0"
rxjava = "io.reactivex:rxjava:1.3.8"
flowreactivenetwork = "ru.beryukhov:flowreactivenetwork:1.0.4"
okhttp-core = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp_version" }
okhttp-logging = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp_version" }
@ -27,7 +26,7 @@ conscrypt-android = "org.conscrypt:conscrypt-android:2.5.2"
quickjs-android = "app.cash.quickjs:quickjs-android:0.9.2"
jsoup = "org.jsoup:jsoup:1.16.2"
jsoup = "org.jsoup:jsoup:1.17.1"
disklrucache = "com.jakewharton:disklrucache:2.0.2"
unifile = "com.github.tachiyomiorg:unifile:7c257e1c64"
@ -94,11 +93,10 @@ voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.
voyager-tab-navigator = { module = "cafe.adriel.voyager:voyager-tab-navigator", version.ref = "voyager" }
voyager-transitions = { module = "cafe.adriel.voyager:voyager-transitions", version.ref = "voyager" }
ktlint = "org.jlleitschuh.gradle:ktlint-gradle:11.6.1"
ktlint = "org.jlleitschuh.gradle:ktlint-gradle:12.0.2"
google-api-services-drive = "com.google.apis:google-api-services-drive:v3-rev197-1.25.0"
google-api-client-oauth = "com.google.oauth-client:google-oauth-client:1.34.1"
[bundles]
okhttp = ["okhttp-core", "okhttp-logging", "okhttp-brotli", "okhttp-dnsoverhttps"]
js-engine = ["quickjs-android"]

View File

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

View File

@ -158,12 +158,10 @@
<string name="label_settings">ማስተካከያዎች</string>
<string name="label_more">ተጨማሪ</string>
<string name="name">ስም</string>
<string name="custom_dir">ብጁ አካባቢ</string>
<string name="pref_remove_bookmarked_chapters">ዕልባት የተደረገባቸውን ምዕራፎች ሰርዝ</string>
<string name="pref_remove_after_read">ካነበብኩ በኋላ</string>
<string name="pref_remove_after_marked_as_read">በእጅ እንደተነበበ ምልክት ከተደረገ በኋላ</string>
<string name="pref_category_delete_chapters">ምዕራፎችን ሰርዝ</string>
<string name="pref_download_directory">አካባቢን ያውርዱ</string>
<string name="pref_webtoon_side_padding">የጎን ሽፋን</string>
<string name="pref_category_reading">ንባብ</string>
<string name="pref_category_reading_mode">የንባብ ሁነታ</string>
@ -246,9 +244,7 @@
<string name="color_filter_g_value"></string>
<string name="invalid_backup_file">ልክ ያልሆነ የመጠባበቂያ ፋይል</string>
<string name="backup_created">ምትኬ ተፈጥሯል</string>
<string name="pref_backup_slots">ከፍተኛ መጠባበቂያዎች</string>
<string name="pref_backup_interval">የመጠባበቂያ ድግግሞሽ</string>
<string name="pref_backup_directory">የመጠባበቂያ ቦታ</string>
<string name="pref_restore_backup_summ">ቤተ-መጽሐፍት ከመጠባበቂያ ፋይል ይመልሱ</string>
<string name="pref_restore_backup">ምትኬ ወደነበረበት</string>
<string name="pref_create_backup_summ">የአሁኑን ቤተ-መጽሐፍት ወደነበረበት ለመመለስ ሊያገለግል ይችላል</string>

View File

@ -116,10 +116,8 @@
<string name="rotation_free">حر</string>
<string name="rotation_force_portrait">الوضع الرأسي اﻹجباري</string>
<string name="rotation_force_landscape">الوضع الأفقي الإجباري</string>
<string name="pref_download_directory">موقع التنزيل</string>
<string name="pref_remove_after_marked_as_read">بعد وضع علامة \"مقروءة\" يدوياً</string>
<string name="pref_remove_after_read">الحذف تلقائيا بعد القراءة</string>
<string name="custom_dir">مجلد مخصص</string>
<string name="disabled">معطل</string>
<string name="last_read_chapter">آخر فصل مقروء</string>
<string name="second_to_last">من الفصل الثاني قبل الأخير</string>
@ -132,9 +130,7 @@
<string name="pref_create_backup_summ">يمكن استخدامها لإستعادة المكتبة الحالية</string>
<string name="pref_restore_backup">إستعادة النسخة الإحتياطية</string>
<string name="pref_restore_backup_summ">إستعادة مكتبة من ملف نسخة إحتياطية</string>
<string name="pref_backup_directory">موقع النسخ الإحتياطي</string>
<string name="pref_backup_interval">معدل النسخ الاحتياطي التلقائي</string>
<string name="pref_backup_slots">أقصى عدد للنسخ الاحتياطية التلقائية</string>
<string name="backup_created">أُنشئت نسخة احتياطية</string>
<string name="restore_completed">اكتملت الاستعادة</string>
<string name="backup_choice">ما الذي تريد نسخه احتياطيّاً؟</string>
@ -350,7 +346,6 @@
<string name="notification_chapters_multiple">الفصول %1$s</string>
<string name="notification_chapters_single_and_more">الفصل %1$s و%2$d فصول أخرى</string>
<string name="notification_chapters_single">الفصل %1$s</string>
<string name="notification_check_updates">جارٍ التحقق من وجود فصول جديدة</string>
<string name="recent_manga_time">الفصل %1$s - %2$s</string>
<string name="updating_library">تُحدَّث المكتبة</string>
<string name="viewer">وضع القراءة</string>
@ -768,4 +763,10 @@
<string name="no_scanlators_found">لم يُعثَر على مترجمين</string>
<string name="scanlator">المترجم</string>
<string name="exclude_scanlators">احجب بعض المترجمين</string>
<string name="action_menu_overflow_description">خيارات أكثر</string>
<string name="selected">محدَّد</string>
<string name="not_selected">غير مُحدَّد</string>
<string name="action_bar_up_description">اصعد</string>
<string name="pref_storage_location">مكان التخزين</string>
<string name="pref_storage_location_info">يُستخدَم في الاحتياط وتنزيل الفصول والمصدر المحليِّ.</string>
</resources>

View File

@ -288,12 +288,10 @@
<string name="second_to_last">Перадапошняя частка</string>
<string name="last_read_chapter">Апошняя прачытаная частка</string>
<string name="disabled">Адключана</string>
<string name="custom_dir">Карыстацкая дырэкторыя</string>
<string name="pref_remove_bookmarked_chapters">Дазволіць выдаленне частак з закладкамі</string>
<string name="pref_remove_after_read">Аўтаматычна пасля чытання</string>
<string name="pref_remove_after_marked_as_read">Пасля таго, як пазначана як \"Прачытанае\"</string>
<string name="pref_category_delete_chapters">Выдаліць часткі</string>
<string name="pref_download_directory">Каталог загрузак</string>
<string name="pref_lowest">Найнізкая</string>
<string name="pref_low">Нізкая</string>
<string name="pref_high">Высокая</string>

View File

@ -106,10 +106,8 @@
<string name="color_filter_g_value">G</string>
<string name="color_filter_b_value">B</string>
<string name="color_filter_a_value">A</string>
<string name="pref_download_directory">Директория на изтеглянията</string>
<string name="pref_remove_after_marked_as_read">След маркиране като прочетено</string>
<string name="pref_remove_after_read">След прочитане автоматично изтрийте</string>
<string name="custom_dir">Персонализирана директория</string>
<string name="disabled">Изключено</string>
<string name="last_read_chapter">Последно прочетена глава</string>
<string name="second_to_last">Предпоследна прочетена глава</string>
@ -214,9 +212,7 @@
<string name="pref_create_backup_summ">Може да се използва за възстановяване на текущата библиотека</string>
<string name="pref_restore_backup">Възстанови резервно копие</string>
<string name="pref_restore_backup_summ">Възстанови библиотеката от резервно копие</string>
<string name="pref_backup_directory">Директория за резервното копие</string>
<string name="pref_backup_interval">Честота на запазване</string>
<string name="pref_backup_slots">Максимален брой копия</string>
<string name="backup_created">Резервно копие създадено</string>
<string name="restore_completed">Възстановяването завършено</string>
<string name="backup_choice">Какво искате да запазите?</string>
@ -310,7 +306,6 @@
<string name="notification_chapters_multiple">Глави %1$s</string>
<string name="notification_chapters_single_and_more">Глава %1$s и %2$d още</string>
<string name="notification_chapters_single">Глава %1$s</string>
<string name="notification_check_updates">Проверяване за нови глави</string>
<string name="recent_manga_time">Гл. %1$s - %2$s</string>
<string name="updating_library">Обновяване на библиотеката</string>
<string name="add_tracking">Добави проследяване</string>

View File

@ -122,10 +122,8 @@
<string name="color_filter_g_value"></string>
<string name="color_filter_b_value">নী</string>
<string name="color_filter_a_value"></string>
<string name="pref_download_directory">ডাউনলোডের স্থান</string>
<string name="pref_remove_after_marked_as_read">পঠিত হিসেবে চিহ্নিত করার পর</string>
<string name="pref_remove_after_read">পড়ার পর স্বয়ংক্রিয়ভাবে</string>
<string name="custom_dir">স্বনির্ধারিত নির্দেশক</string>
<string name="disabled">নিষ্ক্রিয়</string>
<string name="last_read_chapter">শেষ পঠিত অধ্যায়</string>
<string name="second_to_last">২য় থেকে শেষ অধ্যায়</string>
@ -138,9 +136,7 @@
<string name="pref_create_backup_summ">বর্তমান সংগ্রহশালা পুনরুদ্ধারের জন্য ব্যাবহার করা যাবে</string>
<string name="pref_restore_backup">ব্যাকআপ পুনরুদ্ধার</string>
<string name="pref_restore_backup_summ">ব্যাকআপ ফাইল থেকে সংগ্রহশালা পুনরুদ্ধার করুন</string>
<string name="pref_backup_directory">ব্যাকআপের স্থান</string>
<string name="pref_backup_interval">ব্যাকআপ ফ্রিকোয়েন্সি</string>
<string name="pref_backup_slots">সর্বোচ্চ ব্যাকআপ</string>
<string name="backup_created">ব্যাকআপ তৈরী হয়েছে</string>
<string name="restore_completed">পুনরুদ্ধার সম্পন্ন হয়েছে</string>
<string name="backup_choice">আপনি কি ব্যাকআপ করতে ইচ্ছুক?</string>
@ -393,7 +389,6 @@
<string name="notification_chapters_multiple">অধ্যায়গুলি %1$s</string>
<string name="notification_chapters_single_and_more">অধ্যায় %1$s এবং %2$d আরও</string>
<string name="notification_chapters_single">অধ্যায় %1$s</string>
<string name="notification_check_updates">নতুন অধ্যায়ের জন্য অনুসন্ধান করা হচ্ছে</string>
<string name="download_insufficient_space">কম সঞ্চয়স্থানের কারণে অধ্যায়গুলি ডাউনলোড করা যায়নি</string>
<string name="recent_manga_time">অঃ %1$s - %2$s</string>
<string name="updating_library">সংগ্রহশালার হালনাগাদ হচ্ছে</string>

View File

@ -2,66 +2,82 @@
<resources>
<plurals name="lock_after_mins">
<item quantity="one">Després d%1$s minut</item>
<item quantity="many">Després de %1$s minuts</item>
<item quantity="other">Després de %1$s minuts</item>
</plurals>
<plurals name="notification_chapters_generic">
<item quantity="one">%1$d capítol nou</item>
<item quantity="many">%1$d capítols nous</item>
<item quantity="other">%1$d capítols nous</item>
</plurals>
<plurals name="notification_chapters_multiple_and_more">
<item quantity="one">Capítols %1$s i 1 més</item>
<item quantity="many">Capítols %1$s i %2$d més</item>
<item quantity="other">Capítols %1$s i %2$d més</item>
</plurals>
<plurals name="notification_new_chapters_summary">
<item quantity="one">Per a %d element</item>
<item quantity="many">Per a %d elements</item>
<item quantity="other">Per a %d elements</item>
</plurals>
<plurals name="update_check_notification_ext_updates">
<item quantity="one">Hi ha una actualització duna extensió</item>
<item quantity="many">Hi ha actualitzacions de %d extensions</item>
<item quantity="other">Hi ha actualitzacions de %d extensions</item>
</plurals>
<plurals name="download_queue_summary">
<item quantity="one">En resta %1$s</item>
<item quantity="many">En resten %1$s</item>
<item quantity="other">En resten %1$s</item>
</plurals>
<plurals name="restore_completed_message">
<item quantity="one">Fet en %1$s amb %2$s error</item>
<item quantity="many">Fet en %1$s amb %2$s errors</item>
<item quantity="other">Fet en %1$s amb %2$s errors</item>
</plurals>
<plurals name="num_categories">
<item quantity="one">%d categoria</item>
<item quantity="many">%d categories</item>
<item quantity="other">%d categories</item>
</plurals>
<plurals name="manga_num_chapters">
<item quantity="one">%1$s capítol</item>
<item quantity="many">%1$s capítols</item>
<item quantity="other">%1$s capítols</item>
</plurals>
<plurals name="num_trackers">
<item quantity="one">%d servei de seguiment</item>
<item quantity="many">%d serveis de seguiment</item>
<item quantity="other">%d serveis de seguiment</item>
</plurals>
<plurals name="missing_chapters_warning">
<item quantity="one">Sha omès %d capítol. És possible que manqui a la font o que hagi estat filtrat</item>
<item quantity="many">Shan omès %d capítols. És possible que manquin a la font o que hagin estat filtrats</item>
<item quantity="other">Shan omès %d capítols. És possible que manquin a la font o que hagin estat filtrats</item>
</plurals>
<plurals name="relative_time">
<item quantity="one">Ahir</item>
<item quantity="many">Fa %1$d dies</item>
<item quantity="other">Fa %1$d dies</item>
</plurals>
<plurals name="next_unread_chapters">
<item quantity="one">El següent capítol no llegit</item>
<item quantity="many">Els següents %d capítols no llegits</item>
<item quantity="other">Els següents %d capítols no llegits</item>
</plurals>
<plurals name="download_amount">
<item quantity="one">El següent capítol</item>
<item quantity="many">Els següents %d capítols</item>
<item quantity="other">Els següents %d capítols</item>
</plurals>
<plurals name="missing_chapters">
<item quantity="one">Manca %1$s capítol</item>
<item quantity="many">Manquen %1$s capítols</item>
<item quantity="other">Manquen %1$s capítols</item>
</plurals>
<plurals name="day">
<item quantity="one">1 dia</item>
<item quantity="many">%d dies</item>
<item quantity="other">%d dies</item>
</plurals>
</resources>

View File

@ -144,11 +144,9 @@
<string name="color_filter_g_value">G</string>
<string name="color_filter_b_value">B</string>
<string name="color_filter_a_value">A</string>
<string name="pref_download_directory">Ubicació de les baixades</string>
<string name="pref_remove_after_marked_as_read">Després de marcar com a llegit de manera manual</string>
<string name="pref_remove_after_read">Suprimeix automàticament després de llegir</string>
<string name="pref_double_tap_anim_speed">Velocitat de lanimació del doble toc</string>
<string name="custom_dir">Ubicació personalitzada</string>
<string name="disabled">Desactivat</string>
<string name="last_read_chapter">Darrer capítol llegit</string>
<string name="second_to_last">Penúltim capítol llegit</string>
@ -161,9 +159,7 @@
<string name="pref_create_backup_summ">Es pot utilitzar per a restaurar la biblioteca actual</string>
<string name="pref_restore_backup">Restaura una còpia de seguretat</string>
<string name="pref_restore_backup_summ">Restaura la biblioteca del fitxer de còpia de seguretat</string>
<string name="pref_backup_directory">Ubicació de la còpia de seguretat</string>
<string name="pref_backup_interval">Freqüència de la còpia de seguretat automàtica</string>
<string name="pref_backup_slots">Màxim de còpies de seguretat automàtiques</string>
<string name="backup_created">Sha creat la còpia de seguretat</string>
<string name="restore_completed">Sha completat la restauració</string>
<string name="backup_choice">De què voleu fer una còpia de seguretat\?</string>
@ -327,7 +323,6 @@
<string name="notification_chapters_single_and_more">Capítol %1$s i %2$d més</string>
<string name="notification_chapters_multiple">Capítols %1$s</string>
<string name="hide_notification_content">Amaga el contingut de les notificacions</string>
<string name="notification_check_updates">Sestà comprovant si hi ha capítols nous</string>
<string name="pref_disable_battery_optimization">Desactiva loptimització de la bateria</string>
<string name="pref_disable_battery_optimization_summary">Fa que funcionin millor les actualitzacions de la biblioteca en segon pla i les còpies de seguretat</string>
<string name="battery_optimization_disabled">Loptimització de la bateria ja està desactivada</string>
@ -768,4 +763,10 @@
<string name="action_sort_tracker_score">Puntuació del servei de seguiment</string>
<string name="label_data_storage">Dades i emmagatzematge</string>
<string name="exclude_scanlators">Exclou scanlators</string>
<string name="selected">Seleccionat</string>
<string name="not_selected">No seleccionat</string>
<string name="pref_storage_location">Ubicació de lemmagatzematge</string>
<string name="pref_storage_location_info">Sutilitza per a les còpies de seguretat automàtiques, les baixades de capítols i la font local.</string>
<string name="action_menu_overflow_description">Més opcions</string>
<string name="action_bar_up_description">Navega cap amunt</string>
</resources>

View File

@ -297,13 +297,11 @@
<string name="pref_category_reading">Pagbasa</string>
<string name="pref_high">Taas</string>
<string name="pref_low">Ubos</string>
<string name="pref_download_directory">Lokasyon sa pag-download</string>
<string name="pref_category_delete_chapters">Pagtangtang sa mga kapitulo</string>
<string name="pref_remove_after_marked_as_read">Human gimarkahan sa mano-mano ingon nabasa na</string>
<string name="pref_remove_after_read">Human sa pagbasa awtomatikong tangtangon</string>
<string name="pref_remove_bookmarked_chapters">Tugoti ang pagtangtang sa gimarkahan nga mga kapitulo</string>
<string name="pref_remove_exclude_categories">Wala iapil nga mga kategorya</string>
<string name="custom_dir">Pasadya nga lokasyon</string>
<string name="disabled">Nabaldado</string>
<string name="fifth_to_last">Ikalima hangtod sa kataposang pagbasa sa kapitulo</string>
<string name="pref_category_auto_download">Awtomatikong pag-download</string>
@ -350,8 +348,6 @@
<string name="pref_image_scale_type">Uri sa sukdanan sa hulagway</string>
<string name="pref_restore_backup">Ibalik ang backup</string>
<string name="pref_restore_backup_summ">Ibalik ang librarya gikan sa backup file</string>
<string name="pref_backup_directory">Backup nga lokasyon</string>
<string name="pref_backup_slots">Maximum nga pag-backup</string>
<string name="backup_created">Gihimo ang backup</string>
<string name="pref_backup_interval">Kadaghanon sa pag-backup</string>
<string name="invalid_backup_file">Dili balido nga backup file</string>

View File

@ -84,10 +84,8 @@
<string name="color_filter_g_value">G</string>
<string name="color_filter_b_value">B</string>
<string name="color_filter_a_value">A</string>
<string name="pref_download_directory">Umístění pro stažení</string>
<string name="pref_remove_after_marked_as_read">Po ručním označení jako přečtené</string>
<string name="pref_remove_after_read">Po přečtení automaticky smazat</string>
<string name="custom_dir">Vlastní umístění</string>
<string name="pref_download_new">Stahovat nové kapitoly</string>
<string name="username">Uživatelské jméno</string>
<string name="password">Heslo</string>
@ -207,7 +205,6 @@
<string name="services">Sledovače</string>
<string name="pref_create_backup">Vytvořit zálohu</string>
<string name="pref_restore_backup">Obnovit zálohu</string>
<string name="pref_backup_directory">Místo zálohy</string>
<string name="pref_backup_interval">Frekvence automatických záloh</string>
<string name="backup_created">Záloha vytvořena</string>
<string name="restore_completed">Obnova dokončena</string>
@ -258,7 +255,6 @@
<string name="fifth_to_last">Pátá předposlední přečtená kapitola</string>
<string name="pref_create_backup_summ">Lze použít k obnovení aktuální knihovny</string>
<string name="pref_restore_backup_summ">Obnovit knihovnu ze záložního souboru</string>
<string name="pref_backup_slots">Maximum automatických záloh</string>
<string name="login_title">Přihlásit se do %1$s</string>
<string name="show_title">Název zdroje</string>
<string name="show_chapter_number">Číslo kapitoly</string>
@ -420,7 +416,6 @@
<string name="notification_chapters_multiple">Kapitoly %1$s</string>
<string name="notification_chapters_single_and_more">Kapitola %1$s a %2$d dalších</string>
<string name="notification_chapters_single">Kapitola %1$s</string>
<string name="notification_check_updates">Hledám nové kapitoly</string>
<string name="download_insufficient_space">Nelze stáhnout kapitoly kvůli nedostatku místa</string>
<string name="migration_help_guide">Návod k přesunu zdrojů</string>
<string name="clear_history_confirmation">Jste si jistý/á\? Všechna historie bude navždy ztracena.</string>

View File

@ -11,7 +11,6 @@
<string name="last_read_chapter">Юлашки вуланӑ сыпăк</string>
<string name="disabled">Сӳнтернӗ</string>
<string name="pref_remove_after_read">Вуланӑ хыҫҫӑн</string>
<string name="pref_download_directory">Тийев вырӑнӗ</string>
<string name="pref_category_reading">Вулани</string>
<string name="color_filter_b_value">Кӑвак</string>
<string name="color_filter_g_value">Симӗс</string>
@ -190,7 +189,6 @@
<string name="fourth_to_last">Вуланӑ сыпӑкран тӑваттӑмӗшӗ</string>
<string name="third_to_last">Вуланӑ сыпӑкран виҫҫӗмӗшӗ</string>
<string name="second_to_last">Юлашкинчен маларахри</string>
<string name="custom_dir">Усӑҫ палӑртнӑ вырӑнӗ</string>
<string name="pref_remove_after_marked_as_read">Вуланӑ пек палӑртнӑ хыҫҫӑн катерт</string>
<string name="scale_type_original_size">Хӑйӗн виҫе</string>
<string name="pager_viewer">Елсерен</string>
@ -275,7 +273,6 @@
<string name="notification_cover_update_failed">Хуплашкана ҫӗнетеймерӗ</string>
<string name="notification_chapters_single_and_more">%1$s сыпӑкӗ тата ытти %2$d</string>
<string name="notification_chapters_multiple">%1$s сыпӑкӗсем</string>
<string name="notification_check_updates">Ҫӗнӗ сыпӑксен пуррине тӗрӗслени</string>
<string name="download_queue_error">Сыпӑксене тийесе илме пулмасть. Тийевсем пайӗнче ҫӗнӗрен хӑтланса пӑхма пултаратӑн</string>
<string name="copy">Ӑтавла</string>
<string name="migrate">Куҫар</string>
@ -401,8 +398,6 @@
<string name="pref_create_backup_summ">Хальхи вулавăша тавӑрма усӑ курма пулать</string>
<string name="invalid_backup_file_missing_manga">Янтӑвра манкӑсем ҫук.</string>
<string name="backup_created">Янтӑв тӑвӑннӑ</string>
<string name="pref_backup_slots">Май килнӗ таран янтӑвсем</string>
<string name="pref_backup_directory">Янтӑв вырнаҫни</string>
<string name="pref_restore_backup_summ">Вулавӑша янтӑвран тавӑр</string>
<string name="pref_restore_backup">Янтӑв тавӑр</string>
<string name="pref_create_backup">Янтӑв ту</string>

View File

@ -286,7 +286,6 @@
<string name="pref_category_reading">Læser</string>
<string name="channel_ext_updates">Udvidelsesopdateringer</string>
<string name="creating_backup_error">Sikkerhedskopiering mislykkedes</string>
<string name="pref_backup_directory">Placering af sikkerhedskopi</string>
<string name="rotation_free">Fri</string>
<string name="channel_errors">Fejl</string>
<string name="restore_completed">Gendannelse fuldført</string>
@ -300,7 +299,6 @@
<string name="pref_category_reading_mode">Læsetilstand</string>
<string name="remove_manga">Du er ved at fjerne \"%s\" fra dit bibliotek</string>
<string name="pref_highest">Højeste</string>
<string name="pref_backup_slots">Maksimale antal automatiske sikkerhedskopier</string>
<string name="pref_low">Lav</string>
<string name="source_settings">Kildeindstillinger</string>
<string name="app_settings">App-indstillinger</string>

View File

@ -118,10 +118,8 @@
<string name="color_filter_g_value">G</string>
<string name="color_filter_b_value">B</string>
<string name="color_filter_a_value">A</string>
<string name="pref_download_directory">Speicherort</string>
<string name="pref_remove_after_marked_as_read">Nachdem manuell als gelesen markiert</string>
<string name="pref_remove_after_read">Automatisch nach dem Lesen löschen</string>
<string name="custom_dir">Eigener Speicherort</string>
<string name="disabled">Deaktiviert</string>
<string name="last_read_chapter">Ab zuletzt gelesenem Kapitel</string>
<string name="second_to_last">Ab zweitletzt gelesenem Kapitel</string>
@ -134,9 +132,7 @@
<string name="pref_create_backup_summ">Kann benutzt werden, um die aktuelle Bibliothek wiederherzustellen</string>
<string name="pref_restore_backup">Datensicherung wiederherstellen</string>
<string name="pref_restore_backup_summ">Bibliothek mit Hilfe einer Datensicherung wiederherstellen</string>
<string name="pref_backup_directory">Sicherungsspeicherort</string>
<string name="pref_backup_interval">Automatische Datensicherungshäufigkeit</string>
<string name="pref_backup_slots">Maximale Anzahl automatischer Datensicherungen</string>
<string name="backup_created">Datensicherung erstellt</string>
<string name="restore_completed">Wiederherstellen abgeschlossen</string>
<string name="backup_choice">Was möchtest du sichern\?</string>
@ -327,7 +323,6 @@
<string name="notification_chapters_single_and_more">Kapitel %1$s und %2$d mehr</string>
<string name="notification_chapters_multiple">Kapitel %1$s</string>
<string name="hide_notification_content">Benachrichtigungsinhalt verbergen</string>
<string name="notification_check_updates">Überprüfe auf neue Kapitel</string>
<string name="pref_disable_battery_optimization">Akkuverbrauch-Optimierung deaktivieren</string>
<string name="pref_disable_battery_optimization_summary">Hilft bei Bibliotheksaktualisierungen und -sicherungen im Hintergrund</string>
<string name="battery_optimization_disabled">Akkuverbrauch-Optimierung ist bereits deaktiviert</string>
@ -768,4 +763,10 @@
<string name="pref_storage_usage">Speichernutzung</string>
<string name="action_sort_tracker_score">Tracker-Bewertung</string>
<string name="exclude_scanlators">Scanlatoren ausschließen</string>
<string name="selected">Ausgewählt</string>
<string name="not_selected">Nicht ausgewählt</string>
<string name="pref_storage_location">Speicherort</string>
<string name="pref_storage_location_info">Wird für automatische Datensicherungen, heruntergeladene Kapitel und lokale Quellen verwendet.</string>
<string name="action_menu_overflow_description">Weitere Optionen</string>
<string name="action_bar_up_description">Nach oben navigieren</string>
</resources>

View File

@ -145,10 +145,8 @@
<string name="color_filter_g_value">G</string>
<string name="color_filter_b_value">B</string>
<string name="color_filter_a_value">A</string>
<string name="pref_download_directory">Τοποθεσία λήψεων</string>
<string name="pref_remove_after_marked_as_read">Αφού επισημανθεί χειροκίνητα ως αναγνωσμένο</string>
<string name="pref_remove_after_read">Αυτόματη διαγραφή μετά την ανάγνωση</string>
<string name="custom_dir">Προσαρμοσμένη τοποθεσία</string>
<string name="disabled">Απενεργοποιημένο</string>
<string name="last_read_chapter">Τελευταίο αναγνωσμένο κεφάλαιο</string>
<string name="second_to_last">Προτελευταίο αναγνωσμένο κεφάλαιο</string>
@ -161,9 +159,7 @@
<string name="pref_create_backup_summ">Μπορεί να χρησιμοποιηθεί για επαναφορά τρέχουσας βιβλιοθήκης</string>
<string name="pref_restore_backup">Επαναφορά αντιγράφου ασφαλείας</string>
<string name="pref_restore_backup_summ">Επαναφορά βιβλιοθήκης από αρχείο αντιγράφου ασφαλείας</string>
<string name="pref_backup_directory">Τοποθεσία αντιγράφων ασφαλείας</string>
<string name="pref_backup_interval">Συχνότητα αυτόματων αντιγράφων ασφαλείας</string>
<string name="pref_backup_slots">Μέγιστα αυτόματα αντίγραφα ασφαλείας</string>
<string name="backup_created">Δημιουργήθηκε αντίγραφο ασφαλείας</string>
<string name="restore_completed">Η επαναφορά ολοκληρώθηκε</string>
<string name="backup_choice">Τι αντίγραφο ασφαλείας θέλετε να δημιουργήσετε;</string>
@ -308,7 +304,6 @@
<string name="notification_chapters_single_and_more">Κεφάλαιο %1$s και %2$d ακόμη</string>
<string name="notification_chapters_multiple">Κεφάλαια %1$s</string>
<string name="notification_chapters_single">Κεφάλαιο %1$s</string>
<string name="notification_check_updates">Έλεγχος για νέα κεφάλαια</string>
<string name="recent_manga_time">Κεφ. %1$s - %2$s</string>
<string name="updating_library">Ενημέρωση βιβλιοθήκης</string>
<string name="paused">Σε παύση</string>
@ -768,4 +763,10 @@
<string name="no_scanlators_found">Δε βρέθηκαν scanlators</string>
<string name="scanlator">Scanlator</string>
<string name="exclude_scanlators">Εξαίρεση scanlator</string>
<string name="action_menu_overflow_description">Περισσότερες επιλογές</string>
<string name="selected">Επιλεγμένο</string>
<string name="not_selected">Μη επιλεγμένο</string>
<string name="action_bar_up_description">Πλοήγηση προς τα πάνω</string>
<string name="pref_storage_location">Τοποθεσία αποθήκευσης</string>
<string name="pref_storage_location_info">Χρησιμοποιείται για αυτόματα αντίγραφα ασφαλείας, λήψη κεφαλαίων και τοπική πηγή.</string>
</resources>

View File

@ -108,12 +108,10 @@
<string name="update_24hour">Ĉiutage</string>
<string name="invalid_backup_file">Nevalida savkopia dosiero</string>
<string name="backup_created">Savkopio kreita</string>
<string name="pref_backup_directory">Savkopiejo</string>
<string name="pref_create_backup">Krei savkopion</string>
<string name="pref_download_new">Elŝuti novajn ĉapitrojn</string>
<string name="disabled">Malŝaltita</string>
<string name="pref_category_delete_chapters">Forigi ĉapitrojn</string>
<string name="pref_download_directory">Elŝutejo</string>
<string name="double_tap_anim_speed_0">Sen animacioj</string>
<string name="scale_type_original_size">Originala grando</string>
<string name="scale_type_stretch">Streĉi</string>
@ -132,7 +130,6 @@
<string name="in_library">En biblioteko</string>
<string name="manga_added_library">Aldonita al biblioteko</string>
<string name="add_to_library">Aldoni al biblioteko</string>
<string name="custom_dir">Propra dosierujo</string>
<string name="pref_remove_after_read">Aŭtomate post legado</string>
<string name="pref_category_reading">Legada</string>
<string name="pref_category_reading_mode">Legada reĝimo</string>
@ -364,7 +361,6 @@
<string name="channel_ext_updates">Kromaĵaj ĝisdatigoj</string>
<string name="download_notifier_download_paused">Elŝutito paŭzigita</string>
<string name="download_notifier_downloader_title">Elŝutilo</string>
<string name="notification_check_updates">Kontrolas por trovi novajn ĉapitrojn</string>
<string name="pref_category_for_this_series">Por ĉi-serion</string>
<string name="custom_filter">Propra filtrilo</string>
<string name="source_unsupported">Fonto ne subtenita</string>
@ -386,7 +382,6 @@
<string name="restoring_backup">Restaŭras savkopion</string>
<string name="restore_completed">Restaŭrado kompletita</string>
<string name="invalid_backup_file_missing_manga">Savkopio enhavas neniun mangaon.</string>
<string name="pref_backup_slots">Maksimume savkopioj</string>
<string name="rotation_force_landscape">Deviga horizontala</string>
<string name="rotation_force_portrait">Deviga vertikala</string>
<string name="rotation_portrait">Vertikala</string>

View File

@ -87,10 +87,8 @@
<string name="rotation_free">Cualquier dirección</string>
<string name="rotation_force_portrait">Forzar en vertical</string>
<string name="rotation_force_landscape">Forzar en horizontal</string>
<string name="pref_download_directory">Carpeta de descarga</string>
<string name="pref_remove_after_marked_as_read">Borrarlos tras marcarlos como leídos de forma manual</string>
<string name="pref_remove_after_read">Borrar capítulos terminados de forma automática</string>
<string name="custom_dir">Ubicación personalizada</string>
<string name="services">Servicios de seguimiento</string>
<string name="pref_clear_chapter_cache">Vaciar la caché de capítulos</string>
<string name="used_cache">Usado: %1$s</string>
@ -205,9 +203,7 @@
<string name="pref_create_backup_summ">Se puede utilizar para restaurar la biblioteca actual</string>
<string name="pref_restore_backup">Restaurar copia de seguridad</string>
<string name="pref_restore_backup_summ">Restaurar la biblioteca a partir de una copia de seguridad</string>
<string name="pref_backup_directory">Ubicación de la copia de respaldo</string>
<string name="pref_backup_interval">Frecuencia de la copia de seguridad automática</string>
<string name="pref_backup_slots">Copias de seguridad automáticas máximas</string>
<string name="pref_backup_interval">Frecuencia de respaldo automático</string>
<string name="backup_created">Copia de seguridad creada</string>
<string name="restore_completed">Restauración completada</string>
<string name="backup_choice">¿De qué quieres hacer una copia de seguridad\?</string>
@ -328,7 +324,6 @@
<string name="notification_chapters_single_and_more">Capítulo %1$s y %2$d más</string>
<string name="notification_chapters_multiple">Capítulos %1$s</string>
<string name="hide_notification_content">Ocultar el contenido de las notificaciones</string>
<string name="notification_check_updates">Buscando nuevos capítulos</string>
<string name="pref_disable_battery_optimization">Desactivar la optimización de batería</string>
<string name="email">Correo electrónico</string>
<string name="pref_disable_battery_optimization_summary">Mejora la cadencia de las actualizaciones y las copias de respaldo que se hagan en segundo plano</string>
@ -398,7 +393,7 @@
<string name="loader_not_implemented_error">No se ha encontrado la fuente</string>
<string name="action_disable">Desactivar</string>
<string name="requires_app_restart">Es necesario reiniciar la aplicación para que surja efecto</string>
<string name="label_network">Networking</string>
<string name="label_network">Red</string>
<string name="unknown_status">Estado desconocido</string>
<string name="tapping_inverted_both">Ambos</string>
<string name="tapping_inverted_vertical">Vertical</string>
@ -486,7 +481,7 @@
<string name="action_display_show_number_of_items">Mostrar el número de elementos</string>
<string name="action_sort_chapter_fetch_date">Fecha de obtención del capítulo</string>
<string name="rotation_type">Tipo de rotación</string>
<string name="pref_create_folder_per_manga_summary">Crea carpetas según el título de las entradas</string>
<string name="pref_create_folder_per_manga_summary">Crea carpetas según el título de la obra</string>
<string name="pref_create_folder_per_manga">Guardar las páginas en carpetas independientes</string>
<string name="pref_reader_actions">Acciones</string>
<string name="pref_grayscale">Escala de grises</string>
@ -554,7 +549,7 @@
<string name="label_warning">Advertencia</string>
<string name="notification_size_warning">Las actualizaciones grandes pueden implicar un mayor uso de la batería y que los distintos servicios bloqueen o ralenticen el acceso a tu dispositivo. Toca aquí para más información.</string>
<string name="action_display_language_badge">Idioma</string>
<string name="backup_info">También deberías guardar las copias de seguridad en otros lugares. Las copias de seguridad pueden contener datos confidenciales, incluidas las contraseñas almacenadas.</string>
<string name="backup_info">Es una buena idea tener copias de respaldo fuera de tu dispositivo. También incluyen contraseñas y otros datos privados que seguramente no quieras compartir.</string>
<string name="connected_to_wifi">Solo con Wi-Fi</string>
<string name="update_72hour">Cada 3 días</string>
<string name="download_queue_size_warning">Advertencia: Las descargas grandes pueden llevar a que las fuentes se vuelvan cada vez más lentas y en casos extremos que los servidores limiten o impidan el acceso a Tachiyomi. Toca aquí para más información.</string>
@ -653,7 +648,7 @@
<string name="pref_appearance_summary">Temas de colores y formatos de fecha</string>
<string name="pref_advanced_summary">Volcar datos del cuelgue y estado de ahorro de batería</string>
<string name="pref_security_summary">Pantalla segura y desbloqueo biométrico</string>
<string name="pref_backup_summary">Copias de seguridad manuales y automáticas, y el espacio de almacenamiento</string>
<string name="pref_backup_summary">Copias de seguridad manuales y automáticas, almacenamiento</string>
<string name="pref_browse_summary">Fuentes, extensiones y búsqueda global</string>
<string name="crash_screen_title">¡Vaya!</string>
<string name="crash_screen_restart_application">Reiniciar la aplicación</string>
@ -730,7 +725,7 @@
<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>
<string name="action_ok">Aceptar</string>
<string name="track_delete_title">¿Quitar el rastreo de %s\?</string>
<string name="track_delete_title">¿Quieres desvincular %s?</string>
<string name="track_delete_text">Esto eliminará el seguimiento localmente.</string>
<string name="track_delete_remote_text">Quitar también de %s</string>
<string name="delete_downloaded">Borrar los ya descargados</string>
@ -754,18 +749,24 @@
<string name="app_settings">Ajustes de la aplicación</string>
<string name="action_sort_category">Ordenar categorías</string>
<string name="sort_category_confirmation">¿Quieres ordenar las categorías de forma alfabética\?</string>
<string name="file_null_uri_error">Ningún archivo seleccionado</string>
<string name="file_null_uri_error">No has elegido ningún archivo</string>
<string name="relative_time_span_never">Nunca</string>
<string name="pref_flash_page_summ">Reducir el ghosting en las pantallas de tinta electrónica</string>
<string name="last_auto_backup_info">Última copia de seguridad automática: %s</string>
<string name="pref_flash_page">Parpadeo en blanco al cambiar de página</string>
<string name="pref_flash_page_summ">Esta transición minimiza las manchas y el efecto de retención de imagen en pantallas de tinta electrónica</string>
<string name="last_auto_backup_info">Última copia automática: %s</string>
<string name="pref_flash_page">Parpadear a blanco al cambiar de página</string>
<string name="label_data_storage">Datos y almacenamiento</string>
<string name="pref_storage_usage">Almacenamiento utilizado</string>
<string name="action_sort_tracker_score">Puntuación del rastreador</string>
<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 ningún scanlators</string>
<string name="scanlator">Scanlator</string>
<string name="exclude_scanlators">Omitir a los scanlators</string>
<string name="no_scanlators_found">Sin escanducciones</string>
<string name="scanlator">Escanductor</string>
<string name="exclude_scanlators">Excluir escanducciones</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>
</resources>

View File

@ -118,7 +118,6 @@
<string name="pref_webtoon_side_padding">Alboko betegarria</string>
<string name="pref_always_show_chapter_transition">Erakutsi beti kapituluaren trantsizioa</string>
<string name="last_read_chapter">Azkenik irakurritako kapitulua</string>
<string name="pref_download_directory">Deskarga kokapena</string>
<string name="pref_category_delete_chapters">Ezabatu kapituluak</string>
<string name="fourth_to_last">Atzetik hasita laugarren irakurritako kapitulua</string>
<string name="backup_restore_missing_trackers">Saioa hasi gabeko jarraitzaileak:</string>
@ -223,7 +222,6 @@
<string name="pref_remove_after_marked_as_read">Irakurria bezala eskuz markatu ondoren</string>
<string name="pref_remove_bookmarked_chapters">Baimendu laster-markadun kapituluak ezabatzea</string>
<string name="pref_remove_after_read">Irakurri ondoren automatikoki ezabatu</string>
<string name="custom_dir">Kokapen pertsonalizatua</string>
<string name="enhanced_services">Zerbitzu hobetuak</string>
<string name="help_translate">Lagundu itzultzen</string>
<string name="file_picker_error">Ez da aurkitu fitxategiak hautatzeko aplikaziorik</string>
@ -431,7 +429,6 @@
<string name="clear_history_confirmation">Ziur zaude\? Historia guztia galduko da.</string>
<string name="migration_help_guide">Iturrien migrazio gida</string>
<string name="migration_dialog_what_to_include">Hautatu sartu nahi dituzun datuak</string>
<string name="notification_check_updates">Kapitulu berriak bilatzen</string>
<string name="notification_new_chapters">Kapitulu berriak aurkituak</string>
<string name="information_no_recent_manga">Ez da ezer irakurri azkenaldian</string>
<string name="information_no_downloads">Deskargarik ez</string>
@ -458,9 +455,7 @@
<string name="pref_highest">Altuena</string>
<string name="pref_restore_backup">Babeskopia erabili</string>
<string name="pref_restore_backup_summ">Berreskuratu liburutegia babeskopia fitxategitik</string>
<string name="pref_backup_directory">Babeskopiaren kokapena</string>
<string name="pref_backup_interval">Babeskopien maiztasuna</string>
<string name="pref_backup_slots">Gehienezko babeskopiak</string>
<string name="action_sort_alpha">Alfabetikoki</string>
<string name="label_warning">Oharra</string>
<string name="confirm_lock_change">Autentifikatu aldaketa berresteko</string>

View File

@ -52,4 +52,16 @@
<item quantity="one">قسمت خوانده نشده بعدی</item>
<item quantity="other">%d قسمت خوانده نشده بعدی</item>
</plurals>
<plurals name="download_amount">
<item quantity="one">قسمت بعدی</item>
<item quantity="other">%d قسمت بعدی</item>
</plurals>
<plurals name="missing_chapters">
<item quantity="one">%1$s قسمت گم شده</item>
<item quantity="other">%1$s قسمت گم شده</item>
</plurals>
<plurals name="day">
<item quantity="one">1 روز</item>
<item quantity="other">%d روز</item>
</plurals>
</resources>

View File

@ -14,7 +14,6 @@
<string name="notification_chapters_single_and_more">قسمت %1$s و %2$d قسمت دیگر</string>
<string name="notification_chapters_single">قسمت %1$s</string>
<string name="notification_new_chapters">قسمت‌های جدید پیدا شدند</string>
<string name="notification_check_updates">درحال بررسی برای قسمت‌های جدید</string>
<string name="download_queue_error">دانلود قسمت ها با خطا مواجه شد. با مراجعه به بخش دانلودها می توانید دوباره تلاش کنید</string>
<string name="copy">کپی</string>
<string name="migrate">تغییر منبع</string>
@ -120,9 +119,7 @@
<string name="no_results_found">هیچ نتیجه ای یافت نشد</string>
<string name="no_more_results">نتیجه بیشتری یافت نشد</string>
<string name="tabs_header">تب ها</string>
<string name="pref_backup_slots">حداکثر تعداد نسخه‌های پشتیبان</string>
<string name="pref_backup_interval">زمان پشتیبان گیری</string>
<string name="pref_backup_directory">محل پشتیبان گیری</string>
<string name="pref_restore_backup_summ">بازگرداندن کتابخانه از فایل پشتیبان</string>
<string name="pref_restore_backup">برگرداندن نسخه پشتیبان</string>
<string name="pref_create_backup_summ">می تواند برای بازگرداندن کتابخانه فعلی استفاده شود</string>
@ -144,8 +141,6 @@
<string name="pref_category_for_this_series">برای این مجموعه</string>
<string name="decode_image_error">بازکردن عکس با خطا مواجه شد</string>
<string name="plan_to_read">قصد خواندن دارم(Plan to read)</string>
<string name="custom_dir">مکان سفارشی</string>
<string name="pref_download_directory">مکان دانلود</string>
<string name="scale_type_smart_fit">هم اندازه حالت هوشمند</string>
<string name="filter_mode_multiply">Multiply</string>
<string name="filter_mode_overlay">Overlay</string>
@ -601,4 +596,22 @@
<string name="backup_info">شما باید از پشتیبانی ها در جا های دیگر هم کپی داشته باشید.</string>
<string name="action_update_category">بروزرسانی دسته بندی</string>
<string name="action_copy_to_clipboard">کپی کردن به کلیپ‌برد</string>
<string name="loader_rar5_error">فرمت RARv5 پشتیبانی نشده</string>
<string name="on_hiatus">متوقف شده</string>
<string name="unlock_app_title">باز گشایی %s</string>
<string name="delete_downloaded">پاک کردن قسمت های دانلود شده</string>
<string name="action_apply">ذخیره</string>
<string name="action_revert_to_default">باز نشانی به حالت اولیه</string>
<string name="action_sort_category">منظم کردن دسته بندی ها</string>
<string name="action_menu_overflow_description">گزینه های بیشتر</string>
<string name="selected">انتخاب شده</string>
<string name="not_selected">انتخاب نشده</string>
<string name="action_move_to_bottom_all_for_series">مجموعه ها را به پایین منطقل کن</string>
<string name="action_bar_up_description">برو به بالا</string>
<string name="action_sort_tracker_score">امتیاز رهگیز</string>
<string name="label_data_storage">داده های و ذخیره سازی</string>
<string name="sort_category_confirmation">آیا مایلید که دسته بندی ها را به ترتیب الفبا منظم کنید؟</string>
<string name="action_ok">باشه</string>
<string name="action_sort_next_updated">به روز رسانی مورد انتظار بعدی</string>
<string name="download_queue_size_warning">هشدار: حجم زیاد بارگیری ممکن است باعث اهسته تر شدن سرعت ویا مسدود کردن Tachiyomi از منبع شود. برای اطلاعات بیشتر لمس کنید.</string>
</resources>

View File

@ -192,10 +192,8 @@
<string name="color_filter_g_value">Vihreä</string>
<string name="color_filter_b_value">Sininen</string>
<string name="color_filter_a_value">Alpha</string>
<string name="pref_download_directory">Lataus kansio</string>
<string name="pref_remove_after_marked_as_read">Manuaalisesti luetuksi merkitsemisen jälkeen</string>
<string name="pref_remove_after_read">Lukemisen jälkeen</string>
<string name="custom_dir">Mukautettu kansio</string>
<string name="disabled">Pois käytöstä</string>
<string name="last_read_chapter">Viimeksi luettu luku</string>
<string name="second_to_last">Toiseksi viimeinen luku</string>
@ -207,7 +205,6 @@
<string name="pref_create_backup_summ">Voidaan käyttää nykyisen kirjaston palauttamiseen</string>
<string name="pref_restore_backup_summ">Palauta kirjasto varmuuskopiointi-tiedostosta</string>
<string name="pref_backup_interval">Varmuuskopioinnin tiheys</string>
<string name="pref_backup_slots">Varmuuskopioiden enimmäismäärä</string>
<string name="backup_created">Varmuuskopio luotu</string>
<string name="restore_completed">Palautus valmis</string>
<string name="backup_choice">Mitä haluat varmuuskopioida\?</string>
@ -260,7 +257,6 @@
<string name="snack_categories_deleted">Kategoriat poistettu</string>
<string name="pref_create_backup">Luo varmuuskopio</string>
<string name="pref_restore_backup">Palauta varmuuskopio</string>
<string name="pref_backup_directory">Varmuuskopio kansio</string>
<string name="local_source">Paikalliset lähteet</string>
<string name="other_source">Muut</string>
<string name="latest">Viimeisimmät</string>
@ -323,7 +319,6 @@
<string name="notification_chapters_single_and_more">Luku %1$s ja %2$d lisää</string>
<string name="notification_chapters_multiple">Luvut %1$s</string>
<string name="hide_notification_content">Piilota ilmoitusten sisältö</string>
<string name="notification_check_updates">Etsitään uusia lukuja</string>
<string name="lock_when_idle">Lukitse käyttämättömänä</string>
<string name="secure_screen">Salaa näyttö</string>
<string name="secure_screen_summary">Turvallinen ruutu piilottaa sovelluksen sisällön sovelluksia vaihdettaessa ja estää kuvakaappauksen ottamisen</string>

View File

@ -115,10 +115,8 @@
<string name="second_to_last">Pangalawa sa huling nabasa</string>
<string name="last_read_chapter">Huling nabasang kabanata</string>
<string name="disabled">Sarado</string>
<string name="custom_dir">Pinili kong lugar</string>
<string name="pref_remove_after_marked_as_read">Pagkamarkahang nabasa na</string>
<string name="pref_remove_after_read">Pagkatapos basahin, kusang burahin</string>
<string name="pref_download_directory">Lokasyon sa pag-download</string>
<string name="pref_webtoon_side_padding">Kapal ng gilid</string>
<string name="pref_category_reading">Pagbabasa</string>
<string name="pref_always_show_chapter_transition">Ipakita palagi ang paglipat-kabanata</string>
@ -229,9 +227,7 @@
<string name="invalid_backup_file_missing_manga">Hindi naglalaman ang backup ng kahit anong mga entry sa Aklatan.</string>
<string name="invalid_backup_file">Invalid na backup</string>
<string name="backup_created">Nai-backup na</string>
<string name="pref_backup_slots">Pinakamarami na awtomatikong pag-backup</string>
<string name="pref_backup_interval">Awtomatikong dalas ng pag-backup</string>
<string name="pref_backup_directory">Lokasyon ng backup</string>
<string name="pref_restore_backup_summ">I-restore ang Aklatan mula sa backup</string>
<string name="pref_restore_backup">I-restore ang backup</string>
<string name="pref_create_backup_summ">Magagamit para ma-restore ang kasalukuyang Aklatan</string>
@ -276,7 +272,6 @@
<string name="notification_chapters_single_and_more">Kabanata %1$s at karagdagang %2$d pa</string>
<string name="notification_chapters_single">Kabanata %1$s</string>
<string name="notification_new_chapters">May mga bagong kabanata</string>
<string name="notification_check_updates">Naghahanap ng mga bagong kabanata</string>
<string name="download_insufficient_space">Di ma-download ang mga kabanata dahil sa mababang espasyo</string>
<string name="download_queue_error">Di ma-download ang mga kabanata. Subukan mo uli ito sa Dina-download</string>
<string name="copy">Kopyahin</string>
@ -768,4 +763,10 @@
<string name="scanlator">Scanlator</string>
<string name="exclude_scanlators">Ibukod ang mga scanlator</string>
<string name="action_create">Lumikha</string>
<string name="pref_storage_location">Lokasyon ng storage</string>
<string name="pref_storage_location_info">Ginagamit para sa automatikong pa-backup, pag-download ng mga kabanata, at lokal na source.</string>
<string name="action_menu_overflow_description">Ibang opsiyon</string>
<string name="selected">Napili</string>
<string name="not_selected">Di napili</string>
<string name="action_bar_up_description">Mag-navigate pataas</string>
</resources>

View File

@ -105,7 +105,6 @@
<string name="color_filter_g_value">V</string>
<string name="color_filter_b_value">B</string>
<string name="color_filter_a_value">O</string>
<string name="pref_download_directory">Répertoire de téléchargement</string>
<string name="pref_remove_after_read">Suppression automatique après lecture</string>
<string name="disabled">Désactivé</string>
<string name="last_read_chapter">Dernier chapitre lu</string>
@ -186,7 +185,6 @@
<string name="download_notifier_no_network">Aucune connexion disponible</string>
<string name="clear_database_confirmation">Êtes-vous sûr(e) \? Les chapitres lus et la progression des entrées non présentes dans la bibliothèque seront perdues</string>
<string name="confirm_delete_chapters">Supprimer les chapitres sélectionnés \?</string>
<string name="custom_dir">Répertoire personnalisé</string>
<string name="download_notifier_unknown_error">Impossible de télécharger le chapitre en raison d\'une erreur inattendue</string>
<string name="fifth_to_last">Cinquième chapitre avant le dernier lu</string>
<string name="login_success">Connecté</string>
@ -208,7 +206,6 @@
<string name="chapter_paused">En pause</string>
<string name="action_open_log">Ouvrir le fichier</string>
<string name="action_restore">Restaurer</string>
<string name="pref_backup_directory">Dossier de sauvegarde</string>
<string name="restore_completed">Restauration terminée</string>
<string name="backup_choice">Que voulez-vous sauvegarder \?</string>
<string name="delete_downloads_for_manga">Supprimer les chapitres téléchargés ?</string>
@ -219,7 +216,6 @@
<string name="pref_restore_backup">Restaurer une sauvegarde</string>
<string name="pref_restore_backup_summ">Restaurer la bibliothèque à partir d\'un fichier de sauvegarde</string>
<string name="pref_backup_interval">Fréquence de sauvegarde</string>
<string name="pref_backup_slots">Nombre maximal de sauvegardes</string>
<string name="backup_created">Sauvegarde créée</string>
<string name="restoring_backup">Restauration de sauvegarde en cours</string>
<string name="creating_backup">Création de sauvegarde en cours</string>
@ -327,7 +323,6 @@
<string name="notification_chapters_single_and_more">Chapitre %1$s et %2$d autres</string>
<string name="notification_chapters_multiple">Chapitres %1$s</string>
<string name="hide_notification_content">Cacher le contenu des notifications</string>
<string name="notification_check_updates">Recherche de nouveaux chapitres</string>
<string name="pref_disable_battery_optimization">Désactiver la fonction d\'optimisation de la batterie</string>
<string name="pref_disable_battery_optimization_summary">Facilite les mises à jour et sauvegardes de la bibliothèque en arrière-plan</string>
<string name="battery_optimization_disabled">La fonction d\'optimisation de la batterie est déjà désactivée</string>
@ -742,4 +737,20 @@
<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>
<string name="app_settings">Paramètres de l\'application</string>
<string name="delete_downloaded">Supprimer le(s) téléchargement(s)</string>
<string name="action_apply">Appliquer</string>
<string name="action_revert_to_default">Réintialiser les valeurs par défaut</string>
<string name="action_sort_category">Trier les catégories</string>
<string name="action_menu_overflow_description">Plus d\'options</string>
<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="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>
</resources>

View File

@ -10,12 +10,10 @@
<string name="label_download_queue">Cola de baixadas</string>
<string name="last_read_chapter">Último capítulo lido</string>
<string name="disabled">Deshabilitado</string>
<string name="custom_dir">Ubicación personalizada</string>
<string name="pref_remove_bookmarked_chapters">Permitir eliminar os capítulos marcados como favoritos</string>
<string name="pref_remove_after_read">Eliminar automaticamente despois de ler</string>
<string name="pref_remove_after_marked_as_read">Despois de marcar manualmente como lido</string>
<string name="pref_category_delete_chapters">Eliminar capítulos</string>
<string name="pref_download_directory">Localización das baixadas</string>
<string name="pref_webtoon_side_padding">Recheo lateral</string>
<string name="pref_category_reading">Lendo</string>
<string name="pref_category_reading_mode">Modo de lectura</string>
@ -274,7 +272,6 @@
<string name="picture_saved">Imaxe gardada</string>
<string name="pref_restore_backup">Restaurar a copia de seguridade</string>
<string name="pref_backup_interval">Frecuencia das copias de seguridade</string>
<string name="pref_backup_slots">Máximo de copias de seguridade</string>
<string name="backup_restore_missing_sources">Fontes faltantes:</string>
<string name="pref_invalidate_download_cache">Invalidar o índice de baixadas</string>
<string name="pref_dump_crash_logs_summary">Garda os rexistros de erros nun ficheiro para compartilo cos desenvolvedores</string>
@ -370,7 +367,6 @@
<string name="ext_info_age_rating">Clasificación por idades</string>
<string name="enhanced_services">Servizos mellorados</string>
<string name="enhanced_tracking_info">Estes servizos proporcionan funcións melloradas para fontes concretas. Faise un seguemento automático dos elementos ao engadilos á biblioteca.</string>
<string name="pref_backup_directory">Localización da copia de seguridade</string>
<string name="backup_in_progress">Xa se está facendo unha copia de seguridade</string>
<string name="pref_dump_crash_logs">Compartir os rexistros de erros</string>
<string name="error_sharing_cover">Error ao compartir a portada</string>
@ -592,7 +588,6 @@
<string name="local_source">Fonte local</string>
<string name="manga_tracking_tab">En seguimento</string>
<string name="reading">Lendo</string>
<string name="notification_check_updates">Procurando capítulos novos</string>
<string name="download_notifier_text_only_wifi">Non hai ningunha conexión Wi-Fi dispoñible</string>
<string name="crash_screen_description">%s pechouse por un problema inesperado. Aconsellámoste que compartas os rexistros de erros na canle de soporte no Discord (en inglés).</string>
<string name="crash_screen_restart_application">Reinicia a aplicación</string>

View File

@ -27,7 +27,6 @@
<string name="notification_chapters_single_and_more">פרק %1$s ו-%2$d נוספים</string>
<string name="notification_chapters_single">פרק %1$s</string>
<string name="notification_new_chapters">נמצאו פרקים חדשים</string>
<string name="notification_check_updates">מחפש פרקים חדשים</string>
<string name="download_queue_error">לא ניתן להוריד פרקים. אפשר לנסות שוב בדף ההורדות</string>
<string name="copy">העתק</string>
<string name="migrate">העברה</string>
@ -206,9 +205,7 @@
<string name="backup_choice">מה אתה רוצה לגבות\?</string>
<string name="restore_completed">השחזור הושלם</string>
<string name="backup_created">גיבוי נוצר</string>
<string name="pref_backup_slots">מספר גיבויים מקסימלי</string>
<string name="pref_backup_interval">תדירות גיבוי</string>
<string name="pref_backup_directory">מיקום גיבוי</string>
<string name="pref_restore_backup_summ">שחזר ספרייה מקובץ גיבוי</string>
<string name="pref_restore_backup">שחזור גיבוי</string>
<string name="pref_create_backup_summ">ניתן לשימוש על מנת לשחזר את הספרייה הנוכחית</string>
@ -221,10 +218,8 @@
<string name="third_to_last">הפרק השלישי מהסוף שנקרא</string>
<string name="second_to_last">הפרק השני מהסוף שנקרא</string>
<string name="last_read_chapter">פרק שנקרא בפעם האחרונה</string>
<string name="custom_dir">מיקום מותאם אישית</string>
<string name="pref_remove_after_read">אוטומטי לאחר סיום הקריאה</string>
<string name="pref_remove_after_marked_as_read">אחרי שמסומן ידנית כנקרא</string>
<string name="pref_download_directory">מיקום ההורדה</string>
<string name="pref_always_show_chapter_transition">הצג תמיד מעברי פרקים</string>
<string name="color_filter_a_value">אלפא</string>
<string name="color_filter_b_value">כחול</string>

View File

@ -122,10 +122,8 @@
<string name="color_filter_g_value">G</string>
<string name="color_filter_b_value">B</string>
<string name="color_filter_a_value">A</string>
<string name="pref_download_directory">डाउनलोड निर्देशिका</string>
<string name="pref_remove_after_marked_as_read">\'पढ़ें\' के रूप में खुद से चिह्नित करने के बाद</string>
<string name="pref_remove_after_read">पढ़ने के बाद स्वचालित रूप से हटाएं</string>
<string name="custom_dir">इच्छा अनुसार निर्देशिका</string>
<string name="disabled">बंद करें</string>
<string name="last_read_chapter">अंतिम पढ़ा अध्याय</string>
<string name="second_to_last">दूसरा से अंतिम पढ़ा गया अध्याय</string>
@ -138,9 +136,7 @@
<string name="pref_create_backup_summ">वर्तमान पुस्तकालय को पुनर्स्थापित करने के लिए उपयोग किया जा सकता है</string>
<string name="pref_restore_backup">बैकअप पुनर्स्थापित करे</string>
<string name="pref_restore_backup_summ">बैकअप फ़ाइल से लाइब्रेरी पुनर्स्थापित करें</string>
<string name="pref_backup_directory">बैकअप निर्देशिका</string>
<string name="pref_backup_interval">बैकअप फ़्रीक्वेंसी</string>
<string name="pref_backup_slots">अधिकतम बैकअप</string>
<string name="backup_created">बैकअप बनाया गया है</string>
<string name="restore_completed">पुनर्स्थापना पूर्ण हुआ</string>
<string name="backup_choice">आप बैकअप के लिए क्या चाहते हैं?</string>
@ -327,7 +323,6 @@
<string name="notification_chapters_single_and_more">अध्याय %1$s और %2$d अधिक</string>
<string name="notification_chapters_multiple">अध्याय %1$s</string>
<string name="hide_notification_content">अधिसूचना सामग्री छुपाएं</string>
<string name="notification_check_updates">नए अध्यायों के लिए जांच</string>
<string name="pref_disable_battery_optimization">बैटरी अनुकूलन को अक्षम करना</string>
<string name="pref_disable_battery_optimization_summary">पृष्ठभूमि पुस्तकालय अपडेट और बैकअप के साथ मदद करता है</string>
<string name="battery_optimization_disabled">बैटरी अनुकूलन पहले से ही अक्षम है</string>

Some files were not shown because too many files have changed in this diff Show More