Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5d1b1408eb | |||
| 2f54f00bf7 | |||
| 87feb58055 | |||
| 28edaca869 | |||
| d14f012bbb | |||
| adc6bbf54f | |||
| 2b064baca1 | |||
| 983a80ba42 | |||
| 911e959fcf | |||
| a425cae73b | |||
| d12a9d329b |
@@ -52,3 +52,20 @@ When creating a fork, remember to:
|
|||||||
- To avoid having your data polluting the main app's analytics and crash report services:
|
- To avoid having your data polluting the main app's analytics and crash report services:
|
||||||
- If you want to use Firebase analytics, replace [`google-services.json`](https://github.com/tachiyomiorg/tachiyomi/blob/master/app/src/standard/google-services.json) with your own
|
- If you want to use Firebase analytics, replace [`google-services.json`](https://github.com/tachiyomiorg/tachiyomi/blob/master/app/src/standard/google-services.json) with your own
|
||||||
- If you want to use ACRA crash reporting, replace the `ACRA_URI` endpoint in [`build.gradle.kts`](https://github.com/tachiyomiorg/tachiyomi/blob/master/app/build.gradle.kts) with your own
|
- If you want to use ACRA crash reporting, replace the `ACRA_URI` endpoint in [`build.gradle.kts`](https://github.com/tachiyomiorg/tachiyomi/blob/master/app/build.gradle.kts) with your own
|
||||||
|
|
||||||
|
|
||||||
|
### Supporting Cloud Sync - Google Drive Implementation
|
||||||
|
1. Go to [Google Cloud Console](https://console.cloud.google.com)
|
||||||
|
2. Create a new project
|
||||||
|
3. Go to API & Services -> Library -> Google Drive API and click enable
|
||||||
|
4. Go to API & Services -> Oauth consent screen
|
||||||
|
5. Create it, fill in the app name, user support email, and developer contact information
|
||||||
|
6. In the next screen, click add or remove scopes, and add the `.../auth/drive.appdata` and `.../auth/drive.file` scopes
|
||||||
|
7. Don't add any test users and go back to the dashboard
|
||||||
|
8. Click publish
|
||||||
|
9. Go to API & Services -> Credentials
|
||||||
|
10. Click Create credentials -> Oauth client ID
|
||||||
|
11. Select Android, give it a name, and set `eu.kanade.google.oauth` as the package name
|
||||||
|
12. To get the SHA-1 key, run `keytool -printcert -jarfile app-standard-universal-release.apk` on your apk, and copy the listed SHA-1
|
||||||
|
13. Expand advanced settings, and enable Custom URL scheme
|
||||||
|
14. After that just download the json, name it to `client_secrets.json` and put it in `app/src/main/assets/`
|
||||||
@@ -73,7 +73,6 @@ class SyncPreferences(
|
|||||||
syncOnChapterOpen = preferenceStore.getBoolean("sync_on_chapter_open", false).get(),
|
syncOnChapterOpen = preferenceStore.getBoolean("sync_on_chapter_open", false).get(),
|
||||||
syncOnAppStart = preferenceStore.getBoolean("sync_on_app_start", false).get(),
|
syncOnAppStart = preferenceStore.getBoolean("sync_on_app_start", false).get(),
|
||||||
syncOnAppResume = preferenceStore.getBoolean("sync_on_app_resume", false).get(),
|
syncOnAppResume = preferenceStore.getBoolean("sync_on_app_resume", false).get(),
|
||||||
syncOnLibraryUpdate = preferenceStore.getBoolean("sync_on_library_update", false).get(),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +85,5 @@ class SyncPreferences(
|
|||||||
.set(syncTriggerOptions.syncOnAppStart)
|
.set(syncTriggerOptions.syncOnAppStart)
|
||||||
preferenceStore.getBoolean("sync_on_app_resume", false)
|
preferenceStore.getBoolean("sync_on_app_resume", false)
|
||||||
.set(syncTriggerOptions.syncOnAppResume)
|
.set(syncTriggerOptions.syncOnAppResume)
|
||||||
preferenceStore.getBoolean("sync_on_library_update", false)
|
|
||||||
.set(syncTriggerOptions.syncOnLibraryUpdate)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -239,6 +239,7 @@ fun LibraryBottomActionMenu(
|
|||||||
onClickCleanTitles: (() -> Unit)?,
|
onClickCleanTitles: (() -> Unit)?,
|
||||||
onClickMigrate: (() -> Unit)?,
|
onClickMigrate: (() -> Unit)?,
|
||||||
onClickAddToMangaDex: (() -> Unit)?,
|
onClickAddToMangaDex: (() -> Unit)?,
|
||||||
|
onClickResetInfo: (() -> Unit)?,
|
||||||
// SY <--
|
// SY <--
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
@@ -267,7 +268,7 @@ fun LibraryBottomActionMenu(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// SY -->
|
// SY -->
|
||||||
val showOverflow = onClickCleanTitles != null || onClickAddToMangaDex != null
|
val showOverflow = onClickCleanTitles != null || onClickAddToMangaDex != null || onClickResetInfo != null
|
||||||
val configuration = LocalConfiguration.current
|
val configuration = LocalConfiguration.current
|
||||||
val moveMarkPrev = remember { !configuration.isTabletUi() }
|
val moveMarkPrev = remember { !configuration.isTabletUi() }
|
||||||
var overFlowOpen by remember { mutableStateOf(false) }
|
var overFlowOpen by remember { mutableStateOf(false) }
|
||||||
@@ -364,6 +365,12 @@ fun LibraryBottomActionMenu(
|
|||||||
onClick = onClickAddToMangaDex,
|
onClick = onClickAddToMangaDex,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (onClickResetInfo != null) {
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = { Text(text = stringResource(SYMR.strings.reset_info)) },
|
||||||
|
onClick = onClickResetInfo,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Button(
|
Button(
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import coil3.ImageLoader
|
|||||||
import coil3.SingletonImageLoader
|
import coil3.SingletonImageLoader
|
||||||
import coil3.disk.DiskCache
|
import coil3.disk.DiskCache
|
||||||
import coil3.disk.directory
|
import coil3.disk.directory
|
||||||
|
import coil3.gif.AnimatedImageDecoder
|
||||||
|
import coil3.gif.GifDecoder
|
||||||
import coil3.network.okhttp.OkHttpNetworkFetcherFactory
|
import coil3.network.okhttp.OkHttpNetworkFetcherFactory
|
||||||
import coil3.request.allowRgb565
|
import coil3.request.allowRgb565
|
||||||
import coil3.request.crossfade
|
import coil3.request.crossfade
|
||||||
@@ -182,6 +184,11 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
|
|||||||
val callFactoryLazy = lazy { Injekt.get<NetworkHelper>().client }
|
val callFactoryLazy = lazy { Injekt.get<NetworkHelper>().client }
|
||||||
val diskCacheLazy = lazy { CoilDiskCache.get(this@App) }
|
val diskCacheLazy = lazy { CoilDiskCache.get(this@App) }
|
||||||
components {
|
components {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
|
add(AnimatedImageDecoder.Factory())
|
||||||
|
} else {
|
||||||
|
add(GifDecoder.Factory())
|
||||||
|
}
|
||||||
add(OkHttpNetworkFetcherFactory(callFactoryLazy::value))
|
add(OkHttpNetworkFetcherFactory(callFactoryLazy::value))
|
||||||
add(TachiyomiImageDecoder.Factory())
|
add(TachiyomiImageDecoder.Factory())
|
||||||
add(MangaCoverFetcher.MangaFactory(callFactoryLazy, diskCacheLazy))
|
add(MangaCoverFetcher.MangaFactory(callFactoryLazy, diskCacheLazy))
|
||||||
|
|||||||
+5
-4
@@ -313,7 +313,7 @@ class MangaRestorer(
|
|||||||
restoreCategories(manga, categories, backupCategories)
|
restoreCategories(manga, categories, backupCategories)
|
||||||
restoreChapters(manga, chapters)
|
restoreChapters(manga, chapters)
|
||||||
restoreTracking(manga, tracks)
|
restoreTracking(manga, tracks)
|
||||||
restoreHistory(history)
|
restoreHistory(manga, history)
|
||||||
restoreExcludedScanlators(manga, excludedScanlators)
|
restoreExcludedScanlators(manga, excludedScanlators)
|
||||||
updateManga.awaitUpdateFetchInterval(manga, now, currentFetchWindow)
|
updateManga.awaitUpdateFetchInterval(manga, now, currentFetchWindow)
|
||||||
// SY -->
|
// SY -->
|
||||||
@@ -359,13 +359,14 @@ class MangaRestorer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun restoreHistory(backupHistory: List<BackupHistory>) {
|
private suspend fun restoreHistory(manga: Manga, backupHistory: List<BackupHistory>) {
|
||||||
val toUpdate = backupHistory.mapNotNull { history ->
|
val toUpdate = backupHistory.mapNotNull { history ->
|
||||||
val dbHistory = handler.awaitOneOrNull { historyQueries.getHistoryByChapterUrl(history.url) }
|
val dbHistory = handler.awaitOneOrNull { historyQueries.getHistoryByChapterUrl(manga.id, history.url) }
|
||||||
val item = history.getHistoryImpl()
|
val item = history.getHistoryImpl()
|
||||||
|
|
||||||
if (dbHistory == null) {
|
if (dbHistory == null) {
|
||||||
val chapter = handler.awaitOneOrNull { chaptersQueries.getChapterByUrl(history.url) }
|
val chapter = handler.awaitList { chaptersQueries.getChapterByUrl(history.url) }
|
||||||
|
.find { it.manga_id == manga.id }
|
||||||
return@mapNotNull if (chapter == null) {
|
return@mapNotNull if (chapter == null) {
|
||||||
// Chapter doesn't exist; skip
|
// Chapter doesn't exist; skip
|
||||||
null
|
null
|
||||||
|
|||||||
@@ -817,19 +817,11 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
|||||||
KEY_GROUP_EXTRA to groupExtra,
|
KEY_GROUP_EXTRA to groupExtra,
|
||||||
// SY <--
|
// SY <--
|
||||||
)
|
)
|
||||||
val request = OneTimeWorkRequestBuilder<LibraryUpdateJob>()
|
|
||||||
.addTag(TAG)
|
|
||||||
.addTag(WORK_NAME_MANUAL)
|
|
||||||
.setInputData(inputData)
|
|
||||||
.build()
|
|
||||||
wm.enqueueUniqueWork(WORK_NAME_MANUAL, ExistingWorkPolicy.KEEP, request)
|
|
||||||
|
|
||||||
val syncPreferences: SyncPreferences = Injekt.get()
|
val syncPreferences: SyncPreferences = Injekt.get()
|
||||||
|
|
||||||
// Only proceed with SyncDataJob if sync is enabled and the specific sync on library update flag is set
|
// Always sync the data before library update if syncing is enabled.
|
||||||
val syncTriggerOpt = syncPreferences.getSyncTriggerOptions()
|
if (syncPreferences.isSyncEnabled()) {
|
||||||
if (syncPreferences.isSyncEnabled() && syncTriggerOpt.syncOnLibraryUpdate
|
|
||||||
) {
|
|
||||||
// Check if SyncDataJob is already running
|
// Check if SyncDataJob is already running
|
||||||
if (wm.isRunning(SyncDataJob.TAG_MANUAL)) {
|
if (wm.isRunning(SyncDataJob.TAG_MANUAL)) {
|
||||||
// SyncDataJob is already running
|
// SyncDataJob is already running
|
||||||
@@ -842,7 +834,6 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
|||||||
.build()
|
.build()
|
||||||
|
|
||||||
// Chain SyncDataJob to run before LibraryUpdateJob
|
// Chain SyncDataJob to run before LibraryUpdateJob
|
||||||
val inputData = workDataOf(KEY_CATEGORY to category?.id)
|
|
||||||
val libraryUpdateJob = OneTimeWorkRequestBuilder<LibraryUpdateJob>()
|
val libraryUpdateJob = OneTimeWorkRequestBuilder<LibraryUpdateJob>()
|
||||||
.addTag(TAG)
|
.addTag(TAG)
|
||||||
.addTag(WORK_NAME_MANUAL)
|
.addTag(WORK_NAME_MANUAL)
|
||||||
@@ -853,7 +844,6 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
|||||||
.then(libraryUpdateJob)
|
.then(libraryUpdateJob)
|
||||||
.enqueue()
|
.enqueue()
|
||||||
} else {
|
} else {
|
||||||
val inputData = workDataOf(KEY_CATEGORY to category?.id)
|
|
||||||
val request = OneTimeWorkRequestBuilder<LibraryUpdateJob>()
|
val request = OneTimeWorkRequestBuilder<LibraryUpdateJob>()
|
||||||
.addTag(TAG)
|
.addTag(TAG)
|
||||||
.addTag(WORK_NAME_MANUAL)
|
.addTag(WORK_NAME_MANUAL)
|
||||||
|
|||||||
@@ -9,21 +9,18 @@ data class SyncTriggerOptions(
|
|||||||
val syncOnChapterOpen: Boolean = false,
|
val syncOnChapterOpen: Boolean = false,
|
||||||
val syncOnAppStart: Boolean = false,
|
val syncOnAppStart: Boolean = false,
|
||||||
val syncOnAppResume: Boolean = false,
|
val syncOnAppResume: Boolean = false,
|
||||||
val syncOnLibraryUpdate: Boolean = false,
|
|
||||||
) {
|
) {
|
||||||
fun asBooleanArray() = booleanArrayOf(
|
fun asBooleanArray() = booleanArrayOf(
|
||||||
syncOnChapterRead,
|
syncOnChapterRead,
|
||||||
syncOnChapterOpen,
|
syncOnChapterOpen,
|
||||||
syncOnAppStart,
|
syncOnAppStart,
|
||||||
syncOnAppResume,
|
syncOnAppResume,
|
||||||
syncOnLibraryUpdate,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fun anyEnabled() = syncOnChapterRead ||
|
fun anyEnabled() = syncOnChapterRead ||
|
||||||
syncOnChapterOpen ||
|
syncOnChapterOpen ||
|
||||||
syncOnAppStart ||
|
syncOnAppStart ||
|
||||||
syncOnAppResume ||
|
syncOnAppResume
|
||||||
syncOnLibraryUpdate
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val mainOptions = persistentListOf(
|
val mainOptions = persistentListOf(
|
||||||
@@ -47,11 +44,6 @@ data class SyncTriggerOptions(
|
|||||||
getter = SyncTriggerOptions::syncOnAppResume,
|
getter = SyncTriggerOptions::syncOnAppResume,
|
||||||
setter = { options, enabled -> options.copy(syncOnAppResume = enabled) },
|
setter = { options, enabled -> options.copy(syncOnAppResume = enabled) },
|
||||||
),
|
),
|
||||||
Entry(
|
|
||||||
label = MR.strings.sync_on_library_update,
|
|
||||||
getter = SyncTriggerOptions::syncOnLibraryUpdate,
|
|
||||||
setter = { options, enabled -> options.copy(syncOnLibraryUpdate = enabled) },
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fun fromBooleanArray(array: BooleanArray) = SyncTriggerOptions(
|
fun fromBooleanArray(array: BooleanArray) = SyncTriggerOptions(
|
||||||
@@ -59,7 +51,6 @@ data class SyncTriggerOptions(
|
|||||||
syncOnChapterOpen = array[1],
|
syncOnChapterOpen = array[1],
|
||||||
syncOnAppStart = array[2],
|
syncOnAppStart = array[2],
|
||||||
syncOnAppResume = array[3],
|
syncOnAppResume = array[3],
|
||||||
syncOnLibraryUpdate = array[4],
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -750,6 +750,24 @@ class LibraryScreenModel(
|
|||||||
clearSelection()
|
clearSelection()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun resetInfo() {
|
||||||
|
state.value.selection.fastForEach { (manga) ->
|
||||||
|
val mangaInfo = CustomMangaInfo(
|
||||||
|
id = manga.id,
|
||||||
|
title = null,
|
||||||
|
author = null,
|
||||||
|
artist = null,
|
||||||
|
thumbnailUrl = null,
|
||||||
|
description = null,
|
||||||
|
genre = null,
|
||||||
|
status = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
setCustomMangaInfo.set(mangaInfo)
|
||||||
|
}
|
||||||
|
clearSelection()
|
||||||
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1336,6 +1354,18 @@ class LibraryScreenModel(
|
|||||||
val showAddToMangadex: Boolean by lazy {
|
val showAddToMangadex: Boolean by lazy {
|
||||||
selection.any { it.manga.source in mangaDexSourceIds }
|
selection.any { it.manga.source in mangaDexSourceIds }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val showResetInfo: Boolean by lazy {
|
||||||
|
selection.fastAny { (manga) ->
|
||||||
|
manga.title != manga.ogTitle ||
|
||||||
|
manga.author != manga.ogAuthor ||
|
||||||
|
manga.artist != manga.ogArtist ||
|
||||||
|
manga.thumbnailUrl != manga.ogThumbnailUrl ||
|
||||||
|
manga.description != manga.ogDescription ||
|
||||||
|
manga.genre != manga.ogGenre ||
|
||||||
|
manga.status != manga.ogStatus
|
||||||
|
}
|
||||||
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
fun getLibraryItemsByCategoryId(categoryId: Long): List<LibraryItem>? {
|
fun getLibraryItemsByCategoryId(categoryId: Long): List<LibraryItem>? {
|
||||||
|
|||||||
@@ -205,6 +205,7 @@ object LibraryTab : Tab {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onClickAddToMangaDex = screenModel::syncMangaToDex.takeIf { state.showAddToMangadex },
|
onClickAddToMangaDex = screenModel::syncMangaToDex.takeIf { state.showAddToMangadex },
|
||||||
|
onClickResetInfo = screenModel::resetInfo.takeIf { state.showResetInfo },
|
||||||
// SY <--
|
// SY <--
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -201,6 +201,7 @@ private fun onViewCreated(manga: Manga, context: Context, binding: EditMangaDial
|
|||||||
binding.mangaGenresTags.clearFocus()
|
binding.mangaGenresTags.clearFocus()
|
||||||
|
|
||||||
binding.resetTags.setOnClickListener { resetTags(manga, binding, scope) }
|
binding.resetTags.setOnClickListener { resetTags(manga, binding, scope) }
|
||||||
|
binding.resetInfo.setOnClickListener { resetInfo(manga, binding, scope) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resetTags(manga: Manga, binding: EditMangaDialogBinding, scope: CoroutineScope) {
|
private fun resetTags(manga: Manga, binding: EditMangaDialogBinding, scope: CoroutineScope) {
|
||||||
@@ -217,6 +218,15 @@ private fun loadCover(manga: Manga, context: Context, binding: EditMangaDialogBi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun resetInfo(manga: Manga, binding: EditMangaDialogBinding, scope: CoroutineScope) {
|
||||||
|
binding.title.setText("")
|
||||||
|
binding.mangaAuthor.setText("")
|
||||||
|
binding.mangaArtist.setText("")
|
||||||
|
binding.thumbnailUrl.setText("")
|
||||||
|
binding.mangaDescription.setText("")
|
||||||
|
resetTags(manga, binding, scope)
|
||||||
|
}
|
||||||
|
|
||||||
private fun ChipGroup.setChips(items: List<String>, scope: CoroutineScope) {
|
private fun ChipGroup.setChips(items: List<String>, scope: CoroutineScope) {
|
||||||
removeAllViews()
|
removeAllViews()
|
||||||
|
|
||||||
|
|||||||
@@ -139,6 +139,16 @@
|
|||||||
android:text="@string/reset_tags"
|
android:text="@string/reset_tags"
|
||||||
android:textAllCaps="false" />
|
android:textAllCaps="false" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/reset_info"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:text="@string/reset_info"
|
||||||
|
android:textAllCaps="false" />
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="1dp"
|
android:layout_height="1dp"
|
||||||
|
|||||||
@@ -1727,9 +1727,6 @@
|
|||||||
<ID>SwallowedException:GalleryAdder.kt$GalleryAdder$e: Exception</ID>
|
<ID>SwallowedException:GalleryAdder.kt$GalleryAdder$e: Exception</ID>
|
||||||
<ID>SwallowedException:GetChapterByUrlAndMangaId.kt$GetChapterByUrlAndMangaId$e: Exception</ID>
|
<ID>SwallowedException:GetChapterByUrlAndMangaId.kt$GetChapterByUrlAndMangaId$e: Exception</ID>
|
||||||
<ID>SwallowedException:GetPagePreviews.kt$GetPagePreviews$e: Exception</ID>
|
<ID>SwallowedException:GetPagePreviews.kt$GetPagePreviews$e: Exception</ID>
|
||||||
<ID>SwallowedException:GoogleDriveSyncService.kt$GoogleDriveService$e: IOException</ID>
|
|
||||||
<ID>SwallowedException:GoogleDriveSyncService.kt$GoogleDriveService$e: TokenResponseException</ID>
|
|
||||||
<ID>SwallowedException:GoogleDriveSyncService.kt$GoogleDriveSyncService$e: Exception</ID>
|
|
||||||
<ID>SwallowedException:HttpSource.kt$HttpSource$e: URISyntaxException</ID>
|
<ID>SwallowedException:HttpSource.kt$HttpSource$e: URISyntaxException</ID>
|
||||||
<ID>SwallowedException:ImageUtil.kt$ImageUtil$e: Exception</ID>
|
<ID>SwallowedException:ImageUtil.kt$ImageUtil$e: Exception</ID>
|
||||||
<ID>SwallowedException:Kavita.kt$Kavita$e: Exception</ID>
|
<ID>SwallowedException:Kavita.kt$Kavita$e: Exception</ID>
|
||||||
@@ -1767,7 +1764,6 @@
|
|||||||
<ID>SwallowedException:SourceFeedScreenModel.kt$SourceFeedScreenModel$e: Exception</ID>
|
<ID>SwallowedException:SourceFeedScreenModel.kt$SourceFeedScreenModel$e: Exception</ID>
|
||||||
<ID>SwallowedException:StorageStep.kt$StorageStep$e: ActivityNotFoundException</ID>
|
<ID>SwallowedException:StorageStep.kt$StorageStep$e: ActivityNotFoundException</ID>
|
||||||
<ID>SwallowedException:Suwayomi.kt$Suwayomi$e: Exception</ID>
|
<ID>SwallowedException:Suwayomi.kt$Suwayomi$e: Exception</ID>
|
||||||
<ID>SwallowedException:SyncManager.kt$SyncManager$e: IOException</ID>
|
|
||||||
<ID>SwallowedException:TrackInfoDialog.kt$TrackInfoDialogHomeScreen.Model$e: Exception</ID>
|
<ID>SwallowedException:TrackInfoDialog.kt$TrackInfoDialogHomeScreen.Model$e: Exception</ID>
|
||||||
<ID>SwallowedException:UrlImportableSource.kt$UrlImportableSource$e: URISyntaxException</ID>
|
<ID>SwallowedException:UrlImportableSource.kt$UrlImportableSource$e: URISyntaxException</ID>
|
||||||
<ID>ThrowingExceptionsWithoutMessageOrCause:MangaScreenModel.kt$MangaScreenModel$IllegalStateException()</ID>
|
<ID>ThrowingExceptionsWithoutMessageOrCause:MangaScreenModel.kt$MangaScreenModel$IllegalStateException()</ID>
|
||||||
@@ -1966,12 +1962,12 @@
|
|||||||
<ID>TooGenericExceptionThrown:EHentai.kt$EHentai$throw Exception(it.text())</ID>
|
<ID>TooGenericExceptionThrown:EHentai.kt$EHentai$throw Exception(it.text())</ID>
|
||||||
<ID>TooGenericExceptionThrown:ExtensionLoader.kt$ExtensionLoader$throw Exception("Unknown source class type: ${obj.javaClass}")</ID>
|
<ID>TooGenericExceptionThrown:ExtensionLoader.kt$ExtensionLoader$throw Exception("Unknown source class type: ${obj.javaClass}")</ID>
|
||||||
<ID>TooGenericExceptionThrown:GoogleDriveSyncService.kt$GoogleDriveService$throw Exception(context.stringResource(MR.strings.google_drive_not_signed_in))</ID>
|
<ID>TooGenericExceptionThrown:GoogleDriveSyncService.kt$GoogleDriveService$throw Exception(context.stringResource(MR.strings.google_drive_not_signed_in))</ID>
|
||||||
<ID>TooGenericExceptionThrown:GoogleDriveSyncService.kt$GoogleDriveSyncService$throw Exception(context.stringResource(MR.strings.error_before_sync_gdrive) + ": ${e.message}")</ID>
|
<ID>TooGenericExceptionThrown:GoogleDriveSyncService.kt$GoogleDriveSyncService$throw Exception(context.stringResource(MR.strings.error_before_sync_gdrive) + ": ${e.message}", e)</ID>
|
||||||
<ID>TooGenericExceptionThrown:GoogleDriveSyncService.kt$GoogleDriveSyncService$throw Exception(context.stringResource(MR.strings.error_before_sync_gdrive) + ": Max retries reached.")</ID>
|
<ID>TooGenericExceptionThrown:GoogleDriveSyncService.kt$GoogleDriveSyncService$throw Exception(context.stringResource(MR.strings.error_before_sync_gdrive) + ": Max retries reached.")</ID>
|
||||||
<ID>TooGenericExceptionThrown:GoogleDriveSyncService.kt$GoogleDriveSyncService$throw Exception(context.stringResource(MR.strings.error_deleting_google_drive_lock_file))</ID>
|
<ID>TooGenericExceptionThrown:GoogleDriveSyncService.kt$GoogleDriveSyncService$throw Exception(context.stringResource(MR.strings.error_deleting_google_drive_lock_file), e)</ID>
|
||||||
<ID>TooGenericExceptionThrown:GoogleDriveSyncService.kt$GoogleDriveSyncService$throw Exception(context.stringResource(MR.strings.error_uploading_sync_data) + ": ${e.message}")</ID>
|
<ID>TooGenericExceptionThrown:GoogleDriveSyncService.kt$GoogleDriveSyncService$throw Exception(context.stringResource(MR.strings.error_uploading_sync_data) + ": ${e.message}", e)</ID>
|
||||||
<ID>TooGenericExceptionThrown:GoogleDriveSyncService.kt$GoogleDriveSyncService$throw Exception(context.stringResource(MR.strings.google_drive_not_signed_in))</ID>
|
<ID>TooGenericExceptionThrown:GoogleDriveSyncService.kt$GoogleDriveSyncService$throw Exception(context.stringResource(MR.strings.google_drive_not_signed_in))</ID>
|
||||||
<ID>TooGenericExceptionThrown:GoogleDriveSyncService.kt$GoogleDriveSyncService$throw Exception(e.message)</ID>
|
<ID>TooGenericExceptionThrown:GoogleDriveSyncService.kt$GoogleDriveSyncService$throw Exception(e.message, e)</ID>
|
||||||
<ID>TooGenericExceptionThrown:HttpSource.kt$HttpSource$throw RuntimeException(e)</ID>
|
<ID>TooGenericExceptionThrown:HttpSource.kt$HttpSource$throw RuntimeException(e)</ID>
|
||||||
<ID>TooGenericExceptionThrown:KitsuApi.kt$KitsuApi$throw Exception("Could not find manga")</ID>
|
<ID>TooGenericExceptionThrown:KitsuApi.kt$KitsuApi$throw Exception("Could not find manga")</ID>
|
||||||
<ID>TooGenericExceptionThrown:KitsuInterceptor.kt$KitsuInterceptor$throw Exception("Not authenticated with Kitsu")</ID>
|
<ID>TooGenericExceptionThrown:KitsuInterceptor.kt$KitsuInterceptor$throw Exception("Not authenticated with Kitsu")</ID>
|
||||||
|
|||||||
@@ -91,6 +91,9 @@ object ImageUtil {
|
|||||||
// Coil supports animated WebP on Android 9.0+
|
// Coil supports animated WebP on Android 9.0+
|
||||||
// https://coil-kt.github.io/coil/getting_started/#supported-image-formats
|
// https://coil-kt.github.io/coil/getting_started/#supported-image-formats
|
||||||
Format.Webp -> type.isAnimated && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
|
Format.Webp -> type.isAnimated && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
|
||||||
|
// Coil supports animated Heif on Android 11+
|
||||||
|
// https://coil-kt.github.io/coil/getting_started/#supported-image-formats
|
||||||
|
Format.Heif -> type.isAnimated && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ H.time_read
|
|||||||
FROM history H
|
FROM history H
|
||||||
JOIN chapters C
|
JOIN chapters C
|
||||||
ON H.chapter_id = C._id
|
ON H.chapter_id = C._id
|
||||||
WHERE C.url = :chapterUrl AND C._id = H.chapter_id;
|
WHERE C.manga_id = :mangaId AND C.url = :chapterUrl AND C._id = H.chapter_id;
|
||||||
|
|
||||||
resetHistoryById:
|
resetHistoryById:
|
||||||
UPDATE history
|
UPDATE history
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
[versions]
|
[versions]
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
firebase-analytics = "com.google.firebase:firebase-analytics-ktx:21.2.2"
|
firebase-analytics = "com.google.firebase:firebase-analytics-ktx:21.5.1"
|
||||||
firebase-crashlytics-ktx = "com.google.firebase:firebase-crashlytics-ktx:18.3.6"
|
firebase-crashlytics-ktx = "com.google.firebase:firebase-crashlytics-ktx:18.6.2"
|
||||||
firebase-crashlytics-gradle = "com.google.firebase:firebase-crashlytics-gradle:2.9.5"
|
firebase-crashlytics-gradle = "com.google.firebase:firebase-crashlytics-gradle:2.9.9"
|
||||||
|
|
||||||
simularity = "info.debatty:java-string-similarity:2.0.0"
|
simularity = "info.debatty:java-string-similarity:2.0.0"
|
||||||
xlog = "com.elvishew:xlog:1.11.0"
|
xlog = "com.elvishew:xlog:1.11.0"
|
||||||
|
|||||||
@@ -350,6 +350,7 @@
|
|||||||
<!-- Entry Info Edit -->
|
<!-- Entry Info Edit -->
|
||||||
<string name="reset_tags">Reset Tags</string>
|
<string name="reset_tags">Reset Tags</string>
|
||||||
<string name="add_tag">Add Tag</string>
|
<string name="add_tag">Add Tag</string>
|
||||||
|
<string name="reset_info">Reset Info</string>
|
||||||
<string name="title_hint">Title: %1$s</string>
|
<string name="title_hint">Title: %1$s</string>
|
||||||
<string name="description_hint">Description: %1$s</string>
|
<string name="description_hint">Description: %1$s</string>
|
||||||
<string name="author_hint">Author: %1$s</string>
|
<string name="author_hint">Author: %1$s</string>
|
||||||
|
|||||||
@@ -172,6 +172,10 @@
|
|||||||
<string name="library_group_updates_global">總是載入全域更新</string>
|
<string name="library_group_updates_global">總是載入全域更新</string>
|
||||||
<string name="library_group_updates_all_but_ungrouped">僅為未分組的作品載入全域更新,為其他載入目錄更新</string>
|
<string name="library_group_updates_all_but_ungrouped">僅為未分組的作品載入全域更新,為其他載入目錄更新</string>
|
||||||
<string name="library_group_updates_all">每次啟動後載入目錄更新</string>
|
<string name="library_group_updates_all">每次啟動後載入目錄更新</string>
|
||||||
|
<string name="pref_mark_read_dupe_chapters">將重複章節標記為已讀</string>
|
||||||
|
<string name="pref_mark_read_dupe_chapters_summary">閱讀後將重複章節標記為已讀</string>
|
||||||
|
<string name="pref_library_mark_duplicate_chapters">標記新的重複章節為已讀</string>
|
||||||
|
<string name="pref_library_mark_duplicate_chapters_summary">自動將讀過的新章節標記為已讀</string>
|
||||||
|
|
||||||
<!-- Browse settings -->
|
<!-- Browse settings -->
|
||||||
<string name="pref_hide_feed">隱藏訂閱標籤</string>
|
<string name="pref_hide_feed">隱藏訂閱標籤</string>
|
||||||
@@ -311,9 +315,12 @@
|
|||||||
<string name="pref_center_margin">中間空白類型</string>
|
<string name="pref_center_margin">中間空白類型</string>
|
||||||
<string name="pref_center_margin_summary">插入空白以適應可摺疊裝置的螢幕死角。</string>
|
<string name="pref_center_margin_summary">插入空白以適應可摺疊裝置的螢幕死角。</string>
|
||||||
|
|
||||||
<!-- Cache archived manga to disk -->
|
<!-- Archive reader mode -->
|
||||||
<string name="cache_archived_manga_to_disk">快取 CBZ/CBR 檔案中的圖片</string>
|
<string name="archive_mode_load_from_file">從文件加載</string>
|
||||||
<string name="cache_archived_manga_to_disk_subtitle">閱讀時臨時複製壓縮包中的圖片到快取\n可以改善閱讀器的性能</string>
|
<string name="archive_mode_load_into_memory">加載到記憶體中</string>
|
||||||
|
<string name="archive_mode_cache_to_disk">複製到磁碟</string>
|
||||||
|
<string name="pref_archive_reader_mode">檔案讀取模式</string>
|
||||||
|
<string name="pref_archive_reader_mode_summary">加載壓縮文件(如CBZ或CBR)中圖像的方式</string>
|
||||||
|
|
||||||
<!-- Entry Page -->
|
<!-- Entry Page -->
|
||||||
<!-- Entry Info -->
|
<!-- Entry Info -->
|
||||||
|
|||||||
@@ -591,7 +591,6 @@
|
|||||||
<string name="sync_on_chapter_open">Sync on Chapter Open</string>
|
<string name="sync_on_chapter_open">Sync on Chapter Open</string>
|
||||||
<string name="sync_on_app_start">Sync on App Start</string>
|
<string name="sync_on_app_start">Sync on App Start</string>
|
||||||
<string name="sync_on_app_resume">Sync on App Resume</string>
|
<string name="sync_on_app_resume">Sync on App Resume</string>
|
||||||
<string name="sync_on_library_update">Sync on Library Update</string>
|
|
||||||
<string name="sync_library">Sync library</string>
|
<string name="sync_library">Sync library</string>
|
||||||
|
|
||||||
<!-- Advanced section -->
|
<!-- Advanced section -->
|
||||||
|
|||||||
Reference in New Issue
Block a user