mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-03 23:58:55 +01:00 
			
		
		
		
	Fix dependency injection and use custom models extending DB ones
This commit is contained in:
		@@ -1,14 +1,13 @@
 | 
			
		||||
package eu.kanade.tachiyomi.data.backup
 | 
			
		||||
 | 
			
		||||
import com.github.salomonbrys.kotson.fromJson
 | 
			
		||||
import com.google.gson.*
 | 
			
		||||
import com.google.gson.reflect.TypeToken
 | 
			
		||||
import com.google.gson.stream.JsonReader
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.serializer.IdExclusion
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.serializer.IntegerSerializer
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.*
 | 
			
		||||
import java.io.*
 | 
			
		||||
import java.lang.reflect.Type
 | 
			
		||||
import java.util.*
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -191,8 +190,7 @@ class BackupManager(private val db: DatabaseHelper) {
 | 
			
		||||
    private fun restoreCategories(jsonCategories: JsonArray) {
 | 
			
		||||
        // Get categories from file and from db
 | 
			
		||||
        val dbCategories = db.getCategories().executeAsBlocking()
 | 
			
		||||
        val backupCategories = getArrayOrEmpty<Category>(jsonCategories,
 | 
			
		||||
                object : TypeToken<List<Category>>() {}.type)
 | 
			
		||||
        val backupCategories = gson.fromJson<List<CategoryImpl>>(jsonCategories)
 | 
			
		||||
 | 
			
		||||
        // Iterate over them
 | 
			
		||||
        for (category in backupCategories) {
 | 
			
		||||
@@ -224,17 +222,13 @@ class BackupManager(private val db: DatabaseHelper) {
 | 
			
		||||
     * @param jsonMangas the mangas and its related data (chapters, sync, categories) from the json.
 | 
			
		||||
     */
 | 
			
		||||
    private fun restoreMangas(jsonMangas: JsonArray) {
 | 
			
		||||
        val chapterToken = object : TypeToken<List<Chapter>>() {}.type
 | 
			
		||||
        val mangaSyncToken = object : TypeToken<List<MangaSync>>() {}.type
 | 
			
		||||
        val categoriesNamesToken = object : TypeToken<List<String>>() {}.type
 | 
			
		||||
 | 
			
		||||
        for (backupManga in jsonMangas) {
 | 
			
		||||
            // Map every entry to objects
 | 
			
		||||
            val element = backupManga.asJsonObject
 | 
			
		||||
            val manga = gson.fromJson(element.get(MANGA), Manga::class.java)
 | 
			
		||||
            val chapters = getArrayOrEmpty<Chapter>(element.get(CHAPTERS), chapterToken)
 | 
			
		||||
            val sync = getArrayOrEmpty<MangaSync>(element.get(MANGA_SYNC), mangaSyncToken)
 | 
			
		||||
            val categories = getArrayOrEmpty<String>(element.get(CATEGORIES), categoriesNamesToken)
 | 
			
		||||
            val manga = gson.fromJson(element.get(MANGA), MangaImpl::class.java)
 | 
			
		||||
            val chapters = gson.fromJson<List<ChapterImpl>>(element.get(CHAPTERS) ?: JsonArray())
 | 
			
		||||
            val sync = gson.fromJson<List<MangaSyncImpl>>(element.get(MANGA_SYNC) ?: JsonArray())
 | 
			
		||||
            val categories = gson.fromJson<List<String>>(element.get(CATEGORIES) ?: JsonArray())
 | 
			
		||||
 | 
			
		||||
            // Restore everything related to this manga
 | 
			
		||||
            restoreManga(manga)
 | 
			
		||||
@@ -340,7 +334,7 @@ class BackupManager(private val db: DatabaseHelper) {
 | 
			
		||||
    private fun restoreSyncForManga(manga: Manga, sync: List<MangaSync>) {
 | 
			
		||||
        // Fix foreign keys with the current manga id
 | 
			
		||||
        for (mangaSync in sync) {
 | 
			
		||||
            mangaSync.manga_id = manga.id
 | 
			
		||||
            mangaSync.manga_id = manga.id!!
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val dbSyncs = db.getMangasSync(manga).executeAsBlocking()
 | 
			
		||||
@@ -367,15 +361,4 @@ class BackupManager(private val db: DatabaseHelper) {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns a list of items from a json element, or an empty list if the element is null.
 | 
			
		||||
     *
 | 
			
		||||
     * @param element the json to be mapped to a list of items.
 | 
			
		||||
     * @param type the gson mapping to restore the list.
 | 
			
		||||
     * @return a list of items.
 | 
			
		||||
     */
 | 
			
		||||
    private fun <T> getArrayOrEmpty(element: JsonElement?, type: Type): List<T> {
 | 
			
		||||
        return gson.fromJson<List<T>>(element, type) ?: ArrayList<T>()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -16,10 +16,7 @@ import eu.kanade.tachiyomi.data.source.Source
 | 
			
		||||
import eu.kanade.tachiyomi.data.source.SourceManager
 | 
			
		||||
import eu.kanade.tachiyomi.data.source.model.Page
 | 
			
		||||
import eu.kanade.tachiyomi.data.source.online.OnlineSource
 | 
			
		||||
import eu.kanade.tachiyomi.util.DiskUtils
 | 
			
		||||
import eu.kanade.tachiyomi.util.DynamicConcurrentMergeOperator
 | 
			
		||||
import eu.kanade.tachiyomi.util.UrlUtil
 | 
			
		||||
import eu.kanade.tachiyomi.util.saveImageTo
 | 
			
		||||
import eu.kanade.tachiyomi.util.*
 | 
			
		||||
import rx.Observable
 | 
			
		||||
import rx.Subscription
 | 
			
		||||
import rx.android.schedulers.AndroidSchedulers
 | 
			
		||||
@@ -27,12 +24,17 @@ import rx.schedulers.Schedulers
 | 
			
		||||
import rx.subjects.BehaviorSubject
 | 
			
		||||
import rx.subjects.PublishSubject
 | 
			
		||||
import timber.log.Timber
 | 
			
		||||
import uy.kohesive.injekt.Injekt
 | 
			
		||||
import uy.kohesive.injekt.api.get
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.io.FileReader
 | 
			
		||||
import java.util.*
 | 
			
		||||
import java.util.concurrent.TimeUnit
 | 
			
		||||
 | 
			
		||||
class DownloadManager(private val context: Context, private val sourceManager: SourceManager, private val preferences: PreferencesHelper) {
 | 
			
		||||
class DownloadManager(
 | 
			
		||||
        private val context: Context,
 | 
			
		||||
        private val sourceManager: SourceManager = Injekt.get(),
 | 
			
		||||
        private val preferences: PreferencesHelper = Injekt.get()
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    private val gson = Gson()
 | 
			
		||||
 | 
			
		||||
@@ -270,10 +272,8 @@ class DownloadManager(private val context: Context, private val sourceManager: S
 | 
			
		||||
                    }
 | 
			
		||||
                    page
 | 
			
		||||
                }
 | 
			
		||||
                .retryWhen {
 | 
			
		||||
                    it.zipWith(Observable.range(1, 3)) { errors, retries -> retries }
 | 
			
		||||
                            .flatMap { retries -> Observable.timer((retries * 2).toLong(), TimeUnit.SECONDS) }
 | 
			
		||||
                }
 | 
			
		||||
                // Retry 3 times, waiting 2, 4 and 8 seconds between attempts.
 | 
			
		||||
                .retryWhen(RetryWithDelay(3, { (2 shl it - 1) * 1000 }))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Public method to get the image from the filesystem. It does NOT provide any way to download the image
 | 
			
		||||
 
 | 
			
		||||
@@ -7,14 +7,13 @@ import android.os.IBinder
 | 
			
		||||
import android.os.PowerManager
 | 
			
		||||
import com.github.pwittchen.reactivenetwork.library.ConnectivityStatus
 | 
			
		||||
import com.github.pwittchen.reactivenetwork.library.ReactiveNetwork
 | 
			
		||||
import eu.kanade.tachiyomi.App
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 | 
			
		||||
import eu.kanade.tachiyomi.util.toast
 | 
			
		||||
import rx.Subscription
 | 
			
		||||
import rx.android.schedulers.AndroidSchedulers
 | 
			
		||||
import rx.schedulers.Schedulers
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
 | 
			
		||||
class DownloadService : Service() {
 | 
			
		||||
 | 
			
		||||
@@ -29,8 +28,8 @@ class DownloadService : Service() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Inject lateinit var downloadManager: DownloadManager
 | 
			
		||||
    @Inject lateinit var preferences: PreferencesHelper
 | 
			
		||||
    val downloadManager: DownloadManager by injectLazy()
 | 
			
		||||
    val preferences: PreferencesHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    private var wakeLock: PowerManager.WakeLock? = null
 | 
			
		||||
    private var networkChangeSubscription: Subscription? = null
 | 
			
		||||
@@ -39,7 +38,6 @@ class DownloadService : Service() {
 | 
			
		||||
 | 
			
		||||
    override fun onCreate() {
 | 
			
		||||
        super.onCreate()
 | 
			
		||||
        App.get(this).component.inject(this)
 | 
			
		||||
 | 
			
		||||
        createWakeLock()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -7,28 +7,26 @@ import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader
 | 
			
		||||
import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory
 | 
			
		||||
import com.bumptech.glide.load.model.GlideUrl
 | 
			
		||||
import com.bumptech.glide.module.GlideModule
 | 
			
		||||
import eu.kanade.tachiyomi.App
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Manga
 | 
			
		||||
import eu.kanade.tachiyomi.data.network.NetworkHelper
 | 
			
		||||
import uy.kohesive.injekt.Injekt
 | 
			
		||||
import uy.kohesive.injekt.api.get
 | 
			
		||||
import java.io.InputStream
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class used to update Glide module settings
 | 
			
		||||
 */
 | 
			
		||||
class AppGlideModule : GlideModule {
 | 
			
		||||
 | 
			
		||||
    @Inject lateinit var networkHelper: NetworkHelper
 | 
			
		||||
 | 
			
		||||
    override fun applyOptions(context: Context, builder: GlideBuilder) {
 | 
			
		||||
        // Set the cache size of Glide to 15 MiB
 | 
			
		||||
        builder.setDiskCache(InternalCacheDiskCacheFactory(context, 15 * 1024 * 1024))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun registerComponents(context: Context, glide: Glide) {
 | 
			
		||||
        App.get(context).component.inject(this)
 | 
			
		||||
        glide.register(GlideUrl::class.java, InputStream::class.java,
 | 
			
		||||
                OkHttpUrlLoader.Factory(networkHelper.client))
 | 
			
		||||
        val networkFactory = OkHttpUrlLoader.Factory(Injekt.get<NetworkHelper>().client)
 | 
			
		||||
 | 
			
		||||
        glide.register(GlideUrl::class.java, InputStream::class.java, networkFactory)
 | 
			
		||||
        glide.register(Manga::class.java, InputStream::class.java, MangaModelLoader.Factory())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,14 +5,13 @@ import com.bumptech.glide.Glide
 | 
			
		||||
import com.bumptech.glide.load.data.DataFetcher
 | 
			
		||||
import com.bumptech.glide.load.model.*
 | 
			
		||||
import com.bumptech.glide.load.model.stream.StreamModelLoader
 | 
			
		||||
import eu.kanade.tachiyomi.App
 | 
			
		||||
import eu.kanade.tachiyomi.data.cache.CoverCache
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Manga
 | 
			
		||||
import eu.kanade.tachiyomi.data.source.SourceManager
 | 
			
		||||
import eu.kanade.tachiyomi.data.source.online.OnlineSource
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.io.InputStream
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A class for loading a cover associated with a [Manga] that can be present in our own cache.
 | 
			
		||||
@@ -30,12 +29,12 @@ class MangaModelLoader(context: Context) : StreamModelLoader<Manga> {
 | 
			
		||||
    /**
 | 
			
		||||
     * Cover cache where persistent covers are stored.
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var coverCache: CoverCache
 | 
			
		||||
    val coverCache: CoverCache by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Source manager.
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var sourceManager: SourceManager
 | 
			
		||||
    val sourceManager: SourceManager by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Base network loader.
 | 
			
		||||
@@ -54,10 +53,6 @@ class MangaModelLoader(context: Context) : StreamModelLoader<Manga> {
 | 
			
		||||
     */
 | 
			
		||||
    private val cachedHeaders = hashMapOf<Int, LazyHeaders>()
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        App.get(context).component.inject(this)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Factory class for creating [MangaModelLoader] instances.
 | 
			
		||||
     */
 | 
			
		||||
@@ -88,7 +83,7 @@ class MangaModelLoader(context: Context) : StreamModelLoader<Manga> {
 | 
			
		||||
        // Obtain the request url and the file for this url from the LRU cache, or calculate it
 | 
			
		||||
        // and add them to the cache.
 | 
			
		||||
        val (glideUrl, file) = modelCache.get(url, width, height) ?:
 | 
			
		||||
            Pair(GlideUrl(url, getHeaders(manga)), coverCache.getCoverFile(url)).apply {
 | 
			
		||||
            Pair(GlideUrl(url, getHeaders(manga)), coverCache.getCoverFile(url!!)).apply {
 | 
			
		||||
                modelCache.put(url, width, height, this)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -10,12 +10,12 @@ import android.os.PowerManager
 | 
			
		||||
import android.support.v4.app.NotificationCompat
 | 
			
		||||
import com.github.pwittchen.reactivenetwork.library.ConnectivityStatus
 | 
			
		||||
import com.github.pwittchen.reactivenetwork.library.ReactiveNetwork
 | 
			
		||||
import eu.kanade.tachiyomi.App
 | 
			
		||||
import eu.kanade.tachiyomi.Constants
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Category
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Manga
 | 
			
		||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService.Companion.start
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 | 
			
		||||
import eu.kanade.tachiyomi.data.source.SourceManager
 | 
			
		||||
import eu.kanade.tachiyomi.data.source.online.OnlineSource
 | 
			
		||||
@@ -24,9 +24,9 @@ import eu.kanade.tachiyomi.util.*
 | 
			
		||||
import rx.Observable
 | 
			
		||||
import rx.Subscription
 | 
			
		||||
import rx.schedulers.Schedulers
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
import java.util.*
 | 
			
		||||
import java.util.concurrent.atomic.AtomicInteger
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This class will take care of updating the chapters of the manga from the library. It can be
 | 
			
		||||
@@ -41,17 +41,17 @@ class LibraryUpdateService : Service() {
 | 
			
		||||
    /**
 | 
			
		||||
     * Database helper.
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var db: DatabaseHelper
 | 
			
		||||
    val db: DatabaseHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Source manager.
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var sourceManager: SourceManager
 | 
			
		||||
    val sourceManager: SourceManager by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Preferences.
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var preferences: PreferencesHelper
 | 
			
		||||
    val preferences: PreferencesHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Wake lock that will be held until the service is destroyed.
 | 
			
		||||
@@ -126,7 +126,6 @@ class LibraryUpdateService : Service() {
 | 
			
		||||
     */
 | 
			
		||||
    override fun onCreate() {
 | 
			
		||||
        super.onCreate()
 | 
			
		||||
        App.get(this).component.inject(this)
 | 
			
		||||
        createAndAcquireWakeLock()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,23 +2,18 @@ package eu.kanade.tachiyomi.data.mangasync
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.support.annotation.CallSuper
 | 
			
		||||
import eu.kanade.tachiyomi.App
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.MangaSync
 | 
			
		||||
import eu.kanade.tachiyomi.data.network.NetworkHelper
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 | 
			
		||||
import okhttp3.OkHttpClient
 | 
			
		||||
import rx.Completable
 | 
			
		||||
import rx.Observable
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
 | 
			
		||||
abstract class MangaSyncService(private val context: Context, val id: Int) {
 | 
			
		||||
 | 
			
		||||
    @Inject lateinit var preferences: PreferencesHelper
 | 
			
		||||
    @Inject lateinit var networkService: NetworkHelper
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        App.get(context).component.inject(this)
 | 
			
		||||
    }
 | 
			
		||||
    val preferences: PreferencesHelper by injectLazy()
 | 
			
		||||
    val networkService: NetworkHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    open val client: OkHttpClient
 | 
			
		||||
        get() = networkService.client
 | 
			
		||||
 
 | 
			
		||||
@@ -4,25 +4,23 @@ import android.app.Service
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.os.IBinder
 | 
			
		||||
import eu.kanade.tachiyomi.App
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.MangaSync
 | 
			
		||||
import rx.Observable
 | 
			
		||||
import rx.android.schedulers.AndroidSchedulers
 | 
			
		||||
import rx.schedulers.Schedulers
 | 
			
		||||
import rx.subscriptions.CompositeSubscription
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
 | 
			
		||||
class UpdateMangaSyncService : Service() {
 | 
			
		||||
 | 
			
		||||
    @Inject lateinit var syncManager: MangaSyncManager
 | 
			
		||||
    @Inject lateinit var db: DatabaseHelper
 | 
			
		||||
    val syncManager: MangaSyncManager by injectLazy()
 | 
			
		||||
    val db: DatabaseHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    private lateinit var subscriptions: CompositeSubscription
 | 
			
		||||
 | 
			
		||||
    override fun onCreate() {
 | 
			
		||||
        super.onCreate()
 | 
			
		||||
        App.get(this).component.inject(this)
 | 
			
		||||
        subscriptions = CompositeSubscription()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -97,8 +97,8 @@ class MyAnimeList(private val context: Context, id: Int) : MangaSyncService(cont
 | 
			
		||||
                .flatMap { Observable.from(it.select("entry")) }
 | 
			
		||||
                .filter { it.select("type").text() != "Novel" }
 | 
			
		||||
                .map {
 | 
			
		||||
                    MangaSync.create(this).apply {
 | 
			
		||||
                        title = it.selectText("title")
 | 
			
		||||
                    MangaSync.create(id).apply {
 | 
			
		||||
                        title = it.selectText("title")!!
 | 
			
		||||
                        remote_id = it.selectInt("id")
 | 
			
		||||
                        total_chapters = it.selectInt("chapters")
 | 
			
		||||
                    }
 | 
			
		||||
@@ -114,8 +114,8 @@ class MyAnimeList(private val context: Context, id: Int) : MangaSyncService(cont
 | 
			
		||||
                .map { Jsoup.parse(it.body().string()) }
 | 
			
		||||
                .flatMap { Observable.from(it.select("manga")) }
 | 
			
		||||
                .map {
 | 
			
		||||
                    MangaSync.create(this).apply {
 | 
			
		||||
                        title = it.selectText("series_title")
 | 
			
		||||
                    MangaSync.create(id).apply {
 | 
			
		||||
                        title = it.selectText("series_title")!!
 | 
			
		||||
                        remote_id = it.selectInt("series_mangadb_id")
 | 
			
		||||
                        last_chapter_read = it.selectInt("my_read_chapters")
 | 
			
		||||
                        status = it.selectInt("my_status")
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,7 @@
 | 
			
		||||
package eu.kanade.tachiyomi.data.source.model;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Chapter;
 | 
			
		||||
import eu.kanade.tachiyomi.data.network.ProgressListener;
 | 
			
		||||
import eu.kanade.tachiyomi.ui.reader.ReaderChapter;
 | 
			
		||||
import rx.subjects.PublishSubject;
 | 
			
		||||
 | 
			
		||||
public class Page implements ProgressListener {
 | 
			
		||||
@@ -11,7 +9,7 @@ public class Page implements ProgressListener {
 | 
			
		||||
    private int pageNumber;
 | 
			
		||||
    private String url;
 | 
			
		||||
    private String imageUrl;
 | 
			
		||||
    private transient Chapter chapter;
 | 
			
		||||
    private transient ReaderChapter chapter;
 | 
			
		||||
    private transient String imagePath;
 | 
			
		||||
    private transient volatile int status;
 | 
			
		||||
    private transient volatile int progress;
 | 
			
		||||
@@ -90,16 +88,12 @@ public class Page implements ProgressListener {
 | 
			
		||||
        this.statusSubject = subject;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Chapter getChapter() {
 | 
			
		||||
    public ReaderChapter getChapter() {
 | 
			
		||||
        return chapter;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setChapter(Chapter chapter) {
 | 
			
		||||
    public void setChapter(ReaderChapter chapter) {
 | 
			
		||||
        this.chapter = chapter;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean isLastPage() {
 | 
			
		||||
        List<Page> chapterPages = chapter.getPages();
 | 
			
		||||
        return chapterPages.size() -1 == pageNumber;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
package eu.kanade.tachiyomi.data.source.online
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import eu.kanade.tachiyomi.App
 | 
			
		||||
import eu.kanade.tachiyomi.data.cache.ChapterCache
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Chapter
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Manga
 | 
			
		||||
@@ -14,9 +13,10 @@ import eu.kanade.tachiyomi.data.source.Language
 | 
			
		||||
import eu.kanade.tachiyomi.data.source.Source
 | 
			
		||||
import eu.kanade.tachiyomi.data.source.model.MangasPage
 | 
			
		||||
import eu.kanade.tachiyomi.data.source.model.Page
 | 
			
		||||
import eu.kanade.tachiyomi.util.UrlUtil
 | 
			
		||||
import okhttp3.*
 | 
			
		||||
import rx.Observable
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A simple implementation for sources from a website.
 | 
			
		||||
@@ -28,17 +28,17 @@ abstract class OnlineSource(context: Context) : Source {
 | 
			
		||||
    /**
 | 
			
		||||
     * Network service.
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var network: NetworkHelper
 | 
			
		||||
    val network: NetworkHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Chapter cache.
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var chapterCache: ChapterCache
 | 
			
		||||
    val chapterCache: ChapterCache by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Preferences helper.
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var preferences: PreferencesHelper
 | 
			
		||||
    val preferences: PreferencesHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Base url of the website without the trailing slash, like: http://mysite.com
 | 
			
		||||
@@ -61,11 +61,6 @@ abstract class OnlineSource(context: Context) : Source {
 | 
			
		||||
    open val client: OkHttpClient
 | 
			
		||||
        get() = network.client
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        // Inject dependencies.
 | 
			
		||||
        App.get(context).component.inject(this)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Headers builder for requests. Implementations can override this method for custom headers.
 | 
			
		||||
     */
 | 
			
		||||
@@ -443,6 +438,15 @@ abstract class OnlineSource(context: Context) : Source {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun Chapter.setUrlWithoutDomain(url: String) {
 | 
			
		||||
        this.url = UrlUtil.getPath(url)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun Manga.setUrlWithoutDomain(url: String) {
 | 
			
		||||
        this.url = UrlUtil.getPath(url)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // Overridable method to allow custom parsing.
 | 
			
		||||
    open fun parseChapterNumber(chapter: Chapter) {
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -26,8 +26,7 @@ abstract class ParsedOnlineSource(context: Context) : OnlineSource(context) {
 | 
			
		||||
    override fun popularMangaParse(response: Response, page: MangasPage) {
 | 
			
		||||
        val document = Jsoup.parse(response.body().string())
 | 
			
		||||
        for (element in document.select(popularMangaSelector())) {
 | 
			
		||||
            Manga().apply {
 | 
			
		||||
                source = this@ParsedOnlineSource.id
 | 
			
		||||
            Manga.create(id).apply {
 | 
			
		||||
                popularMangaFromElement(element, this)
 | 
			
		||||
                page.mangas.add(this)
 | 
			
		||||
            }
 | 
			
		||||
@@ -70,8 +69,7 @@ abstract class ParsedOnlineSource(context: Context) : OnlineSource(context) {
 | 
			
		||||
    override fun searchMangaParse(response: Response, page: MangasPage, query: String) {
 | 
			
		||||
        val document = Jsoup.parse(response.body().string())
 | 
			
		||||
        for (element in document.select(searchMangaSelector())) {
 | 
			
		||||
            Manga().apply {
 | 
			
		||||
                source = this@ParsedOnlineSource.id
 | 
			
		||||
            Manga.create(id).apply {
 | 
			
		||||
                searchMangaFromElement(element, this)
 | 
			
		||||
                page.mangas.add(this)
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -54,10 +54,9 @@ class YamlOnlineSource(context: Context, mappings: Map<*, *>) : OnlineSource(con
 | 
			
		||||
    override fun popularMangaParse(response: Response, page: MangasPage) {
 | 
			
		||||
        val document = Jsoup.parse(response.body().string())
 | 
			
		||||
        for (element in document.select(map.popular.manga_css)) {
 | 
			
		||||
            Manga().apply {
 | 
			
		||||
                source = this@YamlOnlineSource.id
 | 
			
		||||
            Manga.create(id).apply {
 | 
			
		||||
                title = element.text()
 | 
			
		||||
                setUrl(element.attr("href"))
 | 
			
		||||
                setUrlWithoutDomain(element.attr("href"))
 | 
			
		||||
                page.mangas.add(this)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -84,10 +83,9 @@ class YamlOnlineSource(context: Context, mappings: Map<*, *>) : OnlineSource(con
 | 
			
		||||
    override fun searchMangaParse(response: Response, page: MangasPage, query: String) {
 | 
			
		||||
        val document = Jsoup.parse(response.body().string())
 | 
			
		||||
        for (element in document.select(map.search.manga_css)) {
 | 
			
		||||
            Manga().apply {
 | 
			
		||||
                source = this@YamlOnlineSource.id
 | 
			
		||||
            Manga.create(id).apply {
 | 
			
		||||
                title = element.text()
 | 
			
		||||
                setUrl(element.attr("href"))
 | 
			
		||||
                setUrlWithoutDomain(element.attr("href"))
 | 
			
		||||
                page.mangas.add(this)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -123,7 +121,7 @@ class YamlOnlineSource(context: Context, mappings: Map<*, *>) : OnlineSource(con
 | 
			
		||||
                val chapter = Chapter.create()
 | 
			
		||||
                element.select(title).first().let {
 | 
			
		||||
                    chapter.name = it.text()
 | 
			
		||||
                    chapter.setUrl(it.attr("href"))
 | 
			
		||||
                    chapter.setUrlWithoutDomain(it.attr("href"))
 | 
			
		||||
                }
 | 
			
		||||
                val dateElement = element.select(date?.select).first()
 | 
			
		||||
                chapter.date_upload = date?.getDate(dateElement, pool, dateFormat)?.time ?: 0
 | 
			
		||||
 
 | 
			
		||||
@@ -62,8 +62,7 @@ class Batoto(context: Context, override val id: Int) : ParsedOnlineSource(contex
 | 
			
		||||
    override fun popularMangaParse(response: Response, page: MangasPage) {
 | 
			
		||||
        val document = Jsoup.parse(response.body().string())
 | 
			
		||||
        for (element in document.select(popularMangaSelector())) {
 | 
			
		||||
            Manga().apply {
 | 
			
		||||
                source = this@Batoto.id
 | 
			
		||||
            Manga.create(id).apply {
 | 
			
		||||
                popularMangaFromElement(element, this)
 | 
			
		||||
                page.mangas.add(this)
 | 
			
		||||
            }
 | 
			
		||||
@@ -78,7 +77,7 @@ class Batoto(context: Context, override val id: Int) : ParsedOnlineSource(contex
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaFromElement(element: Element, manga: Manga) {
 | 
			
		||||
        element.select("a[href^=http://bato.to]").first().let {
 | 
			
		||||
            manga.setUrl(it.attr("href"))
 | 
			
		||||
            manga.setUrlWithoutDomain(it.attr("href"))
 | 
			
		||||
            manga.title = it.text().trim()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -90,8 +89,7 @@ class Batoto(context: Context, override val id: Int) : ParsedOnlineSource(contex
 | 
			
		||||
    override fun searchMangaParse(response: Response, page: MangasPage, query: String) {
 | 
			
		||||
        val document = Jsoup.parse(response.body().string())
 | 
			
		||||
        for (element in document.select(searchMangaSelector())) {
 | 
			
		||||
            Manga().apply {
 | 
			
		||||
                source = this@Batoto.id
 | 
			
		||||
            Manga.create(id).apply {
 | 
			
		||||
                searchMangaFromElement(element, this)
 | 
			
		||||
                page.mangas.add(this)
 | 
			
		||||
            }
 | 
			
		||||
@@ -156,7 +154,7 @@ class Batoto(context: Context, override val id: Int) : ParsedOnlineSource(contex
 | 
			
		||||
    override fun chapterFromElement(element: Element, chapter: Chapter) {
 | 
			
		||||
        val urlElement = element.select("a[href^=http://bato.to/reader").first()
 | 
			
		||||
 | 
			
		||||
        chapter.setUrl(urlElement.attr("href"))
 | 
			
		||||
        chapter.setUrlWithoutDomain(urlElement.attr("href"))
 | 
			
		||||
        chapter.name = urlElement.text()
 | 
			
		||||
        chapter.date_upload = element.select("td").getOrNull(4)?.let {
 | 
			
		||||
            parseDateFromElement(it)
 | 
			
		||||
 
 | 
			
		||||
@@ -35,7 +35,7 @@ class Kissmanga(context: Context, override val id: Int) : ParsedOnlineSource(con
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaFromElement(element: Element, manga: Manga) {
 | 
			
		||||
        element.select("td a:eq(0)").first().let {
 | 
			
		||||
            manga.setUrl(it.attr("href"))
 | 
			
		||||
            manga.setUrlWithoutDomain(it.attr("href"))
 | 
			
		||||
            manga.title = it.text()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -88,7 +88,7 @@ class Kissmanga(context: Context, override val id: Int) : ParsedOnlineSource(con
 | 
			
		||||
    override fun chapterFromElement(element: Element, chapter: Chapter) {
 | 
			
		||||
        val urlElement = element.select("a").first()
 | 
			
		||||
 | 
			
		||||
        chapter.setUrl(urlElement.attr("href"))
 | 
			
		||||
        chapter.setUrlWithoutDomain(urlElement.attr("href"))
 | 
			
		||||
        chapter.name = urlElement.text()
 | 
			
		||||
        chapter.date_upload = element.select("td:eq(1)").first()?.text()?.let {
 | 
			
		||||
            SimpleDateFormat("MM/dd/yyyy").parse(it).time
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,7 @@ class Mangafox(context: Context, override val id: Int) : ParsedOnlineSource(cont
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaFromElement(element: Element, manga: Manga) {
 | 
			
		||||
        element.select("a.title").first().let {
 | 
			
		||||
            manga.setUrl(it.attr("href"))
 | 
			
		||||
            manga.setUrlWithoutDomain(it.attr("href"))
 | 
			
		||||
            manga.title = it.text()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -43,7 +43,7 @@ class Mangafox(context: Context, override val id: Int) : ParsedOnlineSource(cont
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaFromElement(element: Element, manga: Manga) {
 | 
			
		||||
        element.select("a.series_preview").first().let {
 | 
			
		||||
            manga.setUrl(it.attr("href"))
 | 
			
		||||
            manga.setUrlWithoutDomain(it.attr("href"))
 | 
			
		||||
            manga.title = it.text()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -74,7 +74,7 @@ class Mangafox(context: Context, override val id: Int) : ParsedOnlineSource(cont
 | 
			
		||||
    override fun chapterFromElement(element: Element, chapter: Chapter) {
 | 
			
		||||
        val urlElement = element.select("a.tips").first()
 | 
			
		||||
 | 
			
		||||
        chapter.setUrl(urlElement.attr("href"))
 | 
			
		||||
        chapter.setUrlWithoutDomain(urlElement.attr("href"))
 | 
			
		||||
        chapter.name = urlElement.text()
 | 
			
		||||
        chapter.date_upload = element.select("span.date").first()?.text()?.let { parseChapterDate(it) } ?: 0
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ class Mangahere(context: Context, override val id: Int) : ParsedOnlineSource(con
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaFromElement(element: Element, manga: Manga) {
 | 
			
		||||
        element.select("div.title > a").first().let {
 | 
			
		||||
            manga.setUrl(it.attr("href"))
 | 
			
		||||
            manga.setUrlWithoutDomain(it.attr("href"))
 | 
			
		||||
            manga.title = it.text()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -41,7 +41,7 @@ class Mangahere(context: Context, override val id: Int) : ParsedOnlineSource(con
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaFromElement(element: Element, manga: Manga) {
 | 
			
		||||
        element.select("a.manga_info").first().let {
 | 
			
		||||
            manga.setUrl(it.attr("href"))
 | 
			
		||||
            manga.setUrlWithoutDomain(it.attr("href"))
 | 
			
		||||
            manga.title = it.text()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -71,7 +71,7 @@ class Mangahere(context: Context, override val id: Int) : ParsedOnlineSource(con
 | 
			
		||||
    override fun chapterFromElement(element: Element, chapter: Chapter) {
 | 
			
		||||
        val urlElement = element.select("a").first()
 | 
			
		||||
 | 
			
		||||
        chapter.setUrl(urlElement.attr("href"))
 | 
			
		||||
        chapter.setUrlWithoutDomain(urlElement.attr("href"))
 | 
			
		||||
        chapter.name = urlElement.text()
 | 
			
		||||
        chapter.date_upload = element.select("span.right").first()?.text()?.let { parseChapterDate(it) } ?: 0
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,7 @@ class Readmangatoday(context: Context, override val id: Int) : ParsedOnlineSourc
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaFromElement(element: Element, manga: Manga) {
 | 
			
		||||
        element.select("div.title > h2 > a").first().let {
 | 
			
		||||
            manga.setUrl(it.attr("href"))
 | 
			
		||||
            manga.setUrlWithoutDomain(it.attr("href"))
 | 
			
		||||
            manga.title = it.attr("title")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -54,7 +54,7 @@ class Readmangatoday(context: Context, override val id: Int) : ParsedOnlineSourc
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaFromElement(element: Element, manga: Manga) {
 | 
			
		||||
        element.select("div.title > h2 > a").first().let {
 | 
			
		||||
            manga.setUrl(it.attr("href"))
 | 
			
		||||
            manga.setUrlWithoutDomain(it.attr("href"))
 | 
			
		||||
            manga.title = it.attr("title")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -83,7 +83,7 @@ class Readmangatoday(context: Context, override val id: Int) : ParsedOnlineSourc
 | 
			
		||||
    override fun chapterFromElement(element: Element, chapter: Chapter) {
 | 
			
		||||
        val urlElement = element.select("a").first()
 | 
			
		||||
 | 
			
		||||
        chapter.setUrl(urlElement.attr("href"))
 | 
			
		||||
        chapter.setUrlWithoutDomain(urlElement.attr("href"))
 | 
			
		||||
        chapter.name = urlElement.select("span.val").text()
 | 
			
		||||
        chapter.date_upload = element.select("span.dte").first()?.text()?.let { parseChapterDate(it) } ?: 0
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,7 @@ class Mangachan(context: Context, override val id: Int) : ParsedOnlineSource(con
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaFromElement(element: Element, manga: Manga) {
 | 
			
		||||
        element.select("h2 > a").first().let {
 | 
			
		||||
            manga.setUrl(it.attr("href"))
 | 
			
		||||
            manga.setUrlWithoutDomain(it.attr("href"))
 | 
			
		||||
            manga.title = it.text()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -69,7 +69,7 @@ class Mangachan(context: Context, override val id: Int) : ParsedOnlineSource(con
 | 
			
		||||
    override fun chapterFromElement(element: Element, chapter: Chapter) {
 | 
			
		||||
        val urlElement = element.select("a").first()
 | 
			
		||||
 | 
			
		||||
        chapter.setUrl(urlElement.attr("href"))
 | 
			
		||||
        chapter.setUrlWithoutDomain(urlElement.attr("href"))
 | 
			
		||||
        chapter.name = urlElement.text()
 | 
			
		||||
        chapter.date_upload = element.select("div.date").first()?.text()?.let {
 | 
			
		||||
            SimpleDateFormat("yyyy-MM-dd", Locale.US).parse(it).time
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ class Mintmanga(context: Context, override val id: Int) : ParsedOnlineSource(con
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaFromElement(element: Element, manga: Manga) {
 | 
			
		||||
        element.select("h3 > a").first().let {
 | 
			
		||||
            manga.setUrl(it.attr("href"))
 | 
			
		||||
            manga.setUrlWithoutDomain(it.attr("href"))
 | 
			
		||||
            manga.title = it.attr("title")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -69,7 +69,7 @@ class Mintmanga(context: Context, override val id: Int) : ParsedOnlineSource(con
 | 
			
		||||
    override fun chapterFromElement(element: Element, chapter: Chapter) {
 | 
			
		||||
        val urlElement = element.select("a").first()
 | 
			
		||||
 | 
			
		||||
        chapter.setUrl(urlElement.attr("href") + "?mature=1")
 | 
			
		||||
        chapter.setUrlWithoutDomain(urlElement.attr("href") + "?mature=1")
 | 
			
		||||
        chapter.name = urlElement.text().replace(" новое", "")
 | 
			
		||||
        chapter.date_upload = element.select("td:eq(1)").first()?.text()?.let {
 | 
			
		||||
            SimpleDateFormat("dd/MM/yy", Locale.US).parse(it).time
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ class Readmanga(context: Context, override val id: Int) : ParsedOnlineSource(con
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaFromElement(element: Element, manga: Manga) {
 | 
			
		||||
        element.select("h3 > a").first().let {
 | 
			
		||||
            manga.setUrl(it.attr("href"))
 | 
			
		||||
            manga.setUrlWithoutDomain(it.attr("href"))
 | 
			
		||||
            manga.title = it.attr("title")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -69,7 +69,7 @@ class Readmanga(context: Context, override val id: Int) : ParsedOnlineSource(con
 | 
			
		||||
    override fun chapterFromElement(element: Element, chapter: Chapter) {
 | 
			
		||||
        val urlElement = element.select("a").first()
 | 
			
		||||
 | 
			
		||||
        chapter.setUrl(urlElement.attr("href") + "?mature=1")
 | 
			
		||||
        chapter.setUrlWithoutDomain(urlElement.attr("href") + "?mature=1")
 | 
			
		||||
        chapter.name = urlElement.text().replace(" новое", "")
 | 
			
		||||
        chapter.date_upload = element.select("td:eq(1)").first()?.text()?.let {
 | 
			
		||||
            SimpleDateFormat("dd/MM/yy", Locale.US).parse(it).time
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,6 @@ import android.content.Intent
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import android.os.AsyncTask
 | 
			
		||||
import android.support.v4.app.NotificationCompat
 | 
			
		||||
import eu.kanade.tachiyomi.App
 | 
			
		||||
import eu.kanade.tachiyomi.Constants
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.network.GET
 | 
			
		||||
@@ -18,8 +17,8 @@ import eu.kanade.tachiyomi.data.network.newCallWithProgress
 | 
			
		||||
import eu.kanade.tachiyomi.util.notificationManager
 | 
			
		||||
import eu.kanade.tachiyomi.util.saveTo
 | 
			
		||||
import timber.log.Timber
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
import java.io.File
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
 | 
			
		||||
class UpdateDownloader(private val context: Context) :
 | 
			
		||||
        AsyncTask<String, Int, UpdateDownloader.DownloadResult>() {
 | 
			
		||||
@@ -40,7 +39,7 @@ class UpdateDownloader(private val context: Context) :
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Inject lateinit var network: NetworkHelper
 | 
			
		||||
    val network: NetworkHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Default download dir
 | 
			
		||||
@@ -59,9 +58,6 @@ class UpdateDownloader(private val context: Context) :
 | 
			
		||||
    private val notificationId: Int
 | 
			
		||||
        get() = Constants.NOTIFICATION_UPDATER_ID
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        App.get(context).component.inject(this)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Class containing download result
 | 
			
		||||
 
 | 
			
		||||
@@ -8,9 +8,9 @@ import rx.Observable
 | 
			
		||||
import rx.Subscription
 | 
			
		||||
import rx.android.schedulers.AndroidSchedulers
 | 
			
		||||
import rx.schedulers.Schedulers
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.io.InputStream
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Presenter of [BackupFragment].
 | 
			
		||||
@@ -20,7 +20,7 @@ class BackupPresenter : BasePresenter<BackupFragment>() {
 | 
			
		||||
    /**
 | 
			
		||||
     * Database.
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var db: DatabaseHelper
 | 
			
		||||
    val db: DatabaseHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Backup manager.
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,6 @@ abstract class BaseRxActivity<P : BasePresenter<*>> : NucleusAppCompatActivity<P
 | 
			
		||||
        setPresenterFactory {
 | 
			
		||||
            superFactory.createPresenter().apply {
 | 
			
		||||
                val app = application as App
 | 
			
		||||
                app.componentReflection.inject(this)
 | 
			
		||||
                context = app.applicationContext
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,6 @@ abstract class BaseRxFragment<P : BasePresenter<*>> : NucleusSupportFragment<P>(
 | 
			
		||||
        setPresenterFactory {
 | 
			
		||||
            superFactory.createPresenter().apply {
 | 
			
		||||
                val app = activity.application as App
 | 
			
		||||
                app.componentReflection.inject(this)
 | 
			
		||||
                context = app.applicationContext
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -56,7 +56,7 @@ class CatalogueAdapter(val fragment: CatalogueFragment) : FlexibleAdapter<Catalo
 | 
			
		||||
     * @return an identifier for the item.
 | 
			
		||||
     */
 | 
			
		||||
    override fun getItemId(position: Int): Long {
 | 
			
		||||
        return mItems[position].id
 | 
			
		||||
        return mItems[position].id!!
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -383,7 +383,7 @@ class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleViewHold
 | 
			
		||||
     * @return the holder of the manga or null if it's not bound.
 | 
			
		||||
     */
 | 
			
		||||
    private fun getHolder(manga: Manga): CatalogueGridHolder? {
 | 
			
		||||
        return catalogue_grid.findViewHolderForItemId(manga.id) as? CatalogueGridHolder
 | 
			
		||||
        return catalogue_grid.findViewHolderForItemId(manga.id!!) as? CatalogueGridHolder
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@ import rx.android.schedulers.AndroidSchedulers
 | 
			
		||||
import rx.schedulers.Schedulers
 | 
			
		||||
import rx.subjects.PublishSubject
 | 
			
		||||
import timber.log.Timber
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Presenter of [CatalogueFragment].
 | 
			
		||||
@@ -29,22 +29,22 @@ class CataloguePresenter : BasePresenter<CatalogueFragment>() {
 | 
			
		||||
    /**
 | 
			
		||||
     * Source manager.
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var sourceManager: SourceManager
 | 
			
		||||
    val sourceManager: SourceManager by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Database.
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var db: DatabaseHelper
 | 
			
		||||
    val db: DatabaseHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Preferences.
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var prefs: PreferencesHelper
 | 
			
		||||
    val prefs: PreferencesHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Cover cache.
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var coverCache: CoverCache
 | 
			
		||||
    val coverCache: CoverCache by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Enabled sources.
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Category
 | 
			
		||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
 | 
			
		||||
import rx.android.schedulers.AndroidSchedulers
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Presenter of CategoryActivity.
 | 
			
		||||
@@ -17,7 +17,7 @@ class CategoryPresenter : BasePresenter<CategoryActivity>() {
 | 
			
		||||
    /**
 | 
			
		||||
     * Used to connect to database
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var db: DatabaseHelper
 | 
			
		||||
    val db: DatabaseHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * List containing categories
 | 
			
		||||
 
 | 
			
		||||
@@ -35,7 +35,7 @@ class DownloadAdapter(private val context: Context) : FlexibleAdapter<DownloadHo
 | 
			
		||||
     * @return an identifier for the item.
 | 
			
		||||
     */
 | 
			
		||||
    override fun getItemId(position: Int): Long {
 | 
			
		||||
        return getItem(position).chapter.id
 | 
			
		||||
        return getItem(position).chapter.id!!
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -262,7 +262,7 @@ class DownloadFragment : BaseRxFragment<DownloadPresenter>() {
 | 
			
		||||
     * @return the holder of the download or null if it's not bound.
 | 
			
		||||
     */
 | 
			
		||||
    private fun getHolder(download: Download): DownloadHolder? {
 | 
			
		||||
        return recycler.findViewHolderForItemId(download.chapter.id) as? DownloadHolder
 | 
			
		||||
        return recycler.findViewHolderForItemId(download.chapter.id!!) as? DownloadHolder
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ import eu.kanade.tachiyomi.data.download.model.DownloadQueue
 | 
			
		||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
 | 
			
		||||
import rx.Observable
 | 
			
		||||
import timber.log.Timber
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Presenter of [DownloadFragment].
 | 
			
		||||
@@ -24,7 +24,7 @@ class DownloadPresenter : BasePresenter<DownloadFragment>() {
 | 
			
		||||
    /**
 | 
			
		||||
     * Download manager.
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var downloadManager: DownloadManager
 | 
			
		||||
    val downloadManager: DownloadManager by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Property to get the queue from the download manager.
 | 
			
		||||
 
 | 
			
		||||
@@ -49,7 +49,7 @@ class LibraryCategoryAdapter(val fragment: LibraryCategoryFragment) :
 | 
			
		||||
     * @return an identifier for the item.
 | 
			
		||||
     */
 | 
			
		||||
    override fun getItemId(position: Int): Long {
 | 
			
		||||
        return mItems[position].id
 | 
			
		||||
        return mItems[position].id!!
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -72,8 +72,8 @@ class LibraryCategoryAdapter(val fragment: LibraryCategoryFragment) :
 | 
			
		||||
     * @return true if the manga should be included, false otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    override fun filterObject(manga: Manga, query: String): Boolean = with(manga) {
 | 
			
		||||
        title != null && title.toLowerCase().contains(query) ||
 | 
			
		||||
                author != null && author.toLowerCase().contains(query)
 | 
			
		||||
        title.toLowerCase().contains(query) ||
 | 
			
		||||
                author != null && author!!.toLowerCase().contains(query)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -16,10 +16,10 @@ import rx.Observable
 | 
			
		||||
import rx.android.schedulers.AndroidSchedulers
 | 
			
		||||
import rx.schedulers.Schedulers
 | 
			
		||||
import rx.subjects.BehaviorSubject
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
import java.io.IOException
 | 
			
		||||
import java.io.InputStream
 | 
			
		||||
import java.util.*
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Presenter of [LibraryFragment].
 | 
			
		||||
@@ -49,27 +49,27 @@ class LibraryPresenter : BasePresenter<LibraryFragment>() {
 | 
			
		||||
    /**
 | 
			
		||||
     * Database.
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var db: DatabaseHelper
 | 
			
		||||
    val db: DatabaseHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Preferences.
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var preferences: PreferencesHelper
 | 
			
		||||
    val preferences: PreferencesHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Cover cache.
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var coverCache: CoverCache
 | 
			
		||||
    val coverCache: CoverCache by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Source manager.
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var sourceManager: SourceManager
 | 
			
		||||
    val sourceManager: SourceManager by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Download manager.
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var downloadManager: DownloadManager
 | 
			
		||||
    val downloadManager: DownloadManager by injectLazy()
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        /**
 | 
			
		||||
@@ -279,7 +279,7 @@ class LibraryPresenter : BasePresenter<LibraryFragment>() {
 | 
			
		||||
    @Throws(IOException::class)
 | 
			
		||||
    fun editCoverWithStream(inputStream: InputStream, manga: Manga): Boolean {
 | 
			
		||||
        if (manga.thumbnail_url != null && manga.favorite) {
 | 
			
		||||
            coverCache.copyToCache(manga.thumbnail_url, inputStream)
 | 
			
		||||
            coverCache.copyToCache(manga.thumbnail_url!!, inputStream)
 | 
			
		||||
            return true
 | 
			
		||||
        }
 | 
			
		||||
        return false
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,6 @@ import android.os.Bundle
 | 
			
		||||
import android.support.v4.app.Fragment
 | 
			
		||||
import android.support.v4.view.GravityCompat
 | 
			
		||||
import android.view.MenuItem
 | 
			
		||||
import eu.kanade.tachiyomi.App
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 | 
			
		||||
import eu.kanade.tachiyomi.ui.backup.BackupFragment
 | 
			
		||||
@@ -18,11 +17,11 @@ import eu.kanade.tachiyomi.ui.recently_read.RecentlyReadFragment
 | 
			
		||||
import eu.kanade.tachiyomi.ui.setting.SettingsActivity
 | 
			
		||||
import kotlinx.android.synthetic.main.activity_main.*
 | 
			
		||||
import kotlinx.android.synthetic.main.toolbar.*
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
 | 
			
		||||
class MainActivity : BaseActivity() {
 | 
			
		||||
 | 
			
		||||
    @Inject lateinit var preferences: PreferencesHelper
 | 
			
		||||
    val preferences: PreferencesHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    override fun onCreate(savedState: Bundle?) {
 | 
			
		||||
        setAppTheme()
 | 
			
		||||
@@ -34,8 +33,6 @@ class MainActivity : BaseActivity() {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        App.get(this).component.inject(this)
 | 
			
		||||
 | 
			
		||||
        // Inflate activity_main.xml.
 | 
			
		||||
        setContentView(R.layout.activity_main)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@ import eu.kanade.tachiyomi.ui.manga.info.ChapterCountEvent
 | 
			
		||||
import eu.kanade.tachiyomi.util.SharedData
 | 
			
		||||
import rx.Observable
 | 
			
		||||
import rx.Subscription
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Presenter of [MangaActivity].
 | 
			
		||||
@@ -19,12 +19,12 @@ class MangaPresenter : BasePresenter<MangaActivity>() {
 | 
			
		||||
    /**
 | 
			
		||||
     * Database helper.
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var db: DatabaseHelper
 | 
			
		||||
    val db: DatabaseHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Manga sync manager.
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var syncManager: MangaSyncManager
 | 
			
		||||
    val syncManager: MangaSyncManager by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Manga associated with this instance.
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,19 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.manga.chapter
 | 
			
		||||
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Chapter
 | 
			
		||||
import eu.kanade.tachiyomi.data.download.model.Download
 | 
			
		||||
 | 
			
		||||
class ChapterModel(c: Chapter) : Chapter by c {
 | 
			
		||||
 | 
			
		||||
    private var _status: Int = 0
 | 
			
		||||
 | 
			
		||||
    var status: Int
 | 
			
		||||
        get() = download?.status ?: _status
 | 
			
		||||
        set(value) { _status = value }
 | 
			
		||||
 | 
			
		||||
    var download: Download? = null
 | 
			
		||||
 | 
			
		||||
    val isDownloaded: Boolean
 | 
			
		||||
        get() = status == Download.DOWNLOADED
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -3,10 +3,9 @@ package eu.kanade.tachiyomi.ui.manga.chapter
 | 
			
		||||
import android.view.ViewGroup
 | 
			
		||||
import eu.davidea.flexibleadapter.FlexibleAdapter
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Chapter
 | 
			
		||||
import eu.kanade.tachiyomi.util.inflate
 | 
			
		||||
 | 
			
		||||
class ChaptersAdapter(val fragment: ChaptersFragment) : FlexibleAdapter<ChaptersHolder, Chapter>() {
 | 
			
		||||
class ChaptersAdapter(val fragment: ChaptersFragment) : FlexibleAdapter<ChaptersHolder, ChapterModel>() {
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        setHasStableIds(true)
 | 
			
		||||
@@ -30,10 +29,10 @@ class ChaptersAdapter(val fragment: ChaptersFragment) : FlexibleAdapter<Chapters
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun getItemId(position: Int): Long {
 | 
			
		||||
        return mItems[position].id
 | 
			
		||||
        return mItems[position].id!!
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun setItems(chapters: List<Chapter>) {
 | 
			
		||||
    fun setItems(chapters: List<ChapterModel>) {
 | 
			
		||||
        mItems = chapters
 | 
			
		||||
        notifyDataSetChanged()
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -134,8 +134,7 @@ class ChaptersFragment : BaseRxFragment<ChaptersPresenter>(), ActionMode.Callbac
 | 
			
		||||
                presenter.setDownloadedFilter(item.isChecked)
 | 
			
		||||
            }
 | 
			
		||||
            R.id.action_filter_empty -> {
 | 
			
		||||
                presenter.setReadFilter(false)
 | 
			
		||||
                presenter.setDownloadedFilter(false)
 | 
			
		||||
                presenter.removeFilters()
 | 
			
		||||
                activity.supportInvalidateOptionsMenu()
 | 
			
		||||
            }
 | 
			
		||||
            R.id.action_sort -> presenter.revertSortOrder()
 | 
			
		||||
@@ -150,7 +149,7 @@ class ChaptersFragment : BaseRxFragment<ChaptersPresenter>(), ActionMode.Callbac
 | 
			
		||||
        setDownloadedFilter()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun onNextChapters(chapters: List<Chapter>) {
 | 
			
		||||
    fun onNextChapters(chapters: List<ChapterModel>) {
 | 
			
		||||
        // If the list is empty, fetch chapters from source if the conditions are met
 | 
			
		||||
        // We use presenter chapters instead because they are always unfiltered
 | 
			
		||||
        if (presenter.chapters.isEmpty())
 | 
			
		||||
@@ -206,7 +205,7 @@ class ChaptersFragment : BaseRxFragment<ChaptersPresenter>(), ActionMode.Callbac
 | 
			
		||||
                    // Save the new display mode
 | 
			
		||||
                    presenter.setDisplayMode(itemView.id)
 | 
			
		||||
                    // Refresh ui
 | 
			
		||||
                    adapter.notifyDataSetChanged()
 | 
			
		||||
                    adapter.notifyItemRangeChanged(0, adapter.itemCount)
 | 
			
		||||
                    true
 | 
			
		||||
                }
 | 
			
		||||
                .show()
 | 
			
		||||
@@ -271,7 +270,7 @@ class ChaptersFragment : BaseRxFragment<ChaptersPresenter>(), ActionMode.Callbac
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getHolder(chapter: Chapter): ChaptersHolder? {
 | 
			
		||||
        return recycler.findViewHolderForItemId(chapter.id) as? ChaptersHolder
 | 
			
		||||
        return recycler.findViewHolderForItemId(chapter.id!!) as? ChaptersHolder
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
 | 
			
		||||
@@ -309,7 +308,7 @@ class ChaptersFragment : BaseRxFragment<ChaptersPresenter>(), ActionMode.Callbac
 | 
			
		||||
        actionMode = null
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getSelectedChapters(): List<Chapter> {
 | 
			
		||||
    fun getSelectedChapters(): List<ChapterModel> {
 | 
			
		||||
        return adapter.selectedItems.map { adapter.getItem(it) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -322,27 +321,27 @@ class ChaptersFragment : BaseRxFragment<ChaptersPresenter>(), ActionMode.Callbac
 | 
			
		||||
        setContextTitle(adapter.selectedItemCount)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun markAsRead(chapters: List<Chapter>) {
 | 
			
		||||
    fun markAsRead(chapters: List<ChapterModel>) {
 | 
			
		||||
        presenter.markChaptersRead(chapters, true)
 | 
			
		||||
        if (presenter.preferences.removeAfterMarkedAsRead()) {
 | 
			
		||||
            deleteChapters(chapters)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun markAsUnread(chapters: List<Chapter>) {
 | 
			
		||||
    fun markAsUnread(chapters: List<ChapterModel>) {
 | 
			
		||||
        presenter.markChaptersRead(chapters, false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun markPreviousAsRead(chapter: Chapter) {
 | 
			
		||||
    fun markPreviousAsRead(chapter: ChapterModel) {
 | 
			
		||||
        presenter.markPreviousChaptersAsRead(chapter)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun downloadChapters(chapters: List<Chapter>) {
 | 
			
		||||
    fun downloadChapters(chapters: List<ChapterModel>) {
 | 
			
		||||
        destroyActionModeIfNeeded()
 | 
			
		||||
        presenter.downloadChapters(chapters)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun deleteChapters(chapters: List<Chapter>) {
 | 
			
		||||
    fun deleteChapters(chapters: List<ChapterModel>) {
 | 
			
		||||
        destroyActionModeIfNeeded()
 | 
			
		||||
        DeletingChaptersDialog().show(childFragmentManager, DeletingChaptersDialog.TAG)
 | 
			
		||||
        presenter.deleteChapters(chapters)
 | 
			
		||||
@@ -350,7 +349,7 @@ class ChaptersFragment : BaseRxFragment<ChaptersPresenter>(), ActionMode.Callbac
 | 
			
		||||
 | 
			
		||||
    fun onChaptersDeleted() {
 | 
			
		||||
        dismissDeletingDialog()
 | 
			
		||||
        adapter.notifyDataSetChanged()
 | 
			
		||||
        adapter.notifyItemRangeChanged(0, adapter.itemCount)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun onChaptersDeletedError(error: Throwable) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,8 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.manga.chapter
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.widget.PopupMenu
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Chapter
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Manga
 | 
			
		||||
import eu.kanade.tachiyomi.data.download.model.Download
 | 
			
		||||
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder
 | 
			
		||||
@@ -26,7 +24,7 @@ class ChaptersHolder(
 | 
			
		||||
    private val decimalFormat = DecimalFormat("#.###", DecimalFormatSymbols().apply { decimalSeparator = '.' })
 | 
			
		||||
    private val df = DateFormat.getDateInstance(DateFormat.SHORT)
 | 
			
		||||
 | 
			
		||||
    private var item: Chapter? = null
 | 
			
		||||
    private var item: ChapterModel? = null
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        // We need to post a Runnable to show the popup to make sure that the PopupMenu is
 | 
			
		||||
@@ -35,19 +33,16 @@ class ChaptersHolder(
 | 
			
		||||
        view.chapter_menu.setOnClickListener { it.post { showPopupMenu(it) } }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun onSetValues(chapter: Chapter, manga: Manga?) = with(view) {
 | 
			
		||||
    fun onSetValues(chapter: ChapterModel, manga: Manga?) = with(view) {
 | 
			
		||||
        item = chapter
 | 
			
		||||
 | 
			
		||||
        val name: String
 | 
			
		||||
        when (manga?.displayMode) {
 | 
			
		||||
        chapter_title.text = when (manga?.displayMode) {
 | 
			
		||||
            Manga.DISPLAY_NUMBER -> {
 | 
			
		||||
                val formattedNumber = decimalFormat.format(chapter.chapter_number.toDouble())
 | 
			
		||||
                name = context.getString(R.string.display_mode_chapter, formattedNumber)
 | 
			
		||||
                context.getString(R.string.display_mode_chapter, formattedNumber)
 | 
			
		||||
            }
 | 
			
		||||
            else -> name = chapter.name
 | 
			
		||||
            else -> chapter.name
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        chapter_title.text = name
 | 
			
		||||
        chapter_title.setTextColor(if (chapter.read) readColor else unreadColor)
 | 
			
		||||
 | 
			
		||||
        if (chapter.date_upload > 0) {
 | 
			
		||||
@@ -57,31 +52,26 @@ class ChaptersHolder(
 | 
			
		||||
            chapter_date.text = ""
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!chapter.read && chapter.last_page_read > 0) {
 | 
			
		||||
            chapter_pages.text = context.getString(R.string.chapter_progress, chapter.last_page_read + 1)
 | 
			
		||||
        chapter_pages.text = if (!chapter.read && chapter.last_page_read > 0) {
 | 
			
		||||
            context.getString(R.string.chapter_progress, chapter.last_page_read + 1)
 | 
			
		||||
        } else {
 | 
			
		||||
            chapter_pages.text = ""
 | 
			
		||||
            ""
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        notifyStatus(chapter.status)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun notifyStatus(status: Int) = with(view) {
 | 
			
		||||
    fun notifyStatus(status: Int) = with(view.download_text) {
 | 
			
		||||
        when (status) {
 | 
			
		||||
            Download.QUEUE -> download_text.setText(R.string.chapter_queued)
 | 
			
		||||
            Download.DOWNLOADING -> download_text.setText(R.string.chapter_downloading)
 | 
			
		||||
            Download.DOWNLOADED -> download_text.setText(R.string.chapter_downloaded)
 | 
			
		||||
            Download.ERROR -> download_text.setText(R.string.chapter_error)
 | 
			
		||||
            else -> download_text.text = ""
 | 
			
		||||
            Download.QUEUE -> setText(R.string.chapter_queued)
 | 
			
		||||
            Download.DOWNLOADING -> setText(R.string.chapter_downloading)
 | 
			
		||||
            Download.DOWNLOADED -> setText(R.string.chapter_downloaded)
 | 
			
		||||
            Download.ERROR -> setText(R.string.chapter_error)
 | 
			
		||||
            else -> text = ""
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun onProgressChange(context: Context, downloaded: Int, total: Int) {
 | 
			
		||||
        view.download_text.text = context.getString(
 | 
			
		||||
                R.string.chapter_downloading_progress, downloaded, total)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun showPopupMenu(view: View) = item?.let { item ->
 | 
			
		||||
    private fun showPopupMenu(view: View) = item?.let { chapter ->
 | 
			
		||||
        // Create a PopupMenu, giving it the clicked view for an anchor
 | 
			
		||||
        val popup = PopupMenu(view.context, view)
 | 
			
		||||
 | 
			
		||||
@@ -89,32 +79,35 @@ class ChaptersHolder(
 | 
			
		||||
        popup.menuInflater.inflate(R.menu.chapter_single, popup.menu)
 | 
			
		||||
 | 
			
		||||
        // Hide download and show delete if the chapter is downloaded
 | 
			
		||||
        if (item.isDownloaded) {
 | 
			
		||||
        if (chapter.isDownloaded) {
 | 
			
		||||
            popup.menu.findItem(R.id.action_download).isVisible = false
 | 
			
		||||
            popup.menu.findItem(R.id.action_delete).isVisible = true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Hide mark as unread when the chapter is unread
 | 
			
		||||
        if (!item.read && item.last_page_read == 0) {
 | 
			
		||||
        if (!chapter.read && chapter.last_page_read == 0) {
 | 
			
		||||
            popup.menu.findItem(R.id.action_mark_as_unread).isVisible = false
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Hide mark as read when the chapter is read
 | 
			
		||||
        if (item.read) {
 | 
			
		||||
        if (chapter.read) {
 | 
			
		||||
            popup.menu.findItem(R.id.action_mark_as_read).isVisible = false
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Set a listener so we are notified if a menu item is clicked
 | 
			
		||||
        popup.setOnMenuItemClickListener { menuItem ->
 | 
			
		||||
            val chapter = listOf(item)
 | 
			
		||||
            val chapterList = listOf(chapter)
 | 
			
		||||
 | 
			
		||||
            when (menuItem.itemId) {
 | 
			
		||||
                R.id.action_download -> adapter.fragment.downloadChapters(chapter)
 | 
			
		||||
                R.id.action_delete -> adapter.fragment.deleteChapters(chapter)
 | 
			
		||||
                R.id.action_mark_as_read -> adapter.fragment.markAsRead(chapter)
 | 
			
		||||
                R.id.action_mark_as_unread -> adapter.fragment.markAsUnread(chapter)
 | 
			
		||||
                R.id.action_mark_previous_as_read -> adapter.fragment.markPreviousAsRead(item)
 | 
			
		||||
            with(adapter.fragment) {
 | 
			
		||||
                when (menuItem.itemId) {
 | 
			
		||||
                    R.id.action_download -> downloadChapters(chapterList)
 | 
			
		||||
                    R.id.action_delete -> deleteChapters(chapterList)
 | 
			
		||||
                    R.id.action_mark_as_read -> markAsRead(chapterList)
 | 
			
		||||
                    R.id.action_mark_as_unread -> markAsUnread(chapterList)
 | 
			
		||||
                    R.id.action_mark_previous_as_read -> markPreviousAsRead(chapter)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,108 +20,197 @@ import rx.android.schedulers.AndroidSchedulers
 | 
			
		||||
import rx.schedulers.Schedulers
 | 
			
		||||
import rx.subjects.PublishSubject
 | 
			
		||||
import timber.log.Timber
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Presenter of [ChaptersFragment].
 | 
			
		||||
 */
 | 
			
		||||
class ChaptersPresenter : BasePresenter<ChaptersFragment>() {
 | 
			
		||||
 | 
			
		||||
    @Inject lateinit var db: DatabaseHelper
 | 
			
		||||
    @Inject lateinit var sourceManager: SourceManager
 | 
			
		||||
    @Inject lateinit var preferences: PreferencesHelper
 | 
			
		||||
    @Inject lateinit var downloadManager: DownloadManager
 | 
			
		||||
    /**
 | 
			
		||||
     * Database helper.
 | 
			
		||||
     */
 | 
			
		||||
    val db: DatabaseHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Source manager.
 | 
			
		||||
     */
 | 
			
		||||
    val sourceManager: SourceManager by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Preferences.
 | 
			
		||||
     */
 | 
			
		||||
    val preferences: PreferencesHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Downloads manager.
 | 
			
		||||
     */
 | 
			
		||||
    val downloadManager: DownloadManager by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Active manga.
 | 
			
		||||
     */
 | 
			
		||||
    lateinit var manga: Manga
 | 
			
		||||
        private set
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Source of the manga.
 | 
			
		||||
     */
 | 
			
		||||
    lateinit var source: Source
 | 
			
		||||
        private set
 | 
			
		||||
 | 
			
		||||
    lateinit var chapters: List<Chapter>
 | 
			
		||||
    /**
 | 
			
		||||
     * List of chapters of the manga. It's always unfiltered and unsorted.
 | 
			
		||||
     */
 | 
			
		||||
    lateinit var chapters: List<ChapterModel>
 | 
			
		||||
        private set
 | 
			
		||||
 | 
			
		||||
    lateinit var chaptersSubject: PublishSubject<List<Chapter>>
 | 
			
		||||
    /**
 | 
			
		||||
     * Subject of list of chapters to allow updating the view without going to DB.
 | 
			
		||||
     */
 | 
			
		||||
    val chaptersSubject by lazy { PublishSubject.create<List<ChapterModel>>() }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether the chapter list has been requested to the source.
 | 
			
		||||
     */
 | 
			
		||||
    var hasRequested = false
 | 
			
		||||
        private set
 | 
			
		||||
 | 
			
		||||
    var hasRequested: Boolean = false
 | 
			
		||||
        private set
 | 
			
		||||
    companion object {
 | 
			
		||||
        /**
 | 
			
		||||
         * Id of the restartable which sends a filtered and ordered list of chapters to the view.
 | 
			
		||||
         */
 | 
			
		||||
        private const val GET_CHAPTERS = 1
 | 
			
		||||
 | 
			
		||||
    private val DB_CHAPTERS = 1
 | 
			
		||||
    private val FETCH_CHAPTERS = 2
 | 
			
		||||
    private val CHAPTER_STATUS_CHANGES = 3
 | 
			
		||||
        /**
 | 
			
		||||
         * Id of the restartable which requests an updated list of chapters to the source.
 | 
			
		||||
         */
 | 
			
		||||
        private const val FETCH_CHAPTERS = 2
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Id of the restartable which listens for download status changes.
 | 
			
		||||
         */
 | 
			
		||||
        private const val CHAPTER_STATUS_CHANGES = 3
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onCreate(savedState: Bundle?) {
 | 
			
		||||
        super.onCreate(savedState)
 | 
			
		||||
 | 
			
		||||
        chaptersSubject = PublishSubject.create()
 | 
			
		||||
 | 
			
		||||
        startableLatestCache(DB_CHAPTERS,
 | 
			
		||||
                { getDbChaptersObs() },
 | 
			
		||||
        startableLatestCache(GET_CHAPTERS,
 | 
			
		||||
                // On each subject emission, apply filters and sort then update the view.
 | 
			
		||||
                { chaptersSubject
 | 
			
		||||
                        .flatMap { applyChapterFilters(it) }
 | 
			
		||||
                        .observeOn(AndroidSchedulers.mainThread()) },
 | 
			
		||||
                { view, chapters -> view.onNextChapters(chapters) })
 | 
			
		||||
 | 
			
		||||
        startableFirst(FETCH_CHAPTERS,
 | 
			
		||||
                { getOnlineChaptersObs() },
 | 
			
		||||
                { getRemoteChaptersObservable() },
 | 
			
		||||
                { view, result -> view.onFetchChaptersDone() },
 | 
			
		||||
                { view, error -> view.onFetchChaptersError(error) })
 | 
			
		||||
 | 
			
		||||
        startableLatestCache(CHAPTER_STATUS_CHANGES,
 | 
			
		||||
                { getChapterStatusObs() },
 | 
			
		||||
                { getChapterStatusObservable() },
 | 
			
		||||
                { view, download -> view.onChapterStatusChange(download) },
 | 
			
		||||
                { view, error -> Timber.e(error.cause, error.message) })
 | 
			
		||||
 | 
			
		||||
        // Find the active manga from the shared data or return.
 | 
			
		||||
        manga = SharedData.get(MangaEvent::class.java)?.manga ?: return
 | 
			
		||||
        Observable.just(manga)
 | 
			
		||||
                .subscribeLatestCache({ view, manga -> view.onNextManga(manga) })
 | 
			
		||||
 | 
			
		||||
        // Find the source for this manga.
 | 
			
		||||
        source = sourceManager.get(manga.source)!!
 | 
			
		||||
        start(DB_CHAPTERS)
 | 
			
		||||
 | 
			
		||||
        // Prepare the publish subject.
 | 
			
		||||
        start(GET_CHAPTERS)
 | 
			
		||||
 | 
			
		||||
        // Add the subscription that retrieves the chapters from the database, keeps subscribed to
 | 
			
		||||
        // changes, and sends the list of chapters to the publish subject.
 | 
			
		||||
        add(db.getChapters(manga).asRxObservable()
 | 
			
		||||
                .map { chapters ->
 | 
			
		||||
                    // Convert every chapter to a model.
 | 
			
		||||
                    chapters.map { it.toModel() }
 | 
			
		||||
                }
 | 
			
		||||
                .doOnNext { chapters ->
 | 
			
		||||
                    // Store the last emission
 | 
			
		||||
                    this.chapters = chapters
 | 
			
		||||
                    SharedData.get(ChapterCountEvent::class.java)?.emit(chapters.size)
 | 
			
		||||
                    for (chapter in chapters) {
 | 
			
		||||
                        setChapterStatus(chapter)
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // Listen for download status changes
 | 
			
		||||
                    start(CHAPTER_STATUS_CHANGES)
 | 
			
		||||
 | 
			
		||||
                    // Emit the number of chapters to the info tab.
 | 
			
		||||
                    SharedData.get(ChapterCountEvent::class.java)?.emit(chapters.size)
 | 
			
		||||
                }
 | 
			
		||||
                .subscribe { chaptersSubject.onNext(it) })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Converts a chapter from the database to an extended model, allowing to store new fields.
 | 
			
		||||
     */
 | 
			
		||||
    private fun Chapter.toModel(): ChapterModel {
 | 
			
		||||
        // Create the model object.
 | 
			
		||||
        val model = ChapterModel(this)
 | 
			
		||||
 | 
			
		||||
        // Find an active download for this chapter.
 | 
			
		||||
        val download = downloadManager.queue.find { it.chapter.id == id }
 | 
			
		||||
 | 
			
		||||
        if (download != null) {
 | 
			
		||||
            // If there's an active download, assign it.
 | 
			
		||||
            model.download = download
 | 
			
		||||
        } else {
 | 
			
		||||
            // Otherwise ask the manager if the chapter is downloaded and assign it to the status.
 | 
			
		||||
            model.status = if (downloadManager.isChapterDownloaded(source, manga, this))
 | 
			
		||||
                Download.DOWNLOADED
 | 
			
		||||
            else
 | 
			
		||||
                Download.NOT_DOWNLOADED
 | 
			
		||||
        }
 | 
			
		||||
        return model
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Requests an updated list of chapters from the source.
 | 
			
		||||
     */
 | 
			
		||||
    fun fetchChaptersFromSource() {
 | 
			
		||||
        hasRequested = true
 | 
			
		||||
        start(FETCH_CHAPTERS)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Updates the UI after applying the filters.
 | 
			
		||||
     */
 | 
			
		||||
    private fun refreshChapters() {
 | 
			
		||||
        chaptersSubject.onNext(chapters)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getOnlineChaptersObs(): Observable<Pair<Int, Int>> {
 | 
			
		||||
        return source.fetchChapterList(manga)
 | 
			
		||||
                .subscribeOn(Schedulers.io())
 | 
			
		||||
                .map { syncChaptersWithSource(db, it, manga, source) }
 | 
			
		||||
                .observeOn(AndroidSchedulers.mainThread())
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns an observable that updates the chapter list with the latest from the source.
 | 
			
		||||
     */
 | 
			
		||||
    fun getRemoteChaptersObservable() = source.fetchChapterList(manga)
 | 
			
		||||
            .subscribeOn(Schedulers.io())
 | 
			
		||||
            .map { syncChaptersWithSource(db, it, manga, source) }
 | 
			
		||||
            .observeOn(AndroidSchedulers.mainThread())
 | 
			
		||||
 | 
			
		||||
    fun getDbChaptersObs(): Observable<List<Chapter>> {
 | 
			
		||||
        return chaptersSubject
 | 
			
		||||
                .flatMap { applyChapterFilters(it) }
 | 
			
		||||
                .observeOn(AndroidSchedulers.mainThread())
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns an observable that listens to download queue status changes.
 | 
			
		||||
     */
 | 
			
		||||
    fun getChapterStatusObservable() = downloadManager.queue.getStatusObservable()
 | 
			
		||||
            .observeOn(AndroidSchedulers.mainThread())
 | 
			
		||||
            .filter { download -> download.manga.id == manga.id }
 | 
			
		||||
            .doOnNext { onDownloadStatusChange(it) }
 | 
			
		||||
 | 
			
		||||
    fun getChapterStatusObs(): Observable<Download> {
 | 
			
		||||
        return downloadManager.queue.getStatusObservable()
 | 
			
		||||
                .observeOn(AndroidSchedulers.mainThread())
 | 
			
		||||
                .filter { download -> download.manga.id == manga.id }
 | 
			
		||||
                .doOnNext { updateChapterStatus(it) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun applyChapterFilters(chapters: List<Chapter>): Observable<List<Chapter>> {
 | 
			
		||||
    /**
 | 
			
		||||
     * Applies the view filters to the list of chapters obtained from the database.
 | 
			
		||||
     *
 | 
			
		||||
     * @param chapters the list of chapters from the database
 | 
			
		||||
     * @return an observable of the list of chapters filtered and sorted.
 | 
			
		||||
     */
 | 
			
		||||
    private fun applyChapterFilters(chapters: List<ChapterModel>): Observable<List<ChapterModel>> {
 | 
			
		||||
        var observable = Observable.from(chapters).subscribeOn(Schedulers.io())
 | 
			
		||||
        if (onlyUnread()) {
 | 
			
		||||
            observable = observable.filter { chapter -> !chapter.read }
 | 
			
		||||
            observable = observable.filter { !it.read }
 | 
			
		||||
        }
 | 
			
		||||
        if (onlyDownloaded()) {
 | 
			
		||||
            observable = observable.filter { chapter -> chapter.status == Download.DOWNLOADED }
 | 
			
		||||
            observable = observable.filter { it.isDownloaded }
 | 
			
		||||
        }
 | 
			
		||||
        val sortFunction: (Chapter, Chapter) -> Int = when (manga.sorting) {
 | 
			
		||||
            Manga.SORTING_SOURCE -> when (sortDescending()) {
 | 
			
		||||
@@ -137,37 +226,40 @@ class ChaptersPresenter : BasePresenter<ChaptersFragment>() {
 | 
			
		||||
        return observable.toSortedList(sortFunction)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun setChapterStatus(chapter: Chapter) {
 | 
			
		||||
        for (download in downloadManager.queue) {
 | 
			
		||||
            if (chapter.id == download.chapter.id) {
 | 
			
		||||
                chapter.status = download.status
 | 
			
		||||
                return
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when a download for the active manga changes status.
 | 
			
		||||
     *
 | 
			
		||||
     * @param download the download whose status changed.
 | 
			
		||||
     */
 | 
			
		||||
    fun onDownloadStatusChange(download: Download) {
 | 
			
		||||
        // Assign the download to the model object.
 | 
			
		||||
        if (download.status == Download.QUEUE) {
 | 
			
		||||
            chapters.find { it.id == download.chapter.id }?.let {
 | 
			
		||||
                if (it.download == null) {
 | 
			
		||||
                    it.download = download
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (downloadManager.isChapterDownloaded(source, manga, chapter)) {
 | 
			
		||||
            chapter.status = Download.DOWNLOADED
 | 
			
		||||
        } else {
 | 
			
		||||
            chapter.status = Download.NOT_DOWNLOADED
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun updateChapterStatus(download: Download) {
 | 
			
		||||
        for (chapter in chapters) {
 | 
			
		||||
            if (download.chapter.id == chapter.id) {
 | 
			
		||||
                chapter.status = download.status
 | 
			
		||||
                break
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // Force UI update if downloaded filter active and download finished.
 | 
			
		||||
        if (onlyDownloaded() && download.status == Download.DOWNLOADED)
 | 
			
		||||
            refreshChapters()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getNextUnreadChapter(): Chapter? {
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the next unread chapter or null if everything is read.
 | 
			
		||||
     */
 | 
			
		||||
    fun getNextUnreadChapter(): ChapterModel? {
 | 
			
		||||
        return chapters.sortedByDescending { it.source_order }.find { !it.read }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun markChaptersRead(selectedChapters: List<Chapter>, read: Boolean) {
 | 
			
		||||
    /**
 | 
			
		||||
     * Mark the selected chapter list as read/unread.
 | 
			
		||||
     *
 | 
			
		||||
     * @param selectedChapters the list of selected chapters.
 | 
			
		||||
     * @param read whether to mark chapters as read or unread.
 | 
			
		||||
     */
 | 
			
		||||
    fun markChaptersRead(selectedChapters: List<ChapterModel>, read: Boolean) {
 | 
			
		||||
        Observable.from(selectedChapters)
 | 
			
		||||
                .doOnNext { chapter ->
 | 
			
		||||
                    chapter.read = read
 | 
			
		||||
@@ -181,21 +273,36 @@ class ChaptersPresenter : BasePresenter<ChaptersFragment>() {
 | 
			
		||||
                .subscribe()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun markPreviousChaptersAsRead(selected: Chapter) {
 | 
			
		||||
    /**
 | 
			
		||||
     * Mark the previous chapters to the selected one as read.
 | 
			
		||||
     *
 | 
			
		||||
     * @param chapter the selected chapter.
 | 
			
		||||
     */
 | 
			
		||||
    fun markPreviousChaptersAsRead(chapter: ChapterModel) {
 | 
			
		||||
        Observable.from(chapters)
 | 
			
		||||
                .filter { it.isRecognizedNumber && it.chapter_number < selected.chapter_number }
 | 
			
		||||
                .filter { it.isRecognizedNumber && it.chapter_number < chapter.chapter_number }
 | 
			
		||||
                .doOnNext { it.read = true }
 | 
			
		||||
                .toList()
 | 
			
		||||
                .flatMap { db.updateChaptersProgress(it).asRxObservable() }
 | 
			
		||||
                .subscribe()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun downloadChapters(chapters: List<Chapter>) {
 | 
			
		||||
    /**
 | 
			
		||||
     * Downloads the given list of chapters with the manager.
 | 
			
		||||
     *
 | 
			
		||||
     * @param chapters the list of chapters to download.
 | 
			
		||||
     */
 | 
			
		||||
    fun downloadChapters(chapters: List<ChapterModel>) {
 | 
			
		||||
        DownloadService.start(context)
 | 
			
		||||
        downloadManager.downloadChapters(manga, chapters)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun deleteChapters(chapters: List<Chapter>) {
 | 
			
		||||
    /**
 | 
			
		||||
     * Deletes the given list of chapter.
 | 
			
		||||
     *
 | 
			
		||||
     * @param chapters the list of chapters to delete.
 | 
			
		||||
     */
 | 
			
		||||
    fun deleteChapters(chapters: List<ChapterModel>) {
 | 
			
		||||
        val wasRunning = downloadManager.isRunning
 | 
			
		||||
        if (wasRunning) {
 | 
			
		||||
            DownloadService.stop(context)
 | 
			
		||||
@@ -216,49 +323,97 @@ class ChaptersPresenter : BasePresenter<ChaptersFragment>() {
 | 
			
		||||
                })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun deleteChapter(chapter: Chapter) {
 | 
			
		||||
    /**
 | 
			
		||||
     * Deletes a chapter from disk. This method is called in a background thread.
 | 
			
		||||
     *
 | 
			
		||||
     * @param chapter the chapter to delete.
 | 
			
		||||
     */
 | 
			
		||||
    private fun deleteChapter(chapter: ChapterModel) {
 | 
			
		||||
        downloadManager.queue.del(chapter)
 | 
			
		||||
        downloadManager.deleteChapter(source, manga, chapter)
 | 
			
		||||
        chapter.status = Download.NOT_DOWNLOADED
 | 
			
		||||
        chapter.download = null
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Reverses the sorting and requests an UI update.
 | 
			
		||||
     */
 | 
			
		||||
    fun revertSortOrder() {
 | 
			
		||||
        manga.setChapterOrder(if (sortDescending()) Manga.SORT_ASC else Manga.SORT_DESC)
 | 
			
		||||
        db.updateFlags(manga).executeAsBlocking()
 | 
			
		||||
        refreshChapters()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sets the read filter and requests an UI update.
 | 
			
		||||
     *
 | 
			
		||||
     * @param onlyUnread whether to display only unread chapters or all chapters.
 | 
			
		||||
     */
 | 
			
		||||
    fun setReadFilter(onlyUnread: Boolean) {
 | 
			
		||||
        manga.readFilter = if (onlyUnread) Manga.SHOW_UNREAD else Manga.SHOW_ALL
 | 
			
		||||
        db.updateFlags(manga).executeAsBlocking()
 | 
			
		||||
        refreshChapters()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sets the download filter and requests an UI update.
 | 
			
		||||
     *
 | 
			
		||||
     * @param onlyDownloaded whether to display only downloaded chapters or all chapters.
 | 
			
		||||
     */
 | 
			
		||||
    fun setDownloadedFilter(onlyDownloaded: Boolean) {
 | 
			
		||||
        manga.downloadedFilter = if (onlyDownloaded) Manga.SHOW_DOWNLOADED else Manga.SHOW_ALL
 | 
			
		||||
        db.updateFlags(manga).executeAsBlocking()
 | 
			
		||||
        refreshChapters()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Removes all filters and requests an UI update.
 | 
			
		||||
     */
 | 
			
		||||
    fun removeFilters() {
 | 
			
		||||
        manga.readFilter = Manga.SHOW_ALL
 | 
			
		||||
        manga.downloadedFilter = Manga.SHOW_ALL
 | 
			
		||||
        db.updateFlags(manga).executeAsBlocking()
 | 
			
		||||
        refreshChapters()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sets the active display mode.
 | 
			
		||||
     *
 | 
			
		||||
     * @param mode the mode to set.
 | 
			
		||||
     */
 | 
			
		||||
    fun setDisplayMode(mode: Int) {
 | 
			
		||||
        manga.displayMode = mode
 | 
			
		||||
        db.updateFlags(manga).executeAsBlocking()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun setSorting(mode: Int) {
 | 
			
		||||
        manga.sorting = mode
 | 
			
		||||
    /**
 | 
			
		||||
     * Sets the sorting method and requests an UI update.
 | 
			
		||||
     *
 | 
			
		||||
     * @param sort the sorting mode.
 | 
			
		||||
     */
 | 
			
		||||
    fun setSorting(sort: Int) {
 | 
			
		||||
        manga.sorting = sort
 | 
			
		||||
        db.updateFlags(manga).executeAsBlocking()
 | 
			
		||||
        refreshChapters()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether the display only downloaded filter is enabled.
 | 
			
		||||
     */
 | 
			
		||||
    fun onlyDownloaded(): Boolean {
 | 
			
		||||
        return manga.downloadedFilter == Manga.SHOW_DOWNLOADED
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether the display only unread filter is enabled.
 | 
			
		||||
     */
 | 
			
		||||
    fun onlyUnread(): Boolean {
 | 
			
		||||
        return manga.readFilter == Manga.SHOW_UNREAD
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether the sorting method is descending or ascending.
 | 
			
		||||
     */
 | 
			
		||||
    fun sortDescending(): Boolean {
 | 
			
		||||
        return manga.sortDescending()
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -104,7 +104,12 @@ class MangaInfoFragment : BaseRxFragment<MangaInfoPresenter>() {
 | 
			
		||||
        manga_genres.text = manga.genre
 | 
			
		||||
 | 
			
		||||
        // Update status TextView.
 | 
			
		||||
        manga_status.text = manga.getStatus(activity)
 | 
			
		||||
        manga_status.setText(when (manga.status) {
 | 
			
		||||
            Manga.ONGOING -> R.string.ongoing
 | 
			
		||||
            Manga.COMPLETED -> R.string.completed
 | 
			
		||||
            Manga.LICENSED -> R.string.licensed
 | 
			
		||||
            else -> R.string.unknown
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        // Update description TextView.
 | 
			
		||||
        manga_summary.text = manga.description
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ import eu.kanade.tachiyomi.util.SharedData
 | 
			
		||||
import rx.Observable
 | 
			
		||||
import rx.android.schedulers.AndroidSchedulers
 | 
			
		||||
import rx.schedulers.Schedulers
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Presenter of MangaInfoFragment.
 | 
			
		||||
@@ -36,17 +36,17 @@ class MangaInfoPresenter : BasePresenter<MangaInfoFragment>() {
 | 
			
		||||
    /**
 | 
			
		||||
     * Used to connect to database.
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var db: DatabaseHelper
 | 
			
		||||
    val db: DatabaseHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Used to connect to different manga sources.
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var sourceManager: SourceManager
 | 
			
		||||
    val sourceManager: SourceManager by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Used to connect to cache.
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var coverCache: CoverCache
 | 
			
		||||
    val coverCache: CoverCache by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The id of the restartable.
 | 
			
		||||
 
 | 
			
		||||
@@ -15,12 +15,12 @@ import rx.Observable
 | 
			
		||||
import rx.android.schedulers.AndroidSchedulers
 | 
			
		||||
import rx.schedulers.Schedulers
 | 
			
		||||
import timber.log.Timber
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
 | 
			
		||||
class MyAnimeListPresenter : BasePresenter<MyAnimeListFragment>() {
 | 
			
		||||
 | 
			
		||||
    @Inject lateinit var db: DatabaseHelper
 | 
			
		||||
    @Inject lateinit var syncManager: MangaSyncManager
 | 
			
		||||
    val db: DatabaseHelper by injectLazy()
 | 
			
		||||
    val syncManager: MangaSyncManager by injectLazy()
 | 
			
		||||
 | 
			
		||||
    val myAnimeList by lazy { syncManager.myAnimeList }
 | 
			
		||||
 | 
			
		||||
@@ -124,7 +124,7 @@ class MyAnimeListPresenter : BasePresenter<MyAnimeListFragment>() {
 | 
			
		||||
 | 
			
		||||
    fun registerManga(sync: MangaSync?) {
 | 
			
		||||
        if (sync != null) {
 | 
			
		||||
            sync.manga_id = manga.id
 | 
			
		||||
            sync.manga_id = manga.id!!
 | 
			
		||||
            add(myAnimeList.bind(sync)
 | 
			
		||||
                    .flatMap { db.insertMangaSync(sync).asRxObservable() }
 | 
			
		||||
                    .subscribeOn(Schedulers.io())
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,22 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.recent_updates
 | 
			
		||||
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Chapter
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.MangaChapter
 | 
			
		||||
import eu.kanade.tachiyomi.data.download.model.Download
 | 
			
		||||
 | 
			
		||||
class RecentChapter(mc: MangaChapter) : Chapter by mc.chapter {
 | 
			
		||||
 | 
			
		||||
    val manga = mc.manga
 | 
			
		||||
 | 
			
		||||
    private var _status: Int = 0
 | 
			
		||||
 | 
			
		||||
    var status: Int
 | 
			
		||||
        get() = download?.status ?: _status
 | 
			
		||||
        set(value) { _status = value }
 | 
			
		||||
 | 
			
		||||
    var download: Download? = null
 | 
			
		||||
 | 
			
		||||
    val isDownloaded: Boolean
 | 
			
		||||
        get() = status == Download.DOWNLOADED
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -5,7 +5,6 @@ import android.view.View
 | 
			
		||||
import android.view.ViewGroup
 | 
			
		||||
import eu.davidea.flexibleadapter.FlexibleAdapter
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.MangaChapter
 | 
			
		||||
import eu.kanade.tachiyomi.util.inflate
 | 
			
		||||
import java.util.*
 | 
			
		||||
 | 
			
		||||
@@ -18,7 +17,8 @@ import java.util.*
 | 
			
		||||
 * @constructor creates an instance of the adapter.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
class RecentChaptersAdapter(val fragment: RecentChaptersFragment) : FlexibleAdapter<RecyclerView.ViewHolder, Any>() {
 | 
			
		||||
class RecentChaptersAdapter(val fragment: RecentChaptersFragment)
 | 
			
		||||
: FlexibleAdapter<RecyclerView.ViewHolder, Any>() {
 | 
			
		||||
    /**
 | 
			
		||||
     * The id of the view type
 | 
			
		||||
     */
 | 
			
		||||
@@ -45,7 +45,7 @@ class RecentChaptersAdapter(val fragment: RecentChaptersFragment) : FlexibleAdap
 | 
			
		||||
        val item = getItem(position)
 | 
			
		||||
        when (holder.itemViewType) {
 | 
			
		||||
            VIEW_TYPE_CHAPTER -> {
 | 
			
		||||
                if (item is MangaChapter) {
 | 
			
		||||
                if (item is RecentChapter) {
 | 
			
		||||
                    (holder as RecentChaptersHolder).onSetValues(item)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -89,7 +89,7 @@ class RecentChaptersAdapter(val fragment: RecentChaptersFragment) : FlexibleAdap
 | 
			
		||||
     * @param position position of item
 | 
			
		||||
     */
 | 
			
		||||
    override fun getItemViewType(position: Int): Int {
 | 
			
		||||
        return if (getItem(position) is MangaChapter) VIEW_TYPE_CHAPTER else VIEW_TYPE_SECTION
 | 
			
		||||
        return if (getItem(position) is RecentChapter) VIEW_TYPE_CHAPTER else VIEW_TYPE_SECTION
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -110,8 +110,8 @@ class RecentChaptersAdapter(val fragment: RecentChaptersFragment) : FlexibleAdap
 | 
			
		||||
     */
 | 
			
		||||
    override fun getItemId(position: Int): Long {
 | 
			
		||||
        val item = getItem(position)
 | 
			
		||||
        if (item is MangaChapter)
 | 
			
		||||
            return item.chapter.id
 | 
			
		||||
        if (item is RecentChapter)
 | 
			
		||||
            return item.id!!
 | 
			
		||||
        else
 | 
			
		||||
            return item.hashCode().toLong()
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -7,14 +7,12 @@ import android.view.*
 | 
			
		||||
import com.afollestad.materialdialogs.MaterialDialog
 | 
			
		||||
import eu.davidea.flexibleadapter.FlexibleAdapter
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.MangaChapter
 | 
			
		||||
import eu.kanade.tachiyomi.data.download.model.Download
 | 
			
		||||
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder
 | 
			
		||||
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment
 | 
			
		||||
import eu.kanade.tachiyomi.ui.main.MainActivity
 | 
			
		||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
 | 
			
		||||
import eu.kanade.tachiyomi.util.getResourceDrawable
 | 
			
		||||
import eu.kanade.tachiyomi.util.toast
 | 
			
		||||
import eu.kanade.tachiyomi.widget.DeletingChaptersDialog
 | 
			
		||||
import eu.kanade.tachiyomi.widget.DividerItemDecoration
 | 
			
		||||
import eu.kanade.tachiyomi.widget.NpaLinearLayoutManager
 | 
			
		||||
@@ -28,7 +26,9 @@ import timber.log.Timber
 | 
			
		||||
 * UI related actions should be called from here.
 | 
			
		||||
 */
 | 
			
		||||
@RequiresPresenter(RecentChaptersPresenter::class)
 | 
			
		||||
class RecentChaptersFragment : BaseRxFragment<RecentChaptersPresenter>(), ActionMode.Callback, FlexibleViewHolder.OnListItemClickListener {
 | 
			
		||||
class RecentChaptersFragment
 | 
			
		||||
: BaseRxFragment<RecentChaptersPresenter>(), ActionMode.Callback, FlexibleViewHolder.OnListItemClickListener {
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        /**
 | 
			
		||||
         * Create new RecentChaptersFragment.
 | 
			
		||||
@@ -40,6 +40,230 @@ class RecentChaptersFragment : BaseRxFragment<RecentChaptersPresenter>(), Action
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Action mode for multiple selection.
 | 
			
		||||
     */
 | 
			
		||||
    private var actionMode: ActionMode? = null
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Adapter containing the recent chapters.
 | 
			
		||||
     */
 | 
			
		||||
    lateinit var adapter: RecentChaptersAdapter
 | 
			
		||||
        private set
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when view gets created
 | 
			
		||||
     * @param inflater layout inflater
 | 
			
		||||
     * @param container view group
 | 
			
		||||
     * @param savedState status of saved state
 | 
			
		||||
     */
 | 
			
		||||
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedState: Bundle?): View {
 | 
			
		||||
        // Inflate view
 | 
			
		||||
        return inflater.inflate(R.layout.fragment_recent_chapters, container, false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when view is created
 | 
			
		||||
     * @param view created view
 | 
			
		||||
     * @param savedInstanceState status of saved sate
 | 
			
		||||
     */
 | 
			
		||||
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
 | 
			
		||||
        // Init RecyclerView and adapter
 | 
			
		||||
        recycler.layoutManager = NpaLinearLayoutManager(activity)
 | 
			
		||||
        recycler.addItemDecoration(DividerItemDecoration(context.theme.getResourceDrawable(R.attr.divider_drawable)))
 | 
			
		||||
        recycler.setHasFixedSize(true)
 | 
			
		||||
        adapter = RecentChaptersAdapter(this)
 | 
			
		||||
        recycler.adapter = adapter
 | 
			
		||||
 | 
			
		||||
        // Update toolbar text
 | 
			
		||||
        setToolbarTitle(R.string.label_recent_updates)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns selected chapters
 | 
			
		||||
     * @return list of selected chapters
 | 
			
		||||
     */
 | 
			
		||||
    fun getSelectedChapters(): List<RecentChapter> {
 | 
			
		||||
        return adapter.selectedItems.map { adapter.getItem(it) as? RecentChapter }.filterNotNull()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when item in list is clicked
 | 
			
		||||
     * @param position position of clicked item
 | 
			
		||||
     */
 | 
			
		||||
    override fun onListItemClick(position: Int): Boolean {
 | 
			
		||||
        // Get item from position
 | 
			
		||||
        val item = adapter.getItem(position)
 | 
			
		||||
        if (item is RecentChapter) {
 | 
			
		||||
            if (actionMode != null && adapter.mode == FlexibleAdapter.MODE_MULTI) {
 | 
			
		||||
                toggleSelection(position)
 | 
			
		||||
                return true
 | 
			
		||||
            } else {
 | 
			
		||||
                openChapter(item)
 | 
			
		||||
                return false
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return false
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when item in list is long clicked
 | 
			
		||||
     * @param position position of clicked item
 | 
			
		||||
     */
 | 
			
		||||
    override fun onListItemLongClick(position: Int) {
 | 
			
		||||
        if (actionMode == null)
 | 
			
		||||
            actionMode = activity.startSupportActionMode(this)
 | 
			
		||||
 | 
			
		||||
        toggleSelection(position)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called to toggle selection
 | 
			
		||||
     * @param position position of selected item
 | 
			
		||||
     */
 | 
			
		||||
    private fun toggleSelection(position: Int) {
 | 
			
		||||
        adapter.toggleSelection(position, false)
 | 
			
		||||
 | 
			
		||||
        val count = adapter.selectedItemCount
 | 
			
		||||
        if (count == 0) {
 | 
			
		||||
            actionMode?.finish()
 | 
			
		||||
        } else {
 | 
			
		||||
            setContextTitle(count)
 | 
			
		||||
            actionMode?.invalidate()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set the context title
 | 
			
		||||
     * @param count count of selected items
 | 
			
		||||
     */
 | 
			
		||||
    private fun setContextTitle(count: Int) {
 | 
			
		||||
        actionMode?.title = getString(R.string.label_selected, count)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Open chapter in reader
 | 
			
		||||
     * @param chapter selected chapter
 | 
			
		||||
     */
 | 
			
		||||
    private fun openChapter(chapter: RecentChapter) {
 | 
			
		||||
        val intent = ReaderActivity.newIntent(activity, chapter.manga, chapter)
 | 
			
		||||
        startActivity(intent)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Download selected items
 | 
			
		||||
     * @param chapters list of selected [RecentChapter]s
 | 
			
		||||
     */
 | 
			
		||||
    fun downloadChapters(chapters: List<RecentChapter>) {
 | 
			
		||||
        destroyActionModeIfNeeded()
 | 
			
		||||
        presenter.downloadChapters(chapters)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Populate adapter with chapters
 | 
			
		||||
     * @param chapters list of [Any]
 | 
			
		||||
     */
 | 
			
		||||
    fun onNextRecentChapters(chapters: List<Any>) {
 | 
			
		||||
        (activity as MainActivity).updateEmptyView(chapters.isEmpty(),
 | 
			
		||||
                R.string.information_no_recent, R.drawable.ic_update_black_128dp)
 | 
			
		||||
 | 
			
		||||
        destroyActionModeIfNeeded()
 | 
			
		||||
        adapter.setItems(chapters)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update download status of chapter
 | 
			
		||||
     * @param download [Download] object containing download progress.
 | 
			
		||||
     */
 | 
			
		||||
    fun onChapterStatusChange(download: Download) {
 | 
			
		||||
        getHolder(download)?.notifyStatus(download.status)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns holder belonging to chapter
 | 
			
		||||
     * @param download [Download] object containing download progress.
 | 
			
		||||
     */
 | 
			
		||||
    private fun getHolder(download: Download): RecentChaptersHolder? {
 | 
			
		||||
        return recycler.findViewHolderForItemId(download.chapter.id!!) as? RecentChaptersHolder
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Mark chapter as read
 | 
			
		||||
     * @param chapters list of chapters
 | 
			
		||||
     */
 | 
			
		||||
    fun markAsRead(chapters: List<RecentChapter>) {
 | 
			
		||||
        presenter.markChapterRead(chapters, true)
 | 
			
		||||
        if (presenter.preferences.removeAfterMarkedAsRead()) {
 | 
			
		||||
            deleteChapters(chapters)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete selected chapters
 | 
			
		||||
     * @param chapters list of [RecentChapter] objects
 | 
			
		||||
     */
 | 
			
		||||
    fun deleteChapters(chapters: List<RecentChapter>) {
 | 
			
		||||
        destroyActionModeIfNeeded()
 | 
			
		||||
        DeletingChaptersDialog().show(childFragmentManager, DeletingChaptersDialog.TAG)
 | 
			
		||||
        presenter.deleteChapters(chapters)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Destory [ActionMode] if it's shown
 | 
			
		||||
     */
 | 
			
		||||
    fun destroyActionModeIfNeeded() {
 | 
			
		||||
        actionMode?.finish()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Mark chapter as unread
 | 
			
		||||
     * @param chapters list of selected [RecentChapter]
 | 
			
		||||
     */
 | 
			
		||||
    fun markAsUnread(chapters: List<RecentChapter>) {
 | 
			
		||||
        presenter.markChapterRead(chapters, false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Start downloading chapter
 | 
			
		||||
     * @param chapter selected chapter with manga
 | 
			
		||||
     */
 | 
			
		||||
    fun downloadChapter(chapter: RecentChapter) {
 | 
			
		||||
        presenter.downloadChapter(chapter)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Start deleting chapter
 | 
			
		||||
     * @param chapter selected chapter with manga
 | 
			
		||||
     */
 | 
			
		||||
    fun deleteChapter(chapter: RecentChapter) {
 | 
			
		||||
        DeletingChaptersDialog().show(childFragmentManager, DeletingChaptersDialog.TAG)
 | 
			
		||||
        presenter.deleteChapters(listOf(chapter))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when chapters are deleted
 | 
			
		||||
     */
 | 
			
		||||
    fun onChaptersDeleted() {
 | 
			
		||||
        dismissDeletingDialog()
 | 
			
		||||
        adapter.notifyDataSetChanged()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when error while deleting
 | 
			
		||||
     * @param error error message
 | 
			
		||||
     */
 | 
			
		||||
    fun onChaptersDeletedError(error: Throwable) {
 | 
			
		||||
        dismissDeletingDialog()
 | 
			
		||||
        Timber.e(error, error.message)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called to dismiss deleting dialog
 | 
			
		||||
     */
 | 
			
		||||
    fun dismissDeletingDialog() {
 | 
			
		||||
        (childFragmentManager.findFragmentByTag(DeletingChaptersDialog.TAG) as? DialogFragment)?.dismiss()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
 | 
			
		||||
        return false
 | 
			
		||||
    }
 | 
			
		||||
@@ -88,229 +312,4 @@ class RecentChaptersFragment : BaseRxFragment<RecentChaptersPresenter>(), Action
 | 
			
		||||
        actionMode = null
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Action mode for multiple selection.
 | 
			
		||||
     */
 | 
			
		||||
    private var actionMode: ActionMode? = null
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Adapter containing the recent chapters.
 | 
			
		||||
     */
 | 
			
		||||
    lateinit var adapter: RecentChaptersAdapter
 | 
			
		||||
        private set
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when view gets created
 | 
			
		||||
     * @param inflater layout inflater
 | 
			
		||||
     * @param container view group
 | 
			
		||||
     * @param savedState status of saved state
 | 
			
		||||
     */
 | 
			
		||||
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedState: Bundle?): View? {
 | 
			
		||||
        // Inflate view
 | 
			
		||||
        return inflater.inflate(R.layout.fragment_recent_chapters, container, false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when view is created
 | 
			
		||||
     * @param view created view
 | 
			
		||||
     * @param savedInstanceState status of saved sate
 | 
			
		||||
     */
 | 
			
		||||
    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
 | 
			
		||||
        // Init RecyclerView and adapter
 | 
			
		||||
        recycler.layoutManager = NpaLinearLayoutManager(activity)
 | 
			
		||||
        recycler.addItemDecoration(DividerItemDecoration(context.theme.getResourceDrawable(R.attr.divider_drawable)))
 | 
			
		||||
        recycler.setHasFixedSize(true)
 | 
			
		||||
        adapter = RecentChaptersAdapter(this)
 | 
			
		||||
        recycler.adapter = adapter
 | 
			
		||||
 | 
			
		||||
        // Update toolbar text
 | 
			
		||||
        setToolbarTitle(R.string.label_recent_updates)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns selected chapters
 | 
			
		||||
     * @return list of [MangaChapter]s
 | 
			
		||||
     */
 | 
			
		||||
    fun getSelectedChapters(): List<MangaChapter> {
 | 
			
		||||
        return adapter.selectedItems.map { adapter.getItem(it) as? MangaChapter }.filterNotNull()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when item in list is clicked
 | 
			
		||||
     * @param position position of clicked item
 | 
			
		||||
     */
 | 
			
		||||
    override fun onListItemClick(position: Int): Boolean {
 | 
			
		||||
        // Get item from position
 | 
			
		||||
        val item = adapter.getItem(position)
 | 
			
		||||
        if (item is MangaChapter) {
 | 
			
		||||
            if (actionMode != null && adapter.mode == FlexibleAdapter.MODE_MULTI) {
 | 
			
		||||
                toggleSelection(position)
 | 
			
		||||
                return true
 | 
			
		||||
            } else {
 | 
			
		||||
                openChapter(item)
 | 
			
		||||
                return false
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return false
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when item in list is long clicked
 | 
			
		||||
     * @param position position of clicked item
 | 
			
		||||
     */
 | 
			
		||||
    override fun onListItemLongClick(position: Int) {
 | 
			
		||||
        if (actionMode == null)
 | 
			
		||||
            actionMode = activity.startSupportActionMode(this)
 | 
			
		||||
 | 
			
		||||
        toggleSelection(position)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called to toggle selection
 | 
			
		||||
     * @param position position of selected item
 | 
			
		||||
     */
 | 
			
		||||
    private fun toggleSelection(position: Int) {
 | 
			
		||||
        adapter.toggleSelection(position, false)
 | 
			
		||||
 | 
			
		||||
        val count = adapter.selectedItemCount
 | 
			
		||||
        if (count == 0) {
 | 
			
		||||
            actionMode?.finish()
 | 
			
		||||
        } else {
 | 
			
		||||
            setContextTitle(count)
 | 
			
		||||
            actionMode?.invalidate()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set the context title
 | 
			
		||||
     * @param count count of selected items
 | 
			
		||||
     */
 | 
			
		||||
    private fun setContextTitle(count: Int) {
 | 
			
		||||
        actionMode?.title = getString(R.string.label_selected, count)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Open chapter in reader
 | 
			
		||||
     * @param mangaChapter selected [MangaChapter]
 | 
			
		||||
     */
 | 
			
		||||
    private fun openChapter(mangaChapter: MangaChapter) {
 | 
			
		||||
        val intent = ReaderActivity.newIntent(activity, mangaChapter.manga, mangaChapter.chapter)
 | 
			
		||||
        startActivity(intent)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Download selected items
 | 
			
		||||
     * @param mangaChapters list of selected [MangaChapter]s
 | 
			
		||||
     */
 | 
			
		||||
    fun downloadChapters(mangaChapters: List<MangaChapter>) {
 | 
			
		||||
        destroyActionModeIfNeeded()
 | 
			
		||||
        presenter.downloadChapters(mangaChapters)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Populate adapter with chapters
 | 
			
		||||
     * @param chapters list of [Any]
 | 
			
		||||
     */
 | 
			
		||||
    fun onNextMangaChapters(chapters: List<Any>) {
 | 
			
		||||
        (activity as MainActivity).updateEmptyView(chapters.isEmpty(),
 | 
			
		||||
                R.string.information_no_recent, R.drawable.ic_update_black_128dp)
 | 
			
		||||
 | 
			
		||||
        destroyActionModeIfNeeded()
 | 
			
		||||
        adapter.setItems(chapters)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update download status of chapter
 | 
			
		||||
     * @param download [Download] object containing download progress.
 | 
			
		||||
     */
 | 
			
		||||
    fun onChapterStatusChange(download: Download) {
 | 
			
		||||
        getHolder(download)?.onStatusChange(download.status)
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns holder belonging to chapter
 | 
			
		||||
     * @param download [Download] object containing download progress.
 | 
			
		||||
     */
 | 
			
		||||
    private fun getHolder(download: Download): RecentChaptersHolder? {
 | 
			
		||||
        return recycler.findViewHolderForItemId(download.chapter.id) as? RecentChaptersHolder
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Mark chapter as read
 | 
			
		||||
     * @param mangaChapters list of [MangaChapter] objects
 | 
			
		||||
     */
 | 
			
		||||
    fun markAsRead(mangaChapters: List<MangaChapter>) {
 | 
			
		||||
        presenter.markChapterRead(mangaChapters, true)
 | 
			
		||||
        if (presenter.preferences.removeAfterMarkedAsRead()) {
 | 
			
		||||
            deleteChapters(mangaChapters)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete selected chapters
 | 
			
		||||
     * @param mangaChapters list of [MangaChapter] objects
 | 
			
		||||
     */
 | 
			
		||||
    fun deleteChapters(mangaChapters: List<MangaChapter>) {
 | 
			
		||||
        destroyActionModeIfNeeded()
 | 
			
		||||
        DeletingChaptersDialog().show(childFragmentManager, DeletingChaptersDialog.TAG)
 | 
			
		||||
        presenter.deleteChapters(mangaChapters)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Destory [ActionMode] if it's shown
 | 
			
		||||
     */
 | 
			
		||||
    fun destroyActionModeIfNeeded() {
 | 
			
		||||
        actionMode?.finish()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Mark chapter as unread
 | 
			
		||||
     * @param mangaChapters list of selected [MangaChapter]
 | 
			
		||||
     */
 | 
			
		||||
    fun markAsUnread(mangaChapters: List<MangaChapter>) {
 | 
			
		||||
        presenter.markChapterRead(mangaChapters, false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Start downloading chapter
 | 
			
		||||
     * @param item selected chapter with manga
 | 
			
		||||
     */
 | 
			
		||||
    fun downloadChapter(item: MangaChapter) {
 | 
			
		||||
        presenter.downloadChapter(item)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Start deleting chapter
 | 
			
		||||
     * @param item selected chapter with manga
 | 
			
		||||
     */
 | 
			
		||||
    fun deleteChapter(item: MangaChapter) {
 | 
			
		||||
        DeletingChaptersDialog().show(childFragmentManager, DeletingChaptersDialog.TAG)
 | 
			
		||||
        presenter.deleteChapter(item)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when chapters are deleted
 | 
			
		||||
     */
 | 
			
		||||
    fun onChaptersDeleted() {
 | 
			
		||||
        dismissDeletingDialog()
 | 
			
		||||
        adapter.notifyDataSetChanged()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when error while deleting
 | 
			
		||||
     * @param error error message
 | 
			
		||||
     */
 | 
			
		||||
    fun onChaptersDeletedError(error: Throwable) {
 | 
			
		||||
        dismissDeletingDialog()
 | 
			
		||||
        Timber.e(error, error.message)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called to dismiss deleting dialog
 | 
			
		||||
     */
 | 
			
		||||
    fun dismissDeletingDialog() {
 | 
			
		||||
        (childFragmentManager.findFragmentByTag(DeletingChaptersDialog.TAG) as? DialogFragment)?.dismiss()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.ui.recent_updates
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.widget.PopupMenu
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.MangaChapter
 | 
			
		||||
import eu.kanade.tachiyomi.data.download.model.Download
 | 
			
		||||
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder
 | 
			
		||||
import eu.kanade.tachiyomi.util.getResourceColor
 | 
			
		||||
@@ -19,8 +18,11 @@ import kotlinx.android.synthetic.main.item_recent_chapters.view.*
 | 
			
		||||
 * @param listener a listener to react to single tap and long tap events.
 | 
			
		||||
 * @constructor creates a new recent chapter holder.
 | 
			
		||||
 */
 | 
			
		||||
class RecentChaptersHolder(view: View, private val adapter: RecentChaptersAdapter, listener: OnListItemClickListener) :
 | 
			
		||||
        FlexibleViewHolder(view, adapter, listener) {
 | 
			
		||||
class RecentChaptersHolder(
 | 
			
		||||
        private val view: View,
 | 
			
		||||
        private val adapter: RecentChaptersAdapter,
 | 
			
		||||
        listener: OnListItemClickListener)
 | 
			
		||||
: FlexibleViewHolder(view, adapter, listener) {
 | 
			
		||||
    /**
 | 
			
		||||
     * Color of read chapter
 | 
			
		||||
     */
 | 
			
		||||
@@ -34,100 +36,97 @@ class RecentChaptersHolder(view: View, private val adapter: RecentChaptersAdapte
 | 
			
		||||
    /**
 | 
			
		||||
     * Object containing chapter information
 | 
			
		||||
     */
 | 
			
		||||
    private var mangaChapter: MangaChapter? = null
 | 
			
		||||
    private var chapter: RecentChapter? = null
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        // We need to post a Runnable to show the popup to make sure that the PopupMenu is
 | 
			
		||||
        // correctly positioned. The reason being that the view may change position before the
 | 
			
		||||
        // PopupMenu is shown.
 | 
			
		||||
        itemView.chapter_menu.setOnClickListener { it.post({ showPopupMenu(it) }) }
 | 
			
		||||
        view.chapter_menu.setOnClickListener { it.post({ showPopupMenu(it) }) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set values of view
 | 
			
		||||
     *
 | 
			
		||||
     * @param item item containing chapter information
 | 
			
		||||
     * @param chapter item containing chapter information
 | 
			
		||||
     */
 | 
			
		||||
    fun onSetValues(item: MangaChapter) {
 | 
			
		||||
        this.mangaChapter = item
 | 
			
		||||
    fun onSetValues(chapter: RecentChapter) {
 | 
			
		||||
        this.chapter = chapter
 | 
			
		||||
 | 
			
		||||
        // Set chapter title
 | 
			
		||||
        itemView.chapter_title.text = item.chapter.name
 | 
			
		||||
        view.chapter_title.text = chapter.name
 | 
			
		||||
 | 
			
		||||
        // Set manga title
 | 
			
		||||
        itemView.manga_title.text = item.manga.title
 | 
			
		||||
        view.manga_title.text = chapter.manga.title
 | 
			
		||||
 | 
			
		||||
        // Check if chapter is read and set correct color
 | 
			
		||||
        if (item.chapter.read) {
 | 
			
		||||
            itemView.chapter_title.setTextColor(readColor)
 | 
			
		||||
            itemView.manga_title.setTextColor(readColor)
 | 
			
		||||
        if (chapter.read) {
 | 
			
		||||
            view.chapter_title.setTextColor(readColor)
 | 
			
		||||
            view.manga_title.setTextColor(readColor)
 | 
			
		||||
        } else {
 | 
			
		||||
            itemView.chapter_title.setTextColor(unreadColor)
 | 
			
		||||
            itemView.manga_title.setTextColor(unreadColor)
 | 
			
		||||
            view.chapter_title.setTextColor(unreadColor)
 | 
			
		||||
            view.manga_title.setTextColor(unreadColor)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Set chapter status
 | 
			
		||||
        onStatusChange(item.chapter.status)
 | 
			
		||||
        notifyStatus(chapter.status)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Updates chapter status in view.
 | 
			
		||||
 | 
			
		||||
     *
 | 
			
		||||
     * @param status download status
 | 
			
		||||
     */
 | 
			
		||||
    fun onStatusChange(status: Int) {
 | 
			
		||||
    fun notifyStatus(status: Int) = with(view.download_text) {
 | 
			
		||||
        when (status) {
 | 
			
		||||
            Download.QUEUE -> itemView.download_text.setText(R.string.chapter_queued)
 | 
			
		||||
            Download.DOWNLOADING -> itemView.download_text.setText(R.string.chapter_downloading)
 | 
			
		||||
            Download.DOWNLOADED -> itemView.download_text.setText(R.string.chapter_downloaded)
 | 
			
		||||
            Download.ERROR -> itemView.download_text.setText(R.string.chapter_error)
 | 
			
		||||
            else -> itemView.download_text.text = ""
 | 
			
		||||
            Download.QUEUE -> setText(R.string.chapter_queued)
 | 
			
		||||
            Download.DOWNLOADING -> setText(R.string.chapter_downloading)
 | 
			
		||||
            Download.DOWNLOADED -> setText(R.string.chapter_downloaded)
 | 
			
		||||
            Download.ERROR -> setText(R.string.chapter_error)
 | 
			
		||||
            else -> text = ""
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Show pop up menu
 | 
			
		||||
     *
 | 
			
		||||
     * @param view view containing popup menu.
 | 
			
		||||
     */
 | 
			
		||||
    private fun showPopupMenu(view: View) {
 | 
			
		||||
    private fun showPopupMenu(view: View) = chapter?.let { chapter ->
 | 
			
		||||
        // Create a PopupMenu, giving it the clicked view for an anchor
 | 
			
		||||
        val popup = PopupMenu(adapter.fragment.activity, view)
 | 
			
		||||
        val popup = PopupMenu(view.context, view)
 | 
			
		||||
 | 
			
		||||
        // Inflate our menu resource into the PopupMenu's Menu
 | 
			
		||||
        popup.menuInflater.inflate(R.menu.chapter_recent, popup.menu)
 | 
			
		||||
 | 
			
		||||
        mangaChapter?.let {
 | 
			
		||||
        // Hide download and show delete if the chapter is downloaded and
 | 
			
		||||
        if (chapter.isDownloaded) {
 | 
			
		||||
            popup.menu.findItem(R.id.action_download).isVisible = false
 | 
			
		||||
            popup.menu.findItem(R.id.action_delete).isVisible = true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            // Hide download and show delete if the chapter is downloaded and
 | 
			
		||||
            if (it.chapter.isDownloaded) {
 | 
			
		||||
                val menu = popup.menu
 | 
			
		||||
                menu.findItem(R.id.action_download).isVisible = false
 | 
			
		||||
                menu.findItem(R.id.action_delete).isVisible = true
 | 
			
		||||
            }
 | 
			
		||||
        // Hide mark as unread when the chapter is unread
 | 
			
		||||
        if (!chapter.read /*&& mangaChapter.chapter.last_page_read == 0*/) {
 | 
			
		||||
            popup.menu.findItem(R.id.action_mark_as_unread).isVisible = false
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            // Hide mark as unread when the chapter is unread
 | 
			
		||||
            if (!it.chapter.read /*&& mangaChapter.chapter.last_page_read == 0*/) {
 | 
			
		||||
                popup.menu.findItem(R.id.action_mark_as_unread).isVisible = false
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Hide mark as read when the chapter is read
 | 
			
		||||
            if (it.chapter.read) {
 | 
			
		||||
                popup.menu.findItem(R.id.action_mark_as_read).isVisible = false
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            // Set a listener so we are notified if a menu item is clicked
 | 
			
		||||
            popup.setOnMenuItemClickListener { menuItem ->
 | 
			
		||||
        // Hide mark as read when the chapter is read
 | 
			
		||||
        if (chapter.read) {
 | 
			
		||||
            popup.menu.findItem(R.id.action_mark_as_read).isVisible = false
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Set a listener so we are notified if a menu item is clicked
 | 
			
		||||
        popup.setOnMenuItemClickListener { menuItem ->
 | 
			
		||||
            with(adapter.fragment) {
 | 
			
		||||
                when (menuItem.itemId) {
 | 
			
		||||
                    R.id.action_download -> adapter.fragment.downloadChapter(it)
 | 
			
		||||
                    R.id.action_delete -> adapter.fragment.deleteChapter(it)
 | 
			
		||||
                    R.id.action_mark_as_read -> adapter.fragment.markAsRead(listOf(it))
 | 
			
		||||
                    R.id.action_mark_as_unread -> adapter.fragment.markAsUnread(listOf(it))
 | 
			
		||||
                    R.id.action_download -> downloadChapter(chapter)
 | 
			
		||||
                    R.id.action_delete -> deleteChapter(chapter)
 | 
			
		||||
                    R.id.action_mark_as_read -> markAsRead(listOf(chapter))
 | 
			
		||||
                    R.id.action_mark_as_unread -> markAsUnread(listOf(chapter))
 | 
			
		||||
                }
 | 
			
		||||
                true
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Finally show the PopupMenu
 | 
			
		||||
 
 | 
			
		||||
@@ -2,8 +2,6 @@ package eu.kanade.tachiyomi.ui.recent_updates
 | 
			
		||||
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Chapter
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Manga
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.MangaChapter
 | 
			
		||||
import eu.kanade.tachiyomi.data.download.DownloadManager
 | 
			
		||||
import eu.kanade.tachiyomi.data.download.DownloadService
 | 
			
		||||
@@ -15,34 +13,34 @@ import rx.Observable
 | 
			
		||||
import rx.android.schedulers.AndroidSchedulers
 | 
			
		||||
import rx.schedulers.Schedulers
 | 
			
		||||
import timber.log.Timber
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
import java.util.*
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
 | 
			
		||||
class RecentChaptersPresenter : BasePresenter<RecentChaptersFragment>() {
 | 
			
		||||
    /**
 | 
			
		||||
     * Used to connect to database
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var db: DatabaseHelper
 | 
			
		||||
    val db: DatabaseHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Used to get settings
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var preferences: PreferencesHelper
 | 
			
		||||
    val preferences: PreferencesHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Used to get information from download manager
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var downloadManager: DownloadManager
 | 
			
		||||
    val downloadManager: DownloadManager by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Used to get source from source id
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var sourceManager: SourceManager
 | 
			
		||||
    val sourceManager: SourceManager by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * List containing chapter and manga information
 | 
			
		||||
     */
 | 
			
		||||
    private var mangaChapters: List<MangaChapter>? = null
 | 
			
		||||
    private var chapters: List<RecentChapter>? = null
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The id of the restartable.
 | 
			
		||||
@@ -60,139 +58,26 @@ class RecentChaptersPresenter : BasePresenter<RecentChaptersFragment>() {
 | 
			
		||||
        // Used to get recent chapters
 | 
			
		||||
        restartableLatestCache(GET_RECENT_CHAPTERS,
 | 
			
		||||
                { getRecentChaptersObservable() },
 | 
			
		||||
                { fragment, chapters ->
 | 
			
		||||
                { view, chapters ->
 | 
			
		||||
                    // Update adapter to show recent manga's
 | 
			
		||||
                    fragment.onNextMangaChapters(chapters)
 | 
			
		||||
                    // Update download status
 | 
			
		||||
                    updateChapterStatus(convertToMangaChaptersList(chapters))
 | 
			
		||||
                    view.onNextRecentChapters(chapters)
 | 
			
		||||
                }
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        // Used to update download status
 | 
			
		||||
        startableLatestCache(CHAPTER_STATUS_CHANGES,
 | 
			
		||||
                { getChapterStatusObs() },
 | 
			
		||||
                { recentChaptersFragment, download ->
 | 
			
		||||
        restartableLatestCache(CHAPTER_STATUS_CHANGES,
 | 
			
		||||
                { getChapterStatusObservable() },
 | 
			
		||||
                { view, download ->
 | 
			
		||||
                    // Set chapter status
 | 
			
		||||
                    recentChaptersFragment.onChapterStatusChange(download)
 | 
			
		||||
                    view.onChapterStatusChange(download)
 | 
			
		||||
                },
 | 
			
		||||
                { view, error -> Timber.e(error.cause, error.message) }
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        if (savedState == null) {
 | 
			
		||||
            // Start fetching recent chapters
 | 
			
		||||
            start(GET_RECENT_CHAPTERS)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns observable containing chapter status.
 | 
			
		||||
     * @return download object containing download progress.
 | 
			
		||||
     */
 | 
			
		||||
    private fun getChapterStatusObs(): Observable<Download> {
 | 
			
		||||
        return downloadManager.queue.getStatusObservable()
 | 
			
		||||
                .observeOn(AndroidSchedulers.mainThread())
 | 
			
		||||
                .filter { download: Download ->
 | 
			
		||||
                    if (chapterIdEquals(download.chapter.id))
 | 
			
		||||
                        true
 | 
			
		||||
                    else
 | 
			
		||||
                        false
 | 
			
		||||
                }
 | 
			
		||||
                .doOnNext { download1: Download -> updateChapterStatus(download1) }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Function to check if chapter is in recent list
 | 
			
		||||
     * @param chaptersId id of chapter
 | 
			
		||||
     * @return exist in recent list
 | 
			
		||||
     */
 | 
			
		||||
    private fun chapterIdEquals(chaptersId: Long): Boolean {
 | 
			
		||||
        mangaChapters!!.forEach { mangaChapter ->
 | 
			
		||||
            if (chaptersId == mangaChapter.chapter.id) {
 | 
			
		||||
                return true
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return false
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns a list only containing MangaChapter objects.
 | 
			
		||||
     * @param input the list that will be converted.
 | 
			
		||||
     * @return list containing MangaChapters objects.
 | 
			
		||||
     */
 | 
			
		||||
    private fun convertToMangaChaptersList(input: List<Any>): List<MangaChapter> {
 | 
			
		||||
        // Create temp list
 | 
			
		||||
        val tempMangaChapterList = ArrayList<MangaChapter>()
 | 
			
		||||
 | 
			
		||||
        // Only add MangaChapter objects
 | 
			
		||||
        //noinspection Convert2streamapi
 | 
			
		||||
        input.forEach { `object` ->
 | 
			
		||||
            if (`object` is MangaChapter) {
 | 
			
		||||
                tempMangaChapterList.add(`object`)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Return temp list
 | 
			
		||||
        return tempMangaChapterList
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update status of chapters.
 | 
			
		||||
     * @param download download object containing progress.
 | 
			
		||||
     */
 | 
			
		||||
    private fun updateChapterStatus(download: Download) {
 | 
			
		||||
        // Loop through list
 | 
			
		||||
        mangaChapters?.let {
 | 
			
		||||
            for (item in it) {
 | 
			
		||||
                if (download.chapter.id == item.chapter.id) {
 | 
			
		||||
                    // Update status.
 | 
			
		||||
                    item.chapter.status = download.status
 | 
			
		||||
                    break
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update status of chapters
 | 
			
		||||
     * @param mangaChapters list containing recent chapters
 | 
			
		||||
     */
 | 
			
		||||
    private fun updateChapterStatus(mangaChapters: List<MangaChapter>) {
 | 
			
		||||
        // Set global list of chapters.
 | 
			
		||||
        this.mangaChapters = mangaChapters
 | 
			
		||||
 | 
			
		||||
        // Update status.
 | 
			
		||||
        //noinspection Convert2streamapi
 | 
			
		||||
        for (mangaChapter in mangaChapters)
 | 
			
		||||
            setChapterStatus(mangaChapter)
 | 
			
		||||
 | 
			
		||||
        // Start onChapterStatusChange restartable.
 | 
			
		||||
        start(CHAPTER_STATUS_CHANGES)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set the chapter status
 | 
			
		||||
     * @param mangaChapter MangaChapter which status gets updated
 | 
			
		||||
     */
 | 
			
		||||
    private fun setChapterStatus(mangaChapter: MangaChapter) {
 | 
			
		||||
        // Check if chapter in queue
 | 
			
		||||
        for (download in downloadManager.queue) {
 | 
			
		||||
            if (mangaChapter.chapter.id == download.chapter.id) {
 | 
			
		||||
                mangaChapter.chapter.status = download.status
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Get source of chapter
 | 
			
		||||
        val source = sourceManager.get(mangaChapter.manga.source)!!
 | 
			
		||||
 | 
			
		||||
        // Check if chapter is downloaded
 | 
			
		||||
        if (downloadManager.isChapterDownloaded(source, mangaChapter.manga, mangaChapter.chapter)) {
 | 
			
		||||
            mangaChapter.chapter.status = Download.DOWNLOADED
 | 
			
		||||
        } else {
 | 
			
		||||
            mangaChapter.chapter.status = Download.NOT_DOWNLOADED
 | 
			
		||||
            start(CHAPTER_STATUS_CHANGES)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -200,33 +85,89 @@ class RecentChaptersPresenter : BasePresenter<RecentChaptersFragment>() {
 | 
			
		||||
     * Get observable containing recent chapters and date
 | 
			
		||||
     * @return observable containing recent chapters and date
 | 
			
		||||
     */
 | 
			
		||||
    fun getRecentChaptersObservable(): Observable<ArrayList<Any>>? {
 | 
			
		||||
    fun getRecentChaptersObservable(): Observable<ArrayList<Any>> {
 | 
			
		||||
        // Set date for recent chapters
 | 
			
		||||
        val cal = Calendar.getInstance()
 | 
			
		||||
        cal.time = Date()
 | 
			
		||||
        cal.add(Calendar.MONTH, -1)
 | 
			
		||||
        val cal = Calendar.getInstance().apply {
 | 
			
		||||
            time = Date()
 | 
			
		||||
            add(Calendar.MONTH, -1)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return db.getRecentChapters(cal.time).asRxObservable()
 | 
			
		||||
                // Convert to a list of recent chapters.
 | 
			
		||||
                .map { mangaChapters ->
 | 
			
		||||
                    mangaChapters.map { it.toModel() }
 | 
			
		||||
                }
 | 
			
		||||
                .doOnNext { chapters = it }
 | 
			
		||||
                // Group chapters by the date they were fetched on a ordered map.
 | 
			
		||||
                .flatMap { recentItems ->
 | 
			
		||||
                    Observable.from(recentItems)
 | 
			
		||||
                            .toMultimap(
 | 
			
		||||
                                    { getMapKey(it.chapter.date_fetch) },
 | 
			
		||||
                                    { getMapKey(it.date_fetch) },
 | 
			
		||||
                                    { it },
 | 
			
		||||
                                    { TreeMap { d1, d2 -> d2.compareTo(d1) } })
 | 
			
		||||
                }
 | 
			
		||||
                // Add every day and all its chapters to a single list.
 | 
			
		||||
                .map { recentItems ->
 | 
			
		||||
                    val items = ArrayList<Any>()
 | 
			
		||||
                    recentItems.entries.forEach { recent ->
 | 
			
		||||
                        items.add(recent.key)
 | 
			
		||||
                        items.addAll(recent.value)
 | 
			
		||||
                    ArrayList<Any>().apply {
 | 
			
		||||
                        for ((key, value) in recentItems) {
 | 
			
		||||
                            add(key)
 | 
			
		||||
                            addAll(value)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    items
 | 
			
		||||
                }
 | 
			
		||||
                .observeOn(AndroidSchedulers.mainThread())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns observable containing chapter status.
 | 
			
		||||
     * @return download object containing download progress.
 | 
			
		||||
     */
 | 
			
		||||
    private fun getChapterStatusObservable(): Observable<Download> {
 | 
			
		||||
        return downloadManager.queue.getStatusObservable()
 | 
			
		||||
                .observeOn(AndroidSchedulers.mainThread())
 | 
			
		||||
                .doOnNext { download -> onDownloadStatusChange(download) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Converts a chapter from the database to an extended model, allowing to store new fields.
 | 
			
		||||
     */
 | 
			
		||||
    private fun MangaChapter.toModel(): RecentChapter {
 | 
			
		||||
        // Create the model object.
 | 
			
		||||
        val model = RecentChapter(this)
 | 
			
		||||
 | 
			
		||||
        // Find an active download for this chapter.
 | 
			
		||||
        val download = downloadManager.queue.find { it.chapter.id == chapter.id }
 | 
			
		||||
 | 
			
		||||
        // If there's an active download, assign it, otherwise ask the manager if the chapter is
 | 
			
		||||
        // downloaded and assign it to the status.
 | 
			
		||||
        if (download != null) {
 | 
			
		||||
            model.download = download
 | 
			
		||||
        } else {
 | 
			
		||||
            // Get source of chapter.
 | 
			
		||||
            val source = sourceManager.get(manga.source)!!
 | 
			
		||||
 | 
			
		||||
            model.status = if (downloadManager.isChapterDownloaded(source, manga, chapter))
 | 
			
		||||
                Download.DOWNLOADED
 | 
			
		||||
            else
 | 
			
		||||
                Download.NOT_DOWNLOADED
 | 
			
		||||
        }
 | 
			
		||||
        return model
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update status of chapters.
 | 
			
		||||
     * @param download download object containing progress.
 | 
			
		||||
     */
 | 
			
		||||
    private fun onDownloadStatusChange(download: Download) {
 | 
			
		||||
        // Assign the download to the model object.
 | 
			
		||||
        if (download.status == Download.QUEUE) {
 | 
			
		||||
            val chapter = chapters?.find { it.id == download.chapter.id }
 | 
			
		||||
            if (chapter != null && chapter.download == null) {
 | 
			
		||||
                chapter.download = download
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get date as time key
 | 
			
		||||
     * @param date desired date
 | 
			
		||||
@@ -244,18 +185,17 @@ class RecentChaptersPresenter : BasePresenter<RecentChaptersFragment>() {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Mark selected chapter as read
 | 
			
		||||
     * @param mangaChapters list of selected MangaChapters
 | 
			
		||||
     * @param chapters list of selected chapters
 | 
			
		||||
     * @param read read status
 | 
			
		||||
     */
 | 
			
		||||
    fun markChapterRead(mangaChapters: List<MangaChapter>, read: Boolean) {
 | 
			
		||||
        Observable.from(mangaChapters)
 | 
			
		||||
                .doOnNext { mangaChapter ->
 | 
			
		||||
                    mangaChapter.chapter.read = read
 | 
			
		||||
    fun markChapterRead(chapters: List<RecentChapter>, read: Boolean) {
 | 
			
		||||
        Observable.from(chapters)
 | 
			
		||||
                .doOnNext { chapter ->
 | 
			
		||||
                    chapter.read = read
 | 
			
		||||
                    if (!read) {
 | 
			
		||||
                        mangaChapter.chapter.last_page_read = 0
 | 
			
		||||
                        chapter.last_page_read = 0
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                .map { mangaChapter -> mangaChapter.chapter }
 | 
			
		||||
                .toList()
 | 
			
		||||
                .flatMap { db.updateChaptersProgress(it).asRxObservable() }
 | 
			
		||||
                .subscribeOn(Schedulers.io())
 | 
			
		||||
@@ -264,9 +204,9 @@ class RecentChaptersPresenter : BasePresenter<RecentChaptersFragment>() {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete selected chapters
 | 
			
		||||
     * @param chapters list of MangaChapters
 | 
			
		||||
     * @param chapters list of chapters
 | 
			
		||||
     */
 | 
			
		||||
    fun deleteChapters(chapters: List<MangaChapter>) {
 | 
			
		||||
    fun deleteChapters(chapters: List<RecentChapter>) {
 | 
			
		||||
        val wasRunning = downloadManager.isRunning
 | 
			
		||||
        if (wasRunning) {
 | 
			
		||||
            DownloadService.stop(context)
 | 
			
		||||
@@ -288,11 +228,11 @@ class RecentChaptersPresenter : BasePresenter<RecentChaptersFragment>() {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Download selected chapters
 | 
			
		||||
     * @param mangaChapters [MangaChapter] that is selected
 | 
			
		||||
     * @param chapters list of recent chapters seleted.
 | 
			
		||||
     */
 | 
			
		||||
    fun downloadChapters(mangaChapters: List<MangaChapter>) {
 | 
			
		||||
    fun downloadChapters(chapters: List<RecentChapter>) {
 | 
			
		||||
        DownloadService.start(context)
 | 
			
		||||
        Observable.from(mangaChapters)
 | 
			
		||||
        Observable.from(chapters)
 | 
			
		||||
                .doOnNext { downloadChapter(it) }
 | 
			
		||||
                .subscribeOn(AndroidSchedulers.mainThread())
 | 
			
		||||
                .subscribe()
 | 
			
		||||
@@ -300,47 +240,23 @@ class RecentChaptersPresenter : BasePresenter<RecentChaptersFragment>() {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Download selected chapter
 | 
			
		||||
     * @param item chapter that is selected
 | 
			
		||||
     * @param chapter chapter that is selected
 | 
			
		||||
     */
 | 
			
		||||
    fun downloadChapter(item: MangaChapter) {
 | 
			
		||||
    fun downloadChapter(chapter: RecentChapter) {
 | 
			
		||||
        DownloadService.start(context)
 | 
			
		||||
        downloadManager.downloadChapters(item.manga, listOf(item.chapter))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete selected chapter
 | 
			
		||||
     * @param item chapter that are selected
 | 
			
		||||
     */
 | 
			
		||||
    fun deleteChapter(item: MangaChapter) {
 | 
			
		||||
        val wasRunning = downloadManager.isRunning
 | 
			
		||||
        if (wasRunning) {
 | 
			
		||||
            DownloadService.stop(context)
 | 
			
		||||
        }
 | 
			
		||||
        Observable.just(item)
 | 
			
		||||
                .doOnNext { deleteChapter(it.chapter, it.manga) }
 | 
			
		||||
                .subscribeOn(Schedulers.io())
 | 
			
		||||
                .observeOn(AndroidSchedulers.mainThread())
 | 
			
		||||
                .subscribeFirst({ view, result ->
 | 
			
		||||
                    view.onChaptersDeleted()
 | 
			
		||||
                    if (wasRunning) {
 | 
			
		||||
                        DownloadService.start(context)
 | 
			
		||||
                    }
 | 
			
		||||
                }, { view, error ->
 | 
			
		||||
                    view.onChaptersDeletedError(error)
 | 
			
		||||
                })
 | 
			
		||||
        downloadManager.downloadChapters(chapter.manga, listOf(chapter))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete selected chapter
 | 
			
		||||
     * @param chapter chapter that is selected
 | 
			
		||||
     * @param manga manga that belongs to chapter
 | 
			
		||||
     */
 | 
			
		||||
    private fun deleteChapter(chapter: Chapter, manga: Manga) {
 | 
			
		||||
        val source = sourceManager.get(manga.source) ?: return
 | 
			
		||||
    private fun deleteChapter(chapter: RecentChapter) {
 | 
			
		||||
        val source = sourceManager.get(chapter.manga.source) ?: return
 | 
			
		||||
        downloadManager.queue.del(chapter)
 | 
			
		||||
        downloadManager.deleteChapter(source, manga, chapter)
 | 
			
		||||
        downloadManager.deleteChapter(source, chapter.manga, chapter)
 | 
			
		||||
        chapter.status = Download.NOT_DOWNLOADED
 | 
			
		||||
        chapter.download = null
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -2,11 +2,12 @@ package eu.kanade.tachiyomi.ui.recent_updates
 | 
			
		||||
 | 
			
		||||
import android.support.v7.widget.RecyclerView
 | 
			
		||||
import android.text.format.DateUtils
 | 
			
		||||
import android.text.format.DateUtils.DAY_IN_MILLIS
 | 
			
		||||
import android.view.View
 | 
			
		||||
import kotlinx.android.synthetic.main.item_recent_chapter_section.view.*
 | 
			
		||||
import java.util.*
 | 
			
		||||
 | 
			
		||||
class SectionViewHolder(view: View) : RecyclerView.ViewHolder(view) {
 | 
			
		||||
class SectionViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Current date
 | 
			
		||||
@@ -19,8 +20,6 @@ class SectionViewHolder(view: View) : RecyclerView.ViewHolder(view) {
 | 
			
		||||
     * @param date of section header
 | 
			
		||||
     */
 | 
			
		||||
    fun onSetValues(date: Date) {
 | 
			
		||||
        val s = DateUtils.getRelativeTimeSpanString(
 | 
			
		||||
                date.time, now, DateUtils.DAY_IN_MILLIS)
 | 
			
		||||
        itemView.section_text.text = s
 | 
			
		||||
        view.section_text.text = DateUtils.getRelativeTimeSpanString(date.time, now, DAY_IN_MILLIS)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -37,8 +37,8 @@ class RecentlyReadHolder(view: View, private val adapter: RecentlyReadAdapter)
 | 
			
		||||
     */
 | 
			
		||||
    fun onSetValues(item: MangaChapterHistory) {
 | 
			
		||||
        // Retrieve objects
 | 
			
		||||
        val manga = item.mangaChapter.manga
 | 
			
		||||
        val chapter = item.mangaChapter.chapter
 | 
			
		||||
        val manga = item.manga
 | 
			
		||||
        val chapter = item.chapter
 | 
			
		||||
        val history = item.history
 | 
			
		||||
 | 
			
		||||
        // Set manga title
 | 
			
		||||
@@ -71,7 +71,7 @@ class RecentlyReadHolder(view: View, private val adapter: RecentlyReadAdapter)
 | 
			
		||||
                    .onPositive { materialDialog, dialogAction ->
 | 
			
		||||
                        // Check if user wants all chapters reset
 | 
			
		||||
                        if (materialDialog.customView?.removeAll?.isChecked as Boolean) {
 | 
			
		||||
                            adapter.fragment.removeAllFromHistory(manga.id)
 | 
			
		||||
                            adapter.fragment.removeAllFromHistory(manga.id!!)
 | 
			
		||||
                        } else {
 | 
			
		||||
                            adapter.fragment.removeFromHistory(history)
 | 
			
		||||
                        }
 | 
			
		||||
 
 | 
			
		||||
@@ -9,9 +9,9 @@ import rx.Observable
 | 
			
		||||
import rx.android.schedulers.AndroidSchedulers
 | 
			
		||||
import rx.schedulers.Schedulers
 | 
			
		||||
import timber.log.Timber
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
import java.text.SimpleDateFormat
 | 
			
		||||
import java.util.*
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Presenter of RecentlyReadFragment.
 | 
			
		||||
@@ -30,7 +30,7 @@ class RecentlyReadPresenter : BasePresenter<RecentlyReadFragment>() {
 | 
			
		||||
    /**
 | 
			
		||||
     * Used to connect to database
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var db: DatabaseHelper
 | 
			
		||||
    val db: DatabaseHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    override fun onCreate(savedState: Bundle?) {
 | 
			
		||||
        super.onCreate(savedState)
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.ui.setting
 | 
			
		||||
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.support.v14.preference.PreferenceFragment
 | 
			
		||||
import eu.kanade.tachiyomi.App
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.cache.ChapterCache
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
 | 
			
		||||
@@ -12,22 +11,21 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 | 
			
		||||
import eu.kanade.tachiyomi.data.source.SourceManager
 | 
			
		||||
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
 | 
			
		||||
import kotlinx.android.synthetic.main.toolbar.*
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
 | 
			
		||||
class SettingsActivity : BaseActivity() {
 | 
			
		||||
 | 
			
		||||
    @Inject lateinit var preferences: PreferencesHelper
 | 
			
		||||
    @Inject lateinit var chapterCache: ChapterCache
 | 
			
		||||
    @Inject lateinit var db: DatabaseHelper
 | 
			
		||||
    @Inject lateinit var sourceManager: SourceManager
 | 
			
		||||
    @Inject lateinit var syncManager: MangaSyncManager
 | 
			
		||||
    @Inject lateinit var networkHelper: NetworkHelper
 | 
			
		||||
    val preferences: PreferencesHelper by injectLazy()
 | 
			
		||||
    val chapterCache: ChapterCache by injectLazy()
 | 
			
		||||
    val db: DatabaseHelper by injectLazy()
 | 
			
		||||
    val sourceManager: SourceManager by injectLazy()
 | 
			
		||||
    val syncManager: MangaSyncManager by injectLazy()
 | 
			
		||||
    val networkHelper: NetworkHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    override fun onCreate(savedState: Bundle?) {
 | 
			
		||||
        setAppTheme()
 | 
			
		||||
        super.onCreate(savedState)
 | 
			
		||||
        setContentView(R.layout.activity_preferences)
 | 
			
		||||
        App.get(this).component.inject(this)
 | 
			
		||||
 | 
			
		||||
        setupToolbar(toolbar)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user