mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-31 06:17:57 +01:00 
			
		
		
		
	Queue tracking updates when offline (closes #1497)
Co-authored-by: Jays2Kings <Jays2Kings@users.noreply.github.com>
This commit is contained in:
		| @@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper | ||||
| import eu.kanade.tachiyomi.data.download.DownloadManager | ||||
| import eu.kanade.tachiyomi.data.preference.PreferencesHelper | ||||
| import eu.kanade.tachiyomi.data.track.TrackManager | ||||
| import eu.kanade.tachiyomi.data.track.job.DelayedTrackingStore | ||||
| import eu.kanade.tachiyomi.extension.ExtensionManager | ||||
| import eu.kanade.tachiyomi.network.NetworkHelper | ||||
| import eu.kanade.tachiyomi.source.SourceManager | ||||
| @@ -23,6 +24,8 @@ class AppModule(val app: Application) : InjektModule { | ||||
|     override fun InjektRegistrar.registerInjectables() { | ||||
|         addSingleton(app) | ||||
|  | ||||
|         addSingletonFactory { Json { ignoreUnknownKeys = true } } | ||||
|  | ||||
|         addSingletonFactory { PreferencesHelper(app) } | ||||
|  | ||||
|         addSingletonFactory { DatabaseHelper(app) } | ||||
| @@ -41,7 +44,7 @@ class AppModule(val app: Application) : InjektModule { | ||||
|  | ||||
|         addSingletonFactory { TrackManager(app) } | ||||
|  | ||||
|         addSingletonFactory { Json { ignoreUnknownKeys = true } } | ||||
|         addSingletonFactory { DelayedTrackingStore(app) } | ||||
|  | ||||
|         // Asynchronously init expensive components for a faster cold start | ||||
|         ContextCompat.getMainExecutor(app).execute { | ||||
|   | ||||
| @@ -0,0 +1,50 @@ | ||||
| package eu.kanade.tachiyomi.data.track.job | ||||
|  | ||||
| import android.content.Context | ||||
| import androidx.core.content.edit | ||||
| import eu.kanade.tachiyomi.data.database.models.Track | ||||
| import timber.log.Timber | ||||
|  | ||||
| class DelayedTrackingStore(context: Context) { | ||||
|  | ||||
|     /** | ||||
|      * Preference file where queued tracking updates are stored. | ||||
|      */ | ||||
|     private val preferences = context.getSharedPreferences("tracking_queue", Context.MODE_PRIVATE) | ||||
|  | ||||
|     fun addItem(track: Track) { | ||||
|         val trackId = track.id.toString() | ||||
|         val (_, lastChapterRead) = preferences.getString(trackId, "0:0.0")!!.split(":") | ||||
|         if (track.last_chapter_read > lastChapterRead.toFloat()) { | ||||
|             val value = "${track.manga_id}:${track.last_chapter_read}" | ||||
|             Timber.i("Queuing track item: $trackId, $value") | ||||
|             preferences.edit { | ||||
|                 putString(trackId, value) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun clear() { | ||||
|         preferences.edit { | ||||
|             clear() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun getItems(): List<DelayedTrackingItem> { | ||||
|         return (preferences.all as Map<String, String>).entries | ||||
|             .map { | ||||
|                 val (mangaId, lastChapterRead) = it.value.split(":") | ||||
|                 DelayedTrackingItem( | ||||
|                     trackId = it.key.toLong(), | ||||
|                     mangaId = mangaId.toLong(), | ||||
|                     lastChapterRead = lastChapterRead.toFloat(), | ||||
|                 ) | ||||
|             } | ||||
|     } | ||||
|  | ||||
|     data class DelayedTrackingItem( | ||||
|         val trackId: Long, | ||||
|         val mangaId: Long, | ||||
|         val lastChapterRead: Float, | ||||
|     ) | ||||
| } | ||||
| @@ -0,0 +1,75 @@ | ||||
| package eu.kanade.tachiyomi.data.track.job | ||||
|  | ||||
| import android.content.Context | ||||
| import androidx.work.BackoffPolicy | ||||
| import androidx.work.Constraints | ||||
| import androidx.work.CoroutineWorker | ||||
| import androidx.work.ExistingWorkPolicy | ||||
| import androidx.work.NetworkType | ||||
| import androidx.work.OneTimeWorkRequestBuilder | ||||
| import androidx.work.WorkManager | ||||
| import androidx.work.WorkerParameters | ||||
| import eu.kanade.tachiyomi.data.database.DatabaseHelper | ||||
| import eu.kanade.tachiyomi.data.track.TrackManager | ||||
| import kotlinx.coroutines.Dispatchers | ||||
| import kotlinx.coroutines.withContext | ||||
| import timber.log.Timber | ||||
| import uy.kohesive.injekt.Injekt | ||||
| import uy.kohesive.injekt.api.get | ||||
| import java.util.concurrent.TimeUnit | ||||
|  | ||||
| class DelayedTrackingUpdateJob(context: Context, workerParams: WorkerParameters) : | ||||
|     CoroutineWorker(context, workerParams) { | ||||
|  | ||||
|     override suspend fun doWork(): Result { | ||||
|         val db = Injekt.get<DatabaseHelper>() | ||||
|         val trackManager = Injekt.get<TrackManager>() | ||||
|         val delayedTrackingStore = Injekt.get<DelayedTrackingStore>() | ||||
|  | ||||
|         withContext(Dispatchers.IO) { | ||||
|             val tracks = delayedTrackingStore.getItems().mapNotNull { | ||||
|                 val manga = db.getManga(it.mangaId).executeAsBlocking() ?: return@withContext | ||||
|                 db.getTracks(manga).executeAsBlocking() | ||||
|                     .find { track -> track.id == it.trackId } | ||||
|                     ?.also { track -> | ||||
|                         track.last_chapter_read = it.lastChapterRead | ||||
|                     } | ||||
|             } | ||||
|  | ||||
|             tracks.forEach { track -> | ||||
|                 try { | ||||
|                     val service = trackManager.getService(track.sync_id) | ||||
|                     if (service != null && service.isLogged) { | ||||
|                         service.update(track, true) | ||||
|                         db.insertTrack(track).executeAsBlocking() | ||||
|                     } | ||||
|                 } catch (e: Exception) { | ||||
|                     Timber.e(e) | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             delayedTrackingStore.clear() | ||||
|         } | ||||
|  | ||||
|         return Result.success() | ||||
|     } | ||||
|  | ||||
|     companion object { | ||||
|         private const val TAG = "DelayedTrackingUpdate" | ||||
|  | ||||
|         fun setupTask(context: Context) { | ||||
|             val constraints = Constraints.Builder() | ||||
|                 .setRequiredNetworkType(NetworkType.CONNECTED) | ||||
|                 .build() | ||||
|  | ||||
|             val request = OneTimeWorkRequestBuilder<DelayedTrackingUpdateJob>() | ||||
|                 .setConstraints(constraints) | ||||
|                 .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 20, TimeUnit.SECONDS) | ||||
|                 .addTag(TAG) | ||||
|                 .build() | ||||
|  | ||||
|             WorkManager.getInstance(context) | ||||
|                 .enqueueUniqueWork(TAG, ExistingWorkPolicy.REPLACE, request) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -11,6 +11,8 @@ import eu.kanade.tachiyomi.data.database.models.Manga | ||||
| import eu.kanade.tachiyomi.data.download.DownloadManager | ||||
| import eu.kanade.tachiyomi.data.preference.PreferencesHelper | ||||
| import eu.kanade.tachiyomi.data.track.TrackManager | ||||
| import eu.kanade.tachiyomi.data.track.job.DelayedTrackingStore | ||||
| import eu.kanade.tachiyomi.data.track.job.DelayedTrackingUpdateJob | ||||
| import eu.kanade.tachiyomi.source.LocalSource | ||||
| import eu.kanade.tachiyomi.source.SourceManager | ||||
| import eu.kanade.tachiyomi.source.model.Page | ||||
| @@ -31,6 +33,7 @@ import eu.kanade.tachiyomi.util.storage.DiskUtil | ||||
| import eu.kanade.tachiyomi.util.storage.getPicturesDir | ||||
| import eu.kanade.tachiyomi.util.storage.getTempShareDir | ||||
| import eu.kanade.tachiyomi.util.system.ImageUtil | ||||
| import eu.kanade.tachiyomi.util.system.isOnline | ||||
| import eu.kanade.tachiyomi.util.updateCoverLastModified | ||||
| import kotlinx.coroutines.async | ||||
| import kotlinx.coroutines.awaitAll | ||||
| @@ -53,7 +56,8 @@ class ReaderPresenter( | ||||
|     private val sourceManager: SourceManager = Injekt.get(), | ||||
|     private val downloadManager: DownloadManager = Injekt.get(), | ||||
|     private val coverCache: CoverCache = Injekt.get(), | ||||
|     private val preferences: PreferencesHelper = Injekt.get() | ||||
|     private val preferences: PreferencesHelper = Injekt.get(), | ||||
|     private val delayedTrackingStore: DelayedTrackingStore = Injekt.get(), | ||||
| ) : BasePresenter<ReaderActivity>() { | ||||
|  | ||||
|     /** | ||||
| @@ -701,6 +705,7 @@ class ReaderPresenter( | ||||
|         val chapterRead = readerChapter.chapter.chapter_number | ||||
|  | ||||
|         val trackManager = Injekt.get<TrackManager>() | ||||
|         val context = Injekt.get<Application>() | ||||
|  | ||||
|         launchIO { | ||||
|             db.getTracks(manga).executeAsBlocking() | ||||
| @@ -713,8 +718,13 @@ class ReaderPresenter( | ||||
|                         // for a while. The view can still be garbage collected. | ||||
|                         async { | ||||
|                             runCatching { | ||||
|                                 service.update(track, true) | ||||
|                                 db.insertTrack(track).executeAsBlocking() | ||||
|                                 if (context.isOnline()) { | ||||
|                                     service.update(track, true) | ||||
|                                     db.insertTrack(track).executeAsBlocking() | ||||
|                                 } else { | ||||
|                                     delayedTrackingStore.addItem(track) | ||||
|                                     DelayedTrackingUpdateJob.setupTask(context) | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                     } else { | ||||
|   | ||||
| @@ -15,6 +15,7 @@ import android.content.res.Configuration | ||||
| import android.content.res.Resources | ||||
| import android.graphics.Color | ||||
| import android.net.ConnectivityManager | ||||
| import android.net.NetworkCapabilities | ||||
| import android.net.Uri | ||||
| import android.net.wifi.WifiManager | ||||
| import android.os.Build | ||||
| @@ -365,3 +366,14 @@ fun Context.createReaderThemeContext(): Context { | ||||
|     } | ||||
|     return this | ||||
| } | ||||
|  | ||||
| fun Context.isOnline(): Boolean { | ||||
|     val networkCapabilities = connectivityManager.activeNetwork ?: return false | ||||
|     val actNw = connectivityManager.getNetworkCapabilities(networkCapabilities) ?: return false | ||||
|     val maxTransport = when { | ||||
|         Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 -> NetworkCapabilities.TRANSPORT_LOWPAN | ||||
|         Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> NetworkCapabilities.TRANSPORT_WIFI_AWARE | ||||
|         else -> NetworkCapabilities.TRANSPORT_VPN | ||||
|     } | ||||
|     return (NetworkCapabilities.TRANSPORT_CELLULAR..maxTransport).any(actNw::hasTransport) | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user