Browse Source

Allow to refresh the entire library info (fixing empty covers after restoring backups). Closes #462

len 8 years ago
parent
commit
1f70be688a

+ 54 - 10
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<Manga>): Observable<Manga> {
+    fun updateChapterList(mangaToUpdate: List<Manga>): Observable<Manga> {
         // Initialize the variables holding the progress of the updates.
         val count = AtomicInteger(0)
         val newUpdates = ArrayList<Manga>()
@@ -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<Manga>): Observable<Manga> {
+        // 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<Manga>()
+
+                    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.
      *

+ 1 - 1
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.

+ 1 - 1
app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryFragment.kt

@@ -241,7 +241,7 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), 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)

+ 8 - 0
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() {

+ 10 - 0
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()

+ 1 - 0
app/src/main/res/values/keys.xml

@@ -57,6 +57,7 @@
     <string name="pref_clear_chapter_cache_key">pref_clear_chapter_cache_key</string>
     <string name="pref_clear_database_key">pref_clear_database_key</string>
     <string name="pref_clear_cookies_key">pref_clear_cookies_key</string>
+    <string name="pref_refresh_library_metadata_key">refresh_library_metadata</string>
 
     <string name="pref_version">pref_version</string>
     <string name="pref_build_time">pref_build_time</string>

+ 2 - 2
app/src/main/res/values/strings.xml

@@ -176,8 +176,8 @@
     <string name="pref_clear_database_summary">Delete manga and chapters that are not in your library</string>
     <string name="clear_database_confirmation">Are you sure? Read chapters and progress of non-library manga will be lost</string>
     <string name="clear_database_completed">Entries deleted</string>
-    <string name="pref_show_warning_message">Show warnings</string>
-    <string name="pref_show_warning_message_summary">Show warning messages during library sync </string>
+    <string name="pref_refresh_library_metadata">Refresh library metadata</string>
+    <string name="pref_refresh_library_metadata_summary">Updates covers, genres, description and manga status information</string>
     <string name="pref_reencode">Reencode images</string>
     <string name="pref_reencode_summary">Enable reencoding if images can\'t be decoded. Expect best results with Skia</string>
 

+ 5 - 0
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"/>
 
+        <Preference
+            android:key="@string/pref_refresh_library_metadata_key"
+            android:summary="@string/pref_refresh_library_metadata_summary"
+            android:title="@string/pref_refresh_library_metadata"/>
+
         <SwitchPreference
             android:defaultValue="false"
             android:key="@string/pref_reencode_key"

+ 1 - 1
app/src/test/java/eu/kanade/tachiyomi/data/library/LibraryUpdateServiceTest.kt

@@ -95,7 +95,7 @@ class LibraryUpdateServiceTest {
         `when`(source.fetchChapterList(favManga[2])).thenReturn(Observable.just(chapters3))
 
         val intent = Intent()
-        service.updateMangaList(service.getMangaToUpdate(intent)).subscribe()
+        service.updateChapterList(service.getMangaToUpdate(intent)).subscribe()
 
         // There are 3 network attempts and 2 insertions (1 request failed)
         assertThat(service.db.getChapters(favManga[0]).executeAsBlocking()).hasSize(2)