Update recent chapters adapter

This commit is contained in:
len 2017-01-22 20:06:43 +01:00
parent 6264e56148
commit c6b89a826c
11 changed files with 272 additions and 358 deletions

View File

@ -57,6 +57,9 @@ class MainActivity : BaseActivity() {
empty_view.hide() empty_view.hide()
val id = item.itemId val id = item.itemId
val oldFragment = supportFragmentManager.findFragmentById(R.id.frame_container)
if (oldFragment == null || oldFragment.tag.toInt() != id) {
when (id) { when (id) {
R.id.nav_drawer_library -> setFragment(LibraryFragment.newInstance(), id) R.id.nav_drawer_library -> setFragment(LibraryFragment.newInstance(), id)
R.id.nav_drawer_recent_updates -> setFragment(RecentChaptersFragment.newInstance(), id) R.id.nav_drawer_recent_updates -> setFragment(RecentChaptersFragment.newInstance(), id)
@ -70,6 +73,7 @@ class MainActivity : BaseActivity() {
} }
R.id.nav_drawer_backup -> setFragment(BackupFragment.newInstance(), id) R.id.nav_drawer_backup -> setFragment(BackupFragment.newInstance(), id)
} }
}
drawer.closeDrawer(GravityCompat.START) drawer.closeDrawer(GravityCompat.START)
true true
} }

View File

@ -0,0 +1,50 @@
package eu.kanade.tachiyomi.ui.recent_updates
import android.text.format.DateUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractHeaderItem
import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.R
import java.util.*
class DateItem(val date: Date) : AbstractHeaderItem<DateItem.Holder>() {
override fun getLayoutRes(): Int {
return R.layout.item_recent_chapter_section
}
override fun createViewHolder(adapter: FlexibleAdapter<*>, inflater: LayoutInflater, parent: ViewGroup): Holder {
return Holder(inflater.inflate(layoutRes, parent, false), adapter)
}
override fun bindViewHolder(adapter: FlexibleAdapter<*>, holder: Holder, position: Int, payloads: List<Any?>?) {
holder.bind(this)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other is DateItem) {
return date == other.date
}
return false
}
override fun hashCode(): Int {
return date.hashCode()
}
class Holder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter, true) {
private val now = Date().time
val section_text = view.findViewById(R.id.section_text) as TextView
fun bind(item: DateItem) {
section_text.text = DateUtils.getRelativeTimeSpanString(item.date.time, now, DateUtils.DAY_IN_MILLIS)
}
}
}

View File

@ -1,22 +0,0 @@
package eu.kanade.tachiyomi.ui.recent_updates
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.MangaChapter
import eu.kanade.tachiyomi.data.download.model.Download
class RecentChapter(mc: MangaChapter) : Chapter by mc.chapter {
val manga = mc.manga
private var _status: Int = 0
var status: Int
get() = download?.status ?: _status
set(value) { _status = value }
@Transient var download: Download? = null
val isDownloaded: Boolean
get() = status == Download.DOWNLOADED
}

View File

@ -2,9 +2,9 @@ package eu.kanade.tachiyomi.ui.recent_updates
import android.view.View import android.view.View
import android.widget.PopupMenu import android.widget.PopupMenu
import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder
import eu.kanade.tachiyomi.util.getResourceColor import eu.kanade.tachiyomi.util.getResourceColor
import kotlinx.android.synthetic.main.item_recent_chapters.view.* import kotlinx.android.synthetic.main.item_recent_chapters.view.*
@ -18,11 +18,9 @@ import kotlinx.android.synthetic.main.item_recent_chapters.view.*
* @param listener a listener to react to single tap and long tap events. * @param listener a listener to react to single tap and long tap events.
* @constructor creates a new recent chapter holder. * @constructor creates a new recent chapter holder.
*/ */
class RecentChaptersHolder( class RecentChapterHolder(private val view: View, private val adapter: RecentChaptersAdapter) :
private val view: View, FlexibleViewHolder(view, adapter) {
private val adapter: RecentChaptersAdapter,
listener: OnListItemClickListener)
: FlexibleViewHolder(view, adapter, listener) {
/** /**
* Color of read chapter * Color of read chapter
*/ */
@ -34,33 +32,33 @@ class RecentChaptersHolder(
private var unreadColor = view.context.getResourceColor(android.R.attr.textColorPrimary) private var unreadColor = view.context.getResourceColor(android.R.attr.textColorPrimary)
/** /**
* Object containing chapter information * Currently bound item.
*/ */
private var chapter: RecentChapter? = null private var item: RecentChapterItem? = null
init { init {
// We need to post a Runnable to show the popup to make sure that the PopupMenu is // We need to post a Runnable to show the popup to make sure that the PopupMenu is
// correctly positioned. The reason being that the view may change position before the // correctly positioned. The reason being that the view may change position before the
// PopupMenu is shown. // PopupMenu is shown.
view.chapter_menu.setOnClickListener { it.post({ showPopupMenu(it) }) } view.chapter_menu.setOnClickListener { it.post { showPopupMenu(it) } }
} }
/** /**
* Set values of view * Set values of view
* *
* @param chapter item containing chapter information * @param item item containing chapter information
*/ */
fun onSetValues(chapter: RecentChapter) { fun bind(item: RecentChapterItem) {
this.chapter = chapter this.item = item
// Set chapter title // Set chapter title
view.chapter_title.text = chapter.name view.chapter_title.text = item.chapter.name
// Set manga title // Set manga title
view.manga_title.text = chapter.manga.title view.manga_title.text = item.manga.title
// Check if chapter is read and set correct color // Check if chapter is read and set correct color
if (chapter.read) { if (item.chapter.read) {
view.chapter_title.setTextColor(readColor) view.chapter_title.setTextColor(readColor)
view.manga_title.setTextColor(readColor) view.manga_title.setTextColor(readColor)
} else { } else {
@ -69,7 +67,7 @@ class RecentChaptersHolder(
} }
// Set chapter status // Set chapter status
notifyStatus(chapter.status) notifyStatus(item.status)
} }
/** /**
@ -92,7 +90,7 @@ class RecentChaptersHolder(
* *
* @param view view containing popup menu. * @param view view containing popup menu.
*/ */
private fun showPopupMenu(view: View) = chapter?.let { chapter -> private fun showPopupMenu(view: View) = item?.let { item ->
// Create a PopupMenu, giving it the clicked view for an anchor // Create a PopupMenu, giving it the clicked view for an anchor
val popup = PopupMenu(view.context, view) val popup = PopupMenu(view.context, view)
@ -100,18 +98,18 @@ class RecentChaptersHolder(
popup.menuInflater.inflate(R.menu.chapter_recent, popup.menu) popup.menuInflater.inflate(R.menu.chapter_recent, popup.menu)
// Hide download and show delete if the chapter is downloaded and // Hide download and show delete if the chapter is downloaded and
if (chapter.isDownloaded) { if (item.isDownloaded) {
popup.menu.findItem(R.id.action_download).isVisible = false popup.menu.findItem(R.id.action_download).isVisible = false
popup.menu.findItem(R.id.action_delete).isVisible = true popup.menu.findItem(R.id.action_delete).isVisible = true
} }
// Hide mark as unread when the chapter is unread // Hide mark as unread when the chapter is unread
if (!chapter.read /*&& mangaChapter.chapter.last_page_read == 0*/) { if (!item.chapter.read /*&& mangaChapter.chapter.last_page_read == 0*/) {
popup.menu.findItem(R.id.action_mark_as_unread).isVisible = false popup.menu.findItem(R.id.action_mark_as_unread).isVisible = false
} }
// Hide mark as read when the chapter is read // Hide mark as read when the chapter is read
if (chapter.read) { if (item.chapter.read) {
popup.menu.findItem(R.id.action_mark_as_read).isVisible = false popup.menu.findItem(R.id.action_mark_as_read).isVisible = false
} }
@ -119,10 +117,10 @@ class RecentChaptersHolder(
popup.setOnMenuItemClickListener { menuItem -> popup.setOnMenuItemClickListener { menuItem ->
with(adapter.fragment) { with(adapter.fragment) {
when (menuItem.itemId) { when (menuItem.itemId) {
R.id.action_download -> downloadChapter(chapter) R.id.action_download -> downloadChapter(item)
R.id.action_delete -> deleteChapter(chapter) R.id.action_delete -> deleteChapter(item)
R.id.action_mark_as_read -> markAsRead(listOf(chapter)) R.id.action_mark_as_read -> markAsRead(listOf(item))
R.id.action_mark_as_unread -> markAsUnread(listOf(chapter)) R.id.action_mark_as_unread -> markAsUnread(listOf(item))
} }
} }

View File

@ -0,0 +1,50 @@
package eu.kanade.tachiyomi.ui.recent_updates
import android.view.LayoutInflater
import android.view.ViewGroup
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractSectionableItem
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.download.model.Download
class RecentChapterItem(val chapter: Chapter, val manga: Manga, header: DateItem) :
AbstractSectionableItem<RecentChapterHolder, DateItem>(header) {
private var _status: Int = 0
var status: Int
get() = download?.status ?: _status
set(value) { _status = value }
@Transient var download: Download? = null
val isDownloaded: Boolean
get() = status == Download.DOWNLOADED
override fun getLayoutRes(): Int {
return R.layout.item_recent_chapters
}
override fun createViewHolder(adapter: FlexibleAdapter<*>, inflater: LayoutInflater, parent: ViewGroup): RecentChapterHolder {
return RecentChapterHolder(inflater.inflate(layoutRes, parent, false), adapter as RecentChaptersAdapter)
}
override fun bindViewHolder(adapter: FlexibleAdapter<*>, holder: RecentChapterHolder, position: Int, payloads: List<Any?>?) {
holder.bind(this)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other is RecentChapterItem) {
return chapter.id!! == other.chapter.id!!
}
return false
}
override fun hashCode(): Int {
return chapter.id!!.hashCode()
}
}

View File

@ -1,128 +1,12 @@
package eu.kanade.tachiyomi.ui.recent_updates package eu.kanade.tachiyomi.ui.recent_updates
import android.support.v7.widget.RecyclerView import eu.davidea.flexibleadapter.FlexibleAdapter
import android.view.View
import android.view.ViewGroup
import eu.davidea.flexibleadapter4.FlexibleAdapter
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.inflate
import java.util.*
/** class RecentChaptersAdapter(val fragment: RecentChaptersFragment) :
* Adapter of RecentChaptersHolder. FlexibleAdapter<RecentChapterItem>(null, fragment, true) {
* Connection between Fragment and Holder
* Holder updates should be called from here.
*
* @param fragment a RecentChaptersFragment object
* @constructor creates an instance of the adapter.
*/
class RecentChaptersAdapter(val fragment: RecentChaptersFragment)
: FlexibleAdapter<RecyclerView.ViewHolder, Any>() {
/**
* The id of the view type
*/
private val VIEW_TYPE_CHAPTER = 0
/**
* The id of the view type
*/
private val VIEW_TYPE_SECTION = 1
init { init {
// Let each each item in the data set be represented with a unique identifier. setDisplayHeadersAtStartUp(true)
setHasStableIds(true) setStickyHeaders(true)
}
/**
* Called when ViewHolder is bind
*
* @param holder bind holder
* @param position position of holder
*/
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
// Check which view type and set correct values.
val item = getItem(position)
when (holder.itemViewType) {
VIEW_TYPE_CHAPTER -> {
if (item is RecentChapter) {
(holder as RecentChaptersHolder).onSetValues(item)
} }
} }
VIEW_TYPE_SECTION -> {
if (item is Date) {
(holder as SectionViewHolder).onSetValues(item)
}
}
}
//When user scrolls this bind the correct selection status
holder.itemView.isActivated = isSelected(position)
}
/**
* 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: View
// Check which view type and set correct values.
when (viewType) {
VIEW_TYPE_CHAPTER -> {
view = parent.inflate(R.layout.item_recent_chapters)
return RecentChaptersHolder(view, this, fragment)
}
VIEW_TYPE_SECTION -> {
view = parent.inflate(R.layout.item_recent_chapter_section)
return SectionViewHolder(view)
}
}
return null
}
/**
* Returns the correct ViewType
*
* @param position position of item
*/
override fun getItemViewType(position: Int): Int {
return if (getItem(position) is RecentChapter) VIEW_TYPE_CHAPTER else VIEW_TYPE_SECTION
}
/**
* Update items
* @param items items
*/
fun setItems(items: List<Any>) {
mItems = items
notifyDataSetChanged()
}
/**
* Needed to determine holder id
*
* @param position position of holder item
*/
override fun getItemId(position: Int): Long {
val item = getItem(position)
if (item is RecentChapter)
return item.id!!
else
return item.hashCode().toLong()
}
/**
* Abstract function (not needed).
*
* @param p0 a string.
*/
override fun updateDataSet(p0: String) {
// Empty function.
}
}

View File

@ -8,11 +8,10 @@ import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView import android.support.v7.widget.RecyclerView
import android.view.* import android.view.*
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import eu.davidea.flexibleadapter4.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.data.library.LibraryUpdateService import eu.kanade.tachiyomi.data.library.LibraryUpdateService
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment
import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.ui.reader.ReaderActivity import eu.kanade.tachiyomi.ui.reader.ReaderActivity
@ -28,8 +27,11 @@ import timber.log.Timber
* UI related actions should be called from here. * UI related actions should be called from here.
*/ */
@RequiresPresenter(RecentChaptersPresenter::class) @RequiresPresenter(RecentChaptersPresenter::class)
class RecentChaptersFragment class RecentChaptersFragment:
: BaseRxFragment<RecentChaptersPresenter>(), ActionMode.Callback, FlexibleViewHolder.OnListItemClickListener { BaseRxFragment<RecentChaptersPresenter>(),
ActionMode.Callback,
FlexibleAdapter.OnItemClickListener,
FlexibleAdapter.OnItemLongClickListener{
companion object { companion object {
/** /**
@ -97,24 +99,31 @@ class RecentChaptersFragment
// Update toolbar text // Update toolbar text
setToolbarTitle(R.string.label_recent_updates) setToolbarTitle(R.string.label_recent_updates)
// Disable toolbar elevation, it looks better with sticky headers.
// ViewCompat.setElevation(activity.appbar, 0f)
} }
// override fun onDestroyView() {
// ViewCompat.setElevation(activity.appbar, 4.dpToPx.toFloat())
// super.onDestroyView()
// }
/** /**
* Returns selected chapters * Returns selected chapters
* @return list of selected chapters * @return list of selected chapters
*/ */
fun getSelectedChapters(): List<RecentChapter> { fun getSelectedChapters(): List<RecentChapterItem> {
return adapter.selectedItems.map { adapter.getItem(it) as? RecentChapter }.filterNotNull() return adapter.selectedPositions.mapNotNull { adapter.getItem(it) }
} }
/** /**
* Called when item in list is clicked * Called when item in list is clicked
* @param position position of clicked item * @param position position of clicked item
*/ */
override fun onListItemClick(position: Int): Boolean { override fun onItemClick(position: Int): Boolean {
// Get item from position // Get item from position
val item = adapter.getItem(position) val item = adapter.getItem(position)
if (item is RecentChapter) {
if (actionMode != null && adapter.mode == FlexibleAdapter.MODE_MULTI) { if (actionMode != null && adapter.mode == FlexibleAdapter.MODE_MULTI) {
toggleSelection(position) toggleSelection(position)
return true return true
@ -123,14 +132,12 @@ class RecentChaptersFragment
return false return false
} }
} }
return false
}
/** /**
* Called when item in list is long clicked * Called when item in list is long clicked
* @param position position of clicked item * @param position position of clicked item
*/ */
override fun onListItemLongClick(position: Int) { override fun onItemLongClick(position: Int) {
if (actionMode == null) if (actionMode == null)
actionMode = activity.startSupportActionMode(this) actionMode = activity.startSupportActionMode(this)
@ -142,31 +149,22 @@ class RecentChaptersFragment
* @param position position of selected item * @param position position of selected item
*/ */
private fun toggleSelection(position: Int) { private fun toggleSelection(position: Int) {
adapter.toggleSelection(position, false) adapter.toggleSelection(position)
val count = adapter.selectedItemCount val count = adapter.selectedItemCount
if (count == 0) { if (count == 0) {
actionMode?.finish() actionMode?.finish()
} else { } else {
setContextTitle(count)
actionMode?.invalidate()
}
}
/**
* Set the context title
* @param count count of selected items
*/
private fun setContextTitle(count: Int) {
actionMode?.title = getString(R.string.label_selected, count) actionMode?.title = getString(R.string.label_selected, count)
} }
}
/** /**
* Open chapter in reader * Open chapter in reader
* @param chapter selected chapter * @param chapter selected chapter
*/ */
private fun openChapter(chapter: RecentChapter) { private fun openChapter(item: RecentChapterItem) {
val intent = ReaderActivity.newIntent(activity, chapter.manga, chapter) val intent = ReaderActivity.newIntent(activity, item.manga, item.chapter)
startActivity(intent) startActivity(intent)
} }
@ -174,7 +172,7 @@ class RecentChaptersFragment
* Download selected items * Download selected items
* @param chapters list of selected [RecentChapter]s * @param chapters list of selected [RecentChapter]s
*/ */
fun downloadChapters(chapters: List<RecentChapter>) { fun downloadChapters(chapters: List<RecentChapterItem>) {
destroyActionModeIfNeeded() destroyActionModeIfNeeded()
presenter.downloadChapters(chapters) presenter.downloadChapters(chapters)
} }
@ -183,12 +181,12 @@ class RecentChaptersFragment
* Populate adapter with chapters * Populate adapter with chapters
* @param chapters list of [Any] * @param chapters list of [Any]
*/ */
fun onNextRecentChapters(chapters: List<Any>) { fun onNextRecentChapters(chapters: List<RecentChapterItem>) {
(activity as MainActivity).updateEmptyView(chapters.isEmpty(), (activity as MainActivity).updateEmptyView(chapters.isEmpty(),
R.string.information_no_recent, R.drawable.ic_update_black_128dp) R.string.information_no_recent, R.drawable.ic_update_black_128dp)
destroyActionModeIfNeeded() destroyActionModeIfNeeded()
adapter.setItems(chapters) adapter.updateDataSet(chapters.toMutableList(), true)
} }
/** /**
@ -203,15 +201,15 @@ class RecentChaptersFragment
* Returns holder belonging to chapter * Returns holder belonging to chapter
* @param download [Download] object containing download progress. * @param download [Download] object containing download progress.
*/ */
private fun getHolder(download: Download): RecentChaptersHolder? { private fun getHolder(download: Download): RecentChapterHolder? {
return recycler.findViewHolderForItemId(download.chapter.id!!) as? RecentChaptersHolder return recycler.findViewHolderForItemId(download.chapter.id!!) as? RecentChapterHolder
} }
/** /**
* Mark chapter as read * Mark chapter as read
* @param chapters list of chapters * @param chapters list of chapters
*/ */
fun markAsRead(chapters: List<RecentChapter>) { fun markAsRead(chapters: List<RecentChapterItem>) {
presenter.markChapterRead(chapters, true) presenter.markChapterRead(chapters, true)
if (presenter.preferences.removeAfterMarkedAsRead()) { if (presenter.preferences.removeAfterMarkedAsRead()) {
deleteChapters(chapters) deleteChapters(chapters)
@ -222,7 +220,7 @@ class RecentChaptersFragment
* Delete selected chapters * Delete selected chapters
* @param chapters list of [RecentChapter] objects * @param chapters list of [RecentChapter] objects
*/ */
fun deleteChapters(chapters: List<RecentChapter>) { fun deleteChapters(chapters: List<RecentChapterItem>) {
destroyActionModeIfNeeded() destroyActionModeIfNeeded()
DeletingChaptersDialog().show(childFragmentManager, DeletingChaptersDialog.TAG) DeletingChaptersDialog().show(childFragmentManager, DeletingChaptersDialog.TAG)
presenter.deleteChapters(chapters) presenter.deleteChapters(chapters)
@ -239,7 +237,7 @@ class RecentChaptersFragment
* Mark chapter as unread * Mark chapter as unread
* @param chapters list of selected [RecentChapter] * @param chapters list of selected [RecentChapter]
*/ */
fun markAsUnread(chapters: List<RecentChapter>) { fun markAsUnread(chapters: List<RecentChapterItem>) {
presenter.markChapterRead(chapters, false) presenter.markChapterRead(chapters, false)
} }
@ -247,15 +245,15 @@ class RecentChaptersFragment
* Start downloading chapter * Start downloading chapter
* @param chapter selected chapter with manga * @param chapter selected chapter with manga
*/ */
fun downloadChapter(chapter: RecentChapter) { fun downloadChapter(chapter: RecentChapterItem) {
presenter.downloadChapter(chapter) presenter.downloadChapters(listOf(chapter))
} }
/** /**
* Start deleting chapter * Start deleting chapter
* @param chapter selected chapter with manga * @param chapter selected chapter with manga
*/ */
fun deleteChapter(chapter: RecentChapter) { fun deleteChapter(chapter: RecentChapterItem) {
DeletingChaptersDialog().show(childFragmentManager, DeletingChaptersDialog.TAG) DeletingChaptersDialog().show(childFragmentManager, DeletingChaptersDialog.TAG)
presenter.deleteChapters(listOf(chapter)) presenter.deleteChapters(listOf(chapter))
} }
@ -281,11 +279,8 @@ class RecentChaptersFragment
* Called to dismiss deleting dialog * Called to dismiss deleting dialog
*/ */
fun dismissDeletingDialog() { fun dismissDeletingDialog() {
(childFragmentManager.findFragmentByTag(DeletingChaptersDialog.TAG) as? DialogFragment)?.dismiss() (childFragmentManager.findFragmentByTag(DeletingChaptersDialog.TAG) as? DialogFragment)
} ?.dismissAllowingStateLoss()
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
return false
} }
/** /**
@ -322,12 +317,16 @@ class RecentChaptersFragment
return true return true
} }
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
return false
}
/** /**
* Called when ActionMode destroyed * Called when ActionMode destroyed
* @param mode the ActionMode object * @param mode the ActionMode object
*/ */
override fun onDestroyActionMode(mode: ActionMode?) { override fun onDestroyActionMode(mode: ActionMode?) {
adapter.mode = FlexibleAdapter.MODE_SINGLE adapter.mode = FlexibleAdapter.MODE_IDLE
adapter.clearSelection() adapter.clearSelection()
actionMode = null actionMode = null
} }

View File

@ -41,7 +41,7 @@ class RecentChaptersPresenter : BasePresenter<RecentChaptersFragment>() {
/** /**
* List containing chapter and manga information * List containing chapter and manga information
*/ */
private var chapters: List<RecentChapter>? = null private var chapters: List<RecentChapterItem> = emptyList()
override fun onCreate(savedState: Bundle?) { override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState) super.onCreate(savedState)
@ -53,15 +53,15 @@ class RecentChaptersPresenter : BasePresenter<RecentChaptersFragment>() {
getChapterStatusObservable() getChapterStatusObservable()
.subscribeLatestCache(RecentChaptersFragment::onChapterStatusChange, .subscribeLatestCache(RecentChaptersFragment::onChapterStatusChange,
{ view, error -> Timber.e(error) }) { view, error -> Timber.e(error) })
} }
/** /**
* Get observable containing recent chapters and date * Get observable containing recent chapters and date
*
* @return observable containing recent chapters and date * @return observable containing recent chapters and date
*/ */
fun getRecentChaptersObservable(): Observable<ArrayList<Any>> { fun getRecentChaptersObservable(): Observable<List<RecentChapterItem>> {
// Set date for recent chapters // Set date limit for recent chapters
val cal = Calendar.getInstance().apply { val cal = Calendar.getInstance().apply {
time = Date() time = Date()
add(Calendar.MONTH, -1) add(Calendar.MONTH, -1)
@ -70,33 +70,48 @@ class RecentChaptersPresenter : BasePresenter<RecentChaptersFragment>() {
return db.getRecentChapters(cal.time).asRxObservable() return db.getRecentChapters(cal.time).asRxObservable()
// Convert to a list of recent chapters. // Convert to a list of recent chapters.
.map { mangaChapters -> .map { mangaChapters ->
mangaChapters.map { it.toModel() } val map = TreeMap<Date, MutableList<MangaChapter>> { d1, d2 -> d2.compareTo(d1) }
val byDay = mangaChapters.groupByTo(map, { getMapKey(it.chapter.date_fetch) })
byDay.flatMap {
val dateItem = DateItem(it.key)
it.value.map { RecentChapterItem(it.chapter, it.manga, dateItem) }
}
} }
.doOnNext { .doOnNext {
it.forEach { item ->
// Find an active download for this chapter.
val download = downloadManager.queue.find { it.chapter.id == item.chapter.id }
// If there's an active download, assign it, otherwise ask the manager if the chapter is
// downloaded and assign it to the status.
if (download != null) {
item.download = download
}
}
setDownloadedChapters(it) setDownloadedChapters(it)
chapters = it chapters = it
} }
// Group chapters by the date they were fetched on a ordered map. }
.flatMap { recentItems ->
Observable.from(recentItems) /**
.toMultimap( * Get date as time key
{ getMapKey(it.date_fetch) }, *
{ it }, * @param date desired date
{ TreeMap { d1, d2 -> d2.compareTo(d1) } }) * @return date as time key
} */
// Add every day and all its chapters to a single list. private fun getMapKey(date: Long): Date {
.map { recentItems -> val cal = Calendar.getInstance()
ArrayList<Any>().apply { cal.time = Date(date)
for ((key, value) in recentItems) { cal[Calendar.HOUR_OF_DAY] = 0
add(key) cal[Calendar.MINUTE] = 0
addAll(value) cal[Calendar.SECOND] = 0
} cal[Calendar.MILLISECOND] = 0
} return cal.time
}
} }
/** /**
* Returns observable containing chapter status. * Returns observable containing chapter status.
*
* @return download object containing download progress. * @return download object containing download progress.
*/ */
private fun getChapterStatusObservable(): Observable<Download> { private fun getChapterStatusObservable(): Observable<Download> {
@ -105,38 +120,21 @@ class RecentChaptersPresenter : BasePresenter<RecentChaptersFragment>() {
.doOnNext { download -> onDownloadStatusChange(download) } .doOnNext { download -> onDownloadStatusChange(download) }
} }
/**
* Converts a chapter from the database to an extended model, allowing to store new fields.
*/
private fun MangaChapter.toModel(): RecentChapter {
// Create the model object.
val model = RecentChapter(this)
// Find an active download for this chapter.
val download = downloadManager.queue.find { it.chapter.id == chapter.id }
// If there's an active download, assign it, otherwise ask the manager if the chapter is
// downloaded and assign it to the status.
if (download != null) {
model.download = download
}
return model
}
/** /**
* Finds and assigns the list of downloaded chapters. * Finds and assigns the list of downloaded chapters.
* *
* @param chapters the list of chapter from the database. * @param items the list of chapter from the database.
*/ */
private fun setDownloadedChapters(chapters: List<RecentChapter>) { private fun setDownloadedChapters(items: List<RecentChapterItem>) {
// Cached list of downloaded manga directories. // Cached list of downloaded manga directories.
val mangaDirectories = mutableMapOf<Long, Array<UniFile>>() val mangaDirectories = mutableMapOf<Long, Array<UniFile>>()
// Cached list of downloaded chapter directories for a manga. // Cached list of downloaded chapter directories for a manga.
val chapterDirectories = mutableMapOf<Long, Array<UniFile>>() val chapterDirectories = mutableMapOf<Long, Array<UniFile>>()
for (chapter in chapters) { for (item in items) {
val manga = chapter.manga val manga = item.manga
val chapter = item.chapter
val source = sourceManager.get(manga.source) ?: continue val source = sourceManager.get(manga.source) ?: continue
val mangaDirs = mangaDirectories.getOrPut(source.id) { val mangaDirs = mangaDirectories.getOrPut(source.id) {
@ -152,64 +150,52 @@ class RecentChaptersPresenter : BasePresenter<RecentChaptersFragment>() {
val chapterDirName = downloadManager.getChapterDirName(chapter) val chapterDirName = downloadManager.getChapterDirName(chapter)
if (chapterDirs.any { it.name == chapterDirName }) { if (chapterDirs.any { it.name == chapterDirName }) {
chapter.status = Download.DOWNLOADED item.status = Download.DOWNLOADED
} }
} }
} }
/** /**
* Update status of chapters. * Update status of chapters.
*
* @param download download object containing progress. * @param download download object containing progress.
*/ */
private fun onDownloadStatusChange(download: Download) { private fun onDownloadStatusChange(download: Download) {
// Assign the download to the model object. // Assign the download to the model object.
if (download.status == Download.QUEUE) { if (download.status == Download.QUEUE) {
val chapter = chapters?.find { it.id == download.chapter.id } val chapter = chapters.find { it.chapter.id == download.chapter.id }
if (chapter != null && chapter.download == null) { if (chapter != null && chapter.download == null) {
chapter.download = download chapter.download = download
} }
} }
} }
/**
* Get date as time key
* @param date desired date
* @return date as time key
*/
private fun getMapKey(date: Long): Date {
val cal = Calendar.getInstance()
cal.time = Date(date)
cal[Calendar.HOUR_OF_DAY] = 0
cal[Calendar.MINUTE] = 0
cal[Calendar.SECOND] = 0
cal[Calendar.MILLISECOND] = 0
return cal.time
}
/** /**
* Mark selected chapter as read * Mark selected chapter as read
* @param chapters list of selected chapters *
* @param items list of selected chapters
* @param read read status * @param read read status
*/ */
fun markChapterRead(chapters: List<RecentChapter>, read: Boolean) { fun markChapterRead(items: List<RecentChapterItem>, read: Boolean) {
Observable.from(chapters) val chapters = items.map { it.chapter }
.doOnNext { chapter -> chapters.forEach {
chapter.read = read it.read = read
if (!read) { if (!read) {
chapter.last_page_read = 0 it.last_page_read = 0
} }
} }
.toList()
.flatMap { db.updateChaptersProgress(it).asRxObservable() } Observable.fromCallable { db.updateChaptersProgress(chapters).executeAsBlocking() }
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.subscribe() .subscribe()
} }
/** /**
* Delete selected chapters * Delete selected chapters
*
* @param chapters list of chapters * @param chapters list of chapters
*/ */
fun deleteChapters(chapters: List<RecentChapter>) { fun deleteChapters(chapters: List<RecentChapterItem>) {
Observable.from(chapters) Observable.from(chapters)
.doOnNext { deleteChapter(it) } .doOnNext { deleteChapter(it) }
.toList() .toList()
@ -217,42 +203,29 @@ class RecentChaptersPresenter : BasePresenter<RecentChaptersFragment>() {
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribeFirst({ view, result -> .subscribeFirst({ view, result ->
view.onChaptersDeleted() view.onChaptersDeleted()
}, { view, error -> }, RecentChaptersFragment::onChaptersDeletedError)
view.onChaptersDeletedError(error)
})
} }
/** /**
* Download selected chapters * Download selected chapters
* @param chapters list of recent chapters seleted. * @param items list of recent chapters seleted.
*/ */
fun downloadChapters(chapters: List<RecentChapter>) { fun downloadChapters(items: List<RecentChapterItem>) {
items.forEach { downloadManager.downloadChapters(it.manga, listOf(it.chapter)) }
DownloadService.start(context) DownloadService.start(context)
Observable.from(chapters)
.doOnNext { downloadChapter(it) }
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe()
}
/**
* Download selected chapter
* @param chapter chapter that is selected
*/
fun downloadChapter(chapter: RecentChapter) {
DownloadService.start(context)
downloadManager.downloadChapters(chapter.manga, listOf(chapter))
} }
/** /**
* Delete selected chapter * Delete selected chapter
* @param chapter chapter that is selected *
* @param item chapter that is selected
*/ */
private fun deleteChapter(chapter: RecentChapter) { private fun deleteChapter(item: RecentChapterItem) {
val source = sourceManager.get(chapter.manga.source) ?: return val source = sourceManager.get(item.manga.source) ?: return
downloadManager.queue.remove(chapter) downloadManager.queue.remove(item.chapter)
downloadManager.deleteChapter(source, chapter.manga, chapter) downloadManager.deleteChapter(source, item.manga, item.chapter)
chapter.status = Download.NOT_DOWNLOADED item.status = Download.NOT_DOWNLOADED
chapter.download = null item.download = null
} }
} }

View File

@ -1,25 +0,0 @@
package eu.kanade.tachiyomi.ui.recent_updates
import android.support.v7.widget.RecyclerView
import android.text.format.DateUtils
import android.text.format.DateUtils.DAY_IN_MILLIS
import android.view.View
import kotlinx.android.synthetic.main.item_recent_chapter_section.view.*
import java.util.*
class SectionViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
/**
* Current date
*/
private val now = Date().time
/**
* Set value of section header
*
* @param date of section header
*/
fun onSetValues(date: Date) {
view.section_text.text = DateUtils.getRelativeTimeSpanString(date.time, now, DAY_IN_MILLIS)
}
}

View File

@ -7,13 +7,17 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView <android.support.v7.widget.RecyclerView
android:id="@+id/recycler" android:id="@+id/recycler"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:descendantFocusability="blocksDescendants" android:descendantFocusability="blocksDescendants"
tools:listitem="@layout/item_recent_chapters"> tools:listitem="@layout/item_recent_chapters"/>
</android.support.v7.widget.RecyclerView> </FrameLayout>
</android.support.v4.widget.SwipeRefreshLayout> </android.support.v4.widget.SwipeRefreshLayout>

View File

@ -9,7 +9,8 @@
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:paddingLeft="?android:attr/listPreferredItemPaddingLeft" android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
android:paddingRight="?android:attr/listPreferredItemPaddingRight" android:paddingRight="?android:attr/listPreferredItemPaddingRight"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"> android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:elevation="4dp">
<TextView <TextView
android:id="@+id/section_text" android:id="@+id/section_text"
@ -17,8 +18,6 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:singleLine="true"/> android:maxLines="1"/>
</FrameLayout> </FrameLayout>