diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreateService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreateService.kt index bc8c82cbca..2f5cdb7b80 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreateService.kt +++ b/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) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreService.kt index dc2d0eb05b..4179e0f0ae 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreService.kt +++ b/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) } /** 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 e2eda3b72c..8b941000c5 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 @@ -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 { - val categoryId = intent.getIntExtra(UPDATE_CATEGORY, -1) + fun getMangaToUpdate(intent: Intent, target: Target): List { + 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): Observable { + // 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. * 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 da58c80927..07f69eddee 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 @@ -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) } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/AndroidComponentUtil.java b/app/src/main/java/eu/kanade/tachiyomi/util/AndroidComponentUtil.java deleted file mode 100644 index 9b94025194..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/util/AndroidComponentUtil.java +++ /dev/null @@ -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; - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/ContextExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/ContextExtensions.kt index b0daa653ba..e37667f76d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/ContextExtensions.kt +++ b/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 } +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8a92406cc0..e0d4b14992 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -239,6 +239,8 @@ Entries deleted Refresh library metadata Updates covers, genres, description and manga status information + Refresh tracking metadata + Updates status, score and last chapter read from the tracking services Version diff --git a/app/src/test/java/eu/kanade/tachiyomi/data/library/LibraryUpdateServiceTest.kt b/app/src/test/java/eu/kanade/tachiyomi/data/library/LibraryUpdateServiceTest.kt index 069d1ce36d..d75580647a 100644 --- a/app/src/test/java/eu/kanade/tachiyomi/data/library/LibraryUpdateServiceTest.kt +++ b/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)