From 8d0bfcd55e5c0c52ff4cc2bc513868128bc6f89a Mon Sep 17 00:00:00 2001 From: arkon Date: Sat, 23 Apr 2022 10:52:34 -0400 Subject: [PATCH 01/61] Move clear webview data action to network group (cherry picked from commit bf0bb5aa88f91b0567cf9de085a35d660e7e41a1) (cherry picked from commit 93b7881505de293bc676f20f5894107c3f3b99af) --- .../ui/setting/SettingsAdvancedController.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt index 3d63ed49f..1a6fcca03 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt @@ -152,12 +152,6 @@ class SettingsAdvancedController : SettingsController() { titleRes = R.string.pref_auto_clear_chapter_cache defaultValue = false } - preference { - key = "pref_clear_webview_data" - titleRes = R.string.pref_clear_webview_data - - onClick { clearWebViewData() } - } preference { key = "pref_clear_database" titleRes = R.string.pref_clear_database @@ -181,6 +175,12 @@ class SettingsAdvancedController : SettingsController() { activity?.toast(R.string.cookies_cleared) } } + preference { + key = "pref_clear_webview_data" + titleRes = R.string.pref_clear_webview_data + + onClick { clearWebViewData() } + } intListPreference { key = Keys.dohProvider titleRes = R.string.pref_dns_over_https From 33f4c0ad0864a6bd522925f23f1e8ec0f6d0c1c0 Mon Sep 17 00:00:00 2001 From: arkon Date: Sun, 24 Apr 2022 09:42:26 -0400 Subject: [PATCH 02/61] Delete entire app_webview folder when clearing WebView data (cherry picked from commit 6e95fde4ece64e5959c04bb4b7fb69299ef37ca5) (cherry picked from commit 1d0520e71646beb89ec228608a9ca9c0b78b324c) --- .../kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt index 1a6fcca03..86b042936 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt @@ -66,6 +66,7 @@ import rikka.sui.Sui import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy +import java.io.File import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys class SettingsAdvancedController : SettingsController() { @@ -513,6 +514,7 @@ class SettingsAdvancedController : SettingsController() { webview.clearHistory() webview.clearSslPreferences() WebStorage.getInstance().deleteAllData() + activity?.applicationInfo?.dataDir?.let { File("$it/app_webview/").deleteRecursively() } activity?.toast(R.string.webview_data_deleted) } catch (e: Throwable) { logcat(LogPriority.ERROR, e) From 518f2c1faaaf035a5407a1369d1ea4451bded12b Mon Sep 17 00:00:00 2001 From: ItsLogic <38233332+ItsLogic@users.noreply.github.com> Date: Sun, 24 Apr 2022 20:21:21 +0100 Subject: [PATCH 03/61] Fix chapter transition setting for one page chapters (#6998) (cherry picked from commit 5e32b8e49fc7879559366357743bc450099453ca) # Conflicts: # app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewer.kt (cherry picked from commit 6df5497dc60498abe5ed67b830a7cca78212086c) --- .../tachiyomi/ui/reader/viewer/pager/PagerViewer.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewer.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewer.kt index 4fd826286..54c642e26 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewer.kt @@ -67,9 +67,14 @@ abstract class PagerViewer(val activity: ReaderActivity) : BaseViewer { set(value) { field = value if (value) { - awaitingIdleViewerChapters?.let { - setChaptersDoubleShift(it) + awaitingIdleViewerChapters?.let { viewerChapters -> + setChaptersDoubleShift(viewerChapters) awaitingIdleViewerChapters = null + if (viewerChapters.currChapter.pages?.size == 1) { + adapter.nextTransition?.to?.let { + activity.requestPreloadChapter(it) + } + } } } } From 77ebecd87de488172f0c08264767925b126dc87c Mon Sep 17 00:00:00 2001 From: arkon Date: Sun, 24 Apr 2022 15:25:08 -0400 Subject: [PATCH 04/61] Add battery not low restriction for global updates (closes #6980) (cherry picked from commit 3feea7114614726cd0f5b87729a6c6195d180949) (cherry picked from commit 4804dcf644695406465d39a1b6d4518afe7c5ce0) --- .../eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt | 2 ++ .../eu/kanade/tachiyomi/data/preference/PreferenceValues.kt | 1 + .../tachiyomi/ui/setting/SettingsLibraryController.kt | 6 ++++-- app/src/main/res/values/strings.xml | 1 + 4 files changed, 8 insertions(+), 2 deletions(-) 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 218c82554..be2c51ab2 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt @@ -8,6 +8,7 @@ import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkManager import androidx.work.Worker import androidx.work.WorkerParameters +import eu.kanade.tachiyomi.data.preference.DEVICE_BATTERY_NOT_LOW import eu.kanade.tachiyomi.data.preference.DEVICE_CHARGING import eu.kanade.tachiyomi.data.preference.DEVICE_ONLY_ON_WIFI import eu.kanade.tachiyomi.data.preference.PreferencesHelper @@ -43,6 +44,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet val constraints = Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .setRequiresCharging(DEVICE_CHARGING in restrictions) + .setRequiresBatteryNotLow(DEVICE_BATTERY_NOT_LOW in restrictions) .build() val request = PeriodicWorkRequestBuilder( diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceValues.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceValues.kt index de40f0b4a..cb6c57fa6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceValues.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceValues.kt @@ -4,6 +4,7 @@ import eu.kanade.tachiyomi.R const val DEVICE_ONLY_ON_WIFI = "wifi" const val DEVICE_CHARGING = "ac" +const val DEVICE_BATTERY_NOT_LOW = "battery_not_low" const val MANGA_NON_COMPLETED = "manga_ongoing" const val MANGA_HAS_UNREAD = "manga_fully_read" diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsLibraryController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsLibraryController.kt index 5fe796b03..cbb8e39f1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsLibraryController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsLibraryController.kt @@ -11,6 +11,7 @@ import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.models.Category import eu.kanade.tachiyomi.data.library.LibraryUpdateJob +import eu.kanade.tachiyomi.data.preference.DEVICE_BATTERY_NOT_LOW import eu.kanade.tachiyomi.data.preference.DEVICE_CHARGING import eu.kanade.tachiyomi.data.preference.DEVICE_ONLY_ON_WIFI import eu.kanade.tachiyomi.data.preference.MANGA_HAS_UNREAD @@ -185,8 +186,8 @@ class SettingsLibraryController : SettingsController() { multiSelectListPreference { bindTo(preferences.libraryUpdateDeviceRestriction()) titleRes = R.string.pref_library_update_restriction - entriesRes = arrayOf(R.string.connected_to_wifi, R.string.charging) - entryValues = arrayOf(DEVICE_ONLY_ON_WIFI, DEVICE_CHARGING) + entriesRes = arrayOf(R.string.connected_to_wifi, R.string.charging, R.string.battery_not_low) + entryValues = arrayOf(DEVICE_ONLY_ON_WIFI, DEVICE_CHARGING, DEVICE_BATTERY_NOT_LOW) visibleIf(preferences.libraryUpdateInterval()) { it > 0 } @@ -203,6 +204,7 @@ class SettingsLibraryController : SettingsController() { when (it) { DEVICE_ONLY_ON_WIFI -> context.getString(R.string.connected_to_wifi) DEVICE_CHARGING -> context.getString(R.string.charging) + DEVICE_BATTERY_NOT_LOW -> context.getString(R.string.battery_not_low) else -> it } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9b722c3ab..16d809a09 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -223,6 +223,7 @@ Automatic updates device restrictions Only on Wi-Fi Charging + Battery not low Restrictions: %s Skip updating titles From c721b90dc3127cb0af0384aa50c5cfd8873792d7 Mon Sep 17 00:00:00 2001 From: arkon Date: Sun, 24 Apr 2022 15:32:50 -0400 Subject: [PATCH 05/61] Default to downloading as CBZ (closes #6942) Generally seems fine. People with weak devices may experience some issues, but they can toggle it off/extract the archives separately if needed. (cherry picked from commit 883945e3e8b4c3fe5ec1bb151c247db7404b037c) (cherry picked from commit f22ff7d3f06b722b153ffb02b6e4dde3dc92c897) --- .../eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index 552f31753..9e073abfe 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt @@ -214,7 +214,7 @@ class PreferencesHelper(val context: Context) { fun downloadOnlyOverWifi() = prefs.getBoolean(Keys.downloadOnlyOverWifi, true) - fun saveChaptersAsCBZ() = flowPrefs.getBoolean("save_chapter_as_cbz", false) + fun saveChaptersAsCBZ() = flowPrefs.getBoolean("save_chapter_as_cbz", true) fun folderPerManga() = prefs.getBoolean(Keys.folderPerManga, false) From ff9fbc5265bc2bc91a49b0698af823b6a6c9641a Mon Sep 17 00:00:00 2001 From: arkon Date: Sun, 24 Apr 2022 15:35:05 -0400 Subject: [PATCH 06/61] Fix update warning notifications being cut off (fixes #6983) (cherry picked from commit 20145f7a12c5f14a27ab01f16ceee3cfca77fdab) (cherry picked from commit 92af7291d54e20d98f9d9cc1c8f61f8c7a6ec7b6) --- .../java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt | 2 +- .../eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt index ad11fb52c..5d9f729f6 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt @@ -190,7 +190,7 @@ internal class DownloadNotifier(private val context: Context) { fun onWarning(reason: String, timeout: Long? = null) { with(errorNotificationBuilder) { setContentTitle(context.getString(R.string.download_notifier_downloader_title)) - setContentText(reason) + setStyle(NotificationCompat.BigTextStyle().bigText(reason)) setSmallIcon(R.drawable.ic_warning_white_24dp) setAutoCancel(true) clearActions() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt index 249ad8bd4..ff97a3461 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt @@ -94,7 +94,7 @@ class LibraryUpdateNotifier(private val context: Context) { fun showQueueSizeWarningNotification() { val notificationBuilder = context.notificationBuilder(Notifications.CHANNEL_LIBRARY_PROGRESS) { setContentTitle(context.getString(R.string.label_warning)) - setContentText(context.getString(R.string.notification_size_warning)) + setStyle(NotificationCompat.BigTextStyle().bigText(context.getString(R.string.notification_size_warning))) setSmallIcon(R.drawable.ic_warning_white_24dp) setTimeoutAfter(Downloader.WARNING_NOTIF_TIMEOUT_MS) } From 98d5173507f2ad8064c0785ac5ee7a88124a1f18 Mon Sep 17 00:00:00 2001 From: arkon Date: Sun, 24 Apr 2022 15:49:24 -0400 Subject: [PATCH 07/61] Fix skipped library entries and size warning notifications using same ID (cherry picked from commit 91ed3a4a5facaaa335d4408e7c5342dcca430f47) (cherry picked from commit da739dfc077d98be66ca140c2bb492e576bf300c) --- .../java/eu/kanade/tachiyomi/data/notification/Notifications.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/notification/Notifications.kt b/app/src/main/java/eu/kanade/tachiyomi/data/notification/Notifications.kt index f8a74abcd..1a5ed595e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/notification/Notifications.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/notification/Notifications.kt @@ -30,7 +30,7 @@ object Notifications { const val CHANNEL_LIBRARY_ERROR = "library_errors_channel" const val ID_LIBRARY_ERROR = -102 const val CHANNEL_LIBRARY_SKIPPED = "library_skipped_channel" - const val ID_LIBRARY_SKIPPED = -103 + const val ID_LIBRARY_SKIPPED = -104 /** * Notification channel and ids used by the downloader. From 9da8a09cb4869bddb5cc31efe9b68ca1efad782e Mon Sep 17 00:00:00 2001 From: FourTOne5 <59261191+FourTOne5@users.noreply.github.com> Date: Sun, 24 Apr 2022 13:36:14 -0700 Subject: [PATCH 08/61] Download new chapters when only excluded categories is selected (#6984) (cherry picked from commit 06bec0ad54954e0a0c76949206814747df5a8370) (cherry picked from commit 7ed22e5d9027be54bdd480aa3785adb19c8a8b80) --- .../data/preference/PreferencesHelper.kt | 6 ++--- .../ui/setting/SettingsDownloadController.kt | 22 +++++++++---------- .../kanade/tachiyomi/util/MangaExtensions.kt | 19 +++++++++------- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index 9e073abfe..4088338d2 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt @@ -290,10 +290,10 @@ class PreferencesHelper(val context: Context) { fun pinnedSources() = flowPrefs.getStringSet("pinned_catalogues", emptySet()) - fun downloadNew() = flowPrefs.getBoolean("download_new", false) + fun downloadNewChapter() = flowPrefs.getBoolean("download_new", false) - fun downloadNewCategories() = flowPrefs.getStringSet("download_new_categories", emptySet()) - fun downloadNewCategoriesExclude() = flowPrefs.getStringSet("download_new_categories_exclude", emptySet()) + fun downloadNewChapterCategories() = flowPrefs.getStringSet("download_new_categories", emptySet()) + fun downloadNewChapterCategoriesExclude() = flowPrefs.getStringSet("download_new_categories_exclude", emptySet()) fun defaultCategory() = prefs.getInt(Keys.defaultCategory, -1) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadController.kt index 82c7cc839..74fce340b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadController.kt @@ -125,20 +125,20 @@ class SettingsDownloadController : SettingsController() { titleRes = R.string.pref_category_auto_download switchPreference { - bindTo(preferences.downloadNew()) + bindTo(preferences.downloadNewChapter()) titleRes = R.string.pref_download_new } preference { - bindTo(preferences.downloadNewCategories()) + bindTo(preferences.downloadNewChapterCategories()) titleRes = R.string.categories onClick { DownloadCategoriesDialog().showDialog(router) } - visibleIf(preferences.downloadNew()) { it } + visibleIf(preferences.downloadNewChapter()) { it } fun updateSummary() { - val selectedCategories = preferences.downloadNewCategories().get() + val selectedCategories = preferences.downloadNewChapterCategories().get() .mapNotNull { id -> categories.find { it.id == id.toInt() } } .sortedBy { it.order } val includedItemsText = if (selectedCategories.isEmpty()) { @@ -147,7 +147,7 @@ class SettingsDownloadController : SettingsController() { selectedCategories.joinToString { it.name } } - val excludedCategories = preferences.downloadNewCategoriesExclude().get() + val excludedCategories = preferences.downloadNewChapterCategoriesExclude().get() .mapNotNull { id -> categories.find { it.id == id.toInt() } } .sortedBy { it.order } val excludedItemsText = if (excludedCategories.isEmpty()) { @@ -163,10 +163,10 @@ class SettingsDownloadController : SettingsController() { } } - preferences.downloadNewCategories().asFlow() + preferences.downloadNewChapterCategories().asFlow() .onEach { updateSummary() } .launchIn(viewScope) - preferences.downloadNewCategoriesExclude().asFlow() + preferences.downloadNewChapterCategoriesExclude().asFlow() .onEach { updateSummary() } .launchIn(viewScope) } @@ -254,8 +254,8 @@ class SettingsDownloadController : SettingsController() { var selected = categories .map { when (it.id.toString()) { - in preferences.downloadNewCategories().get() -> QuadStateTextView.State.CHECKED.ordinal - in preferences.downloadNewCategoriesExclude().get() -> QuadStateTextView.State.INVERSED.ordinal + in preferences.downloadNewChapterCategories().get() -> QuadStateTextView.State.CHECKED.ordinal + in preferences.downloadNewChapterCategoriesExclude().get() -> QuadStateTextView.State.INVERSED.ordinal else -> QuadStateTextView.State.UNCHECKED.ordinal } } @@ -282,8 +282,8 @@ class SettingsDownloadController : SettingsController() { .map { categories[it].id.toString() } .toSet() - preferences.downloadNewCategories().set(included) - preferences.downloadNewCategoriesExclude().set(excluded) + preferences.downloadNewChapterCategories().set(included) + preferences.downloadNewChapterCategoriesExclude().set(excluded) } .setNegativeButton(android.R.string.cancel, null) .create() 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 f1a4fb623..adc3b2c7d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/MangaExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/MangaExtensions.kt @@ -56,14 +56,14 @@ fun Manga.shouldDownloadNewChapters(db: DatabaseHelper, prefs: PreferencesHelper if (!favorite) return false // Boolean to determine if user wants to automatically download new chapters. - val downloadNew = prefs.downloadNew().get() - if (!downloadNew) return false + val downloadNewChapter = prefs.downloadNewChapter().get() + if (!downloadNewChapter) return false - val categoriesToDownload = prefs.downloadNewCategories().get().map(String::toInt) - val categoriesToExclude = prefs.downloadNewCategoriesExclude().get().map(String::toInt) + val includedCategories = prefs.downloadNewChapterCategories().get().map { it.toInt() } + val excludedCategories = prefs.downloadNewChapterCategoriesExclude().get().map { it.toInt() } - // Default: download from all categories - if (categoriesToDownload.isEmpty() && categoriesToExclude.isEmpty()) return true + // Default: Download from all categories + if (includedCategories.isEmpty() && excludedCategories.isEmpty()) return true // Get all categories, else default category (0) val categoriesForManga = @@ -72,8 +72,11 @@ fun Manga.shouldDownloadNewChapters(db: DatabaseHelper, prefs: PreferencesHelper .takeUnless { it.isEmpty() } ?: listOf(0) // In excluded category - if (categoriesForManga.intersect(categoriesToExclude).isNotEmpty()) return false + if (categoriesForManga.any { it in excludedCategories }) return false + + // Included category not selected + if (includedCategories.isEmpty()) return true // In included category - return categoriesForManga.intersect(categoriesToDownload).isNotEmpty() + return categoriesForManga.any { it in includedCategories } } From 5191d7abb19bc5f42dc0bf4e28f1235c7877e895 Mon Sep 17 00:00:00 2001 From: arkon Date: Wed, 27 Apr 2022 22:45:31 -0400 Subject: [PATCH 09/61] Add links to website FAQ for library update and download warning notifications (cherry picked from commit 70698e64940eb01032e948245a01c4191ccd60f0) (cherry picked from commit b846bc2044c0ea2f7f8a7c2186e875744ecf579c) --- .../eu/kanade/tachiyomi/data/download/DownloadNotifier.kt | 4 +++- .../java/eu/kanade/tachiyomi/data/download/Downloader.kt | 3 +++ .../kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt | 5 +++++ app/src/main/res/values/strings.xml | 4 ++-- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt index 5d9f729f6..9c5f37699 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt @@ -1,5 +1,6 @@ package eu.kanade.tachiyomi.data.download +import android.app.PendingIntent import android.content.Context import android.graphics.BitmapFactory import androidx.core.app.NotificationCompat @@ -187,7 +188,7 @@ internal class DownloadNotifier(private val context: Context) { * @param timeout duration after which to automatically dismiss the notification. * Only works on Android 8+. */ - fun onWarning(reason: String, timeout: Long? = null) { + fun onWarning(reason: String, timeout: Long? = null, contentIntent: PendingIntent? = null) { with(errorNotificationBuilder) { setContentTitle(context.getString(R.string.download_notifier_downloader_title)) setStyle(NotificationCompat.BigTextStyle().bigText(reason)) @@ -197,6 +198,7 @@ internal class DownloadNotifier(private val context: Context) { setContentIntent(NotificationHandler.openDownloadManagerPendingActivity(context)) setProgress(0, 0, false) timeout?.let { setTimeoutAfter(it) } + contentIntent?.let { setContentIntent(it) } show(Notifications.ID_DOWNLOAD_CHAPTER_ERROR) } 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 548e942e1..104ccbe4d 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt @@ -11,6 +11,8 @@ import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.download.model.DownloadQueue +import eu.kanade.tachiyomi.data.library.LibraryUpdateNotifier +import eu.kanade.tachiyomi.data.notification.NotificationHandler import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.UnmeteredSource @@ -287,6 +289,7 @@ class Downloader( notifier.onWarning( context.getString(R.string.download_queue_size_warning), WARNING_NOTIF_TIMEOUT_MS, + NotificationHandler.openUrl(context, LibraryUpdateNotifier.HELP_WARNING_URL), ) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt index ff97a3461..449b3f3b0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt @@ -97,6 +97,7 @@ class LibraryUpdateNotifier(private val context: Context) { setStyle(NotificationCompat.BigTextStyle().bigText(context.getString(R.string.notification_size_warning))) setSmallIcon(R.drawable.ic_warning_white_24dp) setTimeoutAfter(Downloader.WARNING_NOTIF_TIMEOUT_MS) + setContentIntent(NotificationHandler.openUrl(context, HELP_WARNING_URL)) } context.notificationManager.notify( @@ -341,6 +342,10 @@ class LibraryUpdateNotifier(private val context: Context) { } return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT) } + + companion object { + const val HELP_WARNING_URL = "https://tachiyomi.org/help/faq/#why-does-the-app-warn-about-large-bulk-updates-and-downloads" + } } private const val NOTIF_MAX_CHAPTERS = 5 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 16d809a09..06ee4d695 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -721,12 +721,12 @@ Couldn\'t download chapters. You can try again in the downloads section Couldn\'t download chapters due to low storage space - Warning: large bulk downloads may lead to sources becoming slower and/or blocking Tachiyomi + Warning: large bulk downloads may lead to sources becoming slower and/or blocking Tachiyomi. Tap to learn more. Checking for new chapters Updating library… (%1$d/%2$d) - Large updates harm sources and may lead to slower updates and also increased battery usage + Large updates harm sources and may lead to slower updates and also increased battery usage. Tap to learn more. New chapters found For %d title From c48f4770eeb390af810f9f5555ffcaddd07ecd35 Mon Sep 17 00:00:00 2001 From: arkon Date: Thu, 28 Apr 2022 18:09:16 -0400 Subject: [PATCH 10/61] Fix Android 13 icon sizing (cherry picked from commit 3a4f107ab7d811071faf0ed6d55a5a3f7e86aab9) # Conflicts: # app/build.gradle.kts --- .../drawable/ic_tachi_monochrome_launcher.xml | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/app/src/main/res/drawable/ic_tachi_monochrome_launcher.xml b/app/src/main/res/drawable/ic_tachi_monochrome_launcher.xml index 5995f044e..732b8e963 100644 --- a/app/src/main/res/drawable/ic_tachi_monochrome_launcher.xml +++ b/app/src/main/res/drawable/ic_tachi_monochrome_launcher.xml @@ -1,8 +1,24 @@ - - - - + + + + + + + + From b8e0b86df88cce502f228fd9b5d0a512454943c6 Mon Sep 17 00:00:00 2001 From: FourTOne5 <59261191+FourTOne5@users.noreply.github.com> Date: Sat, 7 May 2022 08:00:24 +0600 Subject: [PATCH 11/61] Add `-r` flag to ShizukuInstaller `createCommand` (#7080) (cherry picked from commit 3865384cccfd4fc2a9a458f01dafada808dd35d7) --- .../kanade/tachiyomi/extension/installer/ShizukuInstaller.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/installer/ShizukuInstaller.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/installer/ShizukuInstaller.kt index 1fa4b230b..2c3f617e0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/installer/ShizukuInstaller.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/installer/ShizukuInstaller.kt @@ -52,9 +52,9 @@ class ShizukuInstaller(private val service: Service) : Installer(service) { val size = service.getUriSize(entry.uri) ?: throw IllegalStateException() service.contentResolver.openInputStream(entry.uri)!!.use { val createCommand = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - "pm install-create --user current -i ${service.packageName} -S $size" + "pm install-create --user current -r -i ${service.packageName} -S $size" } else { - "pm install-create -i ${service.packageName} -S $size" + "pm install-create -r -i ${service.packageName} -S $size" } val createResult = exec(createCommand) sessionId = SESSION_ID_REGEX.find(createResult.out)?.value From b456e38cc5f41fc1265d23210a52187dafe3e267 Mon Sep 17 00:00:00 2001 From: FourTOne5 <59261191+FourTOne5@users.noreply.github.com> Date: Sat, 7 May 2022 08:15:44 +0600 Subject: [PATCH 12/61] Fix removing manga from library reverts during global update (#7063) * Fix removing manga from library reverts during global update * Review Changes * Review changes 2 # Conflicts: # app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt (cherry picked from commit f966940d1544cef96cc2ba6c6d884cacfa55e085) --- .../data/library/LibraryUpdateService.kt | 74 +++++++++++-------- .../util/chapter/ChapterSourceSync.kt | 2 +- 2 files changed, 46 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt index ee0111a9f..08cc56970 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt @@ -207,6 +207,8 @@ class LibraryUpdateService( */ override fun onDestroy() { updateJob?.cancel() + // Despite what Android Studio + // states this can be null ioScope?.cancel() if (wakeLock.isHeld) { wakeLock.release() @@ -272,8 +274,7 @@ class LibraryUpdateService( /** * Adds list of manga to be updated. * - * @param category the ID of the category to update, or -1 if no category specified. - * @param target the target to update. + * @param categoryId the ID of the category to update, or -1 if no category specified. */ fun addMangaToQueue(categoryId: Int, group: Int, groupExtra: String?) { val libraryManga = db.getLibraryMangas().executeAsBlocking() @@ -357,12 +358,11 @@ class LibraryUpdateService( } /** - * Method that updates the given list of manga. It's called in a background thread, so it's safe + * Method that updates manga in [mangaToUpdate]. It's called in a background thread, so it's safe * to do heavy operations or network calls here. * For each manga it calls [updateManga] and updates the notification showing the current * progress. * - * @param mangaToUpdate the list to update * @return an observable delivering the progress of each update. */ suspend fun updateChapterList() { @@ -389,35 +389,38 @@ class LibraryUpdateService( return@async } + // Don't continue to update if manga not in library + db.getManga(manga.id!!).executeAsBlocking() ?: return@forEach + withUpdateNotification( currentlyUpdatingManga, progressCount, manga, - ) { manga -> + ) { mangaWithNotif -> try { when { - MANGA_NON_COMPLETED in restrictions && manga.status == SManga.COMPLETED -> { - skippedUpdates.add(manga to getString(R.string.skipped_reason_completed)) - } - MANGA_HAS_UNREAD in restrictions && manga.unreadCount != 0 -> { - skippedUpdates.add(manga to getString(R.string.skipped_reason_not_caught_up)) - } - MANGA_NON_READ in restrictions && manga.totalChapters > 0 && !manga.hasStarted -> { - skippedUpdates.add(manga to getString(R.string.skipped_reason_not_started)) - } + MANGA_NON_COMPLETED in restrictions && mangaWithNotif.status == SManga.COMPLETED -> + skippedUpdates.add(mangaWithNotif to getString(R.string.skipped_reason_completed)) + + MANGA_HAS_UNREAD in restrictions && mangaWithNotif.unreadCount != 0 -> + skippedUpdates.add(mangaWithNotif to getString(R.string.skipped_reason_not_caught_up)) + + MANGA_NON_READ in restrictions && mangaWithNotif.totalChapters > 0 && !mangaWithNotif.hasStarted -> + skippedUpdates.add(mangaWithNotif to getString(R.string.skipped_reason_not_started)) + else -> { // Convert to the manga that contains new chapters - val (newChapters, _) = updateManga(manga, loggedServices) + val (newChapters, _) = updateManga(mangaWithNotif, loggedServices) if (newChapters.isNotEmpty()) { - if (manga.shouldDownloadNewChapters(db, preferences)) { - downloadChapters(manga, newChapters) + if (mangaWithNotif.shouldDownloadNewChapters(db, preferences)) { + downloadChapters(mangaWithNotif, newChapters) hasDownloads.set(true) } // Convert to the manga that contains new chapters newUpdates.add( - manga to newChapters.sortedByDescending { ch -> ch.source_order } + mangaWithNotif to newChapters.sortedByDescending { ch -> ch.source_order } .toTypedArray(), ) } @@ -436,11 +439,11 @@ class LibraryUpdateService( e.message } } - failedUpdates.add(manga to errorMessage) + failedUpdates.add(mangaWithNotif to errorMessage) } if (preferences.autoUpdateTrackers()) { - updateTrackings(manga, loggedServices) + updateTrackings(mangaWithNotif, loggedServices) } } } @@ -495,6 +498,7 @@ class LibraryUpdateService( suspend fun updateManga(manga: Manga, loggedServices: List): Pair, List> { val source = sourceManager.getOrStub(manga.source).getMainSource() + var networkSManga: SManga? = null // Update manga details metadata if (preferences.autoUpdateMetadata()) { val updatedManga = source.getMangaDetails(manga.toMangaInfo()) @@ -506,8 +510,7 @@ class LibraryUpdateService( sManga.thumbnail_url = manga.thumbnail_url } - manga.copyFrom(sManga) - db.insertManga(manga).executeAsBlocking() + networkSManga = sManga } // SY --> @@ -532,7 +535,20 @@ class LibraryUpdateService( val chapters = source.getChapterList(manga.toMangaInfo()) .map { it.toSChapter() } - return syncChaptersWithSource(db, chapters, manga, source) + // Get manga from database to account for if it was removed + // from library or database + val dbManga = db.getManga(manga.id!!).executeAsBlocking() + ?: return Pair(emptyList(), emptyList()) + + // Copy into [dbManga] to retain favourite value + networkSManga?.let { + dbManga.copyFrom(it) + db.insertManga(dbManga).executeAsBlocking() + } + + // [dbmanga] was used so that manga data doesn't get overwritten + // incase manga gets new chapter + return syncChaptersWithSource(db, chapters, dbManga, source) } private suspend fun updateCovers() { @@ -555,16 +571,16 @@ class LibraryUpdateService( currentlyUpdatingManga, progressCount, manga, - ) { manga -> - sourceManager.get(manga.source)?.let { source -> + ) { mangaWithNotif -> + sourceManager.get(mangaWithNotif.source)?.let { source -> try { val networkManga = - source.getMangaDetails(manga.toMangaInfo()) + source.getMangaDetails(mangaWithNotif.toMangaInfo()) val sManga = networkManga.toSManga() - manga.prepUpdateCover(coverCache, sManga, true) + mangaWithNotif.prepUpdateCover(coverCache, sManga, true) sManga.thumbnail_url?.let { - manga.thumbnail_url = it - db.insertManga(manga).executeAsBlocking() + mangaWithNotif.thumbnail_url = it + db.insertManga(mangaWithNotif).executeAsBlocking() } } catch (e: Throwable) { // Ignore errors and continue diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterSourceSync.kt b/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterSourceSync.kt index abd0199f5..1001d55d7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterSourceSync.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterSourceSync.kt @@ -26,7 +26,7 @@ fun syncChaptersWithSource( db: DatabaseHelper, rawSourceChapters: List, manga: Manga, - source: Source, + source: Source ): Pair, List> { if (rawSourceChapters.isEmpty()) { throw NoChaptersException() From dfa9b7462f8acddfa19248a37c96c82bf2495944 Mon Sep 17 00:00:00 2001 From: arkon Date: Mon, 9 May 2022 08:45:26 -0400 Subject: [PATCH 13/61] Rename "navigation layout" to "tap zones" (cherry picked from commit c49d862fc58fb4a750ee6de006054823f94e3263) (cherry picked from commit ec9d55e9e8ec1cd6d1f7f639faff66c8c3ecf93a) --- app/src/main/res/values/strings.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 06ee4d695..f3b59c3c8 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -284,8 +284,8 @@ Fullscreen - Show navigation layout overlay - Show tap zones when reader is opened + Show tap zones overlay + Briefly show when reader is opened Dual page split Invert dual page split placement If the placement of the dual page split doesn\'t match reading direction @@ -296,7 +296,7 @@ Show reading mode Briefly show current mode when reader is opened 32-bit color - Reduces banding, but impacts performance + Reduces banding, but may impact performance Crop borders On Off @@ -316,7 +316,7 @@ Navigation Volume keys Invert volume keys - Invert tapping + Invert tap zones None Horizontal Vertical @@ -346,7 +346,7 @@ Webtoon Continuous vertical Paged - Navigation layout + Tap zones Scale type Fit screen Stretch From 4cee1b35835a63db9ad94fa76425233dcd22dbf0 Mon Sep 17 00:00:00 2001 From: nicki <72807749+curche@users.noreply.github.com> Date: Mon, 9 May 2022 20:33:40 +0530 Subject: [PATCH 14/61] Don't save categories in backup if not selected (#7101) Currently, manually created backups contain list of categories even if Categories option is not selected during Backup Prompt. This leads to empty categories being created when restoring such backup files This commit adds a check before saving categories list info to the backup file. The check is the same check which is used while backing up category info of manga in library Tested and worked successfully on app installed on Android 12 (cherry picked from commit 11c01235ac32c8fd3de864c37cab82367b4a9e41) (cherry picked from commit 1269d71d1a608515c4243226bc2bbd53f6cab8dd) --- .../data/backup/full/FullBackupManager.kt | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupManager.kt index 05342a3b5..894699a45 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupManager.kt @@ -75,7 +75,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { backup = Backup( backupManga(databaseManga, flags), - backupCategories(), + backupCategories(flags), emptyList(), backupExtensionInfo(databaseManga), backupSavedSearches(), @@ -154,10 +154,15 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { * * @return list of [BackupCategory] to be backed up */ - private fun backupCategories(): List { - return databaseHelper.getCategories() - .executeAsBlocking() - .map { BackupCategory.copyFrom(it) } + private fun backupCategories(options: Int): List { + // Check if user wants category information in backup + return if (options and BACKUP_CATEGORY_MASK == BACKUP_CATEGORY) { + databaseHelper.getCategories() + .executeAsBlocking() + .map { BackupCategory.copyFrom(it) } + } else { + emptyList() + } } // SY --> From e7cd7c06fa97b48f61590ba17f192d805c01eb25 Mon Sep 17 00:00:00 2001 From: CVIUS <84634607+CVIUS@users.noreply.github.com> Date: Tue, 10 May 2022 21:02:46 +0800 Subject: [PATCH 15/61] Use theme primary color for slider track (#7102) (cherry picked from commit bc053580ad21669cf507dea1aa14a8dd5cb59ef6) (cherry picked from commit ea9ea11eaf8a3e0613c101b0d9324ea6700a234e) --- app/src/main/res/color/slider_active_track.xml | 5 +++++ app/src/main/res/color/slider_inactive_track.xml | 5 +++++ app/src/main/res/values/styles.xml | 2 ++ 3 files changed, 12 insertions(+) create mode 100644 app/src/main/res/color/slider_active_track.xml create mode 100644 app/src/main/res/color/slider_inactive_track.xml diff --git a/app/src/main/res/color/slider_active_track.xml b/app/src/main/res/color/slider_active_track.xml new file mode 100644 index 000000000..764d21bf3 --- /dev/null +++ b/app/src/main/res/color/slider_active_track.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/color/slider_inactive_track.xml b/app/src/main/res/color/slider_inactive_track.xml new file mode 100644 index 000000000..0f624c117 --- /dev/null +++ b/app/src/main/res/color/slider_inactive_track.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index dbf73d1a1..7130d28c4 100755 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -192,6 +192,8 @@ From a7979b83235d3513180b3632bfe50e6ebb0cc80c Mon Sep 17 00:00:00 2001 From: nicki <72807749+curche@users.noreply.github.com> Date: Wed, 11 May 2022 02:34:40 +0530 Subject: [PATCH 16/61] Check for app updates by comparing semver (#7100) Instead of just checking whether the current app version *matches* with latest app version in GitHub Releases, compare the semver from the tag names to check whether the latter is greater and the app needs an update Reference: semver spec #11 https://semver.org/#spec-item-11 Co-authored-by: Andreas <6576096+ghostbear@users.noreply.github.com> Co-authored-by: Andreas <6576096+ghostbear@users.noreply.github.com> (cherry picked from commit e7ed130f2a4fcd7452737476189687fbd130c80d) (cherry picked from commit 81bdc190755a5a49a6e49a89ef3962f9ecf5c0d0) --- .../tachiyomi/data/updater/AppUpdateChecker.kt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateChecker.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateChecker.kt index 462f73406..0450d8e16 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateChecker.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateChecker.kt @@ -55,12 +55,13 @@ class AppUpdateChecker { } // SY --> - private fun isNewVersionSY(versionTag: String) = (versionTag != BuildConfig.VERSION_NAME && (syDebugVersion == "0")) || ((syDebugVersion != "0") && versionTag != syDebugVersion) + private fun isNewVersionSY(versionTag: String) = (versionTag != BuildConfig.VERSION_NAME && syDebugVersion == "0") || (syDebugVersion != "0" && versionTag != syDebugVersion) // SY <-- private fun isNewVersion(versionTag: String): Boolean { // Removes prefixes like "r" or "v" val newVersion = versionTag.replace("[^\\d.]".toRegex(), "") + val oldVersion = BuildConfig.VERSION_NAME.replace("[^\\d.]".toRegex(), "") return if (BuildConfig.DEBUG) { // Preview builds: based on releases in "tachiyomiorg/tachiyomi-preview" repo @@ -69,7 +70,15 @@ class AppUpdateChecker { } else { // Release builds: based on releases in "tachiyomiorg/tachiyomi" repo // tagged as something like "v0.1.2" - newVersion != BuildConfig.VERSION_NAME + val newSemVer = newVersion.split(".").map { it.toInt() } + val oldSemVer = oldVersion.split(".").map { it.toInt() } + + oldSemVer.mapIndexed { index, i -> + if (newSemVer[index] > i) { + return true + } + } + false } } } From a575770be09cc50625b56d1e13cc574c6f210c0b Mon Sep 17 00:00:00 2001 From: Jobobby04 Date: Sat, 14 May 2022 10:42:36 -0400 Subject: [PATCH 17/61] Update build workflow actions (cherry picked from commit c1c934011f79fd61d691792289d5e7a47986fbca) --- .github/workflows/TachiyomiSY-Release-Builder.yml | 3 ++- .github/workflows/build_check.yml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/TachiyomiSY-Release-Builder.yml b/.github/workflows/TachiyomiSY-Release-Builder.yml index 1ad1fe3ea..cd02bf6e2 100644 --- a/.github/workflows/TachiyomiSY-Release-Builder.yml +++ b/.github/workflows/TachiyomiSY-Release-Builder.yml @@ -32,9 +32,10 @@ jobs: uses: actions/checkout@v2 - name: Set up JDK 11 - uses: actions/setup-java@v1 + uses: actions/setup-java@v3 with: java-version: 11 + distribution: adopt - name: Copy CI gradle.properties run: | diff --git a/.github/workflows/build_check.yml b/.github/workflows/build_check.yml index c60906992..90e9eebb7 100644 --- a/.github/workflows/build_check.yml +++ b/.github/workflows/build_check.yml @@ -28,9 +28,10 @@ jobs: uses: actions/checkout@v2 - name: Set up JDK 11 - uses: actions/setup-java@v1 + uses: actions/setup-java@v3 with: java-version: 11 + distribution: adopt - name: Copy CI gradle.properties run: | From 832107b932f3aae1d40684779e572a37d2eefba7 Mon Sep 17 00:00:00 2001 From: CVIUS <84634607+CVIUS@users.noreply.github.com> Date: Thu, 12 May 2022 10:35:30 +0800 Subject: [PATCH 18/61] Fix "Move to top" showing at the most top item in download queue (#7109) (cherry picked from commit b26daf8824e09922f063db6d6410b78d6281957b) (cherry picked from commit 054e6b839ed4277e66fc5351f46d08652bdfd610) --- .../main/java/eu/kanade/tachiyomi/ui/download/DownloadHolder.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadHolder.kt index 8f4623143..514357d65 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadHolder.kt @@ -89,7 +89,7 @@ class DownloadHolder(private val view: View, val adapter: DownloadAdapter) : view.popupMenu( menuRes = R.menu.download_single, initMenu = { - findItem(R.id.move_to_top).isVisible = bindingAdapterPosition != 0 + findItem(R.id.move_to_top).isVisible = bindingAdapterPosition > 1 findItem(R.id.move_to_bottom).isVisible = bindingAdapterPosition != adapter.itemCount - 1 }, From 960d67ec2662d6acd64bdf0e5b66bf5da43963fc Mon Sep 17 00:00:00 2001 From: nzoba <55888232+nzoba@users.noreply.github.com> Date: Sat, 14 May 2022 03:42:23 +0200 Subject: [PATCH 19/61] Add switch to DownloadPageLoader when chapter is downloaded (#7119) (cherry picked from commit 63627c81ebd6b3a9b1ee017f385a72b6fc8c8a49) (cherry picked from commit f7a57d2ddd1bdc51ec97078617a89b5ecbe2225f) --- .../eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt index d02fdc5ff..abb20001e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt @@ -29,6 +29,7 @@ import eu.kanade.tachiyomi.source.online.all.MergedSource import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.reader.chapter.ReaderChapterItem import eu.kanade.tachiyomi.ui.reader.loader.ChapterLoader +import eu.kanade.tachiyomi.ui.reader.loader.HttpPageLoader import eu.kanade.tachiyomi.ui.reader.model.InsertPage import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter import eu.kanade.tachiyomi.ui.reader.model.ReaderPage @@ -425,6 +426,14 @@ class ReaderPresenter( * that the user doesn't have to wait too long to continue reading. */ private fun preload(chapter: ReaderChapter) { + if (chapter.pageLoader is HttpPageLoader) { + val manga = manga ?: return + val isDownloaded = downloadManager.isChapterDownloaded(chapter.chapter, manga) + if (isDownloaded) { + chapter.state = ReaderChapter.State.Wait + } + } + if (chapter.state != ReaderChapter.State.Wait && chapter.state !is ReaderChapter.State.Error) { return } From 3ec11cb81f635e06c2e780767094889de12c97b2 Mon Sep 17 00:00:00 2001 From: CVIUS <84634607+CVIUS@users.noreply.github.com> Date: Sat, 14 May 2022 20:09:15 +0800 Subject: [PATCH 20/61] Fix category tabs incorrect scroll position (#7120) (cherry picked from commit 6d655ff7574c0c51c41cfc6b3abed57fb2115acc) (cherry picked from commit 58db04d8ddf5d10c00dbf06c27f09d44321f4c09) --- .../eu/kanade/tachiyomi/ui/library/LibraryController.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt index b46c831fb..a09cac8c2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt @@ -10,7 +10,6 @@ import android.view.View import android.view.WindowManager import androidx.appcompat.app.AlertDialog import androidx.appcompat.view.ActionMode -import androidx.core.view.doOnAttach import androidx.core.view.isVisible import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeType @@ -345,8 +344,10 @@ class LibraryController( onTabsSettingsChanged(firstLaunch = true) // Delay the scroll position to allow the view to be properly measured. - view.doOnAttach { - (activity as? MainActivity)?.binding?.tabs?.setScrollPosition(binding.libraryPager.currentItem, 0f, true) + view.post { + if (isAttached) { + (activity as? MainActivity)?.binding?.tabs?.setScrollPosition(binding.libraryPager.currentItem, 0f, true) + } } // Send the manga map to child fragments after the adapter is updated. From 9e31806e5c30e2efb798114cac7238dda54e1eb8 Mon Sep 17 00:00:00 2001 From: CVIUS <84634607+CVIUS@users.noreply.github.com> Date: Sat, 14 May 2022 20:51:04 +0800 Subject: [PATCH 21/61] Save reader progress when activity is paused (#7121) (cherry picked from commit f1ab34e27cbd8f26f87e34238af0863d4650b960) (cherry picked from commit 93226248865ea8d32324189cd5a25afc4e717739) --- .../java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt | 5 +++++ .../java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt index 7ada13307..4fe9f2bbf 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt @@ -324,6 +324,11 @@ class ReaderActivity : BaseRxActivity() { super.onSaveInstanceState(outState) } + override fun onPause() { + presenter.saveProgress() + super.onPause() + } + /** * Set menu visibility again on activity resume to apply immersive mode again if needed. * Helps with rotations. diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt index abb20001e..db7377629 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt @@ -558,6 +558,10 @@ class ReaderPresenter( } } + fun saveProgress() { + getCurrentChapter()?.let { onChapterChanged(it) } + } + /** * Called from the activity to preload the given [chapter]. */ From 153022df0a3f323189bc36270b4d6790b6500d4b Mon Sep 17 00:00:00 2001 From: arkon Date: Sun, 15 May 2022 16:51:52 -0400 Subject: [PATCH 22/61] Use jsDelivr as fallback when GitHub can't be reached for extensions (closes #5517) Re-implementation of 24bb2f02dce135e0ceb2856618ecfc0e30dce875 (cherry picked from commit d61bfd7cafa09ff6c5f159c945984f2e8d9904b9) # Conflicts: # app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionGithubApi.kt (cherry picked from commit 4458f74f6c06fb9a2879bd530a20aaec61c06658) # Conflicts: # app/src/main/java/eu/kanade/presentation/extension/ExtensionScreen.kt --- .../extension/api/ExtensionGithubApi.kt | 51 ++++++++++++++++--- .../tachiyomi/extension/model/Extension.kt | 1 + .../ui/browse/extension/ExtensionHolder.kt | 18 +++---- 3 files changed, 52 insertions(+), 18 deletions(-) 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 d24170239..e7b29e25e 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 @@ -12,7 +12,9 @@ import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.network.parseAs import eu.kanade.tachiyomi.util.lang.withIOContext import exh.source.BlacklistedSources +import eu.kanade.tachiyomi.util.system.logcat import kotlinx.serialization.Serializable +import logcat.LogPriority import uy.kohesive.injekt.injectLazy import java.util.Date import java.util.concurrent.TimeUnit @@ -22,21 +24,38 @@ internal class ExtensionGithubApi { private val networkService: NetworkHelper by injectLazy() private val preferences: PreferencesHelper by injectLazy() + private var requiresFallbackSource = false + suspend fun findExtensions(): List { return withIOContext { - val extensions = networkService.client - .newCall(GET("${REPO_URL_PREFIX}index.min.json")) - .await() + val response = try { + networkService.client + .newCall(GET("${REPO_URL_PREFIX}index.min.json")) + .await() + } catch (e: Throwable) { + logcat(LogPriority.ERROR, e) { "Failed to get extensions from GitHub" } + requiresFallbackSource = true + + networkService.client + .newCall(GET("${FALLBACK_REPO_URL_PREFIX}index.min.json")) + .await() + } + + val extensions = response .parseAs>() .toExtensions() /* SY --> */ + preferences.extensionRepos() .get() .flatMap { repoPath -> - val url = "$BASE_URL$repoPath/repo/" + val url = if (requiresFallbackSource) { + "$FALLBACK_BASE_URL$repoPath@repo/" + } else { + "$BASE_URL$repoPath/repo/" + } networkService.client .newCall(GET("${url}index.min.json")) .await() .parseAs>() - .toExtensions(url) + .toExtensions(url, repoSource = true) } // SY <-- @@ -85,7 +104,12 @@ internal class ExtensionGithubApi { return extensionsWithUpdate } - private fun List.toExtensions(/* SY --> */ repoUrl: String = REPO_URL_PREFIX /* SY <-- */): List { + private fun List.toExtensions( + // SY --> + repoUrl: String = getUrlPrefix(), + repoSource: Boolean = false + // SY <-- + ): List { return this .filter { val libVersion = it.version.substringBeforeLast('.').toDouble() @@ -106,6 +130,7 @@ internal class ExtensionGithubApi { iconUrl = "${/* SY --> */ repoUrl /* SY <-- */}icon/${it.apk.replace(".apk", ".png")}", // SY --> repoUrl = repoUrl, + isRepoSource = repoSource // SY <-- ) } @@ -125,6 +150,14 @@ internal class ExtensionGithubApi { return /* SY --> */ "${extension.repoUrl}/apk/${extension.apkName}" // SY <-- } + private fun getUrlPrefix(): String { + return if (requiresFallbackSource) { + FALLBACK_REPO_URL_PREFIX + } else { + REPO_URL_PREFIX + } + } + // SY --> private fun Extension.isBlacklisted( blacklistEnabled: Boolean = preferences.enableSourceBlacklist().get(), @@ -134,8 +167,10 @@ internal class ExtensionGithubApi { // SY <-- } -const val BASE_URL = "https://raw.githubusercontent.com/" -const val REPO_URL_PREFIX = "${BASE_URL}tachiyomiorg/tachiyomi-extensions/repo/" +private const val BASE_URL = "https://raw.githubusercontent.com/" +private const val REPO_URL_PREFIX = "${BASE_URL}tachiyomiorg/tachiyomi-extensions/repo/" +private const val FALLBACK_BASE_URL = "https://cdn.jsdelivr.net/gh/" +private const val FALLBACK_REPO_URL_PREFIX = "${FALLBACK_BASE_URL}tachiyomiorg/tachiyomi-extensions@repo/" @Serializable private data class ExtensionJsonObject( diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/model/Extension.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/model/Extension.kt index c93bf8987..09830001f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/model/Extension.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/model/Extension.kt @@ -48,6 +48,7 @@ sealed class Extension { val iconUrl: String, // SY --> val repoUrl: String, + val isRepoSource: Boolean, // SY <-- ) : Extension() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionHolder.kt index 33209ecfd..db5eadd01 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionHolder.kt @@ -8,7 +8,6 @@ import coil.load import eu.davidea.viewholders.FlexibleViewHolder import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.databinding.ExtensionItemBinding -import eu.kanade.tachiyomi.extension.api.REPO_URL_PREFIX import eu.kanade.tachiyomi.extension.model.Extension import eu.kanade.tachiyomi.extension.model.InstallStep import eu.kanade.tachiyomi.source.ConfigurableSource @@ -57,15 +56,14 @@ class ExtensionHolder(view: View, val adapter: ExtensionAdapter) : // SY --> private fun String.plusRepo(extension: Extension): String { return if (extension is Extension.Available) { - when (extension.repoUrl) { - REPO_URL_PREFIX -> this - else -> { - if (isEmpty()) { - this - } else { - this + " • " - } + itemView.context.getString(R.string.repo_source) - } + if (!extension.isRepoSource) { + this + } else { + if (isEmpty()) { + this + } else { + "$this • " + } + itemView.context.getString(R.string.repo_source) } } else this } From 9820e1097df5682f29c25251cc9ec32804e97db5 Mon Sep 17 00:00:00 2001 From: kasperskier <95685115+kasperskier@users.noreply.github.com> Date: Wed, 18 May 2022 05:19:17 +0800 Subject: [PATCH 23/61] Change jsDelivr CDN URL to Fastly (#7156) (cherry picked from commit 7b242bf11833ebd6dda34df295dfa7cd45cb88d0) # Conflicts: # app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionGithubApi.kt (cherry picked from commit bbdbaa1de68a9fb4f071727962b97fd5177092fc) --- .../eu/kanade/tachiyomi/extension/api/ExtensionGithubApi.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e7b29e25e..ad9b4d38a 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 @@ -169,7 +169,7 @@ internal class ExtensionGithubApi { private const val BASE_URL = "https://raw.githubusercontent.com/" private const val REPO_URL_PREFIX = "${BASE_URL}tachiyomiorg/tachiyomi-extensions/repo/" -private const val FALLBACK_BASE_URL = "https://cdn.jsdelivr.net/gh/" +private const val FALLBACK_BASE_URL = "https://fastly.jsdelivr.net/gh/" private const val FALLBACK_REPO_URL_PREFIX = "${FALLBACK_BASE_URL}tachiyomiorg/tachiyomi-extensions@repo/" @Serializable From b64a2cf8163971f7247d4dd1b363a4727e210468 Mon Sep 17 00:00:00 2001 From: CVIUS <84634607+CVIUS@users.noreply.github.com> Date: Wed, 18 May 2022 05:20:18 +0800 Subject: [PATCH 24/61] Fix webtoon viewer showing transition view when going to next/prev chapter using next/prev button (#7133) (cherry picked from commit b21bcc2d45859ea86d6042b5df9d7c6f30d259cc) (cherry picked from commit 31ac3aece298da961fd64709291af8a6f317397c) --- .../kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt index 3c9ad7044..34c03b43a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt @@ -252,7 +252,7 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr logcat { "moveToPage" } val position = adapter.items.indexOf(page) if (position != -1) { - recycler.scrollToPosition(position) + layoutManager.scrollToPositionWithOffset(position, 0) if (layoutManager.findLastEndVisibleItemPosition() == -1) { onScrolled(pos = position) } From 1c61d371713f59ea1fc829126ad057e890434d1e Mon Sep 17 00:00:00 2001 From: CVIUS <84634607+CVIUS@users.noreply.github.com> Date: Wed, 18 May 2022 05:20:37 +0800 Subject: [PATCH 25/61] Fix reader menu appearing then disappearing in webtoon viewer when there is no next chapter (#7115) (cherry picked from commit 6580f5771f634b0e2c25f8cd42fa1596b2ea4e1c) (cherry picked from commit c0362faaf8965005a04c5fdbf1692865b6a83be4) --- .../tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt index 34c03b43a..46b4e69b5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt @@ -104,6 +104,12 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr activity.requestPreloadChapter(firstItem.to) } } + + val lastIndex = layoutManager.findLastEndVisibleItemPosition() + val lastItem = adapter.items.getOrNull(lastIndex) + if (lastItem is ChapterTransition.Next && lastItem.to == null) { + activity.showMenu() + } } }, ) @@ -223,9 +229,6 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr if (toChapter != null) { logcat { "Request preload destination chapter because we're on the transition" } activity.requestPreloadChapter(toChapter) - } else if (transition is ChapterTransition.Next) { - // No more chapters, show menu because the user is probably going to close the reader - activity.showMenu() } } From 9464ae04aa4c48becf5c46d183af5dc174f9e50e Mon Sep 17 00:00:00 2001 From: FourTOne5 <59261191+FourTOne5@users.noreply.github.com> Date: Wed, 25 May 2022 04:02:02 +0600 Subject: [PATCH 26/61] Local Source - qol, cleanup and cover related fixes (#7166) * Local Source - qol, cleanup and cover related fixes * Review Changes (cherry picked from commit ad17eb138609d684fd5929c3cb7dc644e3a3ec95) # Conflicts: # app/src/main/java/eu/kanade/tachiyomi/source/LocalSource.kt (cherry picked from commit 6fd79f4838d289ef213cd547c38b2b6770df73b3) --- .../eu/kanade/tachiyomi/source/LocalSource.kt | 330 ++++++++++-------- .../tachiyomi/ui/reader/ReaderPresenter.kt | 23 +- 2 files changed, 206 insertions(+), 147 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/LocalSource.kt b/app/src/main/java/eu/kanade/tachiyomi/source/LocalSource.kt index 58a0753f3..bc580147a 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/source/LocalSource.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/LocalSource.kt @@ -2,7 +2,9 @@ package eu.kanade.tachiyomi.source import android.content.Context import com.github.junrar.Archive +import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.FilterList @@ -18,16 +20,16 @@ import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder import eu.kanade.tachiyomi.util.storage.DiskUtil import eu.kanade.tachiyomi.util.storage.EpubFile import eu.kanade.tachiyomi.util.system.ImageUtil -import eu.kanade.tachiyomi.util.system.logcat import kotlinx.coroutines.runBlocking import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json import kotlinx.serialization.json.decodeFromStream import kotlinx.serialization.json.encodeToStream -import logcat.LogPriority import rx.Observable import tachiyomi.source.model.ChapterInfo import tachiyomi.source.model.MangaInfo +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy import java.io.File import java.io.FileInputStream @@ -35,50 +37,10 @@ import java.io.InputStream import java.util.concurrent.TimeUnit import java.util.zip.ZipFile -class LocalSource(private val context: Context) : CatalogueSource, UnmeteredSource { - - companion object { - const val ID = 0L - const val HELP_URL = "https://tachiyomi.org/help/guides/local-manga/" - - private const val COVER_NAME = "cover.jpg" - private val LATEST_THRESHOLD = TimeUnit.MILLISECONDS.convert(7, TimeUnit.DAYS) - - fun updateCover(context: Context, manga: SManga, input: InputStream): File? { - val dir = getBaseDirectories(context).firstOrNull() - if (dir == null) { - input.close() - return null - } - var cover = getCoverFile(File("${dir.absolutePath}/${manga.url}")) - if (cover == null) { - cover = File("${dir.absolutePath}/${manga.url}", COVER_NAME) - } - // It might not exist if using the external SD card - cover.parentFile?.mkdirs() - input.use { - cover.outputStream().use { - input.copyTo(it) - } - } - manga.thumbnail_url = cover.absolutePath - return cover - } - - /** - * Returns valid cover file inside [parent] directory. - */ - private fun getCoverFile(parent: File): File? { - return parent.listFiles()?.find { it.nameWithoutExtension == "cover" }?.takeIf { - it.isFile && ImageUtil.isImage(it.name) { it.inputStream() } - } - } - - private fun getBaseDirectories(context: Context): List { - val c = context.getString(R.string.app_name) + File.separator + "local" - return DiskUtil.getExternalStorages(context).map { File(it.absolutePath, c) } - } - } +class LocalSource( + private val context: Context, + private val coverCache: CoverCache = Injekt.get(), +) : CatalogueSource, UnmeteredSource { private val json: Json by injectLazy() @@ -86,86 +48,100 @@ class LocalSource(private val context: Context) : CatalogueSource, UnmeteredSour private val preferences: PreferencesHelper by injectLazy() // SY <-- - override val id = ID - override val name = context.getString(R.string.local_source) - override val lang = "other" - override val supportsLatest = true + override val name: String = context.getString(R.string.local_source) + + override val id: Long = ID + + override val lang: String = "other" override fun toString() = name + override val supportsLatest: Boolean = true + + // Browse related override fun fetchPopularManga(page: Int) = fetchSearchManga(page, "", POPULAR_FILTERS) + override fun fetchLatestUpdates(page: Int) = fetchSearchManga(page, "", LATEST_FILTERS) + override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable { - val baseDirs = getBaseDirectories(context) + val baseDirsFiles = getBaseDirectoriesFiles(context) // SY --> val allowLocalSourceHiddenFolders = preferences.allowLocalSourceHiddenFolders().get() // SY <-- - val time = if (filters === LATEST_FILTERS) System.currentTimeMillis() - LATEST_THRESHOLD else 0L - var mangaDirs = baseDirs - .asSequence() - .mapNotNull { it.listFiles()?.toList() } - .flatten() - .filter { it.isDirectory } - .filterNot { it.name.startsWith('.') /* SY --> */ && !allowLocalSourceHiddenFolders /* SY <-- */ } - .filter { if (time == 0L) it.name.contains(query, ignoreCase = true) else it.lastModified() >= time } + var mangaDirs = baseDirsFiles + // Filter out files that are hidden and is not a folder + .filter { it.isDirectory && /* SY --> */ (!it.name.startsWith('.') || allowLocalSourceHiddenFolders) /* SY <-- */ } .distinctBy { it.name } - val state = ((if (filters.isEmpty()) POPULAR_FILTERS else filters)[0] as OrderBy).state - when (state?.index) { - 0 -> { - mangaDirs = if (state.ascending) { - mangaDirs.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, { it.name })) - } else { - mangaDirs.sortedWith(compareByDescending(String.CASE_INSENSITIVE_ORDER, { it.name })) - } - } - 1 -> { - mangaDirs = if (state.ascending) { - mangaDirs.sortedBy(File::lastModified) - } else { - mangaDirs.sortedByDescending(File::lastModified) - } + val lastModifiedLimit = if (filters === LATEST_FILTERS) System.currentTimeMillis() - LATEST_THRESHOLD else 0L + // Filter by query or last modified + mangaDirs = mangaDirs.filter { + if (lastModifiedLimit == 0L) { + it.name.contains(query, ignoreCase = true) + } else { + it.lastModified() >= lastModifiedLimit } } + filters.forEach { filter -> + when (filter) { + is OrderBy -> { + when (filter.state!!.index) { + 0 -> { + mangaDirs = if (filter.state!!.ascending) { + mangaDirs.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.name }) + } else { + mangaDirs.sortedWith(compareByDescending(String.CASE_INSENSITIVE_ORDER) { it.name }) + } + } + 1 -> { + mangaDirs = if (filter.state!!.ascending) { + mangaDirs.sortedBy(File::lastModified) + } else { + mangaDirs.sortedByDescending(File::lastModified) + } + } + } + } + + else -> { /* Do nothing */ } + } + } + + // Transform mangaDirs to list of SManga val mangas = mangaDirs.map { mangaDir -> SManga.create().apply { title = mangaDir.name url = mangaDir.name // Try to find the cover - for (dir in baseDirs) { - val cover = getCoverFile(File("${dir.absolutePath}/$url")) - if (cover != null && cover.exists()) { - thumbnail_url = cover.absolutePath - break - } + val cover = getCoverFile(mangaDir.name, baseDirsFiles) + if (cover != null && cover.exists()) { + thumbnail_url = cover.absolutePath } + } + } - val sManga = this - val mangaInfo = this.toMangaInfo() - runBlocking { - val chapters = getChapterList(mangaInfo) - if (chapters.isNotEmpty()) { - val chapter = chapters.last().toSChapter() - val format = getFormat(chapter) - if (format is Format.Epub) { - EpubFile(format.file).use { epub -> - epub.fillMangaMetadata(sManga) - } - } + // Fetch chapters of all the manga + mangas.forEach { manga -> + val mangaInfo = manga.toMangaInfo() + runBlocking { + val chapters = getChapterList(mangaInfo) + if (chapters.isNotEmpty()) { + val chapter = chapters.last().toSChapter() + val format = getFormat(chapter) - // Copy the cover from the first chapter found. - if (thumbnail_url == null) { - try { - val dest = updateCover(chapter, sManga) - thumbnail_url = dest?.absolutePath - } catch (e: Exception) { - logcat(LogPriority.ERROR, e) - } + if (format is Format.Epub) { + EpubFile(format.file).use { epub -> + epub.fillMangaMetadata(manga) } } + + // Copy the cover from the first chapter found if not available + if (manga.thumbnail_url == null) { + updateCover(chapter, manga) + } } } } @@ -200,38 +176,44 @@ class LocalSource(private val context: Context) : CatalogueSource, UnmeteredSour ) // SY <-- - override fun fetchLatestUpdates(page: Int) = fetchSearchManga(page, "", LATEST_FILTERS) - + // Manga details related override suspend fun getMangaDetails(manga: MangaInfo): MangaInfo { - val localDetails = getBaseDirectories(context) - .asSequence() - .mapNotNull { File(it, manga.key).listFiles()?.toList() } - .flatten() + var mangaInfo = manga + + val baseDirsFile = getBaseDirectoriesFiles(context) + + val coverFile = getCoverFile(manga.key, baseDirsFile) + + coverFile?.let { + mangaInfo = mangaInfo.copy(cover = it.absolutePath) + } + + val localDetails = getMangaDirsFiles(manga.key, baseDirsFile) .firstOrNull { it.extension.equals("json", ignoreCase = true) } - return if (localDetails != null) { + if (localDetails != null) { val mangaJson = json.decodeFromStream(localDetails.inputStream()) - manga.copy( - title = mangaJson.title ?: manga.title, - author = mangaJson.author ?: manga.author, - artist = mangaJson.artist ?: manga.artist, - description = mangaJson.description ?: manga.description, - genres = mangaJson.genre ?: manga.genres, - status = mangaJson.status ?: manga.status, + mangaInfo = mangaInfo.copy( + title = mangaJson.title ?: mangaInfo.title, + author = mangaJson.author ?: mangaInfo.author, + artist = mangaJson.artist ?: mangaInfo.artist, + description = mangaJson.description ?: mangaInfo.description, + genres = mangaJson.genre ?: mangaInfo.genres, + status = mangaJson.status ?: mangaInfo.status, ) - } else { - manga } + + return mangaInfo } + // Chapters override suspend fun getChapterList(manga: MangaInfo): List { val sManga = manga.toSManga() - val chapters = getBaseDirectories(context) - .asSequence() - .mapNotNull { File(it, manga.key).listFiles()?.toList() } - .flatten() + val baseDirsFile = getBaseDirectoriesFiles(context) + return getMangaDirsFiles(manga.key, baseDirsFile) + // Only keep supported formats .filter { it.isDirectory || isSupportedFile(it.extension) } .map { chapterFile -> SChapter.create().apply { @@ -243,14 +225,14 @@ class LocalSource(private val context: Context) : CatalogueSource, UnmeteredSour } date_upload = chapterFile.lastModified() + ChapterRecognition.parseChapterNumber(this, sManga) + val format = getFormat(chapterFile) if (format is Format.Epub) { EpubFile(format.file).use { epub -> epub.fillChapterMetadata(this) } } - - ChapterRecognition.parseChapterNumber(this, sManga) } } .map { it.toChapterInfo() } @@ -259,12 +241,24 @@ class LocalSource(private val context: Context) : CatalogueSource, UnmeteredSour if (c == 0) c2.name.compareToCaseInsensitiveNaturalOrder(c1.name) else c } .toList() - - return chapters } - override suspend fun getPageList(chapter: ChapterInfo) = throw Exception("Unused") + // Filters + override fun getFilterList() = FilterList(OrderBy(context)) + private val POPULAR_FILTERS = FilterList(OrderBy(context)) + private val LATEST_FILTERS = FilterList(OrderBy(context).apply { state = Filter.Sort.Selection(1, false) }) + + private class OrderBy(context: Context) : Filter.Sort( + context.getString(R.string.local_filter_order_by), + arrayOf(context.getString(R.string.title), context.getString(R.string.date)), + Selection(0, true), + ) + + // Unused stuff + override suspend fun getPageList(chapter: ChapterInfo) = throw UnsupportedOperationException("Unused") + + // Miscellaneous private fun isSupportedFile(extension: String): Boolean { return extension.lowercase() in SUPPORTED_ARCHIVE_TYPES } @@ -328,25 +322,89 @@ class LocalSource(private val context: Context) : CatalogueSource, UnmeteredSour } } } + .also { coverCache.clearMemoryCache() } } - override fun getFilterList() = POPULAR_FILTERS - - private val POPULAR_FILTERS = FilterList(OrderBy(context)) - private val LATEST_FILTERS = FilterList(OrderBy(context).apply { state = Filter.Sort.Selection(1, false) }) - - private class OrderBy(context: Context) : Filter.Sort( - context.getString(R.string.local_filter_order_by), - arrayOf(context.getString(R.string.title), context.getString(R.string.date)), - Selection(0, true), - ) - sealed class Format { data class Directory(val file: File) : Format() data class Zip(val file: File) : Format() data class Rar(val file: File) : Format() data class Epub(val file: File) : Format() } + + companion object { + const val ID = 0L + const val HELP_URL = "https://tachiyomi.org/help/guides/local-manga/" + + private const val DEFAULT_COVER_NAME = "cover.jpg" + private val LATEST_THRESHOLD = TimeUnit.MILLISECONDS.convert(7, TimeUnit.DAYS) + + private fun getBaseDirectories(context: Context): Sequence { + val localFolder = context.getString(R.string.app_name) + File.separator + "local" + return DiskUtil.getExternalStorages(context) + .map { File(it.absolutePath, localFolder) } + .asSequence() + } + + private fun getBaseDirectoriesFiles(context: Context): Sequence { + return getBaseDirectories(context) + // Get all the files inside all baseDir + .flatMap { it.listFiles().orEmpty().toList() } + } + + private fun getMangaDir(mangaUrl: String, baseDirsFile: Sequence): File? { + return baseDirsFile + // Get the first mangaDir or null + .firstOrNull { it.isDirectory && it.name == mangaUrl } + } + + private fun getMangaDirsFiles(mangaUrl: String, baseDirsFile: Sequence): Sequence { + return baseDirsFile + // Filter out ones that are not related to the manga and is not a directory + .filter { it.isDirectory && it.name == mangaUrl } + // Get all the files inside the filtered folders + .flatMap { it.listFiles().orEmpty().toList() } + } + + private fun getCoverFile(mangaUrl: String, baseDirsFile: Sequence): File? { + return getMangaDirsFiles(mangaUrl, baseDirsFile) + // Get all file whose names start with 'cover' + .filter { it.isFile && it.nameWithoutExtension.equals("cover", ignoreCase = true) } + // Get the first actual image + .firstOrNull { + ImageUtil.isImage(it.name) { it.inputStream() } + } + } + + fun updateCover(context: Context, manga: SManga, inputStream: InputStream): File? { + val baseDirsFiles = getBaseDirectoriesFiles(context) + + val mangaDir = getMangaDir(manga.url, baseDirsFiles) + if (mangaDir == null) { + inputStream.close() + return null + } + + var coverFile = getCoverFile(manga.url, baseDirsFiles) + if (coverFile == null) { + coverFile = File(mangaDir.absolutePath, DEFAULT_COVER_NAME) + } + + // It might not exist at this point + coverFile.parentFile?.mkdirs() + inputStream.use { input -> + coverFile.outputStream().use { output -> + input.copyTo(output) + } + } + + // Create a .nomedia file + DiskUtil.createNoMediaFile(UniFile.fromFile(mangaDir), context) + + manga.thumbnail_url = coverFile.absolutePath + return coverFile + } + } } private val SUPPORTED_ARCHIVE_TYPES = listOf("zip", "cbz", "rar", "cbr", "epub") diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt index db7377629..6b43ac805 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt @@ -7,7 +7,6 @@ import android.net.Uri import android.os.Bundle import androidx.annotation.ColorInt import com.jakewharton.rxrelay.BehaviorRelay -import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.models.Chapter @@ -885,20 +884,22 @@ class ReaderPresenter( Observable .fromCallable { - if (manga.isLocal()) { - val context = Injekt.get() - LocalSource.updateCover(context, manga, stream()) - manga.updateCoverLastModified(db) - R.string.cover_updated - SetAsCoverResult.Success - } else { - if (manga.favorite) { - coverCache.setCustomCoverToCache(manga, stream()) + stream().use { + if (manga.isLocal()) { + val context = Injekt.get() + LocalSource.updateCover(context, manga, it) manga.updateCoverLastModified(db) coverCache.clearMemoryCache() SetAsCoverResult.Success } else { - SetAsCoverResult.AddToLibraryFirst + if (manga.favorite) { + coverCache.setCustomCoverToCache(manga, it) + manga.updateCoverLastModified(db) + coverCache.clearMemoryCache() + SetAsCoverResult.Success + } else { + SetAsCoverResult.AddToLibraryFirst + } } } } From ac8e5cf78cc12bc9a12b1e29854abcd1aff4a14e Mon Sep 17 00:00:00 2001 From: Chris <52449218+shadow578@users.noreply.github.com> Date: Sat, 28 May 2022 15:09:53 +0200 Subject: [PATCH 27/61] Fix global update ignoring network constraint (#7188) * update library update network constraint logic * add explicit 'only on unmetered network' update constraint (cherry picked from commit 63238b388d1af3a0036f1d9a43cb4d2e87aabf5e) # Conflicts: # app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsLibraryController.kt (cherry picked from commit dd8dc8fbe9ae1fdc6b6dbcaad91a77366ee21c64) --- .../tachiyomi/data/library/LibraryUpdateJob.kt | 17 +++++------------ .../data/preference/PreferenceValues.kt | 1 + .../ui/setting/SettingsLibraryController.kt | 6 ++++-- app/src/main/res/values/strings.xml | 1 + 4 files changed, 11 insertions(+), 14 deletions(-) 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 be2c51ab2..43f8d538d 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt @@ -8,10 +8,7 @@ import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkManager import androidx.work.Worker import androidx.work.WorkerParameters -import eu.kanade.tachiyomi.data.preference.DEVICE_BATTERY_NOT_LOW -import eu.kanade.tachiyomi.data.preference.DEVICE_CHARGING -import eu.kanade.tachiyomi.data.preference.DEVICE_ONLY_ON_WIFI -import eu.kanade.tachiyomi.data.preference.PreferencesHelper +import eu.kanade.tachiyomi.data.preference.* import eu.kanade.tachiyomi.util.system.isConnectedToWifi import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -22,8 +19,9 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet override fun doWork(): Result { val preferences = Injekt.get() - if (requiresWifiConnection(preferences) && !context.isConnectedToWifi()) { - Result.failure() + val restrictions = preferences.libraryUpdateDeviceRestriction().get() + if ((DEVICE_ONLY_ON_WIFI in restrictions) && !context.isConnectedToWifi()) { + return Result.failure() } return if (LibraryUpdateService.start(context)) { @@ -42,7 +40,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet if (interval > 0) { val restrictions = preferences.libraryUpdateDeviceRestriction().get() val constraints = Constraints.Builder() - .setRequiredNetworkType(NetworkType.CONNECTED) + .setRequiredNetworkType(if (DEVICE_NETWORK_NOT_METERED in restrictions) { NetworkType.UNMETERED } else { NetworkType.CONNECTED }) .setRequiresCharging(DEVICE_CHARGING in restrictions) .setRequiresBatteryNotLow(DEVICE_BATTERY_NOT_LOW in restrictions) .build() @@ -62,10 +60,5 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet WorkManager.getInstance(context).cancelAllWorkByTag(TAG) } } - - fun requiresWifiConnection(preferences: PreferencesHelper): Boolean { - val restrictions = preferences.libraryUpdateDeviceRestriction().get() - return DEVICE_ONLY_ON_WIFI in restrictions - } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceValues.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceValues.kt index cb6c57fa6..398eccd75 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceValues.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceValues.kt @@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.data.preference import eu.kanade.tachiyomi.R const val DEVICE_ONLY_ON_WIFI = "wifi" +const val DEVICE_NETWORK_NOT_METERED = "network_not_metered" const val DEVICE_CHARGING = "ac" const val DEVICE_BATTERY_NOT_LOW = "battery_not_low" diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsLibraryController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsLibraryController.kt index cbb8e39f1..650c0586f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsLibraryController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsLibraryController.kt @@ -13,6 +13,7 @@ import eu.kanade.tachiyomi.data.database.models.Category import eu.kanade.tachiyomi.data.library.LibraryUpdateJob import eu.kanade.tachiyomi.data.preference.DEVICE_BATTERY_NOT_LOW import eu.kanade.tachiyomi.data.preference.DEVICE_CHARGING +import eu.kanade.tachiyomi.data.preference.DEVICE_NETWORK_NOT_METERED import eu.kanade.tachiyomi.data.preference.DEVICE_ONLY_ON_WIFI import eu.kanade.tachiyomi.data.preference.MANGA_HAS_UNREAD import eu.kanade.tachiyomi.data.preference.MANGA_NON_COMPLETED @@ -186,8 +187,8 @@ class SettingsLibraryController : SettingsController() { multiSelectListPreference { bindTo(preferences.libraryUpdateDeviceRestriction()) titleRes = R.string.pref_library_update_restriction - entriesRes = arrayOf(R.string.connected_to_wifi, R.string.charging, R.string.battery_not_low) - entryValues = arrayOf(DEVICE_ONLY_ON_WIFI, DEVICE_CHARGING, DEVICE_BATTERY_NOT_LOW) + entriesRes = arrayOf(R.string.connected_to_wifi, R.string.network_not_metered, R.string.charging, R.string.battery_not_low) + entryValues = arrayOf(DEVICE_ONLY_ON_WIFI, DEVICE_NETWORK_NOT_METERED, DEVICE_CHARGING, DEVICE_BATTERY_NOT_LOW) visibleIf(preferences.libraryUpdateInterval()) { it > 0 } @@ -203,6 +204,7 @@ class SettingsLibraryController : SettingsController() { .map { when (it) { DEVICE_ONLY_ON_WIFI -> context.getString(R.string.connected_to_wifi) + DEVICE_NETWORK_NOT_METERED -> context.getString(R.string.network_not_metered) DEVICE_CHARGING -> context.getString(R.string.charging) DEVICE_BATTERY_NOT_LOW -> context.getString(R.string.battery_not_low) else -> it diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f3b59c3c8..2af84db0a 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -222,6 +222,7 @@ Weekly Automatic updates device restrictions Only on Wi-Fi + Only on unmetered Network Charging Battery not low Restrictions: %s From acc4d4a32058f779f83520738d72c71c3236c082 Mon Sep 17 00:00:00 2001 From: kasperskier <95685115+kasperskier@users.noreply.github.com> Date: Sun, 5 Jun 2022 00:48:18 +0800 Subject: [PATCH 28/61] ChapterSourceSync: set default timestamp to max timestamp (#7197) (cherry picked from commit dd5da56695d8787ce5cf154b5d83aafaadf49def) (cherry picked from commit 1d00dee9b7c349e365370fe20fda9e7cbce0b1f2) --- .../kanade/tachiyomi/util/chapter/ChapterSourceSync.kt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterSourceSync.kt b/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterSourceSync.kt index 1001d55d7..f742b9101 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterSourceSync.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterSourceSync.kt @@ -12,6 +12,7 @@ import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.util.Date import java.util.TreeSet +import kotlin.math.max /** * Helper method for syncing the list of chapters from the source with the ones from the database. @@ -60,6 +61,9 @@ fun syncChaptersWithSource( } } + var maxTimestamp = 0L // in previous chapters to add + val rightNow = Date().time + for (sourceChapter in sourceChapters) { // This forces metadata update for the main viewable things in the chapter list. if (source is HttpSource) { @@ -73,7 +77,9 @@ fun syncChaptersWithSource( // Add the chapter if not in db already, or update if the metadata changed. if (dbChapter == null) { if (sourceChapter.date_upload == 0L) { - sourceChapter.date_upload = Date().time + sourceChapter.date_upload = if (maxTimestamp == 0L) rightNow else maxTimestamp + } else { + maxTimestamp = max(maxTimestamp, sourceChapter.date_upload) } toAdd.add(sourceChapter) } else { @@ -98,6 +104,7 @@ fun syncChaptersWithSource( return Pair(emptyList(), emptyList()) } + // Keep it a List instead of a Set. See #6372. val readded = mutableListOf() db.inTransaction { @@ -170,6 +177,7 @@ fun syncChaptersWithSource( db.updateLastUpdated(manga).executeAsBlocking() } + @Suppress("ConvertArgumentToSet") return Pair(toAdd.subtract(readded).toList(), toDelete.subtract(readded).toList()) } From 2bc380a9a36d7fc0462373248b91b21f8aed343e Mon Sep 17 00:00:00 2001 From: kasperskier <95685115+kasperskier@users.noreply.github.com> Date: Wed, 8 Jun 2022 05:58:58 +0800 Subject: [PATCH 29/61] Add more DoH providers (#7256) * Add more DoH providers * Fix IPs (cherry picked from commit 18ea6c4f655cfde314eb6b6d8061af313ba3f78b) (cherry picked from commit d957f2fa8b0d4cad77dc3e42582f800f4d4e30b5) --- .../kanade/tachiyomi/network/DohProviders.kt | 52 +++++++++++++++++++ .../kanade/tachiyomi/network/NetworkHelper.kt | 4 ++ .../ui/setting/SettingsAdvancedController.kt | 12 +++++ 3 files changed, 68 insertions(+) diff --git a/app/src/main/java/eu/kanade/tachiyomi/network/DohProviders.kt b/app/src/main/java/eu/kanade/tachiyomi/network/DohProviders.kt index 9a783495c..f5ab389d6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/network/DohProviders.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/network/DohProviders.kt @@ -13,6 +13,10 @@ const val PREF_DOH_CLOUDFLARE = 1 const val PREF_DOH_GOOGLE = 2 const val PREF_DOH_ADGUARD = 3 const val PREF_DOH_QUAD9 = 4 +const val PREF_DOH_ALIDNS = 5 +const val PREF_DOH_DNSPOD = 6 +const val PREF_DOH_360 = 7 +const val PREF_DOH_QUAD101 = 8 fun OkHttpClient.Builder.dohCloudflare() = dns( DnsOverHttps.Builder().client(build()) @@ -68,3 +72,51 @@ fun OkHttpClient.Builder.dohQuad9() = dns( ) .build(), ) + +fun OkHttpClient.Builder.dohAliDNS() = dns( + DnsOverHttps.Builder().client(build()) + .url("https://dns.alidns.com/dns-query".toHttpUrl()) + .bootstrapDnsHosts( + InetAddress.getByName("223.5.5.5"), + InetAddress.getByName("223.6.6.6"), + InetAddress.getByName("2400:3200::1"), + InetAddress.getByName("2400:3200:baba::1"), + ) + .build(), +) + +fun OkHttpClient.Builder.dohDNSPod() = dns( + DnsOverHttps.Builder().client(build()) + .url("https://doh.pub/dns-query".toHttpUrl()) + .bootstrapDnsHosts( + InetAddress.getByName("1.12.12.12"), + InetAddress.getByName("120.53.53.53"), + ) + .build(), +) + +fun OkHttpClient.Builder.doh360() = dns( + DnsOverHttps.Builder().client(build()) + .url("https://doh.360.cn/dns-query".toHttpUrl()) + .bootstrapDnsHosts( + InetAddress.getByName("101.226.4.6"), + InetAddress.getByName("218.30.118.6"), + InetAddress.getByName("123.125.81.6"), + InetAddress.getByName("140.207.198.6"), + InetAddress.getByName("180.163.249.75"), + InetAddress.getByName("101.199.113.208"), + InetAddress.getByName("36.99.170.86"), + ) + .build(), +) + +fun OkHttpClient.Builder.dohQuad101() = dns( + DnsOverHttps.Builder().client(build()) + .url("https://dns.twnic.tw/dns-query".toHttpUrl()) + .bootstrapDnsHosts( + InetAddress.getByName("101.101.101.101"), + InetAddress.getByName("2001:de4::101"), + InetAddress.getByName("2001:de4::102"), + ) + .build(), +) diff --git a/app/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt index 2dd6b8d7f..4056fce2e 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt @@ -46,6 +46,10 @@ open /* SY <-- */ class NetworkHelper(context: Context) { PREF_DOH_GOOGLE -> builder.dohGoogle() PREF_DOH_ADGUARD -> builder.dohAdGuard() PREF_DOH_QUAD9 -> builder.dohQuad9() + PREF_DOH_ALIDNS -> builder.dohAliDNS() + PREF_DOH_DNSPOD -> builder.dohDNSPod() + PREF_DOH_360 -> builder.doh360() + PREF_DOH_QUAD101 -> builder.dohQuad101() } return builder diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt index 86b042936..9f7600200 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt @@ -22,9 +22,13 @@ import eu.kanade.tachiyomi.data.library.LibraryUpdateService import eu.kanade.tachiyomi.data.library.LibraryUpdateService.Target import eu.kanade.tachiyomi.data.preference.PreferenceValues import eu.kanade.tachiyomi.network.NetworkHelper +import eu.kanade.tachiyomi.network.PREF_DOH_360 import eu.kanade.tachiyomi.network.PREF_DOH_ADGUARD +import eu.kanade.tachiyomi.network.PREF_DOH_ALIDNS import eu.kanade.tachiyomi.network.PREF_DOH_CLOUDFLARE +import eu.kanade.tachiyomi.network.PREF_DOH_DNSPOD import eu.kanade.tachiyomi.network.PREF_DOH_GOOGLE +import eu.kanade.tachiyomi.network.PREF_DOH_QUAD101 import eu.kanade.tachiyomi.network.PREF_DOH_QUAD9 import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager.Companion.DELEGATED_SOURCES @@ -191,6 +195,10 @@ class SettingsAdvancedController : SettingsController() { "Google", "AdGuard", "Quad9", + "AliDNS", + "DNSPod", + "360", + "Quad 101", ) entryValues = arrayOf( "-1", @@ -198,6 +206,10 @@ class SettingsAdvancedController : SettingsController() { PREF_DOH_GOOGLE.toString(), PREF_DOH_ADGUARD.toString(), PREF_DOH_QUAD9.toString(), + PREF_DOH_ALIDNS.toString(), + PREF_DOH_DNSPOD.toString(), + PREF_DOH_360.toString(), + PREF_DOH_QUAD101.toString(), ) defaultValue = "-1" summary = "%s" From afb80a23fc24d84fa20d321704d1237813ce2b50 Mon Sep 17 00:00:00 2001 From: arkon Date: Wed, 8 Jun 2022 22:31:01 -0400 Subject: [PATCH 30/61] Don't show clipboard copy confirmation toast on Android 13 or above (cherry picked from commit 40f5d26945a61de0a6bea27bcee53e7b536174c6) (cherry picked from commit adbf52a347477884771d676dd7299375ae97ace1) --- .../eu/kanade/tachiyomi/util/system/ContextExtensions.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt index 134d8470d..d9a76d31e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt @@ -87,7 +87,11 @@ fun Context.copyToClipboard(label: String, content: String) { val clipboard = getSystemService()!! clipboard.setPrimaryClip(ClipData.newPlainText(label, content)) - toast(getString(R.string.copied_to_clipboard, content.truncateCenter(50))) + // Android 13 and higher shows a visual confirmation of copied contents + // https://developer.android.com/about/versions/13/features/copy-paste + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) { + toast(getString(R.string.copied_to_clipboard, content.truncateCenter(50))) + } } catch (e: Throwable) { logcat(LogPriority.ERROR, e) toast(R.string.clipboard_copy_error) From 1ef971744373649463c5361d8038b84bccd0b532 Mon Sep 17 00:00:00 2001 From: arkon Date: Tue, 14 Jun 2022 22:31:24 -0400 Subject: [PATCH 31/61] Fix wrapped long page numbers in reader (closes #7300) (cherry picked from commit 6bc484617ee382b1ac8262e75671e535647d3bc4) (cherry picked from commit 5edb36ea752144be59ae682dcaa03798290bf91a) --- app/src/main/res/layout/reader_activity.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/layout/reader_activity.xml b/app/src/main/res/layout/reader_activity.xml index b4d711b3f..537e20d92 100755 --- a/app/src/main/res/layout/reader_activity.xml +++ b/app/src/main/res/layout/reader_activity.xml @@ -350,9 +350,10 @@ @@ -371,9 +372,10 @@ From 1ed182853a4c6091a8494615262d4c4c7600df33 Mon Sep 17 00:00:00 2001 From: arkon Date: Mon, 20 Jun 2022 22:51:34 -0400 Subject: [PATCH 32/61] Fix accented UI elements in library sheet being different colors (cherry picked from commit cd5bcc36734e0959569be6dd5ecd3b3813863207) (cherry picked from commit eefdeb3c3f5546fdeb63bdbbeafcf704d45086f6) --- .../java/eu/kanade/tachiyomi/widget/ExtendedNavigationView.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/ExtendedNavigationView.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/ExtendedNavigationView.kt index 027f1846b..4b01ff94e 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/ExtendedNavigationView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/ExtendedNavigationView.kt @@ -73,7 +73,7 @@ open class ExtendedNavigationView @JvmOverloads constructor( * @param context any context. * @param resId the vector resource to load and tint */ - fun tintVector(context: Context, resId: Int, @AttrRes colorAttrRes: Int = R.attr.colorAccent): Drawable { + fun tintVector(context: Context, resId: Int, @AttrRes colorAttrRes: Int = R.attr.colorPrimary): Drawable { return AppCompatResources.getDrawable(context, resId)!!.apply { setTint(context.getResourceColor(if (enabled) colorAttrRes else R.attr.colorControlNormal)) } From 9af0d404795a61cb9020327f2bab748eb6b6ee50 Mon Sep 17 00:00:00 2001 From: jobobby04 Date: Sat, 25 Jun 2022 11:09:41 -0400 Subject: [PATCH 33/61] Fix downloader crash related to UnmeteredSource (#7365) Fix crash when starting a download with chaqpters from a UnmeteredSource (cherry picked from commit 470a5764417ccd63a274ccea0e483a12ec1adbda) (cherry picked from commit 1e53ad97db7ed91d54d872de0330b1cd6cb8bd4c) --- .../main/java/eu/kanade/tachiyomi/data/download/Downloader.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 104ccbe4d..e778e45e2 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt @@ -280,7 +280,8 @@ class Downloader( val maxDownloadsFromSource = queue .groupBy { it.source } .filterKeys { it !is UnmeteredSource } - .maxOf { it.value.size } + .maxOfOrNull { it.value.size } + ?: 0 if ( queuedDownloads > DOWNLOADS_QUEUED_WARNING_THRESHOLD || maxDownloadsFromSource > CHAPTERS_PER_SOURCE_QUEUE_WARNING_THRESHOLD From c6896d87d6b4842a2b4204cf781abaaa47e61dd0 Mon Sep 17 00:00:00 2001 From: arkon Date: Sat, 25 Jun 2022 22:34:48 -0400 Subject: [PATCH 34/61] Use primary color for excluded tristate filter icon (fixes #7360) (cherry picked from commit 3ca1ce463696e2e8eabf63c1e729dbdd2ffe1f71) (cherry picked from commit d2e40a0749b5055d82b66693d41b6f0268adb6ff) --- .../tachiyomi/ui/browse/source/filter/TriStateItem.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/TriStateItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/TriStateItem.kt index 2eed3e7a8..04e4e1edc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/TriStateItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/TriStateItem.kt @@ -42,10 +42,10 @@ open class TriStateItem(val filter: Filter.TriState) : AbstractFlexibleItem throw Exception("Unknown state") }, )?.apply { - val color = if (filter.state == Filter.TriState.STATE_INCLUDE) { - view.context.getResourceColor(R.attr.colorAccent) - } else { + val color = if (filter.state == Filter.TriState.STATE_IGNORE) { view.context.getResourceColor(R.attr.colorOnBackground, 0.38f) + } else { + view.context.getResourceColor(R.attr.colorPrimary) } setTint(color) From 501dedf84554e74ccad13803fda9c13ab6f3a653 Mon Sep 17 00:00:00 2001 From: Osyx <9387558+Osyx@users.noreply.github.com> Date: Sun, 26 Jun 2022 16:01:31 +0200 Subject: [PATCH 35/61] Add new "Lavender" theme (#7343) * Add new "Lavender" theme * Add light theme values for Lavender theme * Fix order of enums * Fix accented UI elements in set categories sheet being different colors Co-authored-by: CrepeTF (cherry picked from commit ad106bd8842dfc9c047c0412b92a0cb1dc1aba1a) (cherry picked from commit bd6f778de2a8790988e35c2bf1ee45e1d898852f) --- .../data/preference/PreferenceValues.kt | 5 ++- .../ui/base/delegate/ThemingDelegate.kt | 3 ++ .../materialdialogs/QuadStateTextView.kt | 2 +- .../main/res/values-night/color_lavender.xml | 37 +++++++++++++++++++ app/src/main/res/values/color_lavender.xml | 36 ++++++++++++++++++ app/src/main/res/values/strings.xml | 1 + app/src/main/res/values/themes.xml | 28 ++++++++++++++ 7 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 app/src/main/res/values-night/color_lavender.xml create mode 100644 app/src/main/res/values/color_lavender.xml diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceValues.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceValues.kt index 398eccd75..be2f9d6ac 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceValues.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceValues.kt @@ -30,13 +30,14 @@ object PreferenceValues { enum class AppTheme(val titleResId: Int?) { DEFAULT(R.string.label_default), MONET(R.string.theme_monet), + GREEN_APPLE(R.string.theme_greenapple), + LAVENDER(R.string.theme_lavender), MIDNIGHT_DUSK(R.string.theme_midnightdusk), STRAWBERRY_DAIQUIRI(R.string.theme_strawberrydaiquiri), - YOTSUBA(R.string.theme_yotsuba), TAKO(R.string.theme_tako), - GREEN_APPLE(R.string.theme_greenapple), TEALTURQUOISE(R.string.theme_tealturquoise), YINYANG(R.string.theme_yinyang), + YOTSUBA(R.string.theme_yotsuba), // Deprecated DARK_BLUE(null), diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/delegate/ThemingDelegate.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/delegate/ThemingDelegate.kt index c3d74a15c..f3e54385a 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/delegate/ThemingDelegate.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/delegate/ThemingDelegate.kt @@ -20,6 +20,9 @@ interface ThemingDelegate { PreferenceValues.AppTheme.GREEN_APPLE -> { resIds += R.style.Theme_Tachiyomi_GreenApple } + PreferenceValues.AppTheme.LAVENDER -> { + resIds += R.style.Theme_Tachiyomi_Lavender + } PreferenceValues.AppTheme.MIDNIGHT_DUSK -> { resIds += R.style.Theme_Tachiyomi_MidnightDusk } diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/materialdialogs/QuadStateTextView.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/materialdialogs/QuadStateTextView.kt index b6b21fcca..e8e9fe77f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/materialdialogs/QuadStateTextView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/materialdialogs/QuadStateTextView.kt @@ -29,7 +29,7 @@ class QuadStateTextView @JvmOverloads constructor(context: Context, attrs: Attri val tint = if (state == State.UNCHECKED) { context.getThemeColor(R.attr.colorControlNormal) } else { - context.getThemeColor(R.attr.colorAccent) + context.getThemeColor(R.attr.colorPrimary) } if (tint != 0) { TextViewCompat.setCompoundDrawableTintList(this, ColorStateList.valueOf(tint)) diff --git a/app/src/main/res/values-night/color_lavender.xml b/app/src/main/res/values-night/color_lavender.xml new file mode 100644 index 000000000..d76e55486 --- /dev/null +++ b/app/src/main/res/values-night/color_lavender.xml @@ -0,0 +1,37 @@ + + + + #A177FF + #111129 + #A177FF + #111129 + #A177FF + #111129 + #A177FF + #111129 + #5E25E1 + #E8E8E8 + #111129 + #DEE8FF + #111129 + #DEE8FF + #111129 + #DEE8FF + #2CB6B6B6 + #E8E8E8 + #A8905FFF + #DEE8FF + #221247 + #A177FF + @color/lavender_primary + diff --git a/app/src/main/res/values/color_lavender.xml b/app/src/main/res/values/color_lavender.xml new file mode 100644 index 000000000..f6cfda65c --- /dev/null +++ b/app/src/main/res/values/color_lavender.xml @@ -0,0 +1,36 @@ + + + + #7B46AF + #EDE2FF + #7B46AF + #EDE2FF + #7B46AF + #EDE2FF + #7B46AF + #EDE2FF + #EDE2FF + #7B46AF + #EDE2FF + #7B46AF + #EDE2FF + #1B1B22 + #EDE2FF + #1B1B22 + #B9B0CC + #D849454E + #7B46AF + #F3EFF4 + #313033 + #D6BAFF + @color/lavender_primary + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2af84db0a..906d8d6e9 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -157,6 +157,7 @@ App theme Dynamic Green Apple + Lavender Midnight Dusk Strawberry Daiquiri Tako diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 81a1c64cb..67867d06f 100755 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -121,6 +121,34 @@ @color/greenapple_primaryInverse + + +