Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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(
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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