Added recently read tab

This commit is contained in:
NoodleMage 2016-05-21 20:55:27 +02:00
parent 4e784cd7c3
commit 51a34c9bef
32 changed files with 556 additions and 35 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,17 +26,23 @@ 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.createMangaIdIndexQuery)
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
if (oldVersion < 2) {
db.execSQL(ChapterTable.sourceOrderUpdateQuery)
}
if (oldVersion < 3) {
db.execSQL(HistoryTable.createTableQuery)
db.execSQL(HistoryTable.createMangaIdIndexQuery)
}
}
override fun onConfigure(db: SQLiteDatabase) {

View File

@ -0,0 +1,29 @@
package eu.kanade.tachiyomi.data.database.models;
import com.pushtorefresh.storio.sqlite.annotations.StorIOSQLiteColumn;
import com.pushtorefresh.storio.sqlite.annotations.StorIOSQLiteType;
import eu.kanade.tachiyomi.data.database.tables.HistoryTable;
@StorIOSQLiteType(table = HistoryTable.TABLE)
public class History {
@StorIOSQLiteColumn(name = HistoryTable.COL_ID, key = true)
public Long id;
@StorIOSQLiteColumn(name = HistoryTable.COL_MANGA_ID)
public long manga_id;
@StorIOSQLiteColumn(name = HistoryTable.COL_LAST_READ)
public long last_read;
public History() {
}
public static History create(Manga manga) {
History history = new History();
history.manga_id = manga.id;
return history;
}
}

View File

@ -0,0 +1,33 @@
package eu.kanade.tachiyomi.data.database.queries
import com.pushtorefresh.storio.sqlite.queries.Query
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.Manga
import eu.kanade.tachiyomi.data.database.tables.HistoryTable
import java.util.*
interface HistoryQueries : DbProvider {
fun insertHistory(history: History) = db.put().`object`(history).prepare()
fun getRecentManga(date: Date) = db.get()
.listOfObjects(Manga::class.java)
.withQuery(RawQuery.builder()
.query(getRecentMangasQuery())
.args(date.time)
.observesTables(HistoryTable.TABLE)
.build())
.prepare()
fun getHistoryByMangaId(id: Long) = db.get()
.`object`(History::class.java)
.withQuery(Query.builder()
.table(HistoryTable.TABLE)
.where("${HistoryTable.COL_MANGA_ID} = ?")
.whereArgs(id)
.build())
.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,16 @@ fun getRecentsQuery() = """
ORDER BY ${Chapter.COL_DATE_UPLOAD} DESC
"""
/**
* Query to get the recent chapters of manga from the library up to a date.
*/
fun getRecentMangasQuery() = """
SELECT ${Manga.TABLE}.* FROM ${Manga.TABLE} JOIN ${History.TABLE}
ON ${Manga.TABLE}.${Manga.COL_ID} = ${History.TABLE}.${History.COL_MANGA_ID}
WHERE ${History.COL_LAST_READ} > ?
ORDER BY ${History.COL_LAST_READ} DESC
"""
/**
* Query to get the categories for a manga.

View File

@ -0,0 +1,25 @@
package eu.kanade.tachiyomi.data.database.tables
object HistoryTable {
const val TABLE = "history"
const val COL_ID = "_id"
const val COL_MANGA_ID = "manga_id"
const val COL_LAST_READ = "last_read"
val createTableQuery: String
get() = """CREATE TABLE $TABLE(
$COL_ID INTEGER NOT NULL PRIMARY KEY,
$COL_MANGA_ID INTEGER NOT NULL,
$COL_LAST_READ LONG,
FOREIGN KEY($COL_MANGA_ID) REFERENCES ${MangaTable.TABLE} (${MangaTable.COL_ID})
ON DELETE CASCADE
)"""
val createMangaIdIndexQuery: String
get() = "CREATE INDEX ${HistoryTable.TABLE}_${HistoryTable.COL_MANGA_ID}_index ON ${HistoryTable.TABLE}(${HistoryTable.COL_MANGA_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.chapters.RecentChaptersPresenter
import eu.kanade.tachiyomi.ui.recent.manga.RecentMangaPresenter
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(recentMangaPresenter: RecentMangaPresenter)
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.chapters.RecentChaptersFragment
import eu.kanade.tachiyomi.ui.recent.manga.RecentMangaFragment
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(RecentMangaFragment.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>() {
@ -83,6 +85,8 @@ class ReaderPresenter : BasePresenter<ReaderActivity>() {
initializeSubjects()
setMangaLastRead()
startableLatestCache(GET_ADJACENT_CHAPTERS,
{ getAdjacentChaptersObservable() },
{ view, pair -> view.onAdjacentChapters(pair.first, pair.second) })
@ -222,6 +226,15 @@ class ReaderPresenter : BasePresenter<ReaderActivity>() {
.doOnNext { mangaSyncList = it }
}
private fun setMangaLastRead() {
var history = db.getHistoryByMangaId(manga.id).executeAsBlocking()
if (history == null) {
history = History.create(manga)
}
history?.last_read = Date().time
db.insertHistory(history!!).executeAsBlocking()
}
// Loads the given chapter
private fun loadChapter(chapter: Chapter, requestedPage: Int = 0) {
if (isSeamlessMode) {
@ -283,6 +296,7 @@ class ReaderPresenter : BasePresenter<ReaderActivity>() {
}
}
// Check whether the given chapter is downloaded
fun isChapterDownloaded(chapter: Chapter): Boolean {
return downloadManager.isChapterDownloaded(source, manga, chapter)

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.ui.recent
package eu.kanade.tachiyomi.ui.recent.chapters
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.chapters
import android.os.Bundle
import android.support.v4.app.DialogFragment
@ -115,7 +115,7 @@ class RecentChaptersFragment : BaseRxFragment<RecentChaptersPresenter>(), Flexib
*/
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)
adapter.setItems(chapters)
}

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.ui.recent
package eu.kanade.tachiyomi.ui.recent.chapters
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.chapters
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.chapters
import android.support.v7.widget.RecyclerView
import android.text.format.DateUtils

View File

@ -0,0 +1,44 @@
package eu.kanade.tachiyomi.ui.recent.manga
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.Manga
import eu.kanade.tachiyomi.util.inflate
class RecentMangaAdapter(val fragment: RecentMangaFragment) : 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 RecentMangaHolder(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 Manga
(holder as RecentMangaHolder).onSetValues(item)
}
/**
* Update items
* @param items items
*/
fun setItems(items: List<Manga>) {
mItems = items
notifyDataSetChanged()
}
override fun updateDataSet(param: String?) {
// Empty function
}
}

View File

@ -0,0 +1,108 @@
package eu.kanade.tachiyomi.ui.recent.manga
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.Manga
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.util.toast
import eu.kanade.tachiyomi.widget.NpaLinearLayoutManager
import kotlinx.android.synthetic.main.fragment_recent_manga.*
import nucleus.factory.RequiresPresenter
/**
* TODO
*/
@RequiresPresenter(RecentMangaPresenter::class)
class RecentMangaFragment : BaseRxFragment<RecentMangaPresenter>() {
companion object {
/**
* Create new RecentChaptersFragment.
*
*/
@JvmStatic
fun newInstance(): RecentMangaFragment {
return RecentMangaFragment()
}
}
/**
* Adapter containing the recent manga.
*/
lateinit var adapter: RecentMangaAdapter
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?) {
recycler.layoutManager = NpaLinearLayoutManager(activity)
adapter = RecentMangaAdapter(this)
recycler.setHasFixedSize(true)
recycler.adapter = adapter
// Update toolbar text
setToolbarTitle(R.string.label_recent_manga)
}
/**
* Populate adapter with chapters
*
* @param chapters list of chapters
*/
fun onNextMangaChapters(chapters: List<Manga>) {
(activity as MainActivity).updateEmptyView(chapters.isEmpty(),
R.string.information_no_recent_manga, R.drawable.ic_glasses_black_128dp)
adapter.setItems(chapters)
}
fun removeFromHistory(id: Long) {
presenter.removeFromHistory(id)
adapter.notifyDataSetChanged()
}
fun getNextUnreadChapter(manga: Manga): Chapter? {
return presenter.getNextUnreadChapter(manga)
}
fun openChapter(chapter: Chapter?, manga: Manga) {
if (chapter != null) {
val intent = ReaderActivity.newIntent(activity, manga, chapter)
startActivity(intent)
} else {
adapter.fragment.context.toast(R.string.no_next_chapter)
}
}
fun openMangaInfo(manga: Manga) {
val intent = MangaActivity.newIntent(activity, manga, true)
startActivity(intent)
}
fun getLastRead(id: Long): String? {
return presenter.getLastRead(id)
}
}

View File

@ -0,0 +1,43 @@
package eu.kanade.tachiyomi.ui.recent.manga
import android.support.v7.widget.RecyclerView
import android.view.View
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.source.SourceManager
import kotlinx.android.synthetic.main.item_recent_manga.view.*
class RecentMangaHolder(view: View, private val adapter: RecentMangaAdapter) :
RecyclerView.ViewHolder(view) {
fun onSetValues(manga: Manga) {
// Set manga title
itemView.manga_title.text = manga.title
itemView.manga_source.text = SourceManager(adapter.fragment.context).get(manga.source)?.visibleName
itemView.last_read.text = adapter.fragment.getLastRead(manga.id)
itemView.remove.setOnClickListener {
adapter.fragment.removeFromHistory(manga.id)
}
itemView.continue_reading.setOnClickListener {
val chapter = adapter.fragment.getNextUnreadChapter(manga)
adapter.fragment.openChapter(chapter, manga)
}
itemView.cover.setOnClickListener {
adapter.fragment.openMangaInfo(manga)
}
// Set cover
if (!manga.thumbnail_url.isNullOrEmpty()) {
Glide.with(itemView.context)
.load(manga)
.diskCacheStrategy(DiskCacheStrategy.RESULT)
.centerCrop()
.into(itemView.cover)
}
}
}

View File

@ -0,0 +1,73 @@
package eu.kanade.tachiyomi.ui.recent.manga
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.ui.base.presenter.BasePresenter
import rx.Observable
import rx.android.schedulers.AndroidSchedulers
import java.text.SimpleDateFormat
import java.util.*
import javax.inject.Inject
/**
* The id of the restartable.
*/
const private val GET_RECENT_MANGA = 1
class RecentMangaPresenter : BasePresenter<RecentMangaFragment>() {
/**
* 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() },
{ recentChaptersFragment, chapters ->
// Update adapter to show recent manga's
recentChaptersFragment.onNextMangaChapters(chapters)
}
)
if (savedState == null) {
// Start fetching recent chapters
start(GET_RECENT_MANGA)
}
}
fun getRecentMangaObservable(): Observable<MutableList<Manga>>? {
// Set date for recent chapters
val cal = Calendar.getInstance()
cal.time = Date()
cal.add(Calendar.MONTH, -1)
return db.getRecentManga(cal.time).asRxObservable()
.observeOn(AndroidSchedulers.mainThread())
}
fun removeFromHistory(id: Long) {
val history = db.getHistoryByMangaId(id).executeAsBlocking()
history?.let {
it.last_read = 0L
}
db.insertHistory(history!!).executeAsBlocking()
}
fun getNextUnreadChapter(manga: Manga): Chapter? {
return db.getNextUnreadChapter(manga).executeAsBlocking()
}
fun getLastRead(id: Long): String? {
val history = db.getHistoryByMangaId(id).executeAsBlocking()
return SimpleDateFormat("dd-MM-yyyy HH:mm",
java.util.Locale.getDefault()).format(Date(history?.last_read as Long));
}
}

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="128dp"
android:height="128dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M3,10C2.76,10 2.55,10.09 2.41,10.25C2.27,10.4 2.21,10.62 2.24,10.86L2.74,13.85C2.82,14.5 3.4,15 4,15H7C7.64,15 8.36,14.44 8.5,13.82L9.56,10.63C9.6,10.5 9.57,10.31 9.5,10.19C9.39,10.07 9.22,10 9,10H3M7,17H4C2.38,17 0.96,15.74 0.76,14.14L0.26,11.15C0.15,10.3 0.39,9.5 0.91,8.92C1.43,8.34 2.19,8 3,8H9C9.83,8 10.58,8.35 11.06,8.96C11.17,9.11 11.27,9.27 11.35,9.45C11.78,9.36 12.22,9.36 12.64,9.45C12.72,9.27 12.82,9.11 12.94,8.96C13.41,8.35 14.16,8 15,8H21C21.81,8 22.57,8.34 23.09,8.92C23.6,9.5 23.84,10.3 23.74,11.11L23.23,14.18C23.04,15.74 21.61,17 20,17H17C15.44,17 13.92,15.81 13.54,14.3L12.64,11.59C12.26,11.31 11.73,11.31 11.35,11.59L10.43,14.37C10.07,15.82 8.56,17 7,17M15,10C14.78,10 14.61,10.07 14.5,10.19C14.42,10.31 14.4,10.5 14.45,10.7L15.46,13.75C15.64,14.44 16.36,15 17,15H20C20.59,15 21.18,14.5 21.25,13.89L21.76,10.82C21.79,10.62 21.73,10.4 21.59,10.25C21.45,10.09 21.24,10 21,10H15Z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M3,10C2.76,10 2.55,10.09 2.41,10.25C2.27,10.4 2.21,10.62 2.24,10.86L2.74,13.85C2.82,14.5 3.4,15 4,15H7C7.64,15 8.36,14.44 8.5,13.82L9.56,10.63C9.6,10.5 9.57,10.31 9.5,10.19C9.39,10.07 9.22,10 9,10H3M7,17H4C2.38,17 0.96,15.74 0.76,14.14L0.26,11.15C0.15,10.3 0.39,9.5 0.91,8.92C1.43,8.34 2.19,8 3,8H9C9.83,8 10.58,8.35 11.06,8.96C11.17,9.11 11.27,9.27 11.35,9.45C11.78,9.36 12.22,9.36 12.64,9.45C12.72,9.27 12.82,9.11 12.94,8.96C13.41,8.35 14.16,8 15,8H21C21.81,8 22.57,8.34 23.09,8.92C23.6,9.5 23.84,10.3 23.74,11.11L23.23,14.18C23.04,15.74 21.61,17 20,17H17C15.44,17 13.92,15.81 13.54,14.3L12.64,11.59C12.26,11.31 11.73,11.31 11.35,11.59L10.43,14.37C10.07,15.82 8.56,17 7,17M15,10C14.78,10 14.61,10.07 14.5,10.19C14.42,10.31 14.4,10.5 14.45,10.7L15.46,13.75C15.64,14.44 16.36,15 17,15H20C20.59,15 21.18,14.5 21.25,13.89L21.76,10.82C21.79,10.62 21.73,10.4 21.59,10.25C21.45,10.09 21.24,10 21,10H15Z"/>
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="128dp"
android:height="128dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z"/>
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="128dp"
android:height="128dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M21,10.12h-6.78l2.74,-2.82c-2.73,-2.7 -7.15,-2.8 -9.88,-0.1 -2.73,2.71 -2.73,7.08 0,9.79 2.73,2.71 7.15,2.71 9.88,0C18.32,15.65 19,14.08 19,12.1h2c0,1.98 -0.88,4.55 -2.64,6.29 -3.51,3.48 -9.21,3.48 -12.72,0 -3.5,-3.47 -3.53,-9.11 -0.02,-12.58 3.51,-3.47 9.14,-3.47 12.65,0L21,3v7.12zM12.5,8v4.25l3.5,2.08 -0.72,1.21L11,13V8h1.5z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M21,10.12h-6.78l2.74,-2.82c-2.73,-2.7 -7.15,-2.8 -9.88,-0.1 -2.73,2.71 -2.73,7.08 0,9.79 2.73,2.71 7.15,2.71 9.88,0C18.32,15.65 19,14.08 19,12.1h2c0,1.98 -0.88,4.55 -2.64,6.29 -3.51,3.48 -9.21,3.48 -12.72,0 -3.5,-3.47 -3.53,-9.11 -0.02,-12.58 3.51,-3.47 9.14,-3.47 12.65,0L21,3v7.12zM12.5,8v4.25l3.5,2.08 -0.72,1.21L11,13V8h1.5z"/>
</vector>

View File

@ -9,8 +9,7 @@
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:descendantFocusability="blocksDescendants"
tools:listitem="@layout/item_recent_chapter">
tools:listitem="@layout/item_recent_manga">
</android.support.v7.widget.RecyclerView>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/item_recent_manga">
</android.support.v7.widget.RecyclerView>
</RelativeLayout>

View File

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/cv_manga"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/Theme.Widget.CardView"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="150dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/cover"
android:layout_width="100dp"
android:layout_height="match_parent"
android:clickable="true"
android:scaleType="centerCrop"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="@dimen/card_margin">
<TextView
android:id="@+id/manga_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="2"
android:textAllCaps="true"
android:textAppearance="@style/TextAppearance.Medium.Title"/>
<TextView
android:id="@+id/manga_source"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/manga_title"
android:textAppearance="@style/TextAppearance.Medium.Body2"/>
<TextView
android:id="@+id/last_read"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/manga_source"
android:textAppearance="@style/TextAppearance.Medium.Body2.Hint"/>
<TextView
android:id="@+id/remove"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/action_remove"
android:layout_alignParentBottom="true"
android:clickable="true"
android:background="?attr/selectable_list_drawable"
android:textAppearance="@style/TextAppearance.Medium.Button.Negative"/>
<TextView
android:layout_marginLeft="@dimen/card_margin"
android:layout_marginStart="@dimen/card_margin"
android:layout_marginRight="@dimen/card_margin"
android:layout_marginEnd="@dimen/card_margin"
android:id="@+id/continue_reading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/action_continue_reading"
android:layout_alignParentBottom="true"
android:layout_toRightOf="@id/remove"
android:layout_toEndOf="@id/remove"
android:background="?attr/selectable_list_drawable"
android:textAppearance="@style/TextAppearance.Medium.Button"/>
</RelativeLayout>
</LinearLayout>
</android.support.v7.widget.CardView>

View File

@ -7,10 +7,15 @@
android:id="@+id/nav_drawer_library"
android:icon="@drawable/ic_book_black_24dp"
android:title="@string/label_library" />
<item
android:id="@+id/nav_drawer_recent_manga"
android:icon="@drawable/ic_glasses_black_24dp"
android:title="@string/label_recent_manga"/>
<item
android:id="@+id/nav_drawer_recent_updates"
android:icon="@drawable/ic_history_black_24dp"
android:icon="@drawable/ic_update_black_24dp"
android:title="@string/label_recent_updates" />
<item
android:id="@+id/nav_drawer_catalogues"
android:icon="@drawable/ic_explore_black_24dp"

View File

@ -70,4 +70,6 @@
<color name="md_blue_grey_900">#263238</color>
<color name="md_red_500">#F44336</color>
</resources>

View File

@ -7,6 +7,7 @@
<string name="label_settings">Settings</string>
<string name="label_download_queue">Download queue</string>
<string name="label_library">My library</string>
<string name="label_recent_manga">Recently read</string>
<string name="label_recent_updates">Recent updates</string>
<string name="label_catalogues">Catalogues</string>
<string name="label_categories">Categories</string>
@ -46,6 +47,8 @@
<string name="action_previous_chapter">Previous chapter</string>
<string name="action_next_chapter">Next chapter</string>
<string name="action_retry">Retry</string>
<string name="action_remove">Remove</string>
<string name="action_continue_reading">Continue Reading</string>
<string name="action_open_in_browser">Open in browser</string>
<string name="action_display_mode">Change display mode</string>
<string name="action_cancel">Cancel</string>
@ -308,6 +311,7 @@
<!-- Information Text -->
<string name="information_no_downloads">No downloads</string>
<string name="information_no_recent">No recent chapters</string>
<string name="information_no_recent_manga">No recently read manga</string>
<string name="information_empty_library">Empty library</string>
<!-- Download Notification -->

View File

@ -121,6 +121,10 @@
<item name="android:textColor">?attr/colorAccent</item>
</style>
<style name="TextAppearance.Medium.Button.Negative">
<item name="android:textColor">@color/md_red_500</item>
</style>
<!--=======-->
<!--Widgets-->
<!--=======-->
@ -137,6 +141,12 @@
<item name="layout_behavior">eu.kanade.tachiyomi.ui.base.fab.FABAnimationUpDown</item>
</style>
<style name="Theme.Widget.CardView">
<item name="android:layout_marginRight">@dimen/card_margin</item>
<item name="android:layout_marginLeft">@dimen/card_margin</item>
<item name="android:layout_marginTop">@dimen/card_margin</item>
</style>
<style name="Theme.Widget.GridView">
<item name="android:padding">5dp</item>
<item name="android:clipToPadding">false</item>