mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-30 22:07:57 +01:00 
			
		
		
		
	chore: merge upstream changes.
Signed-off-by: KaiserBh <kaiserbh@proton.me>
This commit is contained in:
		
							
								
								
									
										2
									
								
								.github/workflows/build_pull_request.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/build_pull_request.yml
									
									
									
									
										vendored
									
									
								
							| @@ -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 | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/workflows/build_push.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/build_push.yml
									
									
									
									
										vendored
									
									
								
							| @@ -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 | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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" /> | ||||
| @@ -21,6 +24,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" | ||||
| @@ -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" | ||||
|   | ||||
| @@ -190,6 +190,7 @@ private fun ExtensionDetails( | ||||
|             key = { it.source.id }, | ||||
|         ) { source -> | ||||
|             SourceSwitchPreference( | ||||
|                 modifier = Modifier.animateItemPlacement(), | ||||
|                 source = source, | ||||
|                 onClickSourcePreferences = onClickSourcePreferences, | ||||
|                 onClickSource = onClickSource, | ||||
|   | ||||
| @@ -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) }, | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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) }, | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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) }, | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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() | ||||
|                 } | ||||
|             } | ||||
|   | ||||
| @@ -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) | ||||
|     } | ||||
| } | ||||
| @@ -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), | ||||
|                 ) | ||||
|             } | ||||
|   | ||||
| @@ -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, | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -49,7 +49,7 @@ fun BottomReaderBar( | ||||
|  | ||||
|         IconButton(onClick = onClickOrientation) { | ||||
|             Icon( | ||||
|                 painter = painterResource(orientation.iconRes), | ||||
|                 imageVector = orientation.icon, | ||||
|                 contentDescription = stringResource(MR.strings.rotation_type), | ||||
|             ) | ||||
|         } | ||||
|   | ||||
| @@ -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)) }, | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -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()) { | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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), | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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( | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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 } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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() | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -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() | ||||
|     } | ||||
| } | ||||
| @@ -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) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -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 | ||||
|             }, | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|             }, | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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, | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|         } | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|                                     ?: "" | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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) | ||||
|         } | ||||
|   | ||||
| @@ -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( | ||||
|   | ||||
| @@ -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() | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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) | ||||
|     } | ||||
| } | ||||
| @@ -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() | ||||
|   | ||||
| @@ -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, | ||||
|     ), | ||||
|     ; | ||||
|   | ||||
| @@ -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, | ||||
|                     ), | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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) | ||||
|     } | ||||
| } | ||||
| @@ -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 | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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" } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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> | ||||
| @@ -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> | ||||
| @@ -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> | ||||
| @@ -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> | ||||
| @@ -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> | ||||
| @@ -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> | ||||
| @@ -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> | ||||
| @@ -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> | ||||
| @@ -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> | ||||
| @@ -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> | ||||
| @@ -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> | ||||
| @@ -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> | ||||
| @@ -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" | ||||
|   | ||||
| @@ -1,3 +0,0 @@ | ||||
| <resources> | ||||
|     <dimen name="screen_edge_margin">24dp</dimen> | ||||
| </resources> | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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] | ||||
|   | ||||
| @@ -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] | ||||
|   | ||||
| @@ -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"] | ||||
|   | ||||
							
								
								
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							| @@ -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 | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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> | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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ó d’una 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">S’ha omès %d capítol. És possible que manqui a la font o que hagi estat filtrat</item> | ||||
|         <item quantity="many">S’han omès %d capítols. És possible que manquin a la font o que hagin estat filtrats</item> | ||||
|         <item quantity="other">S’han 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> | ||||
| @@ -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 l’animació 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">S’ha creat la còpia de seguretat</string> | ||||
|     <string name="restore_completed">S’ha 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">S’està comprovant si hi ha capítols nous</string> | ||||
|     <string name="pref_disable_battery_optimization">Desactiva l’optimització 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">L’optimització 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 l’emmagatzematge</string> | ||||
|     <string name="pref_storage_location_info">S’utilitza 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> | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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> | ||||
| @@ -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> | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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> | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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> | ||||
| @@ -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> | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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> | ||||
| @@ -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> | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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
		Reference in New Issue
	
	Block a user