From 1f70be688ab55815fca2e358ca294f5525f2a45b Mon Sep 17 00:00:00 2001 From: len Date: Thu, 6 Oct 2016 19:23:59 +0200 Subject: [PATCH] Allow to refresh the entire library info (fixing empty covers after restoring backups). Closes #462 --- .../data/library/LibraryUpdateService.kt | 64 ++++++++++++++++--- .../ui/library/LibraryCategoryView.kt | 2 +- .../tachiyomi/ui/library/LibraryFragment.kt | 2 +- .../ui/setting/SettingsAdvancedFragment.kt | 8 +++ .../ui/setting/SettingsGeneralFragment.kt | 10 +++ app/src/main/res/values/keys.xml | 1 + app/src/main/res/values/strings.xml | 4 +- app/src/main/res/xml/pref_advanced.xml | 5 ++ .../data/library/LibraryUpdateServiceTest.kt | 2 +- 9 files changed, 83 insertions(+), 15 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 612682170f..c327b11765 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt @@ -71,18 +71,18 @@ class LibraryUpdateService : Service() { private val notificationId: Int get() = Constants.NOTIFICATION_LIBRARY_ID - companion object { - /** - * Key for manual library update. - */ - const val UPDATE_IS_MANUAL = "is_manual" /** * Key for category to update. */ const val UPDATE_CATEGORY = "category" + /** + * Key for updating the details instead of the chapters. + */ + const val UPDATE_DETAILS = "details" + /** * Returns the status of the service. * @@ -98,13 +98,13 @@ class LibraryUpdateService : Service() { * running. * * @param context the application context. - * @param isManual whether the update has been manually triggered. * @param category a specific category to update, or null for global update. + * @param details whether to update the details instead of the list of chapters. */ - fun start(context: Context, isManual: Boolean = false, category: Category? = null) { + fun start(context: Context, category: Category? = null, details: Boolean = false) { if (!isRunning(context)) { val intent = Intent(context, LibraryUpdateService::class.java).apply { - putExtra(UPDATE_IS_MANUAL, isManual) + putExtra(UPDATE_DETAILS, details) category?.let { putExtra(UPDATE_CATEGORY, it.id) } } context.startService(intent) @@ -164,7 +164,16 @@ class LibraryUpdateService : Service() { subscription?.unsubscribe() // Update favorite manga. Destroy service when completed or in case of an error. - subscription = Observable.defer { updateMangaList(getMangaToUpdate(intent)) } + subscription = Observable + .defer { + val mangaList = getMangaToUpdate(intent) + + // Update either chapter list or manga details. + if (!intent.getBooleanExtra(UPDATE_DETAILS, false)) + updateChapterList(mangaList) + else + updateDetails(mangaList) + } .subscribeOn(Schedulers.io()) .subscribe({ }, { @@ -216,7 +225,7 @@ class LibraryUpdateService : Service() { * @param mangaToUpdate the list to update * @return an observable delivering the progress of each update. */ - fun updateMangaList(mangaToUpdate: List): Observable { + fun updateChapterList(mangaToUpdate: List): Observable { // Initialize the variables holding the progress of the updates. val count = AtomicInteger(0) val newUpdates = ArrayList() @@ -266,6 +275,41 @@ class LibraryUpdateService : Service() { .map { syncChaptersWithSource(db, it, manga, source) } } + /** + * Method that updates the details of the given list of manga. 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. + */ + fun updateDetails(mangaToUpdate: List): Observable { + // Initialize the variables holding the progress of the updates. + val count = AtomicInteger(0) + + val cancelIntent = PendingIntent.getBroadcast(this, 0, + Intent(this, CancelUpdateReceiver::class.java), 0) + + // Emit each manga and update it sequentially. + return Observable.from(mangaToUpdate) + // Notify manga that will update. + .doOnNext { showProgressNotification(it, count.andIncrement, mangaToUpdate.size, cancelIntent) } + // Update the details of the manga. + .concatMap { manga -> + val source = sourceManager.get(manga.source) as? OnlineSource + ?: return@concatMap Observable.empty() + + source.fetchMangaDetails(manga).doOnNext { networkManga -> + manga.copyFrom(networkManga) + db.insertManga(manga).executeAsBlocking() + } + } + .doOnCompleted { + cancelNotification() + } + } + /** * Returns the text that will be displayed in the notification when there are new chapters. * diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt index 67fa76c7ba..3caece0efa 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt @@ -101,7 +101,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att swipe_refresh.setDistanceToTriggerSync((2 * 64 * resources.displayMetrics.density).toInt()) swipe_refresh.setOnRefreshListener { if (!LibraryUpdateService.isRunning(context)) { - LibraryUpdateService.start(context, true, category) + LibraryUpdateService.start(context, category) context.toast(R.string.updating_category) } // It can be a very long operation, so we disable swipe refresh and show a toast. diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryFragment.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryFragment.kt index 62c783dcca..7e3a0b945d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryFragment.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryFragment.kt @@ -241,7 +241,7 @@ class LibraryFragment : BaseRxFragment(), ActionMode.Callback } R.id.action_library_display_mode -> swapDisplayMode() R.id.action_update_library -> { - LibraryUpdateService.start(activity, true) + LibraryUpdateService.start(activity) } R.id.action_edit_categories -> { val intent = CategoryActivity.newIntent(activity) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedFragment.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedFragment.kt index d4585d8372..42f8e34fce 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedFragment.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedFragment.kt @@ -7,6 +7,7 @@ import com.afollestad.materialdialogs.MaterialDialog import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.cache.ChapterCache import eu.kanade.tachiyomi.data.database.DatabaseHelper +import eu.kanade.tachiyomi.data.library.LibraryUpdateService import eu.kanade.tachiyomi.data.network.NetworkHelper import eu.kanade.tachiyomi.util.plusAssign import eu.kanade.tachiyomi.util.toast @@ -38,6 +39,8 @@ class SettingsAdvancedFragment : SettingsFragment() { private val clearCookies by lazy { findPreference(getString(R.string.pref_clear_cookies_key)) } + private val refreshMetadata by lazy { findPreference(getString(R.string.pref_refresh_library_metadata_key)) } + override fun onViewCreated(view: View, savedState: Bundle?) { super.onViewCreated(view, savedState) @@ -57,6 +60,11 @@ class SettingsAdvancedFragment : SettingsFragment() { clearDatabase() true } + + refreshMetadata.setOnPreferenceClickListener { + LibraryUpdateService.start(context, details = true) + true + } } private fun clearChapterCache() { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralFragment.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralFragment.kt index 8a52298a00..06b5806893 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralFragment.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralFragment.kt @@ -15,6 +15,7 @@ import eu.kanade.tachiyomi.widget.preference.LibraryColumnsDialog import eu.kanade.tachiyomi.widget.preference.SimpleDialogPreference import net.xpece.android.support.preference.MultiSelectListPreference import rx.Observable +import rx.android.schedulers.AndroidSchedulers import uy.kohesive.injekt.injectLazy class SettingsGeneralFragment : SettingsFragment(), @@ -76,6 +77,15 @@ class SettingsGeneralFragment : SettingsFragment(), true } + updateRestriction.setOnPreferenceChangeListener { preference, newValue -> + // Post to event looper to allow the preference to be updated. + subscriptions += Observable.fromCallable { + LibraryUpdateTrigger.setupTask(context) + }.subscribeOn(AndroidSchedulers.mainThread()).subscribe() + + true + } + val dbCategories = db.getCategories().executeAsBlocking() categoryUpdate.apply { entries = dbCategories.map { it.name }.toTypedArray() diff --git a/app/src/main/res/values/keys.xml b/app/src/main/res/values/keys.xml index 8d293cef5e..04ef15297b 100644 --- a/app/src/main/res/values/keys.xml +++ b/app/src/main/res/values/keys.xml @@ -57,6 +57,7 @@ pref_clear_chapter_cache_key pref_clear_database_key pref_clear_cookies_key + refresh_library_metadata pref_version pref_build_time diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 705d71edfb..870c0ffbe3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -176,8 +176,8 @@ Delete manga and chapters that are not in your library Are you sure? Read chapters and progress of non-library manga will be lost Entries deleted - Show warnings - Show warning messages during library sync + Refresh library metadata + Updates covers, genres, description and manga status information Reencode images Enable reencoding if images can\'t be decoded. Expect best results with Skia diff --git a/app/src/main/res/xml/pref_advanced.xml b/app/src/main/res/xml/pref_advanced.xml index 94c5a0a211..418e704dbc 100644 --- a/app/src/main/res/xml/pref_advanced.xml +++ b/app/src/main/res/xml/pref_advanced.xml @@ -20,6 +20,11 @@ android:summary="@string/pref_clear_database_summary" android:title="@string/pref_clear_database"/> + +