From 3135db4bb28d46bd9d09b80cf17b94b999b99153 Mon Sep 17 00:00:00 2001 From: arkon Date: Thu, 7 Dec 2023 22:15:45 -0500 Subject: [PATCH 01/17] Bump dependencies --- gradle/compose.versions.toml | 2 +- gradle/kotlinx.versions.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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] From 8779b263ab8cc2315005fb7228e7ee41429ddaf8 Mon Sep 17 00:00:00 2001 From: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> Date: Fri, 8 Dec 2023 10:17:01 +0700 Subject: [PATCH 02/17] Downloader: Don't queue chapters on GlobalScope (#10217) This fixes auto-download on library update not working on certain cases. --- .../tachiyomi/data/download/Downloader.kt | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) 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) } From ab9a26f6bd2855d13ff663cf52dfe4ecb01fda1d Mon Sep 17 00:00:00 2001 From: arkon Date: Fri, 8 Dec 2023 23:11:53 -0500 Subject: [PATCH 03/17] Migrate to some newer date/time APIs --- .../interactor/SyncChaptersWithSource.kt | 4 +-- .../domain/manga/interactor/UpdateManga.kt | 12 ++++---- .../screen/SettingsAppearanceScreen.kt | 4 +-- .../eu/kanade/presentation/util/TimeUtils.kt | 4 +-- .../tachiyomi/data/backup/BackupCreateJob.kt | 4 +-- .../data/library/LibraryUpdateJob.kt | 4 +-- .../kanade/tachiyomi/data/saver/ImageSaver.kt | 4 +-- .../data/track/anilist/AnilistApi.kt | 30 +++++++++++-------- .../extension/api/ExtensionGithubApi.kt | 8 +++-- .../browse/migration/search/MigrateDialog.kt | 4 +-- .../source/browse/BrowseSourceScreenModel.kt | 4 +-- .../tachiyomi/ui/reader/ReaderViewModel.kt | 3 +- .../ui/updates/UpdatesScreenModel.kt | 9 ++---- .../kanade/tachiyomi/util/MangaExtensions.kt | 8 ++--- .../tachiyomi/util/lang/DateExtensions.kt | 10 ++----- .../domain/updates/interactor/GetUpdates.kt | 6 ++-- .../widget/BaseUpdatesGridGlanceWidget.kt | 13 ++++---- .../presentation/widget/WidgetManager.kt | 2 +- 18 files changed, 65 insertions(+), 68 deletions(-) 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/presentation/more/settings/screen/SettingsAppearanceScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt index 667afa505..292169175 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 @@ -33,7 +33,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 { @@ -123,7 +123,7 @@ object SettingsAppearanceScreen : SearchableSettings { 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/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/data/backup/BackupCreateJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreateJob.kt index f6fe367f3..7f2e742c3 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,7 +25,7 @@ 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 @@ -52,7 +52,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())!!) } 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/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/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/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/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/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/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 }, ) From 8b57169e924aa75a7cb9b49bb3bbce5e83a087da Mon Sep 17 00:00:00 2001 From: arkon Date: Sat, 9 Dec 2023 16:50:02 -0500 Subject: [PATCH 04/17] Add basic onboarding screen (#10199) --- .../eu/kanade/domain/base/BasePreferences.kt | 2 + .../more/onboarding/OnboardingScreen.kt | 68 ++++++++++++++++++ .../more/onboarding/StorageStep.kt | 41 +++++++++++ .../presentation/more/onboarding/ThemeStep.kt | 40 +++++++++++ .../settings/screen/SettingsAdvancedScreen.kt | 5 ++ .../screen/SettingsAppearanceScreen.kt | 72 ++++++++----------- .../settings/screen/SettingsDataScreen.kt | 38 +++++++--- .../widget/AppThemeModePreferenceWidget.kt | 56 +++++++++++++++ .../widget/AppThemePreferenceWidget.kt | 11 ++- .../eu/kanade/tachiyomi/ui/home/HomeScreen.kt | 12 ++-- .../kanade/tachiyomi/ui/main/MainActivity.kt | 13 ++++ .../tachiyomi/ui/more/OnboardingScreen.kt | 34 +++++++++ .../preference/InMemoryPreferenceStore.kt | 2 +- .../commonMain/resources/MR/base/strings.xml | 16 +++-- 14 files changed, 343 insertions(+), 67 deletions(-) create mode 100644 app/src/main/java/eu/kanade/presentation/more/onboarding/OnboardingScreen.kt create mode 100644 app/src/main/java/eu/kanade/presentation/more/onboarding/StorageStep.kt create mode 100644 app/src/main/java/eu/kanade/presentation/more/onboarding/ThemeStep.kt create mode 100644 app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemeModePreferenceWidget.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/more/OnboardingScreen.kt 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/presentation/more/onboarding/OnboardingScreen.kt b/app/src/main/java/eu/kanade/presentation/more/onboarding/OnboardingScreen.kt new file mode 100644 index 000000000..0bfdfbb93 --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/more/onboarding/OnboardingScreen.kt @@ -0,0 +1,68 @@ +package eu.kanade.presentation.more.onboarding + +import androidx.compose.animation.AnimatedContent +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.RocketLaunch +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 eu.kanade.domain.ui.UiPreferences +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.i18n.stringResource +import tachiyomi.presentation.core.screens.InfoScreen + +@Composable +fun OnboardingScreen( + storagePreferences: StoragePreferences, + uiPreferences: UiPreferences, + onComplete: () -> Unit, +) { + var currentStep by remember { mutableIntStateOf(0) } + val steps: List<@Composable () -> Unit> = listOf( + { ThemeStep(uiPreferences = uiPreferences) }, + { StorageStep(storagePref = storagePreferences.baseStorageDirectory()) }, + // TODO: prompt for notification permissions when bumping target to Android 13 + ) + val isLastStep = currentStep == steps.size - 1 + val slideDistance = rememberSlideDistance() + + 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) { + currentStep++ + } else { + onComplete() + } + }, + rejectText = stringResource(MR.strings.onboarding_action_skip), + onRejectClick = onComplete, + ) { + AnimatedContent( + targetState = currentStep, + transitionSpec = { + materialSharedAxisX( + forward = true, + 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..b4204a0e4 --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/more/onboarding/StorageStep.kt @@ -0,0 +1,41 @@ +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.material3.Text +import androidx.compose.runtime.Composable +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( + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text(stringResource(MR.strings.onboarding_storage_info)) + + Button( + onClick = { + try { + pickStorageLocation.launch(null) + } catch (e: ActivityNotFoundException) { + context.toast(MR.strings.file_picker_error) + } + }, + ) { + Text(SettingsDataScreen.storageLocationText(storagePref)) + } + } +} 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 292169175..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 @@ -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,9 +101,10 @@ 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() ?: "") 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 a4fb1b685..e273f05e5 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 @@ -80,13 +81,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) { @@ -101,13 +101,31 @@ object SettingsDataScreen : SearchableSettings { Injekt.get().invalidateCache() } } + } + + @Composable + fun storageLocationText( + storageDirPref: tachiyomi.core.preference.Preference, + ): String { + val context = LocalContext.current + val storageDir by storageDirPref.collectAsState() + + 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/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/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/more/OnboardingScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/more/OnboardingScreen.kt new file mode 100644 index 000000000..0cbd7e625 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/more/OnboardingScreen.kt @@ -0,0 +1,34 @@ +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 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() } + + OnboardingScreen( + storagePreferences = storagePreferences, + uiPreferences = uiPreferences, + onComplete = { + basePreferences.shownOnboardingFlow().set(true) + navigator.pop() + }, + ) + } +} 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/i18n/src/commonMain/resources/MR/base/strings.xml b/i18n/src/commonMain/resources/MR/base/strings.xml index 1d4febdd6..fb190bda9 100644 --- a/i18n/src/commonMain/resources/MR/base/strings.xml +++ b/i18n/src/commonMain/resources/MR/base/strings.xml @@ -173,6 +173,15 @@ 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 storage location where chapter downloads, backups, and local source content will be stored. + General @@ -196,11 +205,10 @@ Theme - Dark mode - Follow system - Off - On App theme + System + Light + Dark Dynamic Green Apple Lavender From e3404cd3d3c096e33c534143b24dbdce5b6e9bf9 Mon Sep 17 00:00:00 2001 From: arkon Date: Sat, 9 Dec 2023 17:49:35 -0500 Subject: [PATCH 05/17] More onboarding screen additions --- .../kanade/presentation/crash/CrashScreen.kt | 4 +- .../more/onboarding/GuidesStep.kt | 47 +++++++++++++++++++ .../more/onboarding/OnboardingScreen.kt | 41 ++++++++++++---- .../more/onboarding/StorageStep.kt | 15 +++++- .../settings/screen/SettingsDataScreen.kt | 4 ++ .../kanade/tachiyomi/ui/library/LibraryTab.kt | 3 +- .../tachiyomi/ui/more/OnboardingScreen.kt | 13 +++-- .../commonMain/resources/MR/base/strings.xml | 6 ++- 8 files changed, 114 insertions(+), 19 deletions(-) create mode 100644 app/src/main/java/eu/kanade/presentation/more/onboarding/GuidesStep.kt 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/more/onboarding/GuidesStep.kt b/app/src/main/java/eu/kanade/presentation/more/onboarding/GuidesStep.kt new file mode 100644 index 000000000..8976a00d2 --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/more/onboarding/GuidesStep.kt @@ -0,0 +1,47 @@ +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.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalUriHandler +import androidx.compose.ui.unit.dp +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() + + 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" 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 index 0bfdfbb93..7facba403 100644 --- a/app/src/main/java/eu/kanade/presentation/more/onboarding/OnboardingScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/onboarding/OnboardingScreen.kt @@ -1,18 +1,27 @@ 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 eu.kanade.domain.ui.UiPreferences 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 @@ -21,16 +30,20 @@ fun OnboardingScreen( storagePreferences: StoragePreferences, uiPreferences: UiPreferences, onComplete: () -> Unit, + onRestoreBackup: () -> Unit, ) { var currentStep by remember { mutableIntStateOf(0) } val steps: List<@Composable () -> Unit> = 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 val slideDistance = rememberSlideDistance() + BackHandler(enabled = currentStep != 0, onBack = { currentStep-- }) + InfoScreen( icon = Icons.Outlined.RocketLaunch, headingText = stringResource(MR.strings.onboarding_heading), @@ -52,17 +65,25 @@ fun OnboardingScreen( rejectText = stringResource(MR.strings.onboarding_action_skip), onRejectClick = onComplete, ) { - AnimatedContent( - targetState = currentStep, - transitionSpec = { - materialSharedAxisX( - forward = true, - slideDistance = slideDistance, - ) - }, - label = "stepContent", + Box( + modifier = Modifier + .padding(vertical = MaterialTheme.padding.small) + .clip(MaterialTheme.shapes.small) + .fillMaxSize() + .background(MaterialTheme.colorScheme.surfaceVariant), ) { - steps[it]() + AnimatedContent( + targetState = currentStep, + transitionSpec = { + materialSharedAxisX( + forward = true, + 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 index b4204a0e4..062e5a7c9 100644 --- a/app/src/main/java/eu/kanade/presentation/more/onboarding/StorageStep.kt +++ b/app/src/main/java/eu/kanade/presentation/more/onboarding/StorageStep.kt @@ -3,8 +3,11 @@ 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 @@ -22,11 +25,19 @@ internal fun StorageStep( val pickStorageLocation = SettingsDataScreen.storageLocationPicker(storagePref) Column( + modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp), ) { - Text(stringResource(MR.strings.onboarding_storage_info)) + Text( + stringResource( + MR.strings.onboarding_storage_info, + stringResource(MR.strings.app_name), + SettingsDataScreen.storageLocationText(storagePref), + ), + ) Button( + modifier = Modifier.fillMaxWidth(), onClick = { try { pickStorageLocation.launch(null) @@ -35,7 +46,7 @@ internal fun StorageStep( } }, ) { - Text(SettingsDataScreen.storageLocationText(storagePref)) + Text(stringResource(MR.strings.onboarding_storage_action_select)) } } } 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 e273f05e5..988bc98a3 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 @@ -110,6 +110,10 @@ object SettingsDataScreen : SearchableSettings { 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() 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/more/OnboardingScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/more/OnboardingScreen.kt index 0cbd7e625..037edd9f5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/more/OnboardingScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/more/OnboardingScreen.kt @@ -8,6 +8,7 @@ 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 @@ -22,12 +23,18 @@ class OnboardingScreen : Screen() { val storagePreferences = remember { Injekt.get() } val uiPreferences = remember { Injekt.get() } + val finishOnboarding = { + basePreferences.shownOnboardingFlow().set(true) + navigator.pop() + } + OnboardingScreen( storagePreferences = storagePreferences, uiPreferences = uiPreferences, - onComplete = { - basePreferences.shownOnboardingFlow().set(true) - navigator.pop() + onComplete = { finishOnboarding() }, + onRestoreBackup = { + finishOnboarding() + navigator.push(SettingsScreen.toDataAndStorageScreen()) }, ) } diff --git a/i18n/src/commonMain/resources/MR/base/strings.xml b/i18n/src/commonMain/resources/MR/base/strings.xml index fb190bda9..9b86c899a 100644 --- a/i18n/src/commonMain/resources/MR/base/strings.xml +++ b/i18n/src/commonMain/resources/MR/base/strings.xml @@ -180,7 +180,10 @@ Next Get started Skip - Select a storage location where chapter downloads, backups, and local source content will be stored. + 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 + New to %s? We recommend checking out the getting started guide. + Already used %s before? @@ -439,6 +442,7 @@ After reading automatically delete Allow deleting bookmarked chapters Excluded categories + No storage location set Invalid location: %s Disabled Last read chapter From f7c5b4243533374f6751055fd4bc037e022db8a8 Mon Sep 17 00:00:00 2001 From: arkon Date: Sat, 9 Dec 2023 18:20:58 -0500 Subject: [PATCH 06/17] More onboarding screen additions 2: Electric Boogaloo --- app/build.gradle.kts | 2 +- .../more/onboarding/GuidesStep.kt | 17 +++++++++- .../more/onboarding/OnboardingScreen.kt | 33 ++++++++++++------- .../java/eu/kanade/tachiyomi/Migrations.kt | 13 ++++---- .../storage/service/StoragePreferences.kt | 3 +- .../commonMain/resources/MR/base/strings.xml | 1 + 6 files changed, 49 insertions(+), 20 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2f701d5a5..183158ef1 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/presentation/more/onboarding/GuidesStep.kt b/app/src/main/java/eu/kanade/presentation/more/onboarding/GuidesStep.kt index 8976a00d2..5899dae55 100644 --- a/app/src/main/java/eu/kanade/presentation/more/onboarding/GuidesStep.kt +++ b/app/src/main/java/eu/kanade/presentation/more/onboarding/GuidesStep.kt @@ -6,11 +6,14 @@ 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 @@ -32,7 +35,9 @@ internal fun GuidesStep( Text(stringResource(MR.strings.getting_started_guide)) } - HorizontalDivider() + HorizontalDivider( + color = MaterialTheme.colorScheme.onPrimaryContainer, + ) Text(stringResource(MR.strings.onboarding_guides_returning_user, stringResource(MR.strings.app_name))) Button( @@ -45,3 +50,13 @@ internal fun GuidesStep( } 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 index 7facba403..b42696105 100644 --- a/app/src/main/java/eu/kanade/presentation/more/onboarding/OnboardingScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/onboarding/OnboardingScreen.kt @@ -16,7 +16,9 @@ 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 @@ -32,16 +34,20 @@ fun OnboardingScreen( onComplete: () -> Unit, onRestoreBackup: () -> Unit, ) { - var currentStep by remember { mutableIntStateOf(0) } - val steps: List<@Composable () -> Unit> = 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 + 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( @@ -56,10 +62,15 @@ fun OnboardingScreen( }, ), onAcceptClick = { - if (!isLastStep) { - currentStep++ - } else { + 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++ + } } }, rejectText = stringResource(MR.strings.onboarding_action_skip), 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/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/i18n/src/commonMain/resources/MR/base/strings.xml b/i18n/src/commonMain/resources/MR/base/strings.xml index 9b86c899a..1714d16c4 100644 --- a/i18n/src/commonMain/resources/MR/base/strings.xml +++ b/i18n/src/commonMain/resources/MR/base/strings.xml @@ -182,6 +182,7 @@ 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? From 8c21aa86e95c509c257a696c2f7959187e5e2681 Mon Sep 17 00:00:00 2001 From: "Weblate (bot)" Date: Sun, 10 Dec 2023 00:23:24 +0100 Subject: [PATCH 07/17] Translations update from Hosted Weblate (#10204) Weblate translations Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/fa/ Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/fil/ Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ja/ Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/lv/ Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/pt_BR/ Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ro/ Translate-URL: https://hosted.weblate.org/projects/tachiyomi/tachiyomi-plurals-xml/ro/ Translation: Tachiyomi/Tachiyomi plurals.xml Translation: Tachiyomi/Tachiyomi strings.xml Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com> Co-authored-by: Arash Co-authored-by: Druvvaldis Co-authored-by: InfinityDouki56 Co-authored-by: Saft Octavian Co-authored-by: TheKingTermux --- .../commonMain/resources/MR/am/strings.xml | 1 - .../commonMain/resources/MR/ar/strings.xml | 1 - .../commonMain/resources/MR/be/strings.xml | 1 - .../commonMain/resources/MR/bg/strings.xml | 1 - .../commonMain/resources/MR/bn/strings.xml | 1 - .../commonMain/resources/MR/ca/strings.xml | 1 - .../commonMain/resources/MR/ceb/strings.xml | 1 - .../commonMain/resources/MR/cs/strings.xml | 1 - .../commonMain/resources/MR/cv/strings.xml | 1 - .../commonMain/resources/MR/da/strings.xml | 1 - .../commonMain/resources/MR/de/strings.xml | 1 - .../commonMain/resources/MR/el/strings.xml | 1 - .../commonMain/resources/MR/eo/strings.xml | 1 - .../commonMain/resources/MR/es/strings.xml | 1 - .../commonMain/resources/MR/eu/strings.xml | 1 - .../commonMain/resources/MR/fa/strings.xml | 16 ++++- .../commonMain/resources/MR/fi/strings.xml | 1 - .../commonMain/resources/MR/fil/strings.xml | 7 +- .../commonMain/resources/MR/fr/strings.xml | 1 - .../commonMain/resources/MR/gl/strings.xml | 1 - .../commonMain/resources/MR/he/strings.xml | 1 - .../commonMain/resources/MR/hi/strings.xml | 1 - .../commonMain/resources/MR/hr/strings.xml | 1 - .../commonMain/resources/MR/hu/strings.xml | 1 - .../commonMain/resources/MR/in/strings.xml | 1 - .../commonMain/resources/MR/it/strings.xml | 1 - .../commonMain/resources/MR/ja/strings.xml | 5 +- .../commonMain/resources/MR/jv/strings.xml | 1 - .../resources/MR/ka-rGE/strings.xml | 1 - .../commonMain/resources/MR/kk/strings.xml | 1 - .../commonMain/resources/MR/km/strings.xml | 1 - .../commonMain/resources/MR/kn/strings.xml | 1 - .../commonMain/resources/MR/ko/strings.xml | 1 - .../commonMain/resources/MR/lt/strings.xml | 1 - .../commonMain/resources/MR/lv/strings.xml | 26 ++++++- .../commonMain/resources/MR/mr/strings.xml | 1 - .../commonMain/resources/MR/ms/strings.xml | 1 - .../resources/MR/nb-rNO/strings.xml | 1 - .../commonMain/resources/MR/ne/strings.xml | 1 - .../commonMain/resources/MR/nl/strings.xml | 1 - .../commonMain/resources/MR/pl/strings.xml | 1 - .../resources/MR/pt-rBR/strings.xml | 22 ++++-- .../commonMain/resources/MR/pt/strings.xml | 1 - .../commonMain/resources/MR/ro/plurals.xml | 15 ++++ .../commonMain/resources/MR/ro/strings.xml | 68 +++++++++++++++++-- .../commonMain/resources/MR/ru/strings.xml | 1 - .../commonMain/resources/MR/sa/strings.xml | 1 - .../commonMain/resources/MR/sah/strings.xml | 1 - .../commonMain/resources/MR/sc/strings.xml | 1 - .../commonMain/resources/MR/sdh/strings.xml | 1 - .../commonMain/resources/MR/sk/strings.xml | 1 - .../commonMain/resources/MR/sq/strings.xml | 1 - .../commonMain/resources/MR/sr/strings.xml | 1 - .../commonMain/resources/MR/sv/strings.xml | 1 - .../commonMain/resources/MR/te/strings.xml | 1 - .../commonMain/resources/MR/th/strings.xml | 1 - .../commonMain/resources/MR/tr/strings.xml | 1 - .../commonMain/resources/MR/uk/strings.xml | 1 - .../commonMain/resources/MR/uz/strings.xml | 1 - .../commonMain/resources/MR/vi/strings.xml | 1 - .../resources/MR/zh-rCN/strings.xml | 1 - .../resources/MR/zh-rTW/strings.xml | 1 - 62 files changed, 140 insertions(+), 74 deletions(-) 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/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 @@ 這個擴充套件已無法使用,其可能無法正確運作或導致本程式發生問題。建議解除安裝。 關閉 遵循系統 - 深色模式 開啟 日期格式 將套用至你書櫃中的作品 From e5693ed6689840e50da9d40af36539fe8611e858 Mon Sep 17 00:00:00 2001 From: arkon Date: Sun, 10 Dec 2023 10:10:17 -0500 Subject: [PATCH 08/17] Upgrade Voyager --- gradle/libs.versions.toml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8724b39d2..b3f828b7a 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" } @@ -102,6 +103,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 From 44d6c4fe440b6a44de3fa90a99b01652660188f3 Mon Sep 17 00:00:00 2001 From: arkon Date: Sun, 10 Dec 2023 10:10:27 -0500 Subject: [PATCH 09/17] Minor cleanup/docs --- i18n/README.md | 5 +++++ .../kotlin/eu/kanade/tachiyomi/source/online/HttpSource.kt | 3 +++ 2 files changed, 8 insertions(+) create mode 100644 i18n/README.md 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/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() From 47e544b710c1d9337a52dd8fbd5401903a14059a Mon Sep 17 00:00:00 2001 From: arkon Date: Sun, 10 Dec 2023 10:51:50 -0500 Subject: [PATCH 10/17] Fix next local chapter not being indicated as downloaded in transition --- .../manga/components/MangaInfoHeader.kt | 13 ++++++++----- .../ui/reader/viewer/ReaderTransitionView.kt | 3 ++- 2 files changed, 10 insertions(+), 6 deletions(-) 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..e844e8d94 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?, @@ -173,6 +172,7 @@ fun MangaActionRow( onTrackingClicked: (() -> Unit)?, onEditIntervalClicked: (() -> Unit)?, onEditCategory: (() -> Unit)?, + modifier: Modifier = Modifier, ) { val defaultActionButtonColor = MaterialTheme.colorScheme.onSurface.copy(alpha = .38f) @@ -226,12 +226,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 +406,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 +556,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/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, From 3a0b3de1754452cc0e10426c79820baa74d30b63 Mon Sep 17 00:00:00 2001 From: arkon Date: Sun, 10 Dec 2023 11:58:20 -0500 Subject: [PATCH 11/17] Always show trackers action in MangaScreen Goes to tracker settings to log in if none are set up. --- .../kanade/presentation/manga/MangaScreen.kt | 6 +-- .../manga/components/MangaInfoHeader.kt | 24 ++++++------ .../kanade/tachiyomi/ui/manga/MangaScreen.kt | 9 ++++- .../tachiyomi/ui/manga/MangaScreenModel.kt | 3 -- .../eu/kanade/tachiyomi/ui/more/MoreTab.kt | 8 ++-- .../tachiyomi/ui/more/OnboardingScreen.kt | 2 +- .../tachiyomi/ui/setting/SettingsScreen.kt | 39 +++++++++---------- 7 files changed, 45 insertions(+), 46 deletions(-) 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 e844e8d94..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 @@ -169,7 +169,7 @@ fun MangaActionRow( onAddToLibraryClicked: () -> Unit, onWebViewClicked: (() -> Unit)?, onWebViewLongClicked: (() -> Unit)?, - onTrackingClicked: (() -> Unit)?, + onTrackingClicked: () -> Unit, onEditIntervalClicked: (() -> Unit)?, onEditCategory: (() -> Unit)?, modifier: Modifier = Modifier, @@ -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), 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 index 037edd9f5..72e091954 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/more/OnboardingScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/more/OnboardingScreen.kt @@ -34,7 +34,7 @@ class OnboardingScreen : Screen() { onComplete = { finishOnboarding() }, onRestoreBackup = { finishOnboarding() - navigator.push(SettingsScreen.toDataAndStorageScreen()) + navigator.push(SettingsScreen(SettingsScreen.Destination.DataAndStorage)) }, ) } 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..5ac3e0716 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,9 +23,8 @@ 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: Destination = Destination.Main, ) : Screen() { @Composable @@ -32,12 +32,11 @@ class SettingsScreen private constructor( val parentNavigator = LocalNavigator.currentOrThrow if (!isTabletUi()) { Navigator( - screen = if (toDataAndStorage) { - SettingsDataScreen - } else if (toAbout) { - AboutScreen - } else { - SettingsMainScreen + screen = when (destination) { + Destination.Main -> SettingsMainScreen + Destination.About -> AboutScreen + Destination.DataAndStorage -> SettingsDataScreen + Destination.Tracking -> SettingsTrackingScreen }, content = { val pop: () -> Unit = { @@ -54,12 +53,11 @@ class SettingsScreen private constructor( ) } else { Navigator( - screen = if (toDataAndStorage) { - SettingsDataScreen - } else if (toAbout) { - AboutScreen - } else { - SettingsAppearanceScreen + screen = when (destination) { + Destination.Main -> SettingsAppearanceScreen + Destination.About -> AboutScreen + Destination.DataAndStorage -> SettingsDataScreen + Destination.Tracking -> SettingsTrackingScreen }, ) { val insets = WindowInsets.systemBars.only(WindowInsetsSides.Horizontal) @@ -78,11 +76,10 @@ 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 interface Destination { + data object Main : Destination + data object About : Destination + data object DataAndStorage : Destination + data object Tracking : Destination } } From cc56fde9fe6408e5dca4c752acc2ba6478291cce Mon Sep 17 00:00:00 2001 From: arkon Date: Sun, 10 Dec 2023 17:28:34 -0500 Subject: [PATCH 12/17] Onboarding screen tweaks - Opposite transition when going back a step - Don't allow skipping (I don't want to deal with an unset storage location in other places) --- .../more/onboarding/OnboardingScreen.kt | 4 +--- .../presentation/core/screens/InfoScreen.kt | 16 +++++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) 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 index b42696105..ef42a68fc 100644 --- a/app/src/main/java/eu/kanade/presentation/more/onboarding/OnboardingScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/onboarding/OnboardingScreen.kt @@ -73,8 +73,6 @@ fun OnboardingScreen( } } }, - rejectText = stringResource(MR.strings.onboarding_action_skip), - onRejectClick = onComplete, ) { Box( modifier = Modifier @@ -87,7 +85,7 @@ fun OnboardingScreen( targetState = currentStep, transitionSpec = { materialSharedAxisX( - forward = true, + forward = targetState > initialState, slideDistance = slideDistance, ) }, 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) + } } } }, From 0d09039e5f0eb5d295c699ca4e6f160d7549e771 Mon Sep 17 00:00:00 2001 From: arkon Date: Sun, 10 Dec 2023 17:29:02 -0500 Subject: [PATCH 13/17] Fix settings screen crashing when saving state --- .../tachiyomi/ui/setting/SettingsScreen.kt | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) 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 5ac3e0716..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 @@ -24,19 +24,21 @@ import eu.kanade.presentation.util.isTabletUi import tachiyomi.presentation.core.components.TwoPanelBox class SettingsScreen( - private val destination: Destination = Destination.Main, + private val destination: Int? = null, ) : Screen() { + constructor(destination: Destination) : this(destination.id) + @Composable override fun Content() { val parentNavigator = LocalNavigator.currentOrThrow if (!isTabletUi()) { Navigator( screen = when (destination) { - Destination.Main -> SettingsMainScreen - Destination.About -> AboutScreen - Destination.DataAndStorage -> SettingsDataScreen - Destination.Tracking -> SettingsTrackingScreen + Destination.About.id -> AboutScreen + Destination.DataAndStorage.id -> SettingsDataScreen + Destination.Tracking.id -> SettingsTrackingScreen + else -> SettingsMainScreen }, content = { val pop: () -> Unit = { @@ -54,10 +56,10 @@ class SettingsScreen( } else { Navigator( screen = when (destination) { - Destination.Main -> SettingsAppearanceScreen - Destination.About -> AboutScreen - Destination.DataAndStorage -> SettingsDataScreen - Destination.Tracking -> SettingsTrackingScreen + Destination.About.id -> AboutScreen + Destination.DataAndStorage.id -> SettingsDataScreen + Destination.Tracking.id -> SettingsTrackingScreen + else -> SettingsAppearanceScreen }, ) { val insets = WindowInsets.systemBars.only(WindowInsetsSides.Horizontal) @@ -76,10 +78,9 @@ class SettingsScreen( } } - sealed interface Destination { - data object Main : Destination - data object About : Destination - data object DataAndStorage : Destination - data object Tracking : Destination + sealed class Destination(val id: Int) { + data object About : Destination(0) + data object DataAndStorage : Destination(1) + data object Tracking : Destination(2) } } From 720169dce3b78e3c761e2c80c1fc6f6fab67b791 Mon Sep 17 00:00:00 2001 From: arkon Date: Sun, 10 Dec 2023 18:37:45 -0500 Subject: [PATCH 14/17] Remove action to delete saved image in notification Can just open it and delete from whatever gallery app the user has which has way more functionality. Closes #8327 --- .../data/notification/NotificationReceiver.kt | 38 ------------------- .../tachiyomi/ui/reader/SaveImageNotifier.kt | 6 --- app/src/main/res/drawable/ic_delete_24dp.xml | 9 ----- 3 files changed, 53 deletions(-) delete mode 100644 app/src/main/res/drawable/ic_delete_24dp.xml 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 8133cdaa1..f4262a1ea 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,14 +7,12 @@ 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 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 @@ -65,12 +63,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( @@ -169,16 +161,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. * @@ -415,26 +397,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/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/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 @@ - - - From 1ef01b53f2e740b493e5a94cd4ac011ab25b3ace Mon Sep 17 00:00:00 2001 From: arkon Date: Mon, 11 Dec 2023 22:02:22 -0500 Subject: [PATCH 15/17] Avoid starting restore job if already running We already check in the settings screen where it's triggered, but who knows. Also addressing some errors for method calls that require SDK 26+ (but don't fail the build, somehow?). --- .../kanade/domain/track/service/DelayedTrackingUpdateJob.kt | 5 ++--- .../java/eu/kanade/tachiyomi/data/backup/BackupCreateJob.kt | 4 +--- .../java/eu/kanade/tachiyomi/data/backup/BackupRestoreJob.kt | 2 ++ 3 files changed, 5 insertions(+), 6 deletions(-) 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/tachiyomi/data/backup/BackupCreateJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreateJob.kt index 7f2e742c3..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 @@ -27,8 +27,6 @@ import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get 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) { @@ -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/BackupRestoreJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreJob.kt index e4595a4b5..202072859 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreJob.kt @@ -26,6 +26,8 @@ class BackupRestoreJob(private val context: Context, workerParams: WorkerParamet private val notifier = BackupNotifier(context) override suspend fun doWork(): Result { + if (isRunning(context)) return Result.failure() + val uri = inputData.getString(LOCATION_URI_KEY)?.toUri() ?: return Result.failure() val sync = inputData.getBoolean(SYNC_KEY, false) From f776c36e7049eb04c6062c5aecbde10de52eedab Mon Sep 17 00:00:00 2001 From: arkon Date: Mon, 11 Dec 2023 22:24:33 -0500 Subject: [PATCH 16/17] Add ability to open available extension websites in WebView Closes #8628 --- .../presentation/browse/ExtensionsScreen.kt | 109 +++++++++++++----- .../ui/browse/extension/ExtensionsTab.kt | 12 ++ 2 files changed, 91 insertions(+), 30 deletions(-) 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/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) }, From 0f9895eec8f5808210f291d1e0ef5cc9f73ccb44 Mon Sep 17 00:00:00 2001 From: arkon Date: Mon, 11 Dec 2023 22:48:42 -0500 Subject: [PATCH 17/17] Clean up category restoring logic --- .../tachiyomi/data/backup/BackupRestoreJob.kt | 2 - .../tachiyomi/data/backup/BackupRestorer.kt | 74 +++++++------------ .../data/backup/models/BackupCategory.kt | 14 ++-- 3 files changed, 33 insertions(+), 57 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreJob.kt index 202072859..e4595a4b5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreJob.kt @@ -26,8 +26,6 @@ class BackupRestoreJob(private val context: Context, workerParams: WorkerParamet private val notifier = BackupNotifier(context) override suspend fun doWork(): Result { - if (isRunning(context)) return Result.failure() - val uri = inputData.getString(LOCATION_URI_KEY)?.toUri() ?: return Result.failure() val sync = inputData.getBoolean(SYNC_KEY, false) 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 38b9bf756..32829963e 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 @@ -74,14 +74,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 @@ -99,7 +97,6 @@ class BackupRestorer( } else { notifier.showRestoreComplete(time, errors.size, logFile.parent, logFile.name) } - return true } private fun writeErrorLog(): File { @@ -121,74 +118,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 ->