mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-04 08:08:55 +01:00 
			
		
		
		
	Added recently read tab (#316)
This commit is contained in:
		@@ -9,7 +9,7 @@ import eu.kanade.tachiyomi.data.database.queries.*
 | 
			
		||||
 * This class provides operations to manage the database through its interfaces.
 | 
			
		||||
 */
 | 
			
		||||
open class DatabaseHelper(context: Context)
 | 
			
		||||
: MangaQueries, ChapterQueries, MangaSyncQueries, CategoryQueries, MangaCategoryQueries {
 | 
			
		||||
: MangaQueries, ChapterQueries, MangaSyncQueries, CategoryQueries, MangaCategoryQueries, HistoryQueries {
 | 
			
		||||
 | 
			
		||||
    override val db = DefaultStorIOSQLite.builder()
 | 
			
		||||
            .sqliteOpenHelper(DbOpenHelper(context))
 | 
			
		||||
@@ -18,6 +18,7 @@ open class DatabaseHelper(context: Context)
 | 
			
		||||
            .addTypeMapping(MangaSync::class.java, MangaSyncSQLiteTypeMapping())
 | 
			
		||||
            .addTypeMapping(Category::class.java, CategorySQLiteTypeMapping())
 | 
			
		||||
            .addTypeMapping(MangaCategory::class.java, MangaCategorySQLiteTypeMapping())
 | 
			
		||||
            .addTypeMapping(History::class.java, HistorySQLiteTypeMapping())
 | 
			
		||||
            .build()
 | 
			
		||||
 | 
			
		||||
    inline fun inTransaction(block: () -> Unit) = db.inTransaction(block)
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@ class DbOpenHelper(context: Context)
 | 
			
		||||
        /**
 | 
			
		||||
         * Version of the database.
 | 
			
		||||
         */
 | 
			
		||||
        const val DATABASE_VERSION = 2
 | 
			
		||||
        const val DATABASE_VERSION = 3
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onCreate(db: SQLiteDatabase) = with(db) {
 | 
			
		||||
@@ -26,11 +26,13 @@ class DbOpenHelper(context: Context)
 | 
			
		||||
        execSQL(MangaSyncTable.createTableQuery)
 | 
			
		||||
        execSQL(CategoryTable.createTableQuery)
 | 
			
		||||
        execSQL(MangaCategoryTable.createTableQuery)
 | 
			
		||||
        execSQL(HistoryTable.createTableQuery)
 | 
			
		||||
 | 
			
		||||
        // DB indexes
 | 
			
		||||
        execSQL(MangaTable.createUrlIndexQuery)
 | 
			
		||||
        execSQL(MangaTable.createFavoriteIndexQuery)
 | 
			
		||||
        execSQL(ChapterTable.createMangaIdIndexQuery)
 | 
			
		||||
        execSQL(HistoryTable.createChapterIdIndexQuery)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
 | 
			
		||||
@@ -41,10 +43,15 @@ class DbOpenHelper(context: Context)
 | 
			
		||||
            db.execSQL("""UPDATE mangas SET thumbnail_url =
 | 
			
		||||
                    REPLACE(thumbnail_url, '93.174.95.110', 'kissmanga.com') WHERE source = 4""")
 | 
			
		||||
        }
 | 
			
		||||
        if (oldVersion < 3) {
 | 
			
		||||
            // Initialize history tables
 | 
			
		||||
            db.execSQL(HistoryTable.createTableQuery)
 | 
			
		||||
            db.execSQL(HistoryTable.createChapterIdIndexQuery)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onConfigure(db: SQLiteDatabase) {
 | 
			
		||||
        db.setForeignKeyConstraintsEnabled(true)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,58 @@
 | 
			
		||||
package eu.kanade.tachiyomi.data.database.models;
 | 
			
		||||
 | 
			
		||||
import com.pushtorefresh.storio.sqlite.annotations.StorIOSQLiteColumn;
 | 
			
		||||
import com.pushtorefresh.storio.sqlite.annotations.StorIOSQLiteType;
 | 
			
		||||
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.tables.HistoryTable;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Object containing the history statistics of a chapter
 | 
			
		||||
 */
 | 
			
		||||
@StorIOSQLiteType(table = HistoryTable.TABLE)
 | 
			
		||||
public class History implements Serializable {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Id of history object.
 | 
			
		||||
     */
 | 
			
		||||
    @StorIOSQLiteColumn(name = HistoryTable.COL_ID, key = true)
 | 
			
		||||
    public Long id;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Chapter id of history object.
 | 
			
		||||
     */
 | 
			
		||||
    @StorIOSQLiteColumn(name = HistoryTable.COL_CHAPTER_ID)
 | 
			
		||||
    public long chapter_id;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Last time chapter was read in time long format
 | 
			
		||||
     */
 | 
			
		||||
    @StorIOSQLiteColumn(name = HistoryTable.COL_LAST_READ)
 | 
			
		||||
    public long last_read;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Total time chapter was read - todo not yet implemented
 | 
			
		||||
     */
 | 
			
		||||
    @StorIOSQLiteColumn(name = HistoryTable.COL_TIME_READ)
 | 
			
		||||
    public long time_read;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Empty history constructor
 | 
			
		||||
     */
 | 
			
		||||
    public History() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * History constructor
 | 
			
		||||
     *
 | 
			
		||||
     * @param chapter chapter object
 | 
			
		||||
     * @return history object
 | 
			
		||||
     */
 | 
			
		||||
    public static History create(Chapter chapter) {
 | 
			
		||||
        History history = new History();
 | 
			
		||||
        history.chapter_id = chapter.id;
 | 
			
		||||
        return history;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -0,0 +1,27 @@
 | 
			
		||||
package eu.kanade.tachiyomi.data.database.models;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Object containing manga, chapter and history
 | 
			
		||||
 */
 | 
			
		||||
public class MangaChapterHistory {
 | 
			
		||||
    /**
 | 
			
		||||
     * Object containing manga and chapter
 | 
			
		||||
     */
 | 
			
		||||
    public MangaChapter mangaChapter;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Object containing history
 | 
			
		||||
     */
 | 
			
		||||
    public History history;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * MangaChapterHistory constructor
 | 
			
		||||
     *
 | 
			
		||||
     * @param mangaChapter object containing manga and chapter
 | 
			
		||||
     * @param history      object containing history
 | 
			
		||||
     */
 | 
			
		||||
    public MangaChapterHistory(MangaChapter mangaChapter, History history) {
 | 
			
		||||
        this.mangaChapter = mangaChapter;
 | 
			
		||||
        this.history = history;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,53 @@
 | 
			
		||||
package eu.kanade.tachiyomi.data.database.queries
 | 
			
		||||
 | 
			
		||||
import com.pushtorefresh.storio.sqlite.queries.RawQuery
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.DbProvider
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.History
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.MangaChapterHistory
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.resolvers.HistoryLastReadPutResolver
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaChapterHistoryGetResolver
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.tables.HistoryTable
 | 
			
		||||
import java.util.*
 | 
			
		||||
 | 
			
		||||
interface HistoryQueries : DbProvider {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Insert history into database
 | 
			
		||||
     * @param history object containing history information
 | 
			
		||||
     */
 | 
			
		||||
    fun insertHistory(history: History) = db.put().`object`(history).prepare()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns history of recent manga containing last read chapter
 | 
			
		||||
     * @param date recent date range
 | 
			
		||||
     */
 | 
			
		||||
    fun getRecentManga(date: Date) = db.get()
 | 
			
		||||
            .listOfObjects(MangaChapterHistory::class.java)
 | 
			
		||||
            .withQuery(RawQuery.builder()
 | 
			
		||||
                    .query(getRecentMangasQuery())
 | 
			
		||||
                    .args(date.time)
 | 
			
		||||
                    .observesTables(HistoryTable.TABLE)
 | 
			
		||||
                    .build())
 | 
			
		||||
            .withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
 | 
			
		||||
            .prepare()
 | 
			
		||||
 | 
			
		||||
    fun getHistoryByMangaId(mangaId: Long) = db.get()
 | 
			
		||||
            .listOfObjects(History::class.java)
 | 
			
		||||
            .withQuery(RawQuery.builder()
 | 
			
		||||
                    .query(getHistoryByMangaId())
 | 
			
		||||
                    .args(mangaId)
 | 
			
		||||
                    .observesTables(HistoryTable.TABLE)
 | 
			
		||||
                    .build())
 | 
			
		||||
            .prepare()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Updates the history last read.
 | 
			
		||||
     * Inserts history object if not yet in database
 | 
			
		||||
     * @param history history object
 | 
			
		||||
     */
 | 
			
		||||
    fun updateHistoryLastRead(history: History) = db.put()
 | 
			
		||||
            .`object`(history)
 | 
			
		||||
            .withPutResolver(HistoryLastReadPutResolver())
 | 
			
		||||
            .prepare()
 | 
			
		||||
}
 | 
			
		||||
@@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.data.database.queries
 | 
			
		||||
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.tables.CategoryTable as Category
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.tables.ChapterTable as Chapter
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.tables.HistoryTable as History
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable as MangaCategory
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable as Manga
 | 
			
		||||
 | 
			
		||||
@@ -39,6 +40,38 @@ fun getRecentsQuery() = """
 | 
			
		||||
    ORDER BY ${Chapter.COL_DATE_UPLOAD} DESC
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Query to get the recently read chapters of manga from the library up to a date.
 | 
			
		||||
 * The max_last_read table contains the most recent chapters grouped by manga
 | 
			
		||||
 * The select statement returns all information of chapters that have the same id as the chapter in max_last_read
 | 
			
		||||
 * and are read after the given time period
 | 
			
		||||
 * @return return limit is 25
 | 
			
		||||
 */
 | 
			
		||||
fun getRecentMangasQuery() = """
 | 
			
		||||
    SELECT ${Manga.TABLE}.${Manga.COL_URL} as mangaUrl, ${Manga.TABLE}.*, ${Chapter.TABLE}.*, ${History.TABLE}.*
 | 
			
		||||
    FROM ${Manga.TABLE}
 | 
			
		||||
    JOIN ${Chapter.TABLE}
 | 
			
		||||
    ON ${Manga.TABLE}.${Manga.COL_ID} = ${Chapter.TABLE}.${Chapter.COL_MANGA_ID}
 | 
			
		||||
    JOIN ${History.TABLE}
 | 
			
		||||
    ON ${Chapter.TABLE}.${Chapter.COL_ID} = ${History.TABLE}.${History.COL_CHAPTER_ID}
 | 
			
		||||
    JOIN (
 | 
			
		||||
    SELECT ${Chapter.TABLE}.${Chapter.COL_MANGA_ID},${Chapter.TABLE}.${Chapter.COL_ID} as ${History.COL_CHAPTER_ID}, MAX(${History.TABLE}.${History.COL_LAST_READ}) as ${History.COL_LAST_READ}
 | 
			
		||||
    FROM ${Chapter.TABLE} JOIN ${History.TABLE}
 | 
			
		||||
    ON ${Chapter.TABLE}.${Chapter.COL_ID} = ${History.TABLE}.${History.COL_CHAPTER_ID}
 | 
			
		||||
    GROUP BY ${Chapter.TABLE}.${Chapter.COL_MANGA_ID}) AS max_last_read
 | 
			
		||||
    ON ${Chapter.TABLE}.${Chapter.COL_MANGA_ID} = max_last_read.${Chapter.COL_MANGA_ID}
 | 
			
		||||
    WHERE ${History.TABLE}.${History.COL_LAST_READ} > ? AND max_last_read.${History.COL_CHAPTER_ID} = ${History.TABLE}.${History.COL_CHAPTER_ID}
 | 
			
		||||
    ORDER BY max_last_read.${History.COL_LAST_READ} DESC
 | 
			
		||||
    LIMIT 25
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
fun getHistoryByMangaId() = """
 | 
			
		||||
    SELECT ${History.TABLE}.*
 | 
			
		||||
    FROM ${History.TABLE}
 | 
			
		||||
    JOIN ${Chapter.TABLE}
 | 
			
		||||
    ON ${History.TABLE}.${History.COL_CHAPTER_ID} = ${Chapter.TABLE}.${Chapter.COL_ID}
 | 
			
		||||
    WHERE ${Chapter.TABLE}.${Chapter.COL_MANGA_ID} = ? AND ${History.TABLE}.${History.COL_CHAPTER_ID} = ${Chapter.TABLE}.${Chapter.COL_ID}
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Query to get the categories for a manga.
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,52 @@
 | 
			
		||||
package eu.kanade.tachiyomi.data.database.resolvers
 | 
			
		||||
 | 
			
		||||
import android.content.ContentValues
 | 
			
		||||
import android.support.annotation.NonNull
 | 
			
		||||
import com.pushtorefresh.storio.sqlite.StorIOSQLite
 | 
			
		||||
import com.pushtorefresh.storio.sqlite.operations.put.PutResolver
 | 
			
		||||
import com.pushtorefresh.storio.sqlite.operations.put.PutResult
 | 
			
		||||
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.inTransactionReturn
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.History
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.tables.HistoryTable
 | 
			
		||||
 | 
			
		||||
class HistoryLastReadPutResolver : PutResolver<History>() {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Updates last_read time of chapter
 | 
			
		||||
     */
 | 
			
		||||
    override fun performPut(@NonNull db: StorIOSQLite, @NonNull history: History): PutResult = db.inTransactionReturn {
 | 
			
		||||
        // Create put query
 | 
			
		||||
        val updateQuery = mapToUpdateQuery(history)
 | 
			
		||||
        val contentValues = mapToContentValues(history)
 | 
			
		||||
 | 
			
		||||
        // Execute query
 | 
			
		||||
        val numberOfRowsUpdated = db.internal().update(updateQuery, contentValues)
 | 
			
		||||
 | 
			
		||||
        // If chapter not found in history insert into database
 | 
			
		||||
        if (numberOfRowsUpdated == 0) {
 | 
			
		||||
            db.put().`object`(history).prepare().asRxObservable().subscribe()
 | 
			
		||||
        }
 | 
			
		||||
        // Update result
 | 
			
		||||
        PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates update query
 | 
			
		||||
     * @param history object
 | 
			
		||||
     */
 | 
			
		||||
    fun mapToUpdateQuery(history: History) = UpdateQuery.builder()
 | 
			
		||||
            .table(HistoryTable.TABLE)
 | 
			
		||||
            .where("${HistoryTable.COL_CHAPTER_ID} = ?")
 | 
			
		||||
            .whereArgs(history.chapter_id)
 | 
			
		||||
            .build()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create content query
 | 
			
		||||
     * @param history object
 | 
			
		||||
     */
 | 
			
		||||
    fun mapToContentValues(history: History) = ContentValues(1).apply {
 | 
			
		||||
        put(HistoryTable.COL_LAST_READ, history.last_read)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -20,7 +20,7 @@ class MangaChapterGetResolver : DefaultGetResolver<MangaChapter>() {
 | 
			
		||||
        val manga = mangaGetResolver.mapFromCursor(cursor)
 | 
			
		||||
        val chapter = chapterGetResolver.mapFromCursor(cursor)
 | 
			
		||||
        manga.id = chapter.manga_id
 | 
			
		||||
        manga.url = cursor.getString(cursor.getColumnIndex("mangaUrl"));
 | 
			
		||||
        manga.url = cursor.getString(cursor.getColumnIndex("mangaUrl"))
 | 
			
		||||
 | 
			
		||||
        return MangaChapter(manga, chapter)
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,51 @@
 | 
			
		||||
package eu.kanade.tachiyomi.data.database.resolvers
 | 
			
		||||
 | 
			
		||||
import android.database.Cursor
 | 
			
		||||
import com.pushtorefresh.storio.sqlite.operations.get.DefaultGetResolver
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.*
 | 
			
		||||
 | 
			
		||||
class MangaChapterHistoryGetResolver : DefaultGetResolver<MangaChapterHistory>() {
 | 
			
		||||
    companion object {
 | 
			
		||||
        val INSTANCE = MangaChapterHistoryGetResolver()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Manga get resolver
 | 
			
		||||
     */
 | 
			
		||||
    private val mangaGetResolver = MangaStorIOSQLiteGetResolver()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Chapter get resolver
 | 
			
		||||
     */
 | 
			
		||||
    private val chapterResolver = ChapterStorIOSQLiteGetResolver()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * History get resolver
 | 
			
		||||
     */
 | 
			
		||||
    private val historyGetResolver = HistoryStorIOSQLiteGetResolver()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Map correct objects from cursor result
 | 
			
		||||
     */
 | 
			
		||||
    override fun mapFromCursor(cursor: Cursor): MangaChapterHistory {
 | 
			
		||||
        // Get manga object
 | 
			
		||||
        val manga = mangaGetResolver.mapFromCursor(cursor)
 | 
			
		||||
 | 
			
		||||
        // Get chapter object
 | 
			
		||||
        val chapter = chapterResolver.mapFromCursor(cursor)
 | 
			
		||||
 | 
			
		||||
        // Get history object
 | 
			
		||||
        val history = historyGetResolver.mapFromCursor(cursor)
 | 
			
		||||
 | 
			
		||||
        // Make certain column conflicts are dealt with
 | 
			
		||||
        manga.id = chapter.manga_id
 | 
			
		||||
        manga.url = cursor.getString(cursor.getColumnIndex("mangaUrl"))
 | 
			
		||||
        chapter.id = history.chapter_id
 | 
			
		||||
 | 
			
		||||
        // Create mangaChapter object
 | 
			
		||||
        val mangaChapter = MangaChapter(manga, chapter)
 | 
			
		||||
 | 
			
		||||
        // Return result
 | 
			
		||||
        return MangaChapterHistory(mangaChapter, history)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,48 @@
 | 
			
		||||
package eu.kanade.tachiyomi.data.database.tables
 | 
			
		||||
 | 
			
		||||
object HistoryTable {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Table name
 | 
			
		||||
     */
 | 
			
		||||
    const val TABLE = "history"
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Id column name
 | 
			
		||||
     */
 | 
			
		||||
    const val COL_ID = "${TABLE}_id"
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Chapter id column name
 | 
			
		||||
     */
 | 
			
		||||
    const val COL_CHAPTER_ID = "${TABLE}_chapter_id"
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Last read column name
 | 
			
		||||
     */
 | 
			
		||||
    const val COL_LAST_READ = "${TABLE}_last_read"
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Time read column name
 | 
			
		||||
     */
 | 
			
		||||
    const val COL_TIME_READ = "${TABLE}_time_read"
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * query to create history table
 | 
			
		||||
     */
 | 
			
		||||
    val createTableQuery: String
 | 
			
		||||
        get() = """CREATE TABLE $TABLE(
 | 
			
		||||
            $COL_ID INTEGER NOT NULL PRIMARY KEY,
 | 
			
		||||
            $COL_CHAPTER_ID INTEGER NOT NULL UNIQUE,
 | 
			
		||||
            $COL_LAST_READ LONG,
 | 
			
		||||
            $COL_TIME_READ LONG,
 | 
			
		||||
            FOREIGN KEY($COL_CHAPTER_ID) REFERENCES ${ChapterTable.TABLE} (${ChapterTable.COL_ID})
 | 
			
		||||
            ON DELETE CASCADE
 | 
			
		||||
            )"""
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * query to index history chapter id
 | 
			
		||||
     */
 | 
			
		||||
    val createChapterIdIndexQuery: String
 | 
			
		||||
        get() = "CREATE INDEX ${TABLE}_${COL_CHAPTER_ID}_index ON $TABLE($COL_CHAPTER_ID)"
 | 
			
		||||
}
 | 
			
		||||
@@ -24,7 +24,8 @@ import eu.kanade.tachiyomi.ui.manga.chapter.ChaptersPresenter
 | 
			
		||||
import eu.kanade.tachiyomi.ui.manga.info.MangaInfoPresenter
 | 
			
		||||
import eu.kanade.tachiyomi.ui.manga.myanimelist.MyAnimeListPresenter
 | 
			
		||||
import eu.kanade.tachiyomi.ui.reader.ReaderPresenter
 | 
			
		||||
import eu.kanade.tachiyomi.ui.recent.RecentChaptersPresenter
 | 
			
		||||
import eu.kanade.tachiyomi.ui.recent_updates.RecentChaptersPresenter
 | 
			
		||||
import eu.kanade.tachiyomi.ui.recently_read.RecentlyReadPresenter
 | 
			
		||||
import eu.kanade.tachiyomi.ui.setting.SettingsActivity
 | 
			
		||||
import javax.inject.Singleton
 | 
			
		||||
 | 
			
		||||
@@ -42,6 +43,7 @@ interface AppComponent {
 | 
			
		||||
    fun inject(myAnimeListPresenter: MyAnimeListPresenter)
 | 
			
		||||
    fun inject(categoryPresenter: CategoryPresenter)
 | 
			
		||||
    fun inject(recentChaptersPresenter: RecentChaptersPresenter)
 | 
			
		||||
    fun inject(recentlyReadPresenter: RecentlyReadPresenter)
 | 
			
		||||
    fun inject(backupPresenter: BackupPresenter)
 | 
			
		||||
 | 
			
		||||
    fun inject(mainActivity: MainActivity)
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,8 @@ import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
 | 
			
		||||
import eu.kanade.tachiyomi.ui.catalogue.CatalogueFragment
 | 
			
		||||
import eu.kanade.tachiyomi.ui.download.DownloadFragment
 | 
			
		||||
import eu.kanade.tachiyomi.ui.library.LibraryFragment
 | 
			
		||||
import eu.kanade.tachiyomi.ui.recent.RecentChaptersFragment
 | 
			
		||||
import eu.kanade.tachiyomi.ui.recent_updates.RecentChaptersFragment
 | 
			
		||||
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.*
 | 
			
		||||
@@ -50,6 +51,7 @@ class MainActivity : BaseActivity() {
 | 
			
		||||
            when (item.itemId) {
 | 
			
		||||
                R.id.nav_drawer_library -> setFragment(LibraryFragment.newInstance())
 | 
			
		||||
                R.id.nav_drawer_recent_updates -> setFragment(RecentChaptersFragment.newInstance())
 | 
			
		||||
                R.id.nav_drawer_recent_manga -> setFragment(RecentlyReadFragment.newInstance())
 | 
			
		||||
                R.id.nav_drawer_catalogues -> setFragment(CatalogueFragment.newInstance())
 | 
			
		||||
                R.id.nav_drawer_downloads -> setFragment(DownloadFragment.newInstance())
 | 
			
		||||
                R.id.nav_drawer_settings -> startActivity(Intent(this, SettingsActivity::class.java))
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ import android.os.Bundle
 | 
			
		||||
import eu.kanade.tachiyomi.data.cache.ChapterCache
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Chapter
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.History
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Manga
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.MangaSync
 | 
			
		||||
import eu.kanade.tachiyomi.data.download.DownloadManager
 | 
			
		||||
@@ -24,6 +25,7 @@ import rx.schedulers.Schedulers
 | 
			
		||||
import rx.subjects.PublishSubject
 | 
			
		||||
import timber.log.Timber
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.util.*
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
 | 
			
		||||
class ReaderPresenter : BasePresenter<ReaderActivity>() {
 | 
			
		||||
@@ -289,6 +291,7 @@ class ReaderPresenter : BasePresenter<ReaderActivity>() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // Check whether the given chapter is downloaded
 | 
			
		||||
    fun isChapterDownloaded(chapter: Chapter): Boolean {
 | 
			
		||||
        return downloadManager.isChapterDownloaded(source, manga, chapter)
 | 
			
		||||
@@ -340,6 +343,12 @@ class ReaderPresenter : BasePresenter<ReaderActivity>() {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        db.updateChapterProgress(chapter).asRxObservable().subscribe()
 | 
			
		||||
        // Update last read data
 | 
			
		||||
        db.updateHistoryLastRead(History.create(chapter)
 | 
			
		||||
                .apply { last_read = Date().time })
 | 
			
		||||
                .asRxObservable()
 | 
			
		||||
                .doOnError { Timber.e(it.message) }
 | 
			
		||||
                .subscribe()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.recent
 | 
			
		||||
package eu.kanade.tachiyomi.ui.recent_updates
 | 
			
		||||
 | 
			
		||||
import android.support.v7.widget.RecyclerView
 | 
			
		||||
import android.view.View
 | 
			
		||||
@@ -72,7 +72,7 @@ class RecentChaptersAdapter(val fragment: RecentChaptersFragment) : FlexibleAdap
 | 
			
		||||
        // Check which view type and set correct values.
 | 
			
		||||
        when (viewType) {
 | 
			
		||||
            VIEW_TYPE_CHAPTER -> {
 | 
			
		||||
                view = parent.inflate(R.layout.item_recent_chapter)
 | 
			
		||||
                view = parent.inflate(R.layout.item_recent_chapters)
 | 
			
		||||
                return RecentChaptersHolder(view, this, fragment)
 | 
			
		||||
            }
 | 
			
		||||
            VIEW_TYPE_SECTION -> {
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.recent
 | 
			
		||||
package eu.kanade.tachiyomi.ui.recent_updates
 | 
			
		||||
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.support.v4.app.DialogFragment
 | 
			
		||||
@@ -213,7 +213,7 @@ class RecentChaptersFragment : BaseRxFragment<RecentChaptersPresenter>(), Action
 | 
			
		||||
     */
 | 
			
		||||
    fun onNextMangaChapters(chapters: List<Any>) {
 | 
			
		||||
        (activity as MainActivity).updateEmptyView(chapters.isEmpty(),
 | 
			
		||||
                R.string.information_no_recent, R.drawable.ic_history_black_128dp)
 | 
			
		||||
                R.string.information_no_recent, R.drawable.ic_update_black_128dp)
 | 
			
		||||
 | 
			
		||||
        destroyActionModeIfNeeded()
 | 
			
		||||
        adapter.setItems(chapters)
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.recent
 | 
			
		||||
package eu.kanade.tachiyomi.ui.recent_updates
 | 
			
		||||
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.widget.PopupMenu
 | 
			
		||||
@@ -7,11 +7,11 @@ 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
 | 
			
		||||
import kotlinx.android.synthetic.main.item_recent_chapter.view.*
 | 
			
		||||
import kotlinx.android.synthetic.main.item_recent_chapters.view.*
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Holder that contains chapter item
 | 
			
		||||
 * Uses R.layout.item_recent_chapter.
 | 
			
		||||
 * Uses R.layout.item_recent_chapters.
 | 
			
		||||
 * UI related actions should be called from here.
 | 
			
		||||
 *
 | 
			
		||||
 * @param view the inflated view for this holder.
 | 
			
		||||
@@ -19,7 +19,7 @@ import kotlinx.android.synthetic.main.item_recent_chapter.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: FlexibleViewHolder.OnListItemClickListener) :
 | 
			
		||||
class RecentChaptersHolder(view: View, private val adapter: RecentChaptersAdapter, listener: OnListItemClickListener) :
 | 
			
		||||
        FlexibleViewHolder(view, adapter, listener) {
 | 
			
		||||
    /**
 | 
			
		||||
     * Color of read chapter
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.recent
 | 
			
		||||
package eu.kanade.tachiyomi.ui.recent_updates
 | 
			
		||||
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.recent
 | 
			
		||||
package eu.kanade.tachiyomi.ui.recent_updates
 | 
			
		||||
 | 
			
		||||
import android.support.v7.widget.RecyclerView
 | 
			
		||||
import android.text.format.DateUtils
 | 
			
		||||
@@ -0,0 +1,52 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.recently_read
 | 
			
		||||
 | 
			
		||||
import android.support.v7.widget.RecyclerView
 | 
			
		||||
import android.view.ViewGroup
 | 
			
		||||
import eu.davidea.flexibleadapter.FlexibleAdapter
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.MangaChapterHistory
 | 
			
		||||
import eu.kanade.tachiyomi.util.inflate
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Adapter of RecentlyReadHolder.
 | 
			
		||||
 * Connection between Fragment and Holder
 | 
			
		||||
 * Holder updates should be called from here.
 | 
			
		||||
 *
 | 
			
		||||
 * @param fragment a RecentlyReadFragment object
 | 
			
		||||
 * @constructor creates an instance of the adapter.
 | 
			
		||||
 */
 | 
			
		||||
class RecentlyReadAdapter(val fragment: RecentlyReadFragment) : FlexibleAdapter<RecyclerView.ViewHolder, Any>() {
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when ViewHolder is created
 | 
			
		||||
     * @param parent parent View
 | 
			
		||||
     * @param viewType int containing viewType
 | 
			
		||||
     */
 | 
			
		||||
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder? {
 | 
			
		||||
        val view = parent.inflate(R.layout.item_recent_manga)
 | 
			
		||||
        return RecentlyReadHolder(view, this)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when ViewHolder is bind
 | 
			
		||||
     * @param holder bind holder
 | 
			
		||||
     * @param position position of holder
 | 
			
		||||
     */
 | 
			
		||||
    override fun onBindViewHolder(holder: RecyclerView.ViewHolder?, position: Int) {
 | 
			
		||||
        val item = getItem(position) as MangaChapterHistory
 | 
			
		||||
        (holder as RecentlyReadHolder).onSetValues(item)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update items
 | 
			
		||||
     * @param items items
 | 
			
		||||
     */
 | 
			
		||||
    fun setItems(items: List<MangaChapterHistory>) {
 | 
			
		||||
        mItems = items
 | 
			
		||||
        notifyDataSetChanged()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun updateDataSet(param: String?) {
 | 
			
		||||
        // Empty function
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,129 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.recently_read
 | 
			
		||||
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.view.LayoutInflater
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.view.ViewGroup
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Chapter
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.History
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Manga
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.MangaChapterHistory
 | 
			
		||||
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment
 | 
			
		||||
import eu.kanade.tachiyomi.ui.main.MainActivity
 | 
			
		||||
import eu.kanade.tachiyomi.ui.manga.MangaActivity
 | 
			
		||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
 | 
			
		||||
import eu.kanade.tachiyomi.widget.NpaLinearLayoutManager
 | 
			
		||||
import kotlinx.android.synthetic.main.fragment_recent_manga.*
 | 
			
		||||
import nucleus.factory.RequiresPresenter
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Fragment that shows recently read manga.
 | 
			
		||||
 * Uses R.layout.fragment_recent_manga.
 | 
			
		||||
 * UI related actions should be called from here.
 | 
			
		||||
 */
 | 
			
		||||
@RequiresPresenter(RecentlyReadPresenter::class)
 | 
			
		||||
class RecentlyReadFragment : BaseRxFragment<RecentlyReadPresenter>() {
 | 
			
		||||
    companion object {
 | 
			
		||||
        /**
 | 
			
		||||
         * Create new RecentChaptersFragment.
 | 
			
		||||
         *
 | 
			
		||||
         */
 | 
			
		||||
        @JvmStatic
 | 
			
		||||
        fun newInstance(): RecentlyReadFragment {
 | 
			
		||||
            return RecentlyReadFragment()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Adapter containing the recent manga.
 | 
			
		||||
     */
 | 
			
		||||
    lateinit var adapter: RecentlyReadAdapter
 | 
			
		||||
        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? {
 | 
			
		||||
        return inflater.inflate(R.layout.fragment_recent_manga, container, false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when view is created
 | 
			
		||||
     *
 | 
			
		||||
     * @param view created view
 | 
			
		||||
     * @param savedInstanceState status of saved sate
 | 
			
		||||
     */
 | 
			
		||||
    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
 | 
			
		||||
        // Initialize adapter
 | 
			
		||||
        recycler.layoutManager = NpaLinearLayoutManager(activity)
 | 
			
		||||
        adapter = RecentlyReadAdapter(this)
 | 
			
		||||
        recycler.setHasFixedSize(true)
 | 
			
		||||
        recycler.adapter = adapter
 | 
			
		||||
 | 
			
		||||
        // Update toolbar text
 | 
			
		||||
        setToolbarTitle(R.string.label_recent_manga)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Populate adapter with chapters
 | 
			
		||||
     *
 | 
			
		||||
     * @param mangaHistory list of manga history
 | 
			
		||||
     */
 | 
			
		||||
    fun onNextManga(mangaHistory: List<MangaChapterHistory>) {
 | 
			
		||||
        (activity as MainActivity).updateEmptyView(mangaHistory.isEmpty(),
 | 
			
		||||
                R.string.information_no_recent_manga, R.drawable.ic_glasses_black_128dp)
 | 
			
		||||
 | 
			
		||||
        adapter.setItems(mangaHistory)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Reset last read of chapter to 0L
 | 
			
		||||
     * @param history history belonging to chapter
 | 
			
		||||
     */
 | 
			
		||||
    fun removeFromHistory(history: History) {
 | 
			
		||||
        presenter.removeFromHistory(history)
 | 
			
		||||
        adapter.notifyDataSetChanged()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Removes all chapters belonging to manga from library
 | 
			
		||||
     * @param mangaId id of manga
 | 
			
		||||
     */
 | 
			
		||||
    fun removeAllFromHistory(mangaId: Long) {
 | 
			
		||||
        presenter.removeAllFromHistory(mangaId)
 | 
			
		||||
        adapter.notifyDataSetChanged()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Open chapter to continue reading
 | 
			
		||||
     * @param chapter chapter that is opened
 | 
			
		||||
     * @param manga manga belonging to chapter
 | 
			
		||||
     */
 | 
			
		||||
    fun openChapter(chapter: Chapter, manga: Manga) {
 | 
			
		||||
        val intent = ReaderActivity.newIntent(activity, manga, chapter)
 | 
			
		||||
        startActivity(intent)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Open manga info page
 | 
			
		||||
     * @param manga manga belonging to info page
 | 
			
		||||
     */
 | 
			
		||||
    fun openMangaInfo(manga: Manga) {
 | 
			
		||||
        val intent = MangaActivity.newIntent(activity, manga, true)
 | 
			
		||||
        startActivity(intent)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the timestamp of last read
 | 
			
		||||
     * @param history history containing time of last read
 | 
			
		||||
     */
 | 
			
		||||
    fun getLastRead(history: History): String? {
 | 
			
		||||
        return presenter.getLastRead(history)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,96 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.recently_read
 | 
			
		||||
 | 
			
		||||
import android.support.v7.widget.RecyclerView
 | 
			
		||||
import android.view.View
 | 
			
		||||
import com.afollestad.materialdialogs.MaterialDialog
 | 
			
		||||
import com.bumptech.glide.Glide
 | 
			
		||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.MangaChapterHistory
 | 
			
		||||
import eu.kanade.tachiyomi.data.source.SourceManager
 | 
			
		||||
import kotlinx.android.synthetic.main.dialog_remove_recently.view.*
 | 
			
		||||
import kotlinx.android.synthetic.main.item_recent_manga.view.*
 | 
			
		||||
import java.text.DecimalFormat
 | 
			
		||||
import java.text.DecimalFormatSymbols
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Holder that contains recent manga item
 | 
			
		||||
 * Uses R.layout.item_recent_manga.
 | 
			
		||||
 * UI related actions should be called from here.
 | 
			
		||||
 *
 | 
			
		||||
 * @param view the inflated view for this holder.
 | 
			
		||||
 * @param adapter the adapter handling this holder.
 | 
			
		||||
 * @constructor creates a new recent chapter holder.
 | 
			
		||||
 */
 | 
			
		||||
class RecentlyReadHolder(view: View, private val adapter: RecentlyReadAdapter) :
 | 
			
		||||
        RecyclerView.ViewHolder(view) {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * DecimalFormat used to display correct chapter number
 | 
			
		||||
     */
 | 
			
		||||
    private val decimalFormat = DecimalFormat("#.###", DecimalFormatSymbols().apply { decimalSeparator = '.' })
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set values of view
 | 
			
		||||
     *
 | 
			
		||||
     * @param item item containing history information
 | 
			
		||||
     */
 | 
			
		||||
    fun onSetValues(item: MangaChapterHistory) {
 | 
			
		||||
        // Retrieve objects
 | 
			
		||||
        val manga = item.mangaChapter.manga
 | 
			
		||||
        val chapter = item.mangaChapter.chapter
 | 
			
		||||
        val history = item.history
 | 
			
		||||
 | 
			
		||||
        // Set manga title
 | 
			
		||||
        itemView.manga_title.text = manga.title
 | 
			
		||||
 | 
			
		||||
        // Set source + chapter title
 | 
			
		||||
        val formattedNumber = decimalFormat.format(chapter.chapter_number.toDouble())
 | 
			
		||||
        itemView.manga_source.text = itemView.context.getString(R.string.recent_manga_source)
 | 
			
		||||
                .format(SourceManager(adapter.fragment.context).get(manga.source)?.name, formattedNumber)
 | 
			
		||||
 | 
			
		||||
        // Set last read timestamp title
 | 
			
		||||
        itemView.last_read.text = adapter.fragment.getLastRead(history)
 | 
			
		||||
 | 
			
		||||
        // Set cover
 | 
			
		||||
        if (!manga.thumbnail_url.isNullOrEmpty()) {
 | 
			
		||||
            Glide.with(itemView.context)
 | 
			
		||||
                    .load(manga)
 | 
			
		||||
                    .diskCacheStrategy(DiskCacheStrategy.RESULT)
 | 
			
		||||
                    .centerCrop()
 | 
			
		||||
                    .into(itemView.cover)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Set remove clickListener
 | 
			
		||||
        itemView.remove.setOnClickListener {
 | 
			
		||||
            MaterialDialog.Builder(itemView.context)
 | 
			
		||||
                    .title(R.string.action_remove)
 | 
			
		||||
                    .customView(R.layout.dialog_remove_recently, true)
 | 
			
		||||
                    .positiveText(R.string.action_remove)
 | 
			
		||||
                    .negativeText(android.R.string.cancel)
 | 
			
		||||
                    .onPositive { materialDialog, dialogAction ->
 | 
			
		||||
                        // Check if user wants all chapters reset
 | 
			
		||||
                        if (materialDialog.customView?.removeAll?.isChecked as Boolean) {
 | 
			
		||||
                            adapter.fragment.removeAllFromHistory(manga.id)
 | 
			
		||||
                        } else {
 | 
			
		||||
                            adapter.fragment.removeFromHistory(history)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    .onNegative { materialDialog, dialogAction ->
 | 
			
		||||
                        materialDialog.dismiss()
 | 
			
		||||
                    }
 | 
			
		||||
                    .show();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Set continue reading clickListener
 | 
			
		||||
        itemView.resume.setOnClickListener {
 | 
			
		||||
            adapter.fragment.openChapter(chapter, manga)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Set open manga info clickListener
 | 
			
		||||
        itemView.cover.setOnClickListener {
 | 
			
		||||
            adapter.fragment.openMangaInfo(manga)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,97 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.recently_read
 | 
			
		||||
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.History
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.MangaChapterHistory
 | 
			
		||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
 | 
			
		||||
import rx.Observable
 | 
			
		||||
import rx.android.schedulers.AndroidSchedulers
 | 
			
		||||
import rx.schedulers.Schedulers
 | 
			
		||||
import timber.log.Timber
 | 
			
		||||
import java.text.SimpleDateFormat
 | 
			
		||||
import java.util.*
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The id of the restartable.
 | 
			
		||||
 */
 | 
			
		||||
const private val GET_RECENT_MANGA = 1
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Presenter of RecentlyReadFragment.
 | 
			
		||||
 * Contains information and data for fragment.
 | 
			
		||||
 * Observable updates should be called from here.
 | 
			
		||||
 */
 | 
			
		||||
class RecentlyReadPresenter : BasePresenter<RecentlyReadFragment>() {
 | 
			
		||||
    /**
 | 
			
		||||
     * Used to connect to database
 | 
			
		||||
     */
 | 
			
		||||
    @Inject lateinit var db: DatabaseHelper
 | 
			
		||||
 | 
			
		||||
    override fun onCreate(savedState: Bundle?) {
 | 
			
		||||
        super.onCreate(savedState)
 | 
			
		||||
 | 
			
		||||
        // Used to get recent manga
 | 
			
		||||
        restartableLatestCache(GET_RECENT_MANGA,
 | 
			
		||||
                { getRecentMangaObservable() },
 | 
			
		||||
                { recentMangaFragment, manga ->
 | 
			
		||||
                    // Update adapter to show recent manga's
 | 
			
		||||
                    recentMangaFragment.onNextManga(manga)
 | 
			
		||||
                }
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        if (savedState == null) {
 | 
			
		||||
            // Start fetching recent manga
 | 
			
		||||
            start(GET_RECENT_MANGA)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get recent manga observable
 | 
			
		||||
     * @return list of history
 | 
			
		||||
     */
 | 
			
		||||
    fun getRecentMangaObservable(): Observable<MutableList<MangaChapterHistory>> {
 | 
			
		||||
        // Set date for recent manga
 | 
			
		||||
        val cal = Calendar.getInstance()
 | 
			
		||||
        cal.time = Date()
 | 
			
		||||
        cal.add(Calendar.MONTH, -1)
 | 
			
		||||
 | 
			
		||||
        return db.getRecentManga(cal.time).asRxObservable()
 | 
			
		||||
                .observeOn(AndroidSchedulers.mainThread())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Reset last read of chapter to 0L
 | 
			
		||||
     * @param history history belonging to chapter
 | 
			
		||||
     */
 | 
			
		||||
    fun removeFromHistory(history: History) {
 | 
			
		||||
        history.last_read = 0L
 | 
			
		||||
        db.updateHistoryLastRead(history).asRxObservable()
 | 
			
		||||
                .doOnError { Timber.e(it.message) }.subscribe()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Removes all chapters belonging to manga from library
 | 
			
		||||
     * @param mangaId id of manga
 | 
			
		||||
     */
 | 
			
		||||
    fun removeAllFromHistory(mangaId: Long) {
 | 
			
		||||
        db.getHistoryByMangaId(mangaId).asRxObservable()
 | 
			
		||||
                .take(1)
 | 
			
		||||
                .flatMapIterable { it }
 | 
			
		||||
                .doOnError { Timber.e(it.message) }
 | 
			
		||||
                .subscribeOn(Schedulers.io())
 | 
			
		||||
                .observeOn(AndroidSchedulers.mainThread())
 | 
			
		||||
                .subscribe({ result -> removeFromHistory(result) })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the timestamp of last read
 | 
			
		||||
     * @param history history containing time of last read
 | 
			
		||||
     */
 | 
			
		||||
    fun getLastRead(history: History): String? {
 | 
			
		||||
        return SimpleDateFormat("dd-MM-yyyy HH:mm",
 | 
			
		||||
                Locale.getDefault()).format(Date(history.last_read))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user