Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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:
|
||||
- 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
|
||||
|
||||
|
||||
### 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(),
|
||||
syncOnAppStart = preferenceStore.getBoolean("sync_on_app_start", 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)
|
||||
preferenceStore.getBoolean("sync_on_app_resume", false)
|
||||
.set(syncTriggerOptions.syncOnAppResume)
|
||||
preferenceStore.getBoolean("sync_on_library_update", false)
|
||||
.set(syncTriggerOptions.syncOnLibraryUpdate)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,6 +239,7 @@ fun LibraryBottomActionMenu(
|
||||
onClickCleanTitles: (() -> Unit)?,
|
||||
onClickMigrate: (() -> Unit)?,
|
||||
onClickAddToMangaDex: (() -> Unit)?,
|
||||
onClickResetInfo: (() -> Unit)?,
|
||||
// SY <--
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
@@ -267,7 +268,7 @@ fun LibraryBottomActionMenu(
|
||||
}
|
||||
}
|
||||
// SY -->
|
||||
val showOverflow = onClickCleanTitles != null || onClickAddToMangaDex != null
|
||||
val showOverflow = onClickCleanTitles != null || onClickAddToMangaDex != null || onClickResetInfo != null
|
||||
val configuration = LocalConfiguration.current
|
||||
val moveMarkPrev = remember { !configuration.isTabletUi() }
|
||||
var overFlowOpen by remember { mutableStateOf(false) }
|
||||
@@ -364,6 +365,12 @@ fun LibraryBottomActionMenu(
|
||||
onClick = onClickAddToMangaDex,
|
||||
)
|
||||
}
|
||||
if (onClickResetInfo != null) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(text = stringResource(SYMR.strings.reset_info)) },
|
||||
onClick = onClickResetInfo,
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Button(
|
||||
|
||||
+5
-4
@@ -313,7 +313,7 @@ class MangaRestorer(
|
||||
restoreCategories(manga, categories, backupCategories)
|
||||
restoreChapters(manga, chapters)
|
||||
restoreTracking(manga, tracks)
|
||||
restoreHistory(history)
|
||||
restoreHistory(manga, history)
|
||||
restoreExcludedScanlators(manga, excludedScanlators)
|
||||
updateManga.awaitUpdateFetchInterval(manga, now, currentFetchWindow)
|
||||
// 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 dbHistory = handler.awaitOneOrNull { historyQueries.getHistoryByChapterUrl(history.url) }
|
||||
val dbHistory = handler.awaitOneOrNull { historyQueries.getHistoryByChapterUrl(manga.id, history.url) }
|
||||
val item = history.getHistoryImpl()
|
||||
|
||||
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) {
|
||||
// Chapter doesn't exist; skip
|
||||
null
|
||||
|
||||
@@ -817,19 +817,11 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||
KEY_GROUP_EXTRA to groupExtra,
|
||||
// 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()
|
||||
|
||||
// Only proceed with SyncDataJob if sync is enabled and the specific sync on library update flag is set
|
||||
val syncTriggerOpt = syncPreferences.getSyncTriggerOptions()
|
||||
if (syncPreferences.isSyncEnabled() && syncTriggerOpt.syncOnLibraryUpdate
|
||||
) {
|
||||
// Always sync the data before library update if syncing is enabled.
|
||||
if (syncPreferences.isSyncEnabled()) {
|
||||
// Check if SyncDataJob is already running
|
||||
if (wm.isRunning(SyncDataJob.TAG_MANUAL)) {
|
||||
// SyncDataJob is already running
|
||||
@@ -842,7 +834,6 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||
.build()
|
||||
|
||||
// Chain SyncDataJob to run before LibraryUpdateJob
|
||||
val inputData = workDataOf(KEY_CATEGORY to category?.id)
|
||||
val libraryUpdateJob = OneTimeWorkRequestBuilder<LibraryUpdateJob>()
|
||||
.addTag(TAG)
|
||||
.addTag(WORK_NAME_MANUAL)
|
||||
@@ -853,7 +844,6 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||
.then(libraryUpdateJob)
|
||||
.enqueue()
|
||||
} else {
|
||||
val inputData = workDataOf(KEY_CATEGORY to category?.id)
|
||||
val request = OneTimeWorkRequestBuilder<LibraryUpdateJob>()
|
||||
.addTag(TAG)
|
||||
.addTag(WORK_NAME_MANUAL)
|
||||
|
||||
@@ -9,21 +9,18 @@ data class SyncTriggerOptions(
|
||||
val syncOnChapterOpen: Boolean = false,
|
||||
val syncOnAppStart: Boolean = false,
|
||||
val syncOnAppResume: Boolean = false,
|
||||
val syncOnLibraryUpdate: Boolean = false,
|
||||
) {
|
||||
fun asBooleanArray() = booleanArrayOf(
|
||||
syncOnChapterRead,
|
||||
syncOnChapterOpen,
|
||||
syncOnAppStart,
|
||||
syncOnAppResume,
|
||||
syncOnLibraryUpdate,
|
||||
)
|
||||
|
||||
fun anyEnabled() = syncOnChapterRead ||
|
||||
syncOnChapterOpen ||
|
||||
syncOnAppStart ||
|
||||
syncOnAppResume ||
|
||||
syncOnLibraryUpdate
|
||||
syncOnAppResume
|
||||
|
||||
companion object {
|
||||
val mainOptions = persistentListOf(
|
||||
@@ -47,11 +44,6 @@ data class SyncTriggerOptions(
|
||||
getter = SyncTriggerOptions::syncOnAppResume,
|
||||
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(
|
||||
@@ -59,7 +51,6 @@ data class SyncTriggerOptions(
|
||||
syncOnChapterOpen = array[1],
|
||||
syncOnAppStart = array[2],
|
||||
syncOnAppResume = array[3],
|
||||
syncOnLibraryUpdate = array[4],
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -750,6 +750,24 @@ class LibraryScreenModel(
|
||||
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 <--
|
||||
|
||||
/**
|
||||
@@ -1336,6 +1354,18 @@ class LibraryScreenModel(
|
||||
val showAddToMangadex: Boolean by lazy {
|
||||
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 <--
|
||||
|
||||
fun getLibraryItemsByCategoryId(categoryId: Long): List<LibraryItem>? {
|
||||
|
||||
@@ -205,6 +205,7 @@ object LibraryTab : Tab {
|
||||
}
|
||||
},
|
||||
onClickAddToMangaDex = screenModel::syncMangaToDex.takeIf { state.showAddToMangadex },
|
||||
onClickResetInfo = screenModel::resetInfo.takeIf { state.showResetInfo },
|
||||
// SY <--
|
||||
)
|
||||
},
|
||||
|
||||
@@ -201,6 +201,7 @@ private fun onViewCreated(manga: Manga, context: Context, binding: EditMangaDial
|
||||
binding.mangaGenresTags.clearFocus()
|
||||
|
||||
binding.resetTags.setOnClickListener { resetTags(manga, binding, scope) }
|
||||
binding.resetInfo.setOnClickListener { resetInfo(manga, binding, scope) }
|
||||
}
|
||||
|
||||
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) {
|
||||
removeAllViews()
|
||||
|
||||
|
||||
@@ -139,6 +139,16 @@
|
||||
android:text="@string/reset_tags"
|
||||
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
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
|
||||
@@ -1727,9 +1727,6 @@
|
||||
<ID>SwallowedException:GalleryAdder.kt$GalleryAdder$e: Exception</ID>
|
||||
<ID>SwallowedException:GetChapterByUrlAndMangaId.kt$GetChapterByUrlAndMangaId$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:ImageUtil.kt$ImageUtil$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:StorageStep.kt$StorageStep$e: ActivityNotFoundException</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:UrlImportableSource.kt$UrlImportableSource$e: URISyntaxException</ID>
|
||||
<ID>ThrowingExceptionsWithoutMessageOrCause:MangaScreenModel.kt$MangaScreenModel$IllegalStateException()</ID>
|
||||
@@ -1966,12 +1962,12 @@
|
||||
<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: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_deleting_google_drive_lock_file))</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_deleting_google_drive_lock_file), e)</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(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:KitsuApi.kt$KitsuApi$throw Exception("Could not find manga")</ID>
|
||||
<ID>TooGenericExceptionThrown:KitsuInterceptor.kt$KitsuInterceptor$throw Exception("Not authenticated with Kitsu")</ID>
|
||||
|
||||
@@ -31,7 +31,7 @@ H.time_read
|
||||
FROM history H
|
||||
JOIN chapters C
|
||||
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:
|
||||
UPDATE history
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
[versions]
|
||||
|
||||
[libraries]
|
||||
firebase-analytics = "com.google.firebase:firebase-analytics-ktx:21.2.2"
|
||||
firebase-crashlytics-ktx = "com.google.firebase:firebase-crashlytics-ktx:18.3.6"
|
||||
firebase-crashlytics-gradle = "com.google.firebase:firebase-crashlytics-gradle:2.9.5"
|
||||
firebase-analytics = "com.google.firebase:firebase-analytics-ktx:21.5.1"
|
||||
firebase-crashlytics-ktx = "com.google.firebase:firebase-crashlytics-ktx:18.6.2"
|
||||
firebase-crashlytics-gradle = "com.google.firebase:firebase-crashlytics-gradle:2.9.9"
|
||||
|
||||
simularity = "info.debatty:java-string-similarity:2.0.0"
|
||||
xlog = "com.elvishew:xlog:1.11.0"
|
||||
|
||||
@@ -350,6 +350,7 @@
|
||||
<!-- Entry Info Edit -->
|
||||
<string name="reset_tags">Reset Tags</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="description_hint">Description: %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_all_but_ungrouped">僅為未分組的作品載入全域更新,為其他載入目錄更新</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 -->
|
||||
<string name="pref_hide_feed">隱藏訂閱標籤</string>
|
||||
@@ -311,9 +315,12 @@
|
||||
<string name="pref_center_margin">中間空白類型</string>
|
||||
<string name="pref_center_margin_summary">插入空白以適應可摺疊裝置的螢幕死角。</string>
|
||||
|
||||
<!-- Cache archived manga to disk -->
|
||||
<string name="cache_archived_manga_to_disk">快取 CBZ/CBR 檔案中的圖片</string>
|
||||
<string name="cache_archived_manga_to_disk_subtitle">閱讀時臨時複製壓縮包中的圖片到快取\n可以改善閱讀器的性能</string>
|
||||
<!-- Archive reader mode -->
|
||||
<string name="archive_mode_load_from_file">從文件加載</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 Info -->
|
||||
|
||||
@@ -591,7 +591,6 @@
|
||||
<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_resume">Sync on App Resume</string>
|
||||
<string name="sync_on_library_update">Sync on Library Update</string>
|
||||
<string name="sync_library">Sync library</string>
|
||||
|
||||
<!-- Advanced section -->
|
||||
|
||||
Reference in New Issue
Block a user