diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6eb320050..4397098b5 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -22,7 +22,7 @@ android { defaultConfig { applicationId = "eu.kanade.tachiyomi" - versionCode = 111 + versionCode = 112 versionName = "0.14.7" buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"") diff --git a/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt b/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt index 791e13180..af23735e9 100644 --- a/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt +++ b/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt @@ -24,6 +24,8 @@ class BasePreferences( fun acraEnabled() = preferenceStore.getBoolean("acra.enable", isPreviewBuildType || isReleaseBuildType) + fun shownOnboardingFlow() = preferenceStore.getBoolean(Preference.appStateKey("onboarding_complete"), false) + enum class ExtensionInstaller(val titleRes: StringResource) { LEGACY(MR.strings.ext_installer_legacy), PACKAGEINSTALLER(MR.strings.ext_installer_packageinstaller), diff --git a/app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChaptersWithSource.kt b/app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChaptersWithSource.kt index 1690180b2..e58f40a71 100644 --- a/app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChaptersWithSource.kt +++ b/app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChaptersWithSource.kt @@ -22,8 +22,8 @@ import tachiyomi.domain.chapter.service.ChapterRecognition import tachiyomi.domain.manga.model.Manga import tachiyomi.source.local.isLocal import java.lang.Long.max +import java.time.Instant import java.time.ZonedDateTime -import java.util.Date import java.util.TreeSet class SyncChaptersWithSource( @@ -83,7 +83,7 @@ class SyncChaptersWithSource( } } - val rightNow = Date().time + val rightNow = Instant.now().toEpochMilli() // Used to not set upload date of older chapters // to a higher value than newer chapters diff --git a/app/src/main/java/eu/kanade/domain/manga/interactor/UpdateManga.kt b/app/src/main/java/eu/kanade/domain/manga/interactor/UpdateManga.kt index 468ea2389..a3592e1f4 100644 --- a/app/src/main/java/eu/kanade/domain/manga/interactor/UpdateManga.kt +++ b/app/src/main/java/eu/kanade/domain/manga/interactor/UpdateManga.kt @@ -10,8 +10,8 @@ import tachiyomi.domain.manga.repository.MangaRepository import tachiyomi.source.local.isLocal import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get +import java.time.Instant import java.time.ZonedDateTime -import java.util.Date class UpdateManga( private val mangaRepository: MangaRepository, @@ -46,14 +46,14 @@ class UpdateManga( // Never refresh covers if the url is empty to avoid "losing" existing covers remoteManga.thumbnail_url.isNullOrEmpty() -> null !manualFetch && localManga.thumbnailUrl == remoteManga.thumbnail_url -> null - localManga.isLocal() -> Date().time + localManga.isLocal() -> Instant.now().toEpochMilli() localManga.hasCustomCover(coverCache) -> { coverCache.deleteFromCache(localManga, false) null } else -> { coverCache.deleteFromCache(localManga, false) - Date().time + Instant.now().toEpochMilli() } } @@ -87,16 +87,16 @@ class UpdateManga( } suspend fun awaitUpdateLastUpdate(mangaId: Long): Boolean { - return mangaRepository.update(MangaUpdate(id = mangaId, lastUpdate = Date().time)) + return mangaRepository.update(MangaUpdate(id = mangaId, lastUpdate = Instant.now().toEpochMilli())) } suspend fun awaitUpdateCoverLastModified(mangaId: Long): Boolean { - return mangaRepository.update(MangaUpdate(id = mangaId, coverLastModified = Date().time)) + return mangaRepository.update(MangaUpdate(id = mangaId, coverLastModified = Instant.now().toEpochMilli())) } suspend fun awaitUpdateFavorite(mangaId: Long, favorite: Boolean): Boolean { val dateAdded = when (favorite) { - true -> Date().time + true -> Instant.now().toEpochMilli() false -> 0 } return mangaRepository.update( diff --git a/app/src/main/java/eu/kanade/domain/track/service/DelayedTrackingUpdateJob.kt b/app/src/main/java/eu/kanade/domain/track/service/DelayedTrackingUpdateJob.kt index ad7c3f6b9..b178cf746 100644 --- a/app/src/main/java/eu/kanade/domain/track/service/DelayedTrackingUpdateJob.kt +++ b/app/src/main/java/eu/kanade/domain/track/service/DelayedTrackingUpdateJob.kt @@ -17,8 +17,7 @@ import tachiyomi.core.util.system.logcat import tachiyomi.domain.track.interactor.GetTracks import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import kotlin.time.Duration.Companion.minutes -import kotlin.time.toJavaDuration +import java.util.concurrent.TimeUnit class DelayedTrackingUpdateJob(private val context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams) { @@ -63,7 +62,7 @@ class DelayedTrackingUpdateJob(private val context: Context, workerParams: Worke val request = OneTimeWorkRequestBuilder() .setConstraints(constraints) - .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 5.minutes.toJavaDuration()) + .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 5, TimeUnit.MINUTES) .addTag(TAG) .build() diff --git a/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt index 2e4c6fc0b..3b63e86fd 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt @@ -14,6 +14,11 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Close +import androidx.compose.material.icons.outlined.GetApp +import androidx.compose.material.icons.outlined.Public +import androidx.compose.material.icons.outlined.Refresh +import androidx.compose.material.icons.outlined.Settings +import androidx.compose.material.icons.outlined.VerifiedUser import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator @@ -62,6 +67,7 @@ fun ExtensionScreen( searchQuery: String?, onLongClickItem: (Extension) -> Unit, onClickItemCancel: (Extension) -> Unit, + onClickItemWebView: (Extension.Available) -> Unit, onInstallExtension: (Extension.Available) -> Unit, onUninstallExtension: (Extension) -> Unit, onUpdateExtension: (Extension.Installed) -> Unit, @@ -94,6 +100,7 @@ fun ExtensionScreen( contentPadding = contentPadding, onLongClickItem = onLongClickItem, onClickItemCancel = onClickItemCancel, + onClickItemWebView = onClickItemWebView, onInstallExtension = onInstallExtension, onUninstallExtension = onUninstallExtension, onUpdateExtension = onUpdateExtension, @@ -111,6 +118,7 @@ private fun ExtensionContent( state: ExtensionsScreenModel.State, contentPadding: PaddingValues, onLongClickItem: (Extension) -> Unit, + onClickItemWebView: (Extension.Available) -> Unit, onClickItemCancel: (Extension) -> Unit, onInstallExtension: (Extension.Available) -> Unit, onUninstallExtension: (Extension) -> Unit, @@ -177,6 +185,7 @@ private fun ExtensionContent( } }, onLongClickItem = onLongClickItem, + onClickItemWebView = onClickItemWebView, onClickItemCancel = onClickItemCancel, onClickItemAction = { when (it) { @@ -217,6 +226,7 @@ private fun ExtensionItem( item: ExtensionUiModel.Item, onClickItem: (Extension) -> Unit, onLongClickItem: (Extension) -> Unit, + onClickItemWebView: (Extension.Available) -> Unit, onClickItemCancel: (Extension) -> Unit, onClickItemAction: (Extension) -> Unit, modifier: Modifier = Modifier, @@ -260,6 +270,7 @@ private fun ExtensionItem( ExtensionItemActions( extension = extension, installStep = installStep, + onClickItemWebView = onClickItemWebView, onClickItemCancel = onClickItemCancel, onClickItemAction = onClickItemAction, ) @@ -343,42 +354,80 @@ private fun ExtensionItemActions( extension: Extension, installStep: InstallStep, modifier: Modifier = Modifier, + onClickItemWebView: (Extension.Available) -> Unit = {}, onClickItemCancel: (Extension) -> Unit = {}, onClickItemAction: (Extension) -> Unit = {}, ) { val isIdle = installStep.isCompleted() - Row(modifier = modifier) { - if (isIdle) { - TextButton( - onClick = { onClickItemAction(extension) }, - ) { - Text( - text = when (installStep) { - InstallStep.Installed -> stringResource(MR.strings.ext_installed) - InstallStep.Error -> stringResource(MR.strings.action_retry) - InstallStep.Idle -> { - when (extension) { - is Extension.Installed -> { - if (extension.hasUpdate) { - stringResource(MR.strings.ext_update) - } else { - stringResource(MR.strings.action_settings) - } - } - is Extension.Untrusted -> stringResource(MR.strings.ext_trust) - is Extension.Available -> stringResource(MR.strings.ext_install) + + Row( + modifier = modifier, + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + when { + !isIdle -> { + IconButton(onClick = { onClickItemCancel(extension) }) { + Icon( + imageVector = Icons.Outlined.Close, + contentDescription = stringResource(MR.strings.action_cancel), + ) + } + } + installStep == InstallStep.Error -> { + IconButton(onClick = { onClickItemAction(extension) }) { + Icon( + imageVector = Icons.Outlined.Refresh, + contentDescription = stringResource(MR.strings.action_retry), + ) + } + } + installStep == InstallStep.Idle -> { + when (extension) { + is Extension.Installed -> { + if (extension.hasUpdate) { + IconButton(onClick = { onClickItemAction(extension) }) { + Icon( + imageVector = Icons.Outlined.GetApp, + contentDescription = stringResource(MR.strings.ext_update), + ) } } - else -> error("Must not show install process text") - }, - ) - } - } else { - IconButton(onClick = { onClickItemCancel(extension) }) { - Icon( - imageVector = Icons.Outlined.Close, - contentDescription = stringResource(MR.strings.action_cancel), - ) + + IconButton(onClick = { onClickItemAction(extension) }) { + Icon( + imageVector = Icons.Outlined.Settings, + contentDescription = stringResource(MR.strings.action_settings), + ) + } + } + is Extension.Untrusted -> { + IconButton(onClick = { onClickItemAction(extension) }) { + Icon( + imageVector = Icons.Outlined.VerifiedUser, + contentDescription = stringResource(MR.strings.ext_trust), + ) + } + } + is Extension.Available -> { + if (extension.sources.isNotEmpty()) { + IconButton( + onClick = { onClickItemWebView(extension) }, + ) { + Icon( + imageVector = Icons.Outlined.Public, + contentDescription = stringResource(MR.strings.action_open_in_web_view), + ) + } + } + + IconButton(onClick = { onClickItemAction(extension) }) { + Icon( + imageVector = Icons.Outlined.GetApp, + contentDescription = stringResource(MR.strings.ext_install), + ) + } + } + } } } } diff --git a/app/src/main/java/eu/kanade/presentation/crash/CrashScreen.kt b/app/src/main/java/eu/kanade/presentation/crash/CrashScreen.kt index f90aeb252..5504f13cc 100644 --- a/app/src/main/java/eu/kanade/presentation/crash/CrashScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/crash/CrashScreen.kt @@ -2,7 +2,7 @@ package eu.kanade.presentation.crash import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.BugReport @@ -47,7 +47,7 @@ fun CrashScreen( modifier = Modifier .padding(vertical = MaterialTheme.padding.small) .clip(MaterialTheme.shapes.small) - .fillMaxWidth() + .fillMaxSize() .background(MaterialTheme.colorScheme.surfaceVariant), ) { Text( diff --git a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt index dda12b12b..18fbbc3f5 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt @@ -96,7 +96,7 @@ fun MangaScreen( onAddToLibraryClicked: () -> Unit, onWebViewClicked: (() -> Unit)?, onWebViewLongClicked: (() -> Unit)?, - onTrackingClicked: (() -> Unit)?, + onTrackingClicked: () -> Unit, // For tags menu onTagSearch: (String) -> Unit, @@ -229,7 +229,7 @@ private fun MangaScreenSmallImpl( onAddToLibraryClicked: () -> Unit, onWebViewClicked: (() -> Unit)?, onWebViewLongClicked: (() -> Unit)?, - onTrackingClicked: (() -> Unit)?, + onTrackingClicked: () -> Unit, // For tags menu onTagSearch: (String) -> Unit, @@ -481,7 +481,7 @@ fun MangaScreenLargeImpl( onAddToLibraryClicked: () -> Unit, onWebViewClicked: (() -> Unit)?, onWebViewLongClicked: (() -> Unit)?, - onTrackingClicked: (() -> Unit)?, + onTrackingClicked: () -> Unit, // For tags menu onTagSearch: (String) -> Unit, diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt index 01a94057a..a643a15b2 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt @@ -92,7 +92,6 @@ private val whitespaceLineRegex = Regex("[\\r\\n]{2,}", setOf(RegexOption.MULTIL @Composable fun MangaInfoBox( - modifier: Modifier = Modifier, isTabletUi: Boolean, appBarPadding: Dp, title: String, @@ -104,6 +103,7 @@ fun MangaInfoBox( status: Long, onCoverClick: () -> Unit, doSearch: (query: String, global: Boolean) -> Unit, + modifier: Modifier = Modifier, ) { Box(modifier = modifier) { // Backdrop @@ -162,7 +162,6 @@ fun MangaInfoBox( @Composable fun MangaActionRow( - modifier: Modifier = Modifier, favorite: Boolean, trackingCount: Int, fetchInterval: Int?, @@ -170,9 +169,10 @@ fun MangaActionRow( onAddToLibraryClicked: () -> Unit, onWebViewClicked: (() -> Unit)?, onWebViewLongClicked: (() -> Unit)?, - onTrackingClicked: (() -> Unit)?, + onTrackingClicked: () -> Unit, onEditIntervalClicked: (() -> Unit)?, onEditCategory: (() -> Unit)?, + modifier: Modifier = Modifier, ) { val defaultActionButtonColor = MaterialTheme.colorScheme.onSurface.copy(alpha = .38f) @@ -200,18 +200,16 @@ fun MangaActionRow( onClick = onEditIntervalClicked, ) } - if (onTrackingClicked != null) { - MangaActionButton( - title = if (trackingCount == 0) { - stringResource(MR.strings.manga_tracking_tab) - } else { - pluralStringResource(MR.plurals.num_trackers, count = trackingCount, trackingCount) - }, - icon = if (trackingCount == 0) Icons.Outlined.Sync else Icons.Outlined.Done, - color = if (trackingCount == 0) defaultActionButtonColor else MaterialTheme.colorScheme.primary, - onClick = onTrackingClicked, - ) - } + MangaActionButton( + title = if (trackingCount == 0) { + stringResource(MR.strings.manga_tracking_tab) + } else { + pluralStringResource(MR.plurals.num_trackers, count = trackingCount, trackingCount) + }, + icon = if (trackingCount == 0) Icons.Outlined.Sync else Icons.Outlined.Done, + color = if (trackingCount == 0) defaultActionButtonColor else MaterialTheme.colorScheme.primary, + onClick = onTrackingClicked, + ) if (onWebViewClicked != null) { MangaActionButton( title = stringResource(MR.strings.action_web_view), @@ -226,12 +224,12 @@ fun MangaActionRow( @Composable fun ExpandableMangaDescription( - modifier: Modifier = Modifier, defaultExpandState: Boolean, description: String?, tagsProvider: () -> List?, onTagSearch: (String) -> Unit, onCopyTagToClipboard: (tag: String) -> Unit, + modifier: Modifier = Modifier, ) { Column(modifier = modifier) { val (expanded, onExpanded) = rememberSaveable { @@ -406,13 +404,13 @@ private fun MangaAndSourceTitlesSmall( @Composable private fun MangaContentInfo( title: String, - textAlign: TextAlign? = LocalTextStyle.current.textAlign, doSearch: (query: String, global: Boolean) -> Unit, author: String?, artist: String?, status: Long, sourceName: String, isStubSource: Boolean, + textAlign: TextAlign? = LocalTextStyle.current.textAlign, ) { val context = LocalContext.current Text( @@ -556,7 +554,10 @@ private fun MangaSummary( expanded: Boolean, modifier: Modifier = Modifier, ) { - val animProgress by animateFloatAsState(if (expanded) 1f else 0f) + val animProgress by animateFloatAsState( + targetValue = if (expanded) 1f else 0f, + label = "summary", + ) Layout( modifier = modifier.clipToBounds(), contents = listOf( diff --git a/app/src/main/java/eu/kanade/presentation/more/onboarding/GuidesStep.kt b/app/src/main/java/eu/kanade/presentation/more/onboarding/GuidesStep.kt new file mode 100644 index 000000000..5899dae55 --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/more/onboarding/GuidesStep.kt @@ -0,0 +1,62 @@ +package eu.kanade.presentation.more.onboarding + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalUriHandler +import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.unit.dp +import eu.kanade.presentation.theme.TachiyomiTheme +import tachiyomi.i18n.MR +import tachiyomi.presentation.core.i18n.stringResource + +@Composable +internal fun GuidesStep( + onRestoreBackup: () -> Unit, +) { + val handler = LocalUriHandler.current + + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text(stringResource(MR.strings.onboarding_guides_new_user, stringResource(MR.strings.app_name))) + Button( + modifier = Modifier.fillMaxWidth(), + onClick = { handler.openUri(GETTING_STARTED_URL) }, + ) { + Text(stringResource(MR.strings.getting_started_guide)) + } + + HorizontalDivider( + color = MaterialTheme.colorScheme.onPrimaryContainer, + ) + + Text(stringResource(MR.strings.onboarding_guides_returning_user, stringResource(MR.strings.app_name))) + Button( + modifier = Modifier.fillMaxWidth(), + onClick = onRestoreBackup, + ) { + Text(stringResource(MR.strings.pref_restore_backup)) + } + } +} + +const val GETTING_STARTED_URL = "https://tachiyomi.org/docs/guides/getting-started" + +@PreviewLightDark +@Composable +private fun GuidesStepPreview() { + TachiyomiTheme { + GuidesStep( + onRestoreBackup = {}, + ) + } +} diff --git a/app/src/main/java/eu/kanade/presentation/more/onboarding/OnboardingScreen.kt b/app/src/main/java/eu/kanade/presentation/more/onboarding/OnboardingScreen.kt new file mode 100644 index 000000000..ef42a68fc --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/more/onboarding/OnboardingScreen.kt @@ -0,0 +1,98 @@ +package eu.kanade.presentation.more.onboarding + +import androidx.activity.compose.BackHandler +import androidx.compose.animation.AnimatedContent +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.RocketLaunch +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalContext +import eu.kanade.domain.ui.UiPreferences +import eu.kanade.tachiyomi.util.system.toast +import soup.compose.material.motion.animation.materialSharedAxisX +import soup.compose.material.motion.animation.rememberSlideDistance +import tachiyomi.domain.storage.service.StoragePreferences +import tachiyomi.i18n.MR +import tachiyomi.presentation.core.components.material.padding +import tachiyomi.presentation.core.i18n.stringResource +import tachiyomi.presentation.core.screens.InfoScreen + +@Composable +fun OnboardingScreen( + storagePreferences: StoragePreferences, + uiPreferences: UiPreferences, + onComplete: () -> Unit, + onRestoreBackup: () -> Unit, +) { + val context = LocalContext.current + val slideDistance = rememberSlideDistance() + + var currentStep by remember { mutableIntStateOf(0) } + val steps: List<@Composable () -> Unit> = remember { + listOf( + { ThemeStep(uiPreferences = uiPreferences) }, + { StorageStep(storagePref = storagePreferences.baseStorageDirectory()) }, + // TODO: prompt for notification permissions when bumping target to Android 13 + { GuidesStep(onRestoreBackup = onRestoreBackup) }, + ) + } + val isLastStep = currentStep == steps.size - 1 + + BackHandler(enabled = currentStep != 0, onBack = { currentStep-- }) + + InfoScreen( + icon = Icons.Outlined.RocketLaunch, + headingText = stringResource(MR.strings.onboarding_heading), + subtitleText = stringResource(MR.strings.onboarding_description), + acceptText = stringResource( + if (isLastStep) { + MR.strings.onboarding_action_finish + } else { + MR.strings.onboarding_action_next + }, + ), + onAcceptClick = { + if (isLastStep) { + onComplete() + } else { + // TODO: this is kind of janky + if (currentStep == 1 && !storagePreferences.baseStorageDirectory().isSet()) { + context.toast(MR.strings.onboarding_storage_selection_required) + } else { + currentStep++ + } + } + }, + ) { + Box( + modifier = Modifier + .padding(vertical = MaterialTheme.padding.small) + .clip(MaterialTheme.shapes.small) + .fillMaxSize() + .background(MaterialTheme.colorScheme.surfaceVariant), + ) { + AnimatedContent( + targetState = currentStep, + transitionSpec = { + materialSharedAxisX( + forward = targetState > initialState, + slideDistance = slideDistance, + ) + }, + label = "stepContent", + ) { + steps[it]() + } + } + } +} diff --git a/app/src/main/java/eu/kanade/presentation/more/onboarding/StorageStep.kt b/app/src/main/java/eu/kanade/presentation/more/onboarding/StorageStep.kt new file mode 100644 index 000000000..062e5a7c9 --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/more/onboarding/StorageStep.kt @@ -0,0 +1,52 @@ +package eu.kanade.presentation.more.onboarding + +import android.content.ActivityNotFoundException +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp +import eu.kanade.presentation.more.settings.screen.SettingsDataScreen +import eu.kanade.tachiyomi.util.system.toast +import tachiyomi.core.preference.Preference +import tachiyomi.i18n.MR +import tachiyomi.presentation.core.components.material.Button +import tachiyomi.presentation.core.i18n.stringResource + +@Composable +internal fun StorageStep( + storagePref: Preference, +) { + val context = LocalContext.current + val pickStorageLocation = SettingsDataScreen.storageLocationPicker(storagePref) + + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text( + stringResource( + MR.strings.onboarding_storage_info, + stringResource(MR.strings.app_name), + SettingsDataScreen.storageLocationText(storagePref), + ), + ) + + Button( + modifier = Modifier.fillMaxWidth(), + onClick = { + try { + pickStorageLocation.launch(null) + } catch (e: ActivityNotFoundException) { + context.toast(MR.strings.file_picker_error) + } + }, + ) { + Text(stringResource(MR.strings.onboarding_storage_action_select)) + } + } +} diff --git a/app/src/main/java/eu/kanade/presentation/more/onboarding/ThemeStep.kt b/app/src/main/java/eu/kanade/presentation/more/onboarding/ThemeStep.kt new file mode 100644 index 000000000..69951e0b5 --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/more/onboarding/ThemeStep.kt @@ -0,0 +1,40 @@ +package eu.kanade.presentation.more.onboarding + +import androidx.compose.foundation.layout.Column +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import eu.kanade.domain.ui.UiPreferences +import eu.kanade.domain.ui.model.setAppCompatDelegateThemeMode +import eu.kanade.presentation.more.settings.widget.AppThemeModePreferenceWidget +import eu.kanade.presentation.more.settings.widget.AppThemePreferenceWidget +import tachiyomi.presentation.core.util.collectAsState + +@Composable +internal fun ThemeStep( + uiPreferences: UiPreferences, +) { + val themeModePref = uiPreferences.themeMode() + val themeMode by themeModePref.collectAsState() + + val appThemePref = uiPreferences.appTheme() + val appTheme by appThemePref.collectAsState() + + val amoledPref = uiPreferences.themeDarkAmoled() + val amoled by amoledPref.collectAsState() + + Column { + AppThemeModePreferenceWidget( + value = themeMode, + onItemClick = { + themeModePref.set(it) + setAppCompatDelegateThemeMode(it) + }, + ) + + AppThemePreferenceWidget( + value = appTheme, + amoled = amoled, + onItemClick = { appThemePref.set(it) }, + ) + } +} diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt index 474036247..56f606567 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt @@ -43,6 +43,7 @@ import eu.kanade.tachiyomi.network.PREF_DOH_NJALLA import eu.kanade.tachiyomi.network.PREF_DOH_QUAD101 import eu.kanade.tachiyomi.network.PREF_DOH_QUAD9 import eu.kanade.tachiyomi.network.PREF_DOH_SHECAN +import eu.kanade.tachiyomi.ui.more.OnboardingScreen import eu.kanade.tachiyomi.util.CrashLogUtil import eu.kanade.tachiyomi.util.system.isPreviewBuildType import eu.kanade.tachiyomi.util.system.isReleaseBuildType @@ -110,6 +111,10 @@ object SettingsAdvancedScreen : SearchableSettings { title = stringResource(MR.strings.pref_debug_info), onClick = { navigator.push(DebugInfoScreen()) }, ), + Preference.PreferenceItem.TextPreference( + title = stringResource(MR.strings.pref_onboarding_guide), + onClick = { navigator.push(OnboardingScreen()) }, + ), ), ) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt index 667afa505..8523de930 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt @@ -2,8 +2,8 @@ package eu.kanade.presentation.more.settings.screen import android.app.Activity import android.content.Context -import android.os.Build import androidx.appcompat.app.AppCompatDelegate +import androidx.compose.foundation.layout.Column import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.ReadOnlyComposable @@ -19,13 +19,11 @@ import eu.kanade.domain.ui.model.TabletUiMode import eu.kanade.domain.ui.model.ThemeMode import eu.kanade.domain.ui.model.setAppCompatDelegateThemeMode import eu.kanade.presentation.more.settings.Preference +import eu.kanade.presentation.more.settings.widget.AppThemeModePreferenceWidget import eu.kanade.presentation.more.settings.widget.AppThemePreferenceWidget import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.toast -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.drop -import kotlinx.coroutines.flow.merge import org.xmlpull.v1.XmlPullParser import tachiyomi.core.i18n.stringResource import tachiyomi.i18n.MR @@ -33,7 +31,7 @@ import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.util.collectAsState import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.util.Date +import java.time.Instant object SettingsAppearanceScreen : SearchableSettings { @@ -43,72 +41,59 @@ object SettingsAppearanceScreen : SearchableSettings { @Composable override fun getPreferences(): List { - val context = LocalContext.current val uiPreferences = remember { Injekt.get() } return listOf( - getThemeGroup(context = context, uiPreferences = uiPreferences), - getDisplayGroup(context = context, uiPreferences = uiPreferences), + getThemeGroup(uiPreferences = uiPreferences), + getDisplayGroup(uiPreferences = uiPreferences), ) } @Composable private fun getThemeGroup( - context: Context, uiPreferences: UiPreferences, ): Preference.PreferenceGroup { + val context = LocalContext.current + val themeModePref = uiPreferences.themeMode() val themeMode by themeModePref.collectAsState() val appThemePref = uiPreferences.appTheme() + val appTheme by appThemePref.collectAsState() val amoledPref = uiPreferences.themeDarkAmoled() val amoled by amoledPref.collectAsState() - LaunchedEffect(themeMode) { - setAppCompatDelegateThemeMode(themeMode) - } - - LaunchedEffect(Unit) { - merge(appThemePref.changes(), amoledPref.changes()) - .drop(2) - .collectLatest { (context as? Activity)?.let { ActivityCompat.recreate(it) } } - } - return Preference.PreferenceGroup( title = stringResource(MR.strings.pref_category_theme), preferenceItems = listOf( - Preference.PreferenceItem.ListPreference( - pref = themeModePref, - title = stringResource(MR.strings.pref_theme_mode), - entries = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - mapOf( - ThemeMode.SYSTEM to stringResource(MR.strings.theme_system), - ThemeMode.LIGHT to stringResource(MR.strings.theme_light), - ThemeMode.DARK to stringResource(MR.strings.theme_dark), - ) - } else { - mapOf( - ThemeMode.LIGHT to stringResource(MR.strings.theme_light), - ThemeMode.DARK to stringResource(MR.strings.theme_dark), - ) - }, - ), Preference.PreferenceItem.CustomPreference( title = stringResource(MR.strings.pref_app_theme), - ) { item -> - val value by appThemePref.collectAsState() - AppThemePreferenceWidget( - title = item.title, - value = value, - amoled = amoled, - onItemClick = { appThemePref.set(it) }, - ) + ) { + Column { + AppThemeModePreferenceWidget( + value = themeMode, + onItemClick = { + themeModePref.set(it) + setAppCompatDelegateThemeMode(it) + }, + ) + + AppThemePreferenceWidget( + value = appTheme, + amoled = amoled, + onItemClick = { appThemePref.set(it) }, + ) + } }, Preference.PreferenceItem.SwitchPreference( pref = amoledPref, title = stringResource(MR.strings.pref_dark_theme_pure_black), enabled = themeMode != ThemeMode.LIGHT, + onValueChanged = { + (context as? Activity)?.let { ActivityCompat.recreate(it) } + true + }, ), ), ) @@ -116,14 +101,15 @@ object SettingsAppearanceScreen : SearchableSettings { @Composable private fun getDisplayGroup( - context: Context, uiPreferences: UiPreferences, ): Preference.PreferenceGroup { + val context = LocalContext.current + val langs = remember { getLangs(context) } var currentLanguage by remember { mutableStateOf(AppCompatDelegate.getApplicationLocales().get(0)?.toLanguageTag() ?: "") } - val now = remember { Date().time } + val now = remember { Instant.now().toEpochMilli() } val dateFormat by uiPreferences.dateFormat().collectAsState() val formattedNow = remember(dateFormat) { diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt index 87eceb99e..c3286d78a 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt @@ -7,6 +7,7 @@ import android.net.Uri import android.os.Environment import android.text.format.Formatter import android.widget.Toast +import androidx.activity.compose.ManagedActivityResultLauncher import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.layout.Box @@ -89,13 +90,12 @@ object SettingsDataScreen : SearchableSettings { } @Composable - private fun getStorageLocationPref( - storagePreferences: StoragePreferences, - ): Preference.PreferenceItem.TextPreference { + fun storageLocationPicker( + storageDirPref: tachiyomi.core.preference.Preference, + ): ManagedActivityResultLauncher { val context = LocalContext.current - val storageDirPref = storagePreferences.baseStorageDirectory() - val storageDir by storageDirPref.collectAsState() - val pickStorageLocation = rememberLauncherForActivityResult( + + return rememberLauncherForActivityResult( contract = ActivityResultContracts.OpenDocumentTree(), ) { uri -> if (uri != null) { @@ -110,13 +110,35 @@ object SettingsDataScreen : SearchableSettings { Injekt.get().invalidateCache() } } + } + + @Composable + fun storageLocationText( + storageDirPref: tachiyomi.core.preference.Preference, + ): String { + val context = LocalContext.current + val storageDir by storageDirPref.collectAsState() + + if (storageDir == storageDirPref.defaultValue()) { + return stringResource(MR.strings.no_location_set) + } + + return remember(storageDir) { + val file = UniFile.fromUri(context, storageDir.toUri()) + file?.filePath ?: file?.uri?.toString() + } ?: stringResource(MR.strings.invalid_location, storageDir) + } + + @Composable + private fun getStorageLocationPref( + storagePreferences: StoragePreferences, + ): Preference.PreferenceItem.TextPreference { + val context = LocalContext.current + val pickStorageLocation = storageLocationPicker(storagePreferences.baseStorageDirectory()) return Preference.PreferenceItem.TextPreference( title = stringResource(MR.strings.pref_storage_location), - subtitle = remember(storageDir) { - val file = UniFile.fromUri(context, storageDir.toUri()) - file?.filePath ?: file?.uri?.toString() - } ?: stringResource(MR.strings.invalid_location, storageDir), + subtitle = storageLocationText(storagePreferences.baseStorageDirectory()), onClick = { try { pickStorageLocation.launch(null) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemeModePreferenceWidget.kt b/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemeModePreferenceWidget.kt new file mode 100644 index 000000000..d1901745b --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemeModePreferenceWidget.kt @@ -0,0 +1,56 @@ +package eu.kanade.presentation.more.settings.widget + +import android.os.Build +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MultiChoiceSegmentedButtonRow +import androidx.compose.material3.SegmentedButton +import androidx.compose.material3.SegmentedButtonDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import eu.kanade.domain.ui.model.ThemeMode +import tachiyomi.i18n.MR +import tachiyomi.presentation.core.i18n.stringResource + +private val options = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + mapOf( + ThemeMode.SYSTEM to MR.strings.theme_system, + ThemeMode.LIGHT to MR.strings.theme_light, + ThemeMode.DARK to MR.strings.theme_dark, + ) +} else { + mapOf( + ThemeMode.LIGHT to MR.strings.theme_light, + ThemeMode.DARK to MR.strings.theme_dark, + ) +} + +@Composable +internal fun AppThemeModePreferenceWidget( + value: ThemeMode, + onItemClick: (ThemeMode) -> Unit, +) { + BasePreferenceWidget( + subcomponent = { + MultiChoiceSegmentedButtonRow( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = PrefsHorizontalPadding), + ) { + options.onEachIndexed { index, (mode, labelRes) -> + SegmentedButton( + checked = mode == value, + onCheckedChange = { onItemClick(mode) }, + shape = SegmentedButtonDefaults.itemShape( + index, + options.size, + ), + ) { + Text(stringResource(labelRes)) + } + } + } + }, + ) +} diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemePreferenceWidget.kt b/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemePreferenceWidget.kt index 5b7599722..2be6e03a4 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemePreferenceWidget.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemePreferenceWidget.kt @@ -1,5 +1,6 @@ package eu.kanade.presentation.more.settings.widget +import android.app.Activity import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable @@ -36,9 +37,11 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.unit.dp +import androidx.core.app.ActivityCompat import eu.kanade.domain.ui.model.AppTheme import eu.kanade.presentation.manga.components.MangaCover import eu.kanade.presentation.theme.TachiyomiTheme @@ -51,13 +54,11 @@ import tachiyomi.presentation.core.util.secondaryItemAlpha @Composable internal fun AppThemePreferenceWidget( - title: String, value: AppTheme, amoled: Boolean, onItemClick: (AppTheme) -> Unit, ) { BasePreferenceWidget( - title = title, subcomponent = { AppThemesList( currentTheme = value, @@ -74,6 +75,7 @@ private fun AppThemesList( amoled: Boolean, onItemClick: (AppTheme) -> Unit, ) { + val context = LocalContext.current val appThemes = remember { AppTheme.entries .filterNot { it.titleRes == null || (it == AppTheme.MONET && !DeviceUtil.isDynamicColorAvailable) } @@ -97,7 +99,10 @@ private fun AppThemesList( ) { AppThemePreviewItem( selected = currentTheme == appTheme, - onClick = { onItemClick(appTheme) }, + onClick = { + onItemClick(appTheme) + (context as? Activity)?.let { ActivityCompat.recreate(it) } + }, ) } diff --git a/app/src/main/java/eu/kanade/presentation/util/TimeUtils.kt b/app/src/main/java/eu/kanade/presentation/util/TimeUtils.kt index b8dc09693..0bb5089ab 100644 --- a/app/src/main/java/eu/kanade/presentation/util/TimeUtils.kt +++ b/app/src/main/java/eu/kanade/presentation/util/TimeUtils.kt @@ -7,7 +7,7 @@ import androidx.compose.runtime.ReadOnlyComposable import tachiyomi.core.i18n.stringResource import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource -import java.util.Date +import java.time.Instant import kotlin.time.Duration import kotlin.time.Duration.Companion.minutes @@ -29,7 +29,7 @@ fun Duration.toDurationString(context: Context, fallback: String): String { @Composable @ReadOnlyComposable fun relativeTimeSpanString(epochMillis: Long): String { - val now = Date().time + val now = Instant.now().toEpochMilli() return when { epochMillis <= 0L -> stringResource(MR.strings.relative_time_span_never) now - epochMillis < 1.minutes.inWholeMilliseconds -> stringResource( diff --git a/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt b/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt index 1c7887dca..f5f6c4f81 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt @@ -396,7 +396,12 @@ object Migrations { newKey = { Preference.privateKey(it) }, ) } - if (oldVersion < 110) { + if (oldVersion < 111) { + File(context.cacheDir, "dl_index_cache") + .takeIf { it.exists() } + ?.delete() + } + if (oldVersion < 112) { val prefsToReplace = listOf( "pref_download_only", "incognito_mode", @@ -409,6 +414,7 @@ object Migrations { "last_app_check", "last_ext_check", "last_version_code", + "storage_dir", ) replacePreferences( preferenceStore = preferenceStore, @@ -416,11 +422,6 @@ object Migrations { newKey = { Preference.appStateKey(it) }, ) } - if (oldVersion < 111) { - File(context.cacheDir, "dl_index_cache") - .takeIf { it.exists() } - ?.delete() - } return true } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreateJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreateJob.kt index f6fe367f3..f314d9c01 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreateJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreateJob.kt @@ -25,10 +25,8 @@ import tachiyomi.domain.backup.service.BackupPreferences import tachiyomi.domain.storage.service.StorageManager import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.util.Date +import java.time.Instant import java.util.concurrent.TimeUnit -import kotlin.time.Duration.Companion.minutes -import kotlin.time.toJavaDuration class BackupCreateJob(private val context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams) { @@ -52,7 +50,7 @@ class BackupCreateJob(private val context: Context, workerParams: WorkerParamete return try { val location = BackupCreator(context).createBackup(uri, flags, isAutoBackup) if (isAutoBackup) { - backupPreferences.lastAutoBackupTimestamp().set(Date().time) + backupPreferences.lastAutoBackupTimestamp().set(Instant.now().toEpochMilli()) } else { notifier.showBackupComplete(UniFile.fromUri(context, location.toUri())!!) } @@ -97,7 +95,7 @@ class BackupCreateJob(private val context: Context, workerParams: WorkerParamete 10, TimeUnit.MINUTES, ) - .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 10.minutes.toJavaDuration()) + .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 10, TimeUnit.MINUTES) .addTag(TAG_AUTO) .setConstraints(constraints) .setInputData(workDataOf(IS_AUTO_BACKUP_KEY to true)) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestorer.kt index 79617b241..35377eb0f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestorer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestorer.kt @@ -21,7 +21,7 @@ import eu.kanade.tachiyomi.source.sourcePreferences import eu.kanade.tachiyomi.util.BackupUtil import eu.kanade.tachiyomi.util.system.createFileInCacheDir import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.isActive +import kotlinx.coroutines.ensureActive import tachiyomi.core.i18n.stringResource import tachiyomi.core.preference.AndroidPreferenceStore import tachiyomi.core.preference.PreferenceStore @@ -76,14 +76,12 @@ class BackupRestorer( private val errors = mutableListOf>() - suspend fun syncFromBackup(uri: Uri, sync: Boolean): Boolean { + suspend fun syncFromBackup(uri: Uri, sync: Boolean) { val startTime = System.currentTimeMillis() restoreProgress = 0 errors.clear() - if (!performRestore(uri, sync)) { - return false - } + performRestore(uri, sync) val endTime = System.currentTimeMillis() val time = endTime - startTime @@ -95,7 +93,6 @@ class BackupRestorer( } else { notifier.showRestoreComplete(time, errors.size, logFile.parent, logFile.name) } - return true } private fun writeErrorLog(): File { @@ -117,74 +114,57 @@ class BackupRestorer( return File("") } - private suspend fun performRestore(uri: Uri, sync: Boolean): Boolean { + private suspend fun performRestore(uri: Uri, sync: Boolean) { val backup = BackupUtil.decodeBackup(context, uri) restoreAmount = backup.backupManga.size + 3 // +3 for categories, app prefs, source prefs - // Restore categories - if (backup.backupCategories.isNotEmpty()) { - restoreCategories(backup.backupCategories) - } - // Store source mapping for error messages val backupMaps = backup.backupBrokenSources.map { BackupSource(it.name, it.sourceId) } + backup.backupSources sourceMapping = backupMaps.associate { it.sourceId to it.name } now = ZonedDateTime.now() currentFetchWindow = fetchInterval.getWindow(now) - return coroutineScope { + coroutineScope { + ensureActive() + restoreCategories(backup.backupCategories) + + ensureActive() restoreAppPreferences(backup.backupPreferences) + + ensureActive() restoreSourcePreferences(backup.backupSourcePreferences) // Restore individual manga backup.backupManga.forEach { - if (!isActive) { - return@coroutineScope false - } - + ensureActive() restoreManga(it, backup.backupCategories, sync) } - // TODO: optionally trigger online library + tracker update - true + // TODO: optionally trigger online library + tracker update } } private suspend fun restoreCategories(backupCategories: List) { - // Get categories from file and from db - val dbCategories = getCategories.await() + if (backupCategories.isNotEmpty()) { + val dbCategories = getCategories.await() + val dbCategoriesByName = dbCategories.associateBy { it.name } - val categories = backupCategories.map { - var category = it.getCategory() - var found = false - for (dbCategory in dbCategories) { - // If the category is already in the db, assign the id to the file's category - // and do nothing - if (category.name == dbCategory.name) { - category = category.copy(id = dbCategory.id) - found = true - break - } - } - if (!found) { - // Let the db assign the id - val id = handler.awaitOneExecutable { - categoriesQueries.insert(category.name, category.order, category.flags) - categoriesQueries.selectLastInsertedRowId() - } - category = category.copy(id = id) + val categories = backupCategories.map { + dbCategoriesByName[it.name] + ?: handler.awaitOneExecutable { + categoriesQueries.insert(it.name, it.order, it.flags) + categoriesQueries.selectLastInsertedRowId() + }.let { id -> it.toCategory(id) } } - category + libraryPreferences.categorizedDisplaySettings().set( + (dbCategories + categories) + .distinctBy { it.flags } + .size > 1, + ) } - libraryPreferences.categorizedDisplaySettings().set( - (dbCategories + categories) - .distinctBy { it.flags } - .size > 1, - ) - restoreProgress += 1 showRestoreProgress( restoreProgress, diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupCategory.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupCategory.kt index c27c86fec..df517e8ed 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupCategory.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupCategory.kt @@ -11,14 +11,12 @@ class BackupCategory( // @ProtoNumber(3) val updateInterval: Int = 0, 1.x value not used in 0.x @ProtoNumber(100) var flags: Long = 0, ) { - fun getCategory(): Category { - return Category( - id = 0, - name = this@BackupCategory.name, - flags = this@BackupCategory.flags, - order = this@BackupCategory.order, - ) - } + fun toCategory(id: Long) = Category( + id = id, + name = this@BackupCategory.name, + flags = this@BackupCategory.flags, + order = this@BackupCategory.order, + ) } val backupCategoryMapper = { category: Category -> diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt index 1dd117f41..30f11c4d1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt @@ -47,7 +47,6 @@ import tachiyomi.core.storage.extension import tachiyomi.core.util.lang.launchIO import tachiyomi.core.util.lang.launchNow import tachiyomi.core.util.lang.withIOContext -import tachiyomi.core.util.lang.withUIContext import tachiyomi.core.util.system.ImageUtil import tachiyomi.core.util.system.logcat import tachiyomi.domain.category.interactor.GetCategories @@ -265,24 +264,21 @@ class Downloader( * @param chapters the list of chapters to download. * @param autoStart whether to start the downloader after enqueing the chapters. */ - fun queueChapters(manga: Manga, chapters: List, autoStart: Boolean) = launchIO { - if (chapters.isEmpty()) { - return@launchIO - } + fun queueChapters(manga: Manga, chapters: List, autoStart: Boolean) { + if (chapters.isEmpty()) return - val source = sourceManager.get(manga.source) as? HttpSource ?: return@launchIO + val source = sourceManager.get(manga.source) as? HttpSource ?: return val wasEmpty = queueState.value.isEmpty() - val chaptersWithoutDir = chapters + val chaptersToQueue = chapters.asSequence() // Filter out those already downloaded. .filter { provider.findChapterDir(it.name, it.scanlator, manga.title, source) == null } // Add chapters to queue from the start. .sortedByDescending { it.sourceOrder } - - val chaptersToQueue = chaptersWithoutDir // Filter out those already enqueued. .filter { chapter -> queueState.value.none { it.chapter.id == chapter.id } } // Create a download for each one. .map { Download(source, manga, it) } + .toList() if (chaptersToQueue.isNotEmpty()) { addAllToQueue(chaptersToQueue) @@ -299,13 +295,11 @@ class Downloader( queuedDownloads > DOWNLOADS_QUEUED_WARNING_THRESHOLD || maxDownloadsFromSource > CHAPTERS_PER_SOURCE_QUEUE_WARNING_THRESHOLD ) { - withUIContext { - notifier.onWarning( - context.stringResource(MR.strings.download_queue_size_warning), - WARNING_NOTIF_TIMEOUT_MS, - NotificationHandler.openUrl(context, LibraryUpdateNotifier.HELP_WARNING_URL), - ) - } + notifier.onWarning( + context.stringResource(MR.strings.download_queue_size_warning), + WARNING_NOTIF_TIMEOUT_MS, + NotificationHandler.openUrl(context, LibraryUpdateNotifier.HELP_WARNING_URL), + ) } DownloadJob.start(context) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt index d733f176e..7e0f89966 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt @@ -69,8 +69,8 @@ import tachiyomi.i18n.MR import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.io.File +import java.time.Instant import java.time.ZonedDateTime -import java.util.Date import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicBoolean @@ -111,7 +111,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet setForegroundSafely() - libraryPreferences.lastUpdatedTimestamp().set(Date().time) + libraryPreferences.lastUpdatedTimestamp().set(Instant.now().toEpochMilli()) val categoryId = inputData.getLong(KEY_CATEGORY, -1L) addMangaToQueue(categoryId) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt b/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt index 51a5bdc72..8ea22b9ba 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt @@ -7,7 +7,6 @@ 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 +14,6 @@ import eu.kanade.tachiyomi.data.sync.SyncDataJob 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.system.cancelNotification import eu.kanade.tachiyomi.util.system.getParcelableExtraCompat import eu.kanade.tachiyomi.util.system.notificationManager @@ -66,12 +64,6 @@ class NotificationReceiver : BroadcastReceiver() { context, intent.getStringExtra(EXTRA_URI)!!.toUri(), ) - // Delete image from path and dismiss notification - ACTION_DELETE_IMAGE -> - deleteImage( - context, - intent.getStringExtra(EXTRA_URI)!!.toUri(), - ) // Share backup file ACTION_SHARE_BACKUP -> shareFile( @@ -172,16 +164,6 @@ class NotificationReceiver : BroadcastReceiver() { } } - /** - * Called to delete image - * - * @param uri path of file - */ - private fun deleteImage(context: Context, uri: Uri) { - UniFile.fromUri(context, uri)?.delete() - DiskUtil.scanMedia(context, uri) - } - /** * Method called when user wants to stop a backup restore job. * @@ -429,26 +411,6 @@ class NotificationReceiver : BroadcastReceiver() { ) } - /** - * Returns [PendingIntent] that starts a service which removes an image from disk - * - * @param context context of application - * @param uri location path of file - * @return [PendingIntent] - */ - internal fun deleteImagePendingBroadcast(context: Context, uri: Uri): PendingIntent { - val intent = Intent(context, NotificationReceiver::class.java).apply { - action = ACTION_DELETE_IMAGE - putExtra(EXTRA_URI, uri.toString()) - } - return PendingIntent.getBroadcast( - context, - 0, - intent, - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE, - ) - } - /** * Returns [PendingIntent] that starts a reader activity containing chapter. * diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt b/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt index 85afc0b23..0bf0aae68 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt @@ -23,7 +23,7 @@ import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.io.File import java.io.InputStream -import java.util.Date +import java.time.Instant class ImageSaver( val context: Context, @@ -79,7 +79,7 @@ class ImageSaver( MediaStore.Images.Media.RELATIVE_PATH to relativePath, MediaStore.Images.Media.DISPLAY_NAME to image.name, MediaStore.Images.Media.MIME_TYPE to type.mime, - MediaStore.Images.Media.DATE_MODIFIED to Date().time * 1000, + MediaStore.Images.Media.DATE_MODIFIED to Instant.now().toEpochMilli(), ) val picture = findUriOrDefault(relativePath, filename) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt index c1d0a855b..c385d7614 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt @@ -26,7 +26,10 @@ import okhttp3.OkHttpClient import okhttp3.RequestBody.Companion.toRequestBody import tachiyomi.core.util.lang.withIOContext import uy.kohesive.injekt.injectLazy -import java.util.Calendar +import java.time.Instant +import java.time.LocalDate +import java.time.ZoneId +import java.time.ZonedDateTime import kotlin.time.Duration.Companion.minutes class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) { @@ -328,13 +331,15 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) { private fun parseDate(struct: JsonObject, dateKey: String): Long { return try { - val date = Calendar.getInstance() - date.set( - struct[dateKey]!!.jsonObject["year"]!!.jsonPrimitive.int, - struct[dateKey]!!.jsonObject["month"]!!.jsonPrimitive.int - 1, - struct[dateKey]!!.jsonObject["day"]!!.jsonPrimitive.int, - ) - date.timeInMillis + return LocalDate + .of( + struct[dateKey]!!.jsonObject["year"]!!.jsonPrimitive.int, + struct[dateKey]!!.jsonObject["month"]!!.jsonPrimitive.int, + struct[dateKey]!!.jsonObject["day"]!!.jsonPrimitive.int, + ) + .atStartOfDay(ZoneId.systemDefault()) + .toInstant() + .toEpochMilli() } catch (_: Exception) { 0L } @@ -349,12 +354,11 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) { } } - val calendar = Calendar.getInstance() - calendar.timeInMillis = dateValue + val dateTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(dateValue), ZoneId.systemDefault()) return buildJsonObject { - put("year", calendar.get(Calendar.YEAR)) - put("month", calendar.get(Calendar.MONTH) + 1) - put("day", calendar.get(Calendar.DAY_OF_MONTH)) + put("year", dateTime.year) + put("month", dateTime.monthValue) + put("day", dateTime.dayOfMonth) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionGithubApi.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionGithubApi.kt index 6d34d9e52..e9421fed9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionGithubApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionGithubApi.kt @@ -17,7 +17,7 @@ import tachiyomi.core.preference.PreferenceStore import tachiyomi.core.util.lang.withIOContext import tachiyomi.core.util.system.logcat import uy.kohesive.injekt.injectLazy -import java.util.Date +import java.time.Instant import kotlin.time.Duration.Companion.days internal class ExtensionGithubApi { @@ -76,14 +76,16 @@ internal class ExtensionGithubApi { fromAvailableExtensionList: Boolean = false, ): List? { // Limit checks to once a day at most - if (!fromAvailableExtensionList && Date().time < lastExtCheck.get() + 1.days.inWholeMilliseconds) { + if (!fromAvailableExtensionList && + Instant.now().toEpochMilli() < lastExtCheck.get() + 1.days.inWholeMilliseconds + ) { return null } val extensions = if (fromAvailableExtensionList) { extensionManager.availableExtensionsFlow.value } else { - findExtensions().also { lastExtCheck.set(Date().time) } + findExtensions().also { lastExtCheck.set(Instant.now().toEpochMilli()) } } val installedExtensions = ExtensionLoader.loadExtensions(context) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsTab.kt index cb72b5f66..9a73307a7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsTab.kt @@ -12,6 +12,7 @@ import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.TabContent import eu.kanade.tachiyomi.extension.model.Extension import eu.kanade.tachiyomi.ui.browse.extension.details.ExtensionDetailsScreen +import eu.kanade.tachiyomi.ui.webview.WebViewScreen import kotlinx.collections.immutable.persistentListOf import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource @@ -47,6 +48,17 @@ fun extensionsTab( }, onClickItemCancel = extensionsScreenModel::cancelInstallUpdateExtension, onClickUpdateAll = extensionsScreenModel::updateAllExtensions, + onClickItemWebView = { extension -> + extension.sources.getOrNull(0)?.let { + navigator.push( + WebViewScreen( + url = it.baseUrl, + initialTitle = it.name, + sourceId = it.id, + ), + ) + } + }, onInstallExtension = extensionsScreenModel::installExtension, onOpenExtension = { navigator.push(ExtensionDetailsScreen(it.pkgName)) }, onTrustExtension = { extensionsScreenModel.trustSignature(it.signatureHash) }, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt index 9e87e8989..fb43e676a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt @@ -53,7 +53,7 @@ import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.screens.LoadingScreen import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.util.Date +import java.time.Instant @Composable internal fun MigrateDialog( @@ -298,7 +298,7 @@ internal class MigrateDialogScreenModel( favorite = true, chapterFlags = oldManga.chapterFlags, viewerFlags = oldManga.viewerFlags, - dateAdded = if (replace) oldManga.dateAdded else Date().time, + dateAdded = if (replace) oldManga.dateAdded else Instant.now().toEpochMilli(), ), ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt index 5e6cbfd98..4d0c49404 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt @@ -50,7 +50,7 @@ import tachiyomi.domain.source.interactor.GetRemoteManga import tachiyomi.domain.source.service.SourceManager import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.util.Date +import java.time.Instant import eu.kanade.tachiyomi.source.model.Filter as SourceModelFilter class BrowseSourceScreenModel( @@ -225,7 +225,7 @@ class BrowseSourceScreenModel( favorite = !manga.favorite, dateAdded = when (manga.favorite) { true -> 0 - false -> Date().time + false -> Instant.now().toEpochMilli() }, ) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/home/HomeScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/home/HomeScreen.kt index 13319c948..561f34df3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/home/HomeScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/home/HomeScreen.kt @@ -126,12 +126,12 @@ object HomeScreen : Screen() { materialFadeThroughIn(initialScale = 1f, durationMillis = TabFadeDuration) togetherWith materialFadeThroughOut(durationMillis = TabFadeDuration) }, - content = { - tabNavigator.saveableState(key = "currentTab", it) { - it.Content() - } - }, - ) + label = "tabContent", + ) { + tabNavigator.saveableState(key = "currentTab", it) { + it.Content() + } + } } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt index 0721b7132..e54dd97d6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt @@ -33,6 +33,7 @@ import eu.kanade.presentation.library.LibrarySettingsDialog import eu.kanade.presentation.library.components.LibraryContent import eu.kanade.presentation.library.components.LibraryToolbar import eu.kanade.presentation.manga.components.LibraryBottomActionMenu +import eu.kanade.presentation.more.onboarding.GETTING_STARTED_URL import eu.kanade.presentation.util.Tab import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.library.LibraryUpdateJob @@ -163,7 +164,7 @@ object LibraryTab : Tab { EmptyScreenAction( stringRes = MR.strings.getting_started_guide, icon = Icons.AutoMirrored.Outlined.HelpOutline, - onClick = { handler.openUri("https://tachiyomi.org/docs/guides/getting-started") }, + onClick = { handler.openUri(GETTING_STARTED_URL) }, ), ), ) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index 998781730..d6750551b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -73,6 +73,7 @@ import eu.kanade.tachiyomi.ui.deeplink.DeepLinkScreen import eu.kanade.tachiyomi.ui.home.HomeScreen import eu.kanade.tachiyomi.ui.manga.MangaScreen import eu.kanade.tachiyomi.ui.more.NewUpdateScreen +import eu.kanade.tachiyomi.ui.more.OnboardingScreen import eu.kanade.tachiyomi.util.system.dpToPx import eu.kanade.tachiyomi.util.system.isNavigationBarNeedsScrim import eu.kanade.tachiyomi.util.system.openInBrowser @@ -251,6 +252,7 @@ class MainActivity : BaseActivity() { HandleOnNewIntent(context = context, navigator = navigator) CheckForUpdates() + ShowOnboarding() } var showChangelog by remember { mutableStateOf(didMigration && !BuildConfig.DEBUG) } @@ -342,6 +344,17 @@ class MainActivity : BaseActivity() { } } + @Composable + private fun ShowOnboarding() { + val navigator = LocalNavigator.currentOrThrow + + LaunchedEffect(Unit) { + if (!preferences.shownOnboardingFlow().get()) { + navigator.push(OnboardingScreen()) + } + } + } + /** * Sets custom splash screen exit animation on devices prior to Android 12. * diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt index f792581de..7b9b18ed6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt @@ -47,6 +47,7 @@ import eu.kanade.tachiyomi.ui.category.CategoryScreen import eu.kanade.tachiyomi.ui.home.HomeScreen import eu.kanade.tachiyomi.ui.manga.track.TrackInfoDialogHomeScreen import eu.kanade.tachiyomi.ui.reader.ReaderActivity +import eu.kanade.tachiyomi.ui.setting.SettingsScreen import eu.kanade.tachiyomi.ui.webview.WebViewScreen import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.toShareIntent @@ -130,7 +131,13 @@ class MangaScreen( screenModel.source, ) }.takeIf { isHttpSource }, - onTrackingClicked = screenModel::showTrackDialog.takeIf { successState.trackingAvailable }, + onTrackingClicked = { + if (successState.trackingCount == 0) { + navigator.push(SettingsScreen(SettingsScreen.Destination.Tracking)) + } else { + screenModel.showTrackDialog() + } + }, onTagSearch = { scope.launch { performGenreSearch(navigator, it, screenModel.source!!) } }, onFilterButtonClicked = screenModel::showSettingsDialog, onRefresh = screenModel::fetchAllFromSource, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt index 73c64e12b..cc38de779 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt @@ -1090,9 +1090,6 @@ class MangaScreenModel( val filterActive: Boolean get() = scanlatorFilterActive || manga.chaptersFiltered() - val trackingAvailable: Boolean - get() = trackItems.isNotEmpty() - val trackingCount: Int get() = trackItems.count { it.track != null } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/more/MoreTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/more/MoreTab.kt index 8d5fa1313..3ca8418ad 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/more/MoreTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/more/MoreTab.kt @@ -53,7 +53,7 @@ object MoreTab : Tab { } override suspend fun onReselect(navigator: Navigator) { - navigator.push(SettingsScreen.toMainScreen()) + navigator.push(SettingsScreen()) } @Composable @@ -72,9 +72,9 @@ object MoreTab : Tab { onClickDownloadQueue = { navigator.push(DownloadQueueScreen) }, onClickCategories = { navigator.push(CategoryScreen()) }, onClickStats = { navigator.push(StatsScreen()) }, - onClickDataAndStorage = { navigator.push(SettingsScreen.toDataAndStorageScreen()) }, - onClickSettings = { navigator.push(SettingsScreen.toMainScreen()) }, - onClickAbout = { navigator.push(SettingsScreen.toAboutScreen()) }, + onClickDataAndStorage = { navigator.push(SettingsScreen(SettingsScreen.Destination.DataAndStorage)) }, + onClickSettings = { navigator.push(SettingsScreen()) }, + onClickAbout = { navigator.push(SettingsScreen(SettingsScreen.Destination.About)) }, ) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/more/OnboardingScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/more/OnboardingScreen.kt new file mode 100644 index 000000000..72e091954 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/more/OnboardingScreen.kt @@ -0,0 +1,41 @@ +package eu.kanade.tachiyomi.ui.more + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import eu.kanade.domain.base.BasePreferences +import eu.kanade.domain.ui.UiPreferences +import eu.kanade.presentation.more.onboarding.OnboardingScreen +import eu.kanade.presentation.util.Screen +import eu.kanade.tachiyomi.ui.setting.SettingsScreen +import tachiyomi.domain.storage.service.StoragePreferences +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get + +class OnboardingScreen : Screen() { + + @Composable + override fun Content() { + val navigator = LocalNavigator.currentOrThrow + + val basePreferences = remember { Injekt.get() } + val storagePreferences = remember { Injekt.get() } + val uiPreferences = remember { Injekt.get() } + + val finishOnboarding = { + basePreferences.shownOnboardingFlow().set(true) + navigator.pop() + } + + OnboardingScreen( + storagePreferences = storagePreferences, + uiPreferences = uiPreferences, + onComplete = { finishOnboarding() }, + onRestoreBackup = { + finishOnboarding() + navigator.push(SettingsScreen(SettingsScreen.Destination.DataAndStorage)) + }, + ) + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt index 47cfa5ef1..37116eeef 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt @@ -74,6 +74,7 @@ import tachiyomi.domain.source.service.SourceManager import tachiyomi.source.local.isLocal import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get +import java.time.Instant import java.util.Date /** @@ -539,7 +540,7 @@ class ReaderViewModel @JvmOverloads constructor( } fun restartReadTimer() { - chapterReadStartTime = Date().time + chapterReadStartTime = Instant.now().toEpochMilli() } fun flushReadTimer() { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt index de5b5f011..2676744ca 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt @@ -83,12 +83,6 @@ class SaveImageNotifier(private val context: Context) { context.stringResource(MR.strings.action_share), NotificationReceiver.shareImagePendingBroadcast(context, uri), ) - // Delete action - addAction( - R.drawable.ic_delete_24dp, - context.stringResource(MR.strings.action_delete), - NotificationReceiver.deleteImagePendingBroadcast(context, uri), - ) updateNotification() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderTransitionView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderTransitionView.kt index 295772c43..737b3633b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderTransitionView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderTransitionView.kt @@ -16,6 +16,7 @@ import eu.kanade.presentation.theme.TachiyomiTheme import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition import tachiyomi.domain.manga.model.Manga +import tachiyomi.source.local.isLocal class ReaderTransitionView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : AbstractComposeView(context, attrs) { @@ -31,7 +32,7 @@ class ReaderTransitionView @JvmOverloads constructor(context: Context, attrs: At Data( transition = transition, currChapterDownloaded = transition.from.pageLoader?.isLocal == true, - goingToChapterDownloaded = transition.to?.chapter?.let { goingToChapter -> + goingToChapterDownloaded = manga.isLocal() || transition.to?.chapter?.let { goingToChapter -> downloadManager.isChapterDownloaded( chapterName = goingToChapter.name, chapterScanlator = goingToChapter.scanlator, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsScreen.kt index 5b2dcedb2..3d396ace9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsScreen.kt @@ -15,6 +15,7 @@ import cafe.adriel.voyager.navigator.currentOrThrow import eu.kanade.presentation.more.settings.screen.SettingsAppearanceScreen import eu.kanade.presentation.more.settings.screen.SettingsDataScreen import eu.kanade.presentation.more.settings.screen.SettingsMainScreen +import eu.kanade.presentation.more.settings.screen.SettingsTrackingScreen import eu.kanade.presentation.more.settings.screen.about.AboutScreen import eu.kanade.presentation.util.DefaultNavigatorScreenTransition import eu.kanade.presentation.util.LocalBackPress @@ -22,22 +23,22 @@ import eu.kanade.presentation.util.Screen import eu.kanade.presentation.util.isTabletUi import tachiyomi.presentation.core.components.TwoPanelBox -class SettingsScreen private constructor( - val toDataAndStorage: Boolean, - val toAbout: Boolean, +class SettingsScreen( + private val destination: Int? = null, ) : Screen() { + constructor(destination: Destination) : this(destination.id) + @Composable override fun Content() { val parentNavigator = LocalNavigator.currentOrThrow if (!isTabletUi()) { Navigator( - screen = if (toDataAndStorage) { - SettingsDataScreen - } else if (toAbout) { - AboutScreen - } else { - SettingsMainScreen + screen = when (destination) { + Destination.About.id -> AboutScreen + Destination.DataAndStorage.id -> SettingsDataScreen + Destination.Tracking.id -> SettingsTrackingScreen + else -> SettingsMainScreen }, content = { val pop: () -> Unit = { @@ -54,12 +55,11 @@ class SettingsScreen private constructor( ) } else { Navigator( - screen = if (toDataAndStorage) { - SettingsDataScreen - } else if (toAbout) { - AboutScreen - } else { - SettingsAppearanceScreen + screen = when (destination) { + Destination.About.id -> AboutScreen + Destination.DataAndStorage.id -> SettingsDataScreen + Destination.Tracking.id -> SettingsTrackingScreen + else -> SettingsAppearanceScreen }, ) { val insets = WindowInsets.systemBars.only(WindowInsetsSides.Horizontal) @@ -78,11 +78,9 @@ class SettingsScreen private constructor( } } - companion object { - fun toMainScreen() = SettingsScreen(toDataAndStorage = false, toAbout = false) - - fun toDataAndStorageScreen() = SettingsScreen(toDataAndStorage = true, toAbout = false) - - fun toAboutScreen() = SettingsScreen(toDataAndStorage = false, toAbout = true) + sealed class Destination(val id: Int) { + data object About : Destination(0) + data object DataAndStorage : Destination(1) + data object Tracking : Destination(2) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt index d4a340ff3..9e4f31296 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt @@ -49,7 +49,7 @@ import tachiyomi.domain.updates.interactor.GetUpdates import tachiyomi.domain.updates.model.UpdatesWithRelations import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.util.Calendar +import java.time.ZonedDateTime import java.util.Date class UpdatesScreenModel( @@ -79,13 +79,10 @@ class UpdatesScreenModel( init { screenModelScope.launchIO { // Set date limit for recent chapters - val calendar = Calendar.getInstance().apply { - time = Date() - add(Calendar.MONTH, -3) - } + val limit = ZonedDateTime.now().minusMonths(3).toInstant() combine( - getUpdates.subscribe(calendar).distinctUntilChanged(), + getUpdates.subscribe(limit).distinctUntilChanged(), downloadCache.changes, downloadManager.queueState, ) { updates, _, _ -> updates } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/MangaExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/MangaExtensions.kt index 4df23b380..50834eadb 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/MangaExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/MangaExtensions.kt @@ -12,7 +12,7 @@ import tachiyomi.source.local.isLocal import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.io.InputStream -import java.util.Date +import java.time.Instant /** * Call before updating [Manga.thumbnail_url] to ensure old cover can be cleared from cache @@ -28,7 +28,7 @@ fun Manga.prepUpdateCover(coverCache: CoverCache, remoteManga: SManga, refreshSa return when { isLocal() -> { - this.copy(coverLastModified = Date().time) + this.copy(coverLastModified = Instant.now().toEpochMilli()) } hasCustomCover(coverCache) -> { coverCache.deleteFromCache(this, false) @@ -36,7 +36,7 @@ fun Manga.prepUpdateCover(coverCache: CoverCache, remoteManga: SManga, refreshSa } else -> { coverCache.deleteFromCache(this, false) - this.copy(coverLastModified = Date().time) + this.copy(coverLastModified = Instant.now().toEpochMilli()) } } } @@ -44,7 +44,7 @@ fun Manga.prepUpdateCover(coverCache: CoverCache, remoteManga: SManga, refreshSa fun Manga.removeCovers(coverCache: CoverCache = Injekt.get()): Manga { if (isLocal()) return this return if (coverCache.deleteFromCache(this, true) > 0) { - return copy(coverLastModified = Date().time) + return copy(coverLastModified = Instant.now().toEpochMilli()) } else { this } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt index 7d974a40b..ab109c49b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt @@ -8,6 +8,7 @@ import java.text.DateFormat import java.time.Instant import java.time.LocalDateTime import java.time.ZoneId +import java.time.temporal.ChronoUnit import java.util.Calendar import java.util.Date @@ -38,13 +39,8 @@ fun Long.convertEpochMillisZone( * @return date as time key */ fun Long.toDateKey(): Date { - val cal = Calendar.getInstance() - cal.time = Date(this) - cal[Calendar.HOUR_OF_DAY] = 0 - cal[Calendar.MINUTE] = 0 - cal[Calendar.SECOND] = 0 - cal[Calendar.MILLISECOND] = 0 - return cal.time + val instant = Instant.ofEpochMilli(this) + return Date.from(instant.truncatedTo(ChronoUnit.DAYS)) } private const val MILLISECONDS_IN_DAY = 86_400_000L diff --git a/app/src/main/res/drawable/ic_delete_24dp.xml b/app/src/main/res/drawable/ic_delete_24dp.xml deleted file mode 100644 index 5b4211859..000000000 --- a/app/src/main/res/drawable/ic_delete_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/core/src/main/java/tachiyomi/core/preference/InMemoryPreferenceStore.kt b/core/src/main/java/tachiyomi/core/preference/InMemoryPreferenceStore.kt index ad3937d91..83106999f 100644 --- a/core/src/main/java/tachiyomi/core/preference/InMemoryPreferenceStore.kt +++ b/core/src/main/java/tachiyomi/core/preference/InMemoryPreferenceStore.kt @@ -11,7 +11,7 @@ import kotlinx.coroutines.flow.stateIn * Local-copy implementation of PreferenceStore mostly for test and preview purposes */ class InMemoryPreferenceStore( - private val initialPreferences: Sequence> = sequenceOf(), + initialPreferences: Sequence> = sequenceOf(), ) : PreferenceStore { private val preferences: Map> = diff --git a/domain/src/main/java/tachiyomi/domain/storage/service/StoragePreferences.kt b/domain/src/main/java/tachiyomi/domain/storage/service/StoragePreferences.kt index 8f7c3fcc6..a49a54e3c 100644 --- a/domain/src/main/java/tachiyomi/domain/storage/service/StoragePreferences.kt +++ b/domain/src/main/java/tachiyomi/domain/storage/service/StoragePreferences.kt @@ -1,5 +1,6 @@ package tachiyomi.domain.storage.service +import tachiyomi.core.preference.Preference import tachiyomi.core.preference.PreferenceStore import tachiyomi.core.storage.FolderProvider @@ -8,5 +9,5 @@ class StoragePreferences( private val preferenceStore: PreferenceStore, ) { - fun baseStorageDirectory() = preferenceStore.getString("storage_dir", folderProvider.path()) + fun baseStorageDirectory() = preferenceStore.getString(Preference.appStateKey("storage_dir"), folderProvider.path()) } diff --git a/domain/src/main/java/tachiyomi/domain/updates/interactor/GetUpdates.kt b/domain/src/main/java/tachiyomi/domain/updates/interactor/GetUpdates.kt index b3c6481d5..361505642 100644 --- a/domain/src/main/java/tachiyomi/domain/updates/interactor/GetUpdates.kt +++ b/domain/src/main/java/tachiyomi/domain/updates/interactor/GetUpdates.kt @@ -3,7 +3,7 @@ package tachiyomi.domain.updates.interactor import kotlinx.coroutines.flow.Flow import tachiyomi.domain.updates.model.UpdatesWithRelations import tachiyomi.domain.updates.repository.UpdatesRepository -import java.util.Calendar +import java.time.Instant class GetUpdates( private val repository: UpdatesRepository, @@ -13,8 +13,8 @@ class GetUpdates( return repository.awaitWithRead(read, after, limit = 500) } - fun subscribe(calendar: Calendar): Flow> { - return repository.subscribeAll(calendar.time.time, limit = 500) + fun subscribe(instant: Instant): Flow> { + return repository.subscribeAll(instant.toEpochMilli(), limit = 500) } fun subscribe(read: Boolean, after: Long): Flow> { diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 6bfad292f..24a348d34 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,5 +1,5 @@ [versions] -compiler = "1.5.5" +compiler = "1.5.6" compose-bom = "2023.12.00-alpha03" accompanist = "0.33.2-alpha" diff --git a/gradle/kotlinx.versions.toml b/gradle/kotlinx.versions.toml index 09a293d64..913b5c012 100644 --- a/gradle/kotlinx.versions.toml +++ b/gradle/kotlinx.versions.toml @@ -1,6 +1,6 @@ [versions] -kotlin_version = "1.9.20" -serialization_version = "1.6.1" +kotlin_version = "1.9.21" +serialization_version = "1.6.2" xml_serialization_version = "0.86.2" [libraries] diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6d27bb4dc..1cf81b2d6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,13 +1,13 @@ [versions] aboutlib_version = "10.9.2" +leakcanary = "2.12" moko = "0.23.0" okhttp_version = "5.0.0-alpha.11" -shizuku_version = "12.2.0" -sqlite = "2.4.0" -sqldelight = "2.0.0" -leakcanary = "2.12" -voyager = "1.0.0-rc10" richtext = "0.17.0" +shizuku_version = "12.2.0" +sqldelight = "2.0.0" +sqlite = "2.4.0" +voyager = "1.0.0" [libraries] desugar = "com.android.tools:desugar_jdk_libs:2.0.4" @@ -90,6 +90,7 @@ kotest-assertions = "io.kotest:kotest-assertions-core:5.8.0" mockk = "io.mockk:mockk:1.13.8" voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" } +voyager-screenmodel = { module = "cafe.adriel.voyager:voyager-screenmodel", version.ref = "voyager" } voyager-tab-navigator = { module = "cafe.adriel.voyager:voyager-tab-navigator", version.ref = "voyager" } voyager-transitions = { module = "cafe.adriel.voyager:voyager-transitions", version.ref = "voyager" } @@ -104,6 +105,6 @@ sqlite = ["sqlite-framework", "sqlite-ktx", "sqlite-android"] coil = ["coil-core", "coil-gif", "coil-compose"] shizuku = ["shizuku-api", "shizuku-provider"] sqldelight = ["sqldelight-android-driver", "sqldelight-coroutines", "sqldelight-android-paging"] -voyager = ["voyager-navigator", "voyager-tab-navigator", "voyager-transitions"] +voyager = ["voyager-navigator", "voyager-screenmodel", "voyager-tab-navigator", "voyager-transitions"] richtext = ["richtext-commonmark", "richtext-m3"] test = ["junit", "kotest-assertions", "mockk"] \ No newline at end of file diff --git a/i18n/README.md b/i18n/README.md new file mode 100644 index 000000000..545c09448 --- /dev/null +++ b/i18n/README.md @@ -0,0 +1,5 @@ +# i18n + +This module houses the string resources and translations. + +Original English strings are manged in `src/commonMain/resources/MR/base/`. Translations are done externally via Weblate. See [our website](https://tachiyomi.org/docs/contribute#translation) for more details. \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/am/strings.xml b/i18n/src/commonMain/resources/MR/am/strings.xml index b829f44db..bea7680b4 100644 --- a/i18n/src/commonMain/resources/MR/am/strings.xml +++ b/i18n/src/commonMain/resources/MR/am/strings.xml @@ -54,7 +54,6 @@ በርቷል ጠፍቷል ስርዓት ይከተሉ - ጨለማ ሁነታ ገጽታ ስለ የላቀ diff --git a/i18n/src/commonMain/resources/MR/ar/strings.xml b/i18n/src/commonMain/resources/MR/ar/strings.xml index 7040a74da..1d5de06b5 100644 --- a/i18n/src/commonMain/resources/MR/ar/strings.xml +++ b/i18n/src/commonMain/resources/MR/ar/strings.xml @@ -309,7 +309,6 @@ اتبع مظهر النظام مفعّل غير مفعّل - الوضع الليلي تم إلغاء وضع تحسين البطارية مُسبقاً يساعد في عملية تحديث المكتبة والنسخ الإحتياطي في الخلفية إطفاء وضع تحسين البطارية diff --git a/i18n/src/commonMain/resources/MR/base/strings.xml b/i18n/src/commonMain/resources/MR/base/strings.xml index b9ec6edf0..7c6255941 100644 --- a/i18n/src/commonMain/resources/MR/base/strings.xml +++ b/i18n/src/commonMain/resources/MR/base/strings.xml @@ -174,6 +174,19 @@ App not available + + Onboarding guide + Welcome! + Let\'s set some things up first. You can always change these in the settings later too. + Next + Get started + Skip + Select a folder where %1$s will store chapter downloads, backups, and more.\n\nA dedicated folder is recommended.\n\nSelected folder: %2$s + Select a folder + A folder must be selected + New to %s? We recommend checking out the getting started guide. + Already used %s before? + General @@ -198,11 +211,10 @@ Theme - Dark mode - Follow system - Off - On App theme + System + Light + Dark Dynamic Green Apple Lavender @@ -436,6 +448,7 @@ After reading automatically delete Allow deleting bookmarked chapters Excluded categories + No storage location set Invalid location: %s Disabled Last read chapter diff --git a/i18n/src/commonMain/resources/MR/be/strings.xml b/i18n/src/commonMain/resources/MR/be/strings.xml index e1799953d..a5c8e7029 100644 --- a/i18n/src/commonMain/resources/MR/be/strings.xml +++ b/i18n/src/commonMain/resources/MR/be/strings.xml @@ -150,7 +150,6 @@ Уключаны Выключаны У адпаведнасці з сістэмнай тэмай - Цёмны рэжым Тэма Інфармацыя Дадаткова diff --git a/i18n/src/commonMain/resources/MR/bg/strings.xml b/i18n/src/commonMain/resources/MR/bg/strings.xml index 8efd0173d..7b564d62f 100644 --- a/i18n/src/commonMain/resources/MR/bg/strings.xml +++ b/i18n/src/commonMain/resources/MR/bg/strings.xml @@ -350,7 +350,6 @@ Система на абонаментите Включено Изключено - Тъмен режим Премести най-горе Премести най-долу Най-стари diff --git a/i18n/src/commonMain/resources/MR/bn/strings.xml b/i18n/src/commonMain/resources/MR/bn/strings.xml index 07728c4bb..91160e927 100644 --- a/i18n/src/commonMain/resources/MR/bn/strings.xml +++ b/i18n/src/commonMain/resources/MR/bn/strings.xml @@ -315,7 +315,6 @@ চালু করুন বন্ধ করুন সিস্টেমকে অনুসরণ করুন - অন্ধকার মোড থিম নীচে সরান শীর্ষে সরান diff --git a/i18n/src/commonMain/resources/MR/ca/strings.xml b/i18n/src/commonMain/resources/MR/ca/strings.xml index b132407c8..34a528fbb 100644 --- a/i18n/src/commonMain/resources/MR/ca/strings.xml +++ b/i18n/src/commonMain/resources/MR/ca/strings.xml @@ -301,7 +301,6 @@ Darrer capítol Mostra els capítols Cancel·la-ho tot - Mode fosc Desactivat Activat Per defecte del sistema diff --git a/i18n/src/commonMain/resources/MR/ceb/strings.xml b/i18n/src/commonMain/resources/MR/ceb/strings.xml index 01b4ce101..ac1497c4d 100644 --- a/i18n/src/commonMain/resources/MR/ceb/strings.xml +++ b/i18n/src/commonMain/resources/MR/ceb/strings.xml @@ -115,7 +115,6 @@ Pagsubay Abante Tema - Dark mode Sa Tema sa app Dinamiko diff --git a/i18n/src/commonMain/resources/MR/cs/strings.xml b/i18n/src/commonMain/resources/MR/cs/strings.xml index a752f5af0..475f0832e 100644 --- a/i18n/src/commonMain/resources/MR/cs/strings.xml +++ b/i18n/src/commonMain/resources/MR/cs/strings.xml @@ -392,7 +392,6 @@ Vyžadovat odemknutí Zapnuto Vypnuto - Temný vzhled Vzhled Přesunout nahoru Sestupně diff --git a/i18n/src/commonMain/resources/MR/cv/strings.xml b/i18n/src/commonMain/resources/MR/cv/strings.xml index d0401db23..d7d1ab6ad 100644 --- a/i18n/src/commonMain/resources/MR/cv/strings.xml +++ b/i18n/src/commonMain/resources/MR/cv/strings.xml @@ -155,7 +155,6 @@ Тӑвӑм-пулӑм кӗнекине уҫ Тасат Уйӑр - Тӗксӗм темӑ Системри пекех Вулӑш Йӗрлев diff --git a/i18n/src/commonMain/resources/MR/da/strings.xml b/i18n/src/commonMain/resources/MR/da/strings.xml index cb2a3bea7..9ecbd6bae 100644 --- a/i18n/src/commonMain/resources/MR/da/strings.xml +++ b/i18n/src/commonMain/resources/MR/da/strings.xml @@ -126,7 +126,6 @@ Avanceret Om Tema - Mørk tilstand Følg system Fra Til diff --git a/i18n/src/commonMain/resources/MR/de/strings.xml b/i18n/src/commonMain/resources/MR/de/strings.xml index 737264efe..73a3164c4 100644 --- a/i18n/src/commonMain/resources/MR/de/strings.xml +++ b/i18n/src/commonMain/resources/MR/de/strings.xml @@ -301,7 +301,6 @@ Neuestes Kapitel Kapitel anzeigen Alle abbrechen - Dunkelmodus Aus An Systemeinstellung diff --git a/i18n/src/commonMain/resources/MR/el/strings.xml b/i18n/src/commonMain/resources/MR/el/strings.xml index d27a7219e..d6740ab9c 100644 --- a/i18n/src/commonMain/resources/MR/el/strings.xml +++ b/i18n/src/commonMain/resources/MR/el/strings.xml @@ -350,7 +350,6 @@ Ακολουθήστε το σύστημα Ενεργοποιημένο Απενεργοποιημένο - Σκοτεινή λειτουργία Μετακίνηση στον πάτο Μετακίνηση στην κορυφή Ακύρωση όλων diff --git a/i18n/src/commonMain/resources/MR/eo/strings.xml b/i18n/src/commonMain/resources/MR/eo/strings.xml index 03555cbfc..f15386204 100644 --- a/i18n/src/commonMain/resources/MR/eo/strings.xml +++ b/i18n/src/commonMain/resources/MR/eo/strings.xml @@ -62,7 +62,6 @@ Ŝalti Malŝalti Laŭ operaciumo - Malhela etoso Etoso Pri Elŝutoj diff --git a/i18n/src/commonMain/resources/MR/es/strings.xml b/i18n/src/commonMain/resources/MR/es/strings.xml index 4fc2ae17d..567fd18fe 100644 --- a/i18n/src/commonMain/resources/MR/es/strings.xml +++ b/i18n/src/commonMain/resources/MR/es/strings.xml @@ -302,7 +302,6 @@ Por capítulo más reciente Ver capítulos Cancelar todo - Modo oscuro No Según ajustes del sistema diff --git a/i18n/src/commonMain/resources/MR/eu/strings.xml b/i18n/src/commonMain/resources/MR/eu/strings.xml index a56f0df97..715820d00 100644 --- a/i18n/src/commonMain/resources/MR/eu/strings.xml +++ b/i18n/src/commonMain/resources/MR/eu/strings.xml @@ -492,7 +492,6 @@ Deskargak Jarraipena Aurreratua - Modu iluna Hizkuntza Marrubi Daiquiri-a Tako diff --git a/i18n/src/commonMain/resources/MR/fa/strings.xml b/i18n/src/commonMain/resources/MR/fa/strings.xml index 35996b2cc..27ba28979 100644 --- a/i18n/src/commonMain/resources/MR/fa/strings.xml +++ b/i18n/src/commonMain/resources/MR/fa/strings.xml @@ -261,7 +261,6 @@ روشن خاموش تم پیش‌فرض سیستم - تم تیره درباره پیشرفته ردیابی @@ -614,4 +613,19 @@ باشه به روز رسانی مورد انتظار بعدی هشدار: حجم زیاد بارگیری ممکن است باعث اهسته تر شدن سرعت ویا مسدود کردن Tachiyomi از منبع شود. برای اطلاعات بیشتر لمس کنید. + به دلیل این که این مجموعه نیازی به به روز رسانی نداشت رد شد + به دلیل وجود چپتر های خوانده نشده رد شد + %1$d بزور رسانی رد شد + به دلیل این که هیچ چپتری خوانده نشده بود رد شد + %1$d بروز رسانی ناموفق + \"%1$s\" به جای \"%2$s\" + %d در هر ردیف + صفحات عریض را بچرخان تا جا شوند + محبوب + حرکت کشیدن به راست + در هنگام عوض شدن صفحه فلاش سفید بزن + حرکت کشیدن به چپ + آخرین به روز رسانی کتابخانه: %s + خارج از دوره انتشار موزد انتظار + برای بزرگ نمایی دوبار ضربه بزنید \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/fi/strings.xml b/i18n/src/commonMain/resources/MR/fi/strings.xml index bb54521b9..35016b8c3 100644 --- a/i18n/src/commonMain/resources/MR/fi/strings.xml +++ b/i18n/src/commonMain/resources/MR/fi/strings.xml @@ -301,7 +301,6 @@ Viimeisin luku Näytä luvut Peruuta kaikki - Pimeä tila Pois päältä Päällä Seuraa järjestelmää diff --git a/i18n/src/commonMain/resources/MR/fil/strings.xml b/i18n/src/commonMain/resources/MR/fil/strings.xml index 82ae744d6..1b63f248e 100644 --- a/i18n/src/commonMain/resources/MR/fil/strings.xml +++ b/i18n/src/commonMain/resources/MR/fil/strings.xml @@ -102,7 +102,6 @@ Nakabukas Nakasara Sundan ang sistema - Madilim na tema Patungkol Karagdagan Pagta-track @@ -272,7 +271,7 @@ Kabanata %1$s at karagdagang %2$d pa Kabanata %1$s May mga bagong kabanata - Di ma-download ang mga kabanata dahil sa mababang espasyo + Di ma-download ang mga kabanata dahil sa mababang espasyo sa storage Di ma-download ang mga kabanata. Subukan mo uli ito sa Dina-download Kopyahin Ilipat @@ -644,7 +643,7 @@ Kusang pag-download, i-download agad Isahang pagsabay sa progress, pinahusay na pagsabay Tema, ayos ng petsa & oras - Mano-mano at kusang pag-backup + Mano-mano at awtomatikong pag-backup, espasyo sa storage Pag-lock aa app, bantayan ang screen Itambak ang mga crash log, pag-o-optimisa sa baterya Mga kategorya, panlahatang update, pag-swipe ng kabanata @@ -764,7 +763,7 @@ Ibukod ang mga scanlator Lumikha Lokasyon ng storage - Ginagamit para sa automatikong pa-backup, pag-download ng mga kabanata, at lokal na source. + Ginagamit para sa automatikong pag-backup, pag-download ng mga kabanata, at lokal na source. Ibang opsiyon Napili Di napili diff --git a/i18n/src/commonMain/resources/MR/fr/strings.xml b/i18n/src/commonMain/resources/MR/fr/strings.xml index ce6307a12..321b6bca8 100644 --- a/i18n/src/commonMain/resources/MR/fr/strings.xml +++ b/i18n/src/commonMain/resources/MR/fr/strings.xml @@ -301,7 +301,6 @@ Dernier chapitre Voir les chapitres Tout annuler - Mode sombre Désactivé Activé Par défaut du système diff --git a/i18n/src/commonMain/resources/MR/gl/strings.xml b/i18n/src/commonMain/resources/MR/gl/strings.xml index e46e0b145..9a36b4f64 100644 --- a/i18n/src/commonMain/resources/MR/gl/strings.xml +++ b/i18n/src/commonMain/resources/MR/gl/strings.xml @@ -138,7 +138,6 @@ Activado Desactivado Utilizar o do sistema - Modo escuro Tema Acerca de Avanzado diff --git a/i18n/src/commonMain/resources/MR/he/strings.xml b/i18n/src/commonMain/resources/MR/he/strings.xml index 3e28e22e7..7c6a7a905 100644 --- a/i18n/src/commonMain/resources/MR/he/strings.xml +++ b/i18n/src/commonMain/resources/MR/he/strings.xml @@ -141,7 +141,6 @@ תבנית תאריך פעיל כבוי - מצב חשוך אודות מתקדם הורדות diff --git a/i18n/src/commonMain/resources/MR/hi/strings.xml b/i18n/src/commonMain/resources/MR/hi/strings.xml index 2c41fa2cb..f2bcd903d 100644 --- a/i18n/src/commonMain/resources/MR/hi/strings.xml +++ b/i18n/src/commonMain/resources/MR/hi/strings.xml @@ -301,7 +301,6 @@ नवीनतम अध्याय अध्याय देखें सब रद्द करो - डार्क मोड बंद चालू करे सिस्टम का पालन करें diff --git a/i18n/src/commonMain/resources/MR/hr/strings.xml b/i18n/src/commonMain/resources/MR/hr/strings.xml index 091f32522..b8c4c3758 100644 --- a/i18n/src/commonMain/resources/MR/hr/strings.xml +++ b/i18n/src/commonMain/resources/MR/hr/strings.xml @@ -50,7 +50,6 @@ Slijedi sustav Uključeno Isključeno - Tamna tema Informacije Napredno Praćenje diff --git a/i18n/src/commonMain/resources/MR/hu/strings.xml b/i18n/src/commonMain/resources/MR/hu/strings.xml index 97faaa878..52b30a592 100644 --- a/i18n/src/commonMain/resources/MR/hu/strings.xml +++ b/i18n/src/commonMain/resources/MR/hu/strings.xml @@ -176,7 +176,6 @@ Biztonság Értesítések kezelése Rendszerbeállítás követése - Sötét mód Téma Ugrás legalulra Ugrás legfelülre diff --git a/i18n/src/commonMain/resources/MR/in/strings.xml b/i18n/src/commonMain/resources/MR/in/strings.xml index 57178454c..5d12d0ccf 100644 --- a/i18n/src/commonMain/resources/MR/in/strings.xml +++ b/i18n/src/commonMain/resources/MR/in/strings.xml @@ -293,7 +293,6 @@ Bab terbaru Lihat bab Batalkan semua - Mode gelap Mati Nyala Ikuti sistem diff --git a/i18n/src/commonMain/resources/MR/it/strings.xml b/i18n/src/commonMain/resources/MR/it/strings.xml index 2171a2e97..7d5367f26 100644 --- a/i18n/src/commonMain/resources/MR/it/strings.xml +++ b/i18n/src/commonMain/resources/MR/it/strings.xml @@ -301,7 +301,6 @@ Ultimo capitolo Visualizza capitoli Annulla tutto - Tema scuro Disattivo Attivo Usa il tema di sistema diff --git a/i18n/src/commonMain/resources/MR/ja/strings.xml b/i18n/src/commonMain/resources/MR/ja/strings.xml index 5f8249a50..c29c25c62 100644 --- a/i18n/src/commonMain/resources/MR/ja/strings.xml +++ b/i18n/src/commonMain/resources/MR/ja/strings.xml @@ -296,7 +296,6 @@ 最新章の更新順 章を見る すべてキャンセル - ダークモード オフ オン システムに従う @@ -726,8 +725,8 @@ ローカルの追跡が削除されます。 %s からも削除 ダウンロードを削除 - 10+チェック後半 - 落とした? 20歳以上後半と2ヶ月 + レイト10+チェック + 落選? 20歳後半と2ヶ月 チェック期間を過ぎました OK ライブラリを同期しています diff --git a/i18n/src/commonMain/resources/MR/jv/strings.xml b/i18n/src/commonMain/resources/MR/jv/strings.xml index ae0ef92e8..dce882777 100644 --- a/i18n/src/commonMain/resources/MR/jv/strings.xml +++ b/i18n/src/commonMain/resources/MR/jv/strings.xml @@ -42,7 +42,6 @@ Format tanggal Murup Mati - Mode Peteng Tema Donlot Bali diff --git a/i18n/src/commonMain/resources/MR/ka-rGE/strings.xml b/i18n/src/commonMain/resources/MR/ka-rGE/strings.xml index 9d4038b90..49ccd47d7 100644 --- a/i18n/src/commonMain/resources/MR/ka-rGE/strings.xml +++ b/i18n/src/commonMain/resources/MR/ka-rGE/strings.xml @@ -79,7 +79,6 @@ თვალყურის დევნება დამატებით ინფორმაცია - ბნელი რეჟიმი სისტემური მსუბუქი ჩართული diff --git a/i18n/src/commonMain/resources/MR/kk/strings.xml b/i18n/src/commonMain/resources/MR/kk/strings.xml index 190113347..ac1e4d2ee 100644 --- a/i18n/src/commonMain/resources/MR/kk/strings.xml +++ b/i18n/src/commonMain/resources/MR/kk/strings.xml @@ -57,7 +57,6 @@ Жүктеулер Толығырақ Кейіп - Қараңғы режим Жүйе бойынша Өшірулі Қосулы diff --git a/i18n/src/commonMain/resources/MR/km/strings.xml b/i18n/src/commonMain/resources/MR/km/strings.xml index 16626c1d4..1a98edc08 100644 --- a/i18n/src/commonMain/resources/MR/km/strings.xml +++ b/i18n/src/commonMain/resources/MR/km/strings.xml @@ -120,7 +120,6 @@ ការកំណត់ពិសេស អំពីរ ពណ៌ - ខ្មៅ តាមទូរសព្ទ បិទ បើក diff --git a/i18n/src/commonMain/resources/MR/kn/strings.xml b/i18n/src/commonMain/resources/MR/kn/strings.xml index 1ea09b5a9..3bfc39543 100644 --- a/i18n/src/commonMain/resources/MR/kn/strings.xml +++ b/i18n/src/commonMain/resources/MR/kn/strings.xml @@ -17,7 +17,6 @@ ಸಿಸ್ಟಮ್ ಅನುಕರಿಸಿ ಆನ್ ಆಫ - ಡಾರ್ಕ್ ಮೋಡ್ ಅಪ್ಲಿಕೇಶನ್ ಬಗ್ಗೆ ಸುಧಾರಿತ ಟ್ರ್ಯಾಕಿಂಗ್ diff --git a/i18n/src/commonMain/resources/MR/ko/strings.xml b/i18n/src/commonMain/resources/MR/ko/strings.xml index af2e5cf34..bd031f966 100644 --- a/i18n/src/commonMain/resources/MR/ko/strings.xml +++ b/i18n/src/commonMain/resources/MR/ko/strings.xml @@ -342,7 +342,6 @@ 켜기 끄기 시스템 설정 사용 - 다크 모드 모두 비활성화 다음 페이지 변경 확인을 위해 인증이 필요합니다 diff --git a/i18n/src/commonMain/resources/MR/lt/strings.xml b/i18n/src/commonMain/resources/MR/lt/strings.xml index 7905d0f18..ebf12d1bc 100644 --- a/i18n/src/commonMain/resources/MR/lt/strings.xml +++ b/i18n/src/commonMain/resources/MR/lt/strings.xml @@ -73,7 +73,6 @@ Įjungta Išjungta Pagal sistemą - Tamsi Tema Apie Papildomi diff --git a/i18n/src/commonMain/resources/MR/lv/strings.xml b/i18n/src/commonMain/resources/MR/lv/strings.xml index 22ec8a73d..98e72cce6 100644 --- a/i18n/src/commonMain/resources/MR/lv/strings.xml +++ b/i18n/src/commonMain/resources/MR/lv/strings.xml @@ -139,7 +139,6 @@ Ieslēgts Izslēgts Sekot sistēmu - Tumšais režīms Motīvs Bibliotēka Atjaunot @@ -744,4 +743,29 @@ Kārtot kategorijas Izsekošanas rezultāts Dati un uzglabāšana + Krātuves atrašanās vieta + Izveidot + Nekad + Samazina spoku rašanos uz e-ink displejiem + Izmanto automātiskajām dublējumkopijām, nodaļu lejupielādei un vietējam avotam. + Pieteikties + Atgriezt noklusējuma iestatījumus + Vairāk iespēju + Pēdējā automātiskā dublēšana: %s + Atlasīts + Nav atrasts neviens scanlators + Nav atlasīts + Pārvietot sēriju uz apakšu + Skanlators + Zibsnīt baltu, kad maina lapu + Krātuves izmantošana + Bibliotēkas atjaunināšana... (%s) + Virzīties uz augšu + Vai vēlaties kategorijas sakārtot pēc alfabēta? + Nav atlasīts neviens fails + Avota iestatījumi + Lietotnes iestatījumi + Relatīviās laika stampas + \"%1$s\", nevis \"%2$s\" + Izslēgt skanlatorus \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/mr/strings.xml b/i18n/src/commonMain/resources/MR/mr/strings.xml index b3c2c1d29..8be56d229 100644 --- a/i18n/src/commonMain/resources/MR/mr/strings.xml +++ b/i18n/src/commonMain/resources/MR/mr/strings.xml @@ -97,7 +97,6 @@ तंत्राचे अनुसरण करा डार्क लाइट - गडद मोड ॲप बद्दल प्रगत ट्रॅकिंग diff --git a/i18n/src/commonMain/resources/MR/ms/strings.xml b/i18n/src/commonMain/resources/MR/ms/strings.xml index c9d4ea92d..ff937353c 100644 --- a/i18n/src/commonMain/resources/MR/ms/strings.xml +++ b/i18n/src/commonMain/resources/MR/ms/strings.xml @@ -301,7 +301,6 @@ Bab terkini Buka bab Batalkan semua - Tema gelap Mati Hidup Ikut sistem diff --git a/i18n/src/commonMain/resources/MR/nb-rNO/strings.xml b/i18n/src/commonMain/resources/MR/nb-rNO/strings.xml index 90f0c92fe..7ee0417ac 100644 --- a/i18n/src/commonMain/resources/MR/nb-rNO/strings.xml +++ b/i18n/src/commonMain/resources/MR/nb-rNO/strings.xml @@ -300,7 +300,6 @@ Mer Vis kapitler Avbryt alle - Mørk Av System diff --git a/i18n/src/commonMain/resources/MR/ne/strings.xml b/i18n/src/commonMain/resources/MR/ne/strings.xml index 40f3508f7..c8d1a1efd 100644 --- a/i18n/src/commonMain/resources/MR/ne/strings.xml +++ b/i18n/src/commonMain/resources/MR/ne/strings.xml @@ -2,7 +2,6 @@ अन अफ - अँध्यारो मोड थीम बारेमा उन्नत सेटिङहरू diff --git a/i18n/src/commonMain/resources/MR/nl/strings.xml b/i18n/src/commonMain/resources/MR/nl/strings.xml index f894c0132..ea9d34918 100644 --- a/i18n/src/commonMain/resources/MR/nl/strings.xml +++ b/i18n/src/commonMain/resources/MR/nl/strings.xml @@ -301,7 +301,6 @@ Laatste hoofdstuk Hoofdstukken bekijken Alles annuleren - Donkere modus Uit Aan Volg systeeminstelling diff --git a/i18n/src/commonMain/resources/MR/pl/strings.xml b/i18n/src/commonMain/resources/MR/pl/strings.xml index 96c824b8c..035e7109f 100644 --- a/i18n/src/commonMain/resources/MR/pl/strings.xml +++ b/i18n/src/commonMain/resources/MR/pl/strings.xml @@ -316,7 +316,6 @@ Systemowy Włącz Wyłącz - Ciemny motyw Przenieś na dół Przenieś na górę Najstarsze diff --git a/i18n/src/commonMain/resources/MR/pt-rBR/strings.xml b/i18n/src/commonMain/resources/MR/pt-rBR/strings.xml index cde48563d..cf3133d7c 100644 --- a/i18n/src/commonMain/resources/MR/pt-rBR/strings.xml +++ b/i18n/src/commonMain/resources/MR/pt-rBR/strings.xml @@ -301,10 +301,9 @@ Último capítulo Visualizar os capítulos Cancelar todos - Modo noturno - Desligado - Ligado - Seguir o sistema + Claro + Escuro + Sistema Gerenciar notificações Segurança e privacidade Exigir desbloqueio @@ -769,4 +768,19 @@ Usado para backups automáticos, downloads de capítulos e na fonte local. Mais opções Navegar para cima + Selecionar uma pasta + Guia de introdução + Novo no %s? Recomendamos dar uma olhada no guia de introdução. + Começar + Bem-vindo(a)! + Já utilizou o %s antes? + Pular + Próximo + Vamos definir algumas coisas primeiro. Você sempre pode fazer alterações nas configurações depois também. + Local de armazenamento não definido + Escolha uma pasta onde o %1$s irá armazenar os downloads de capítulos, backups e mais. +\n +\nUma pasta dedicada é recomendada. +\n +\nPasta selecionada: %2$s \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/pt/strings.xml b/i18n/src/commonMain/resources/MR/pt/strings.xml index a5a8f59fe..a163780d7 100644 --- a/i18n/src/commonMain/resources/MR/pt/strings.xml +++ b/i18n/src/commonMain/resources/MR/pt/strings.xml @@ -301,7 +301,6 @@ Último capítulo Ver capítulos Cancelar tudo - Modo escuro Desligado Ligado Seguir o do sistema diff --git a/i18n/src/commonMain/resources/MR/ro/plurals.xml b/i18n/src/commonMain/resources/MR/ro/plurals.xml index 99aa8b413..2bda6def5 100644 --- a/i18n/src/commonMain/resources/MR/ro/plurals.xml +++ b/i18n/src/commonMain/resources/MR/ro/plurals.xml @@ -65,4 +65,19 @@ Următoarele %d capitole necitite Următoarele %d capitole necitite + + Următorul capitol + Următoarele %d capitole + Următoarele %d capitole + + + Lipsește %1$s capitol + Lipsesc %1$s capitole + Lipsesc %1$s capitole + + + O zi + %d zile + %d zile + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ro/strings.xml b/i18n/src/commonMain/resources/MR/ro/strings.xml index 47afbd831..ec84d1368 100644 --- a/i18n/src/commonMain/resources/MR/ro/strings.xml +++ b/i18n/src/commonMain/resources/MR/ro/strings.xml @@ -173,7 +173,7 @@ Cookies curățate Curățați baza de date Ștergeți istoricul pentru intrările care nu sunt salvate în bibliotecă - Ești sigur\? Capitolele citite și progresul intrărilor din afara bibliotecii vor fi pierdute + Ești sigur? Capitolele citite și progresul înregistrărilor din afara bibliotecii vor fi pierdute Înregistrări șterse Versiune Trimite rapoarte pt. eșuări @@ -300,7 +300,6 @@ Mai multe Vezi capitolele Anulează tot - Mod întunecat Oprită Pornită Tema sistemului @@ -331,7 +330,7 @@ Meniu Cele mai recente Cele mai vechi - Mută pe primă poziție + Mută pe prima poziție Mută pe ultima poziție Actualizări de extensie Se actualizează biblioteca @@ -527,7 +526,7 @@ Sensibilitatea pentru ascunderea meniului la defilare Afișează intrarea Instalați și porniți Shizuku pentru a utiliza Shizuku ca instalator de extensii. - %1$d intrări non-bibliotecare în baza de date + %1$d înregistrări în baza de datecare nu aparțin bibliotecii Pagina %d nu a fost găsită în timpul divizării Doriți să ștergeți categoria \"%s\"\? InternalError: Verificați jurnalele de accident pentru informații suplimentare @@ -544,7 +543,7 @@ Lista de dorințe Lista completă Acoperire tip grilaj - Mutați seria în partea de sus + Mutați seria pe prima poziție Omiteți actualizarea intrărilor Care nu au fost începute Shizuku nu rulează @@ -583,7 +582,7 @@ Nu se poate deschide ultimul capitol citit Nu s-a putut obține lista de extensii Se instalează extensia… - Program instalare + Program de instalare Creează dosare în funcție de titlul intrărilor Automat Pornit @@ -708,4 +707,61 @@ Următoarea actualizare așteptată Fetch lunar (28 de zile) Interval de preluare personalizat + Locație de stocare + Atinge aici pentru ajutor cu Cloudflare + Creați + Niciodată + %d pe rând + Reduceți imaginile fantomă pe ecranele e-ink + Copiați în clipboard + Rotiți paginile late pentru a se potrivi + Folosit pentru copii de rezervă automate, capitole descărcate, și surse locale. + Aplică + Informații de depanare + Sincronizare bibliotecă + Copia de rezervă nu a putut fi creată + Intervale + Reveniți la implicit + Sortează categoriile + Actualizați categoria + Mai multe opțiuni + Sincronizare bibliotecă completă + Ultima copie de rezervă creata la: %s + Personalizați intervalul + Răsturnați orientarea paginilor late rotite + Selectați + Nici un scanlator nu a fost găsit + Nu a fost selectat + Mutați seria pe ultima poziție + Acțiune glisare către dreapta + Licențiat - Nu exista capitole pentru afișare + Fără conexiune la internet + Utilizarea spațiului de stocare + Actualizare bibliotecă… (%s) + Indexul de descărcări a fost invalidat + Navighează în sus + Scorul tracker-ului + Ați dori să sortați categoriile alfabetic? + Sărit peste deoarece nici o lansare nu era așteptată astăzi + Nici o filă selectată + Eliminați monitorizarea %s? + Setări surse + Setări aplicație + Acțiune glisare către stânga + De asemenea elimină din %s + Împărțiți imaginile înalte + Au fost găsite rezultate + Această acțiune va elimina local monitorizarea. + În afara perioadei de lansare estimată + Ok + Atingeți de două ori pentru a mări + Autentificare tracker + Ascundeți înregistrările care se află deja în bibliotecă + Marcaje de timp relative + HTTP %d, verificați site-ul in modul WebView + \"%1$s\" în loc de \"%2$s\" + Nu a putut fi accesat %s + Înregistrări monitorizate + Exclude scanlator + Glisare capitol \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ru/strings.xml b/i18n/src/commonMain/resources/MR/ru/strings.xml index 7f78bef3c..7c2382369 100644 --- a/i18n/src/commonMain/resources/MR/ru/strings.xml +++ b/i18n/src/commonMain/resources/MR/ru/strings.xml @@ -301,7 +301,6 @@ Последняя глава Просмотреть главы Отменить всё - Тёмный режим Выключен Включён Система diff --git a/i18n/src/commonMain/resources/MR/sa/strings.xml b/i18n/src/commonMain/resources/MR/sa/strings.xml index ba0d9e5a6..e7d0629a4 100644 --- a/i18n/src/commonMain/resources/MR/sa/strings.xml +++ b/i18n/src/commonMain/resources/MR/sa/strings.xml @@ -115,7 +115,6 @@ अनुप्रज्ञानम् स्वसमाचारः दृश्यशैली - असितदशा संविधाम् अनुसरतु अशक्तं कुरु गतिशीलम् diff --git a/i18n/src/commonMain/resources/MR/sah/strings.xml b/i18n/src/commonMain/resources/MR/sah/strings.xml index c8300a91c..d504f1ef3 100644 --- a/i18n/src/commonMain/resources/MR/sah/strings.xml +++ b/i18n/src/commonMain/resources/MR/sah/strings.xml @@ -82,7 +82,6 @@ Холбоммут Араарыллыбыт Системнайы тутуһуҥ - Хараҥа тиэмэ Тиэмэ Эбии туһунан Эбии diff --git a/i18n/src/commonMain/resources/MR/sc/strings.xml b/i18n/src/commonMain/resources/MR/sc/strings.xml index 9153343cf..57cfc1e1a 100644 --- a/i18n/src/commonMain/resources/MR/sc/strings.xml +++ b/i18n/src/commonMain/resources/MR/sc/strings.xml @@ -301,7 +301,6 @@ Ùrtimu capìtulu Pòmpia sos capìtulos Annulla totu - Tema iscuru Istudadu Allutu Sighi su sistema diff --git a/i18n/src/commonMain/resources/MR/sdh/strings.xml b/i18n/src/commonMain/resources/MR/sdh/strings.xml index 9b4a4a81c..04a9853f4 100644 --- a/i18n/src/commonMain/resources/MR/sdh/strings.xml +++ b/i18n/src/commonMain/resources/MR/sdh/strings.xml @@ -141,7 +141,6 @@ تۆڕی پەستێنراو تۆڕی ئاسوودە ڕووکار - دۆخی تاریک وەک ڕووکاری سیستەم کوژاوە داگیرساو diff --git a/i18n/src/commonMain/resources/MR/sk/strings.xml b/i18n/src/commonMain/resources/MR/sk/strings.xml index 46fbb2b33..d80d3f2f0 100644 --- a/i18n/src/commonMain/resources/MR/sk/strings.xml +++ b/i18n/src/commonMain/resources/MR/sk/strings.xml @@ -346,7 +346,6 @@ Sledované FAQ a návody Téma - Tmavý režim Vyp. Zap. Téma aplikácie diff --git a/i18n/src/commonMain/resources/MR/sq/strings.xml b/i18n/src/commonMain/resources/MR/sq/strings.xml index ceda2b70d..472bf5342 100644 --- a/i18n/src/commonMain/resources/MR/sq/strings.xml +++ b/i18n/src/commonMain/resources/MR/sq/strings.xml @@ -26,7 +26,6 @@ Rifresko Aplikacioni i padisponueshem Shkarkim automatik, shkarko përpara - Modaliteti i errët Aktiv Molle jeshile Livando diff --git a/i18n/src/commonMain/resources/MR/sr/strings.xml b/i18n/src/commonMain/resources/MR/sr/strings.xml index 8cab68641..f85f74ca0 100644 --- a/i18n/src/commonMain/resources/MR/sr/strings.xml +++ b/i18n/src/commonMain/resources/MR/sr/strings.xml @@ -234,7 +234,6 @@ Последње поглавље Погледај поглавља Откажи све - Тамна тема Икључено Укључено По систему diff --git a/i18n/src/commonMain/resources/MR/sv/strings.xml b/i18n/src/commonMain/resources/MR/sv/strings.xml index 747b406f0..5b4509d0b 100644 --- a/i18n/src/commonMain/resources/MR/sv/strings.xml +++ b/i18n/src/commonMain/resources/MR/sv/strings.xml @@ -301,7 +301,6 @@ Senaste kapitel Visa kapitel Avbryt alla - Mörkt läge Av Följ systemet diff --git a/i18n/src/commonMain/resources/MR/te/strings.xml b/i18n/src/commonMain/resources/MR/te/strings.xml index afa2fba6d..133c3e709 100644 --- a/i18n/src/commonMain/resources/MR/te/strings.xml +++ b/i18n/src/commonMain/resources/MR/te/strings.xml @@ -116,7 +116,6 @@ భాష తరుచుగా అడిగిన ప్రశ్నలు మరియు మార్గదర్శకములు గురించి - నలుపు వీక్షణ వీక్షణము పరికరమును అనుసరించుము ఆపుము diff --git a/i18n/src/commonMain/resources/MR/th/strings.xml b/i18n/src/commonMain/resources/MR/th/strings.xml index 524645175..28934f658 100644 --- a/i18n/src/commonMain/resources/MR/th/strings.xml +++ b/i18n/src/commonMain/resources/MR/th/strings.xml @@ -347,7 +347,6 @@ เปิด ปิด ตามระบบ - โหมดมืด ธีม คลัง โหลดซ้ำ diff --git a/i18n/src/commonMain/resources/MR/tr/strings.xml b/i18n/src/commonMain/resources/MR/tr/strings.xml index 620e77364..73abf0de2 100644 --- a/i18n/src/commonMain/resources/MR/tr/strings.xml +++ b/i18n/src/commonMain/resources/MR/tr/strings.xml @@ -301,7 +301,6 @@ Son bölüm Bölümleri görüntüle Hepsini iptal et - Karanlık kip Kapalı Açık Sisteme uy diff --git a/i18n/src/commonMain/resources/MR/uk/strings.xml b/i18n/src/commonMain/resources/MR/uk/strings.xml index 5ed5a3f12..a01977c80 100644 --- a/i18n/src/commonMain/resources/MR/uk/strings.xml +++ b/i18n/src/commonMain/resources/MR/uk/strings.xml @@ -301,7 +301,6 @@ Останній розділ Подивитись розділи Скасувати все - Темний режим Вимкнено Увімкнено Використовувати системну diff --git a/i18n/src/commonMain/resources/MR/uz/strings.xml b/i18n/src/commonMain/resources/MR/uz/strings.xml index b0edbd333..2419c494a 100644 --- a/i18n/src/commonMain/resources/MR/uz/strings.xml +++ b/i18n/src/commonMain/resources/MR/uz/strings.xml @@ -98,7 +98,6 @@ Eng yangi Yuklanmoqda… InternalError: Qo\'shimcha ma\'lumot uchun xatolar ro\'yhatini ko\'ring - Qorong\'u rejim O\'chiq Yashil olma Mavzu diff --git a/i18n/src/commonMain/resources/MR/vi/strings.xml b/i18n/src/commonMain/resources/MR/vi/strings.xml index d94f2b7d9..679f23b97 100644 --- a/i18n/src/commonMain/resources/MR/vi/strings.xml +++ b/i18n/src/commonMain/resources/MR/vi/strings.xml @@ -318,7 +318,6 @@ Theo hệ thống Bật Tắt - Chủ đề tối Thư viện Làm mới Chuyển tới trước diff --git a/i18n/src/commonMain/resources/MR/zh-rCN/strings.xml b/i18n/src/commonMain/resources/MR/zh-rCN/strings.xml index 73b5d1e1c..dbd2e0f09 100644 --- a/i18n/src/commonMain/resources/MR/zh-rCN/strings.xml +++ b/i18n/src/commonMain/resources/MR/zh-rCN/strings.xml @@ -301,7 +301,6 @@ 作品更新时间 查看章节 全部取消 - 深色模式 关闭 开启 跟随系统 diff --git a/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml b/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml index 699a6c910..7c3c75e89 100644 --- a/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml +++ b/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml @@ -293,7 +293,6 @@ 這個擴充套件已無法使用,其可能無法正確運作或導致本程式發生問題。建議解除安裝。 關閉 遵循系統 - 深色模式 開啟 日期格式 將套用至你書櫃中的作品 diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/screens/InfoScreen.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/screens/InfoScreen.kt index b1a57a50e..46736d51e 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/screens/InfoScreen.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/screens/InfoScreen.kt @@ -38,8 +38,8 @@ fun InfoScreen( subtitleText: String, acceptText: String, onAcceptClick: () -> Unit, - rejectText: String, - onRejectClick: () -> Unit, + rejectText: String? = null, + onRejectClick: (() -> Unit)? = null, content: @Composable ColumnScope.() -> Unit, ) { Scaffold( @@ -69,11 +69,13 @@ fun InfoScreen( ) { Text(text = acceptText) } - OutlinedButton( - modifier = Modifier.fillMaxWidth(), - onClick = onRejectClick, - ) { - Text(text = rejectText) + if (rejectText != null && onRejectClick != null) { + OutlinedButton( + modifier = Modifier.fillMaxWidth(), + onClick = onRejectClick, + ) { + Text(text = rejectText) + } } } }, diff --git a/presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt b/presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt index 1b2e83cae..76d84aee1 100644 --- a/presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt +++ b/presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt @@ -45,8 +45,8 @@ import tachiyomi.presentation.widget.util.appWidgetBackgroundRadius import tachiyomi.presentation.widget.util.calculateRowAndColumnCount import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.util.Calendar -import java.util.Date +import java.time.Instant +import java.time.ZonedDateTime abstract class BaseUpdatesGridGlanceWidget( private val context: Context = Injekt.get(), @@ -89,7 +89,7 @@ abstract class BaseUpdatesGridGlanceWidget( val flow = remember { getUpdates - .subscribe(false, DateLimit.timeInMillis) + .subscribe(false, DateLimit.toEpochMilli()) .map { rawData -> rawData.prepareData(rowCount, columnCount) } @@ -147,10 +147,7 @@ abstract class BaseUpdatesGridGlanceWidget( } companion object { - val DateLimit: Calendar - get() = Calendar.getInstance().apply { - time = Date() - add(Calendar.MONTH, -3) - } + val DateLimit: Instant + get() = ZonedDateTime.now().minusMonths(3).toInstant() } } diff --git a/presentation-widget/src/main/java/tachiyomi/presentation/widget/WidgetManager.kt b/presentation-widget/src/main/java/tachiyomi/presentation/widget/WidgetManager.kt index 2ddcbcc38..285ba7b13 100644 --- a/presentation-widget/src/main/java/tachiyomi/presentation/widget/WidgetManager.kt +++ b/presentation-widget/src/main/java/tachiyomi/presentation/widget/WidgetManager.kt @@ -19,7 +19,7 @@ class WidgetManager( fun Context.init(scope: LifecycleCoroutineScope) { combine( - getUpdates.subscribe(read = false, after = BaseUpdatesGridGlanceWidget.DateLimit.timeInMillis), + getUpdates.subscribe(read = false, after = BaseUpdatesGridGlanceWidget.DateLimit.toEpochMilli()), securityPreferences.useAuthenticator().changes(), transform = { a, _ -> a }, ) diff --git a/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/online/HttpSource.kt b/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/online/HttpSource.kt index 7450a60bf..ac396fadf 100644 --- a/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/online/HttpSource.kt +++ b/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/online/HttpSource.kt @@ -108,6 +108,7 @@ abstract class HttpSource : CatalogueSource { * * @param page the page number to retrieve. */ + @Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getPopularManga")) override fun fetchPopularManga(page: Int): Observable { return client.newCall(popularMangaRequest(page)) .asObservableSuccess() @@ -138,6 +139,7 @@ abstract class HttpSource : CatalogueSource { * @param query the search query. * @param filters the list of filters to apply. */ + @Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getSearchManga")) override fun fetchSearchManga( page: Int, query: String, @@ -182,6 +184,7 @@ abstract class HttpSource : CatalogueSource { * * @param page the page number to retrieve. */ + @Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getLatestUpdates")) override fun fetchLatestUpdates(page: Int): Observable { return client.newCall(latestUpdatesRequest(page)) .asObservableSuccess()