Added recently read tab (#316)

This commit is contained in:
Bram van de Kerkhof
2016-06-06 15:26:56 +02:00
parent 11262f86f9
commit 7ba898f701
38 changed files with 932 additions and 38 deletions

View File

@ -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)

View File

@ -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)
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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()
}

View File

@ -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.

View File

@ -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)
}
}

View File

@ -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)
}

View File

@ -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)
}
}

View File

@ -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)"
}

View File

@ -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)

View File

@ -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))

View File

@ -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()
}
/**

View File

@ -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 -> {

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}
}

View File

@ -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))
}
}