Browse Source

Add an option to refresh all tracking metadata

inorichi 7 years ago
parent
commit
67678cd49e

+ 0 - 4
app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreateService.kt

@@ -13,7 +13,6 @@ import eu.kanade.tachiyomi.data.backup.models.Backup.CATEGORIES
 import eu.kanade.tachiyomi.data.backup.models.Backup.MANGAS
 import eu.kanade.tachiyomi.data.backup.models.Backup.VERSION
 import eu.kanade.tachiyomi.data.database.models.Manga
-import eu.kanade.tachiyomi.util.AndroidComponentUtil
 import eu.kanade.tachiyomi.util.sendLocalBroadcast
 import timber.log.Timber
 import eu.kanade.tachiyomi.BuildConfig.APPLICATION_ID as ID
@@ -60,9 +59,6 @@ class BackupCreateService : IntentService(NAME) {
             context.startService(intent)
         }
 
-        fun isRunning(context: Context): Boolean {
-            return AndroidComponentUtil.isServiceRunning(context, BackupCreateService::class.java)
-        }
     }
 
     private val backupManager by lazy { BackupManager(this) }

+ 2 - 2
app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreService.kt

@@ -22,8 +22,8 @@ import eu.kanade.tachiyomi.data.backup.models.DHistory
 import eu.kanade.tachiyomi.data.database.DatabaseHelper
 import eu.kanade.tachiyomi.data.database.models.*
 import eu.kanade.tachiyomi.source.Source
-import eu.kanade.tachiyomi.util.AndroidComponentUtil
 import eu.kanade.tachiyomi.util.chop
+import eu.kanade.tachiyomi.util.isServiceRunning
 import eu.kanade.tachiyomi.util.sendLocalBroadcast
 import rx.Observable
 import rx.Subscription
@@ -50,7 +50,7 @@ class BackupRestoreService : Service() {
          * @return true if the service is running, false otherwise.
          */
         fun isRunning(context: Context): Boolean {
-            return AndroidComponentUtil.isServiceRunning(context, BackupRestoreService::class.java)
+            return context.isServiceRunning(BackupRestoreService::class.java)
         }
 
         /**

+ 69 - 19
app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt

@@ -16,12 +16,14 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper
 import eu.kanade.tachiyomi.data.database.models.Category
 import eu.kanade.tachiyomi.data.database.models.Chapter
 import eu.kanade.tachiyomi.data.database.models.Manga
+import eu.kanade.tachiyomi.data.database.models.Track
 import eu.kanade.tachiyomi.data.download.DownloadManager
 import eu.kanade.tachiyomi.data.download.DownloadService
 import eu.kanade.tachiyomi.data.library.LibraryUpdateService.Companion.start
 import eu.kanade.tachiyomi.data.notification.NotificationReceiver
 import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 import eu.kanade.tachiyomi.data.preference.getOrDefault
+import eu.kanade.tachiyomi.data.track.TrackManager
 import eu.kanade.tachiyomi.source.SourceManager
 import eu.kanade.tachiyomi.source.model.SManga
 import eu.kanade.tachiyomi.source.online.HttpSource
@@ -48,7 +50,8 @@ class LibraryUpdateService(
         val db: DatabaseHelper = Injekt.get(),
         val sourceManager: SourceManager = Injekt.get(),
         val preferences: PreferencesHelper = Injekt.get(),
-        val downloadManager: DownloadManager = Injekt.get()
+        val downloadManager: DownloadManager = Injekt.get(),
+        val trackManager: TrackManager = Injekt.get()
 ) : Service() {
 
     /**
@@ -85,17 +88,26 @@ class LibraryUpdateService(
             .addAction(R.drawable.ic_clear_grey_24dp_img, getString(android.R.string.cancel), cancelIntent)
     }
 
+    /**
+     * Defines what should be updated within a service execution.
+     */
+    enum class Target {
+        CHAPTERS, // Manga chapters
+        DETAILS,  // Manga metadata
+        TRACKING  // Tracking metadata
+    }
+
     companion object {
 
         /**
          * Key for category to update.
          */
-        const val UPDATE_CATEGORY = "category"
+        const val KEY_CATEGORY = "category"
 
         /**
-         * Key for updating the details instead of the chapters.
+         * Key that defines what should be updated.
          */
-        const val UPDATE_DETAILS = "details"
+        const val KEY_TARGET = "target"
 
         /**
          * Returns the status of the service.
@@ -104,7 +116,7 @@ class LibraryUpdateService(
          * @return true if the service is running, false otherwise.
          */
         fun isRunning(context: Context): Boolean {
-            return AndroidComponentUtil.isServiceRunning(context, LibraryUpdateService::class.java)
+            return context.isServiceRunning(LibraryUpdateService::class.java)
         }
 
         /**
@@ -113,13 +125,13 @@ class LibraryUpdateService(
          *
          * @param context the application context.
          * @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.
+         * @param target defines what should be updated.
          */
-        fun start(context: Context, category: Category? = null, details: Boolean = false) {
+        fun start(context: Context, category: Category? = null, target: Target = Target.CHAPTERS) {
             if (!isRunning(context)) {
                 val intent = Intent(context, LibraryUpdateService::class.java).apply {
-                    putExtra(UPDATE_DETAILS, details)
-                    category?.let { putExtra(UPDATE_CATEGORY, it.id) }
+                    putExtra(KEY_TARGET, target)
+                    category?.let { putExtra(KEY_CATEGORY, it.id) }
                 }
                 context.startService(intent)
             }
@@ -176,6 +188,8 @@ class LibraryUpdateService(
      */
     override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
         if (intent == null) return Service.START_NOT_STICKY
+        val target = intent.getSerializableExtra(KEY_TARGET) as? Target
+                ?: return Service.START_NOT_STICKY
 
         // Unsubscribe from any previous subscription if needed.
         subscription?.unsubscribe()
@@ -183,13 +197,14 @@ class LibraryUpdateService(
         // Update favorite manga. Destroy service when completed or in case of an error.
         subscription = Observable
                 .defer {
-                    val mangaList = getMangaToUpdate(intent)
+                    val mangaList = getMangaToUpdate(intent, target)
 
                     // Update either chapter list or manga details.
-                    if (!intent.getBooleanExtra(UPDATE_DETAILS, false))
-                        updateChapterList(mangaList)
-                    else
-                        updateDetails(mangaList)
+                    when (target) {
+                        Target.CHAPTERS -> updateChapterList(mangaList)
+                        Target.DETAILS -> updateDetails(mangaList)
+                        Target.TRACKING -> updateTrackings(mangaList)
+                    }
                 }
                 .subscribeOn(Schedulers.io())
                 .subscribe({
@@ -207,10 +222,11 @@ class LibraryUpdateService(
      * Returns the list of manga to be updated.
      *
      * @param intent the update intent.
+     * @param target the target to update.
      * @return a list of manga to update
      */
-    fun getMangaToUpdate(intent: Intent): List<Manga> {
-        val categoryId = intent.getIntExtra(UPDATE_CATEGORY, -1)
+    fun getMangaToUpdate(intent: Intent, target: Target): List<Manga> {
+        val categoryId = intent.getIntExtra(KEY_CATEGORY, -1)
 
         var listToUpdate = if (categoryId != -1)
             db.getLibraryMangas().executeAsBlocking().filter { it.category == categoryId }
@@ -224,7 +240,7 @@ class LibraryUpdateService(
                 db.getLibraryMangas().executeAsBlocking().distinctBy { it.id }
         }
 
-        if (!intent.getBooleanExtra(UPDATE_DETAILS, false) && preferences.updateOnlyNonCompleted()) {
+        if (target == Target.CHAPTERS && preferences.updateOnlyNonCompleted()) {
             listToUpdate = listToUpdate.filter { it.status != SManga.COMPLETED }
         }
 
@@ -328,8 +344,6 @@ class LibraryUpdateService(
     /**
      * 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.
@@ -360,6 +374,42 @@ class LibraryUpdateService(
                 }
     }
 
+    /**
+     * Method that updates the metadata of the connected tracking services. It's called in a
+     * background thread, so it's safe to do heavy operations or network calls here.
+     */
+    private fun updateTrackings(mangaToUpdate: List<Manga>): Observable<Manga> {
+        // Initialize the variables holding the progress of the updates.
+        var count = 0
+
+        val loggedServices = trackManager.services.filter { it.isLogged }
+
+        // Emit each manga and update it sequentially.
+        return Observable.from(mangaToUpdate)
+                // Notify manga that will update.
+                .doOnNext { showProgressNotification(it, count++, mangaToUpdate.size) }
+                // Update the tracking details.
+                .concatMap { manga ->
+                    val tracks = db.getTracks(manga).executeAsBlocking()
+
+                    Observable.from(tracks)
+                            .concatMap { track ->
+                                val service = trackManager.getService(track.sync_id)
+                                if (service != null && service in loggedServices) {
+                                    service.refresh(track)
+                                            .doOnNext { db.insertTrack(it).executeAsBlocking() }
+                                            .onErrorReturn { track }
+                                } else {
+                                    Observable.empty()
+                                }
+                            }
+                            .map { manga }
+                }
+                .doOnCompleted {
+                    cancelProgressNotification()
+                }
+    }
+
     /**
      * Shows the notification containing the currently updating manga and the progress.
      *

+ 8 - 1
app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt

@@ -11,6 +11,7 @@ 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.library.LibraryUpdateService.Target
 import eu.kanade.tachiyomi.network.NetworkHelper
 import eu.kanade.tachiyomi.ui.base.controller.DialogController
 import eu.kanade.tachiyomi.ui.library.LibraryController
@@ -60,7 +61,13 @@ class SettingsAdvancedController : SettingsController() {
             titleRes = R.string.pref_refresh_library_metadata
             summaryRes = R.string.pref_refresh_library_metadata_summary
 
-            onClick { LibraryUpdateService.start(context, details = true) }
+            onClick { LibraryUpdateService.start(context, target = Target.DETAILS) }
+        }
+        preference {
+            titleRes = R.string.pref_refresh_library_tracking
+            summaryRes = R.string.pref_refresh_library_tracking_summary
+
+            onClick { LibraryUpdateService.start(context, target = Target.TRACKING) }
         }
     }
 

+ 0 - 37
app/src/main/java/eu/kanade/tachiyomi/util/AndroidComponentUtil.java

@@ -1,37 +0,0 @@
-package eu.kanade.tachiyomi.util;
-
-import android.app.ActivityManager;
-import android.app.ActivityManager.RunningServiceInfo;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManager;
-
-import timber.log.Timber;
-
-public final class AndroidComponentUtil {
-
-    private AndroidComponentUtil() throws InstantiationException {
-        throw new InstantiationException("This class is not for instantiation");
-    }
-
-    public static void toggleComponent(Context context, Class componentClass, boolean enable) {
-        Timber.i((enable ? "Enabling " : "Disabling ") + componentClass.getSimpleName());
-        ComponentName componentName = new ComponentName(context, componentClass);
-        PackageManager pm = context.getPackageManager();
-        pm.setComponentEnabledSetting(componentName,
-                enable ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED :
-                        PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
-                PackageManager.DONT_KILL_APP);
-    }
-
-    public static boolean isServiceRunning(Context context, Class serviceClass) {
-        ActivityManager manager =
-                (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
-        for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
-            if (serviceClass.getName().equals(service.service.getClassName())) {
-                return true;
-            }
-        }
-        return false;
-    }
-}

+ 10 - 1
app/src/main/java/eu/kanade/tachiyomi/util/ContextExtensions.kt

@@ -1,5 +1,6 @@
 package eu.kanade.tachiyomi.util
 
+import android.app.ActivityManager
 import android.app.Notification
 import android.app.NotificationManager
 import android.content.BroadcastReceiver
@@ -135,4 +136,12 @@ fun Context.unregisterLocalReceiver(receiver: BroadcastReceiver) {
     LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver)
 }
 
-
+/**
+ * Returns true if the given service class is running.
+ */
+fun Context.isServiceRunning(serviceClass: Class<*>): Boolean {
+    val className = serviceClass.name
+    val manager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
+    return manager.getRunningServices(Integer.MAX_VALUE)
+            .any { className == it.service.className }
+}

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

@@ -239,6 +239,8 @@
     <string name="clear_database_completed">Entries deleted</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_refresh_library_tracking">Refresh tracking metadata</string>
+    <string name="pref_refresh_library_tracking_summary">Updates status, score and last chapter read from the tracking services</string>
 
       <!-- About section -->
     <string name="version">Version</string>

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

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