|
@@ -2,9 +2,10 @@ package eu.kanade.tachiyomi.ui.recent
|
|
|
|
|
|
import android.os.Bundle
|
|
|
import android.support.v4.app.DialogFragment
|
|
|
-import android.view.LayoutInflater
|
|
|
-import android.view.View
|
|
|
-import android.view.ViewGroup
|
|
|
+import android.support.v7.view.ActionMode
|
|
|
+import android.view.*
|
|
|
+import com.afollestad.materialdialogs.MaterialDialog
|
|
|
+import eu.davidea.flexibleadapter.FlexibleAdapter
|
|
|
import eu.kanade.tachiyomi.R
|
|
|
import eu.kanade.tachiyomi.data.database.models.MangaChapter
|
|
|
import eu.kanade.tachiyomi.data.download.model.Download
|
|
@@ -13,6 +14,7 @@ import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment
|
|
|
import eu.kanade.tachiyomi.ui.main.MainActivity
|
|
|
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
|
|
import eu.kanade.tachiyomi.util.getResourceDrawable
|
|
|
+import eu.kanade.tachiyomi.util.toast
|
|
|
import eu.kanade.tachiyomi.widget.DeletingChaptersDialog
|
|
|
import eu.kanade.tachiyomi.widget.DividerItemDecoration
|
|
|
import eu.kanade.tachiyomi.widget.NpaLinearLayoutManager
|
|
@@ -22,15 +24,15 @@ import timber.log.Timber
|
|
|
|
|
|
/**
|
|
|
* Fragment that shows recent chapters.
|
|
|
- * Uses R.layout.fragment_recent_chapters.
|
|
|
+ * Uses [R.layout.fragment_recent_chapters].
|
|
|
* UI related actions should be called from here.
|
|
|
*/
|
|
|
@RequiresPresenter(RecentChaptersPresenter::class)
|
|
|
-class RecentChaptersFragment : BaseRxFragment<RecentChaptersPresenter>(), FlexibleViewHolder.OnListItemClickListener {
|
|
|
+class RecentChaptersFragment : BaseRxFragment<RecentChaptersPresenter>(), ActionMode.Callback, FlexibleViewHolder.OnListItemClickListener {
|
|
|
companion object {
|
|
|
/**
|
|
|
* Create new RecentChaptersFragment.
|
|
|
- *
|
|
|
+ * @return a new instance of [RecentChaptersFragment].
|
|
|
*/
|
|
|
@JvmStatic
|
|
|
fun newInstance(): RecentChaptersFragment {
|
|
@@ -38,6 +40,59 @@ class RecentChaptersFragment : BaseRxFragment<RecentChaptersPresenter>(), Flexib
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Called when ActionMode item clicked
|
|
|
+ * @param mode the ActionMode object
|
|
|
+ * @param item item from ActionMode.
|
|
|
+ */
|
|
|
+ override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
|
|
|
+ when (item.itemId) {
|
|
|
+ R.id.action_mark_as_read -> markAsRead(getSelectedChapters())
|
|
|
+ R.id.action_mark_as_unread -> markAsUnread(getSelectedChapters())
|
|
|
+ R.id.action_download -> downloadChapters(getSelectedChapters())
|
|
|
+ R.id.action_delete -> {
|
|
|
+ MaterialDialog.Builder(activity)
|
|
|
+ .content(R.string.confirm_delete_chapters)
|
|
|
+ .positiveText(android.R.string.yes)
|
|
|
+ .negativeText(android.R.string.no)
|
|
|
+ .onPositive { dialog, action -> deleteChapters(getSelectedChapters()) }
|
|
|
+ .show()
|
|
|
+ }
|
|
|
+ else -> return false
|
|
|
+ }
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Called when ActionMode created.
|
|
|
+ * @param mode the ActionMode object
|
|
|
+ * @param menu menu object of ActionMode
|
|
|
+ */
|
|
|
+ override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
|
|
|
+ mode.menuInflater.inflate(R.menu.chapter_recent_selection, menu)
|
|
|
+ adapter.mode = FlexibleAdapter.MODE_MULTI
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Called when ActionMode destroyed
|
|
|
+ * @param mode the ActionMode object
|
|
|
+ */
|
|
|
+ override fun onDestroyActionMode(mode: ActionMode?) {
|
|
|
+ adapter.mode = FlexibleAdapter.MODE_SINGLE
|
|
|
+ adapter.clearSelection()
|
|
|
+ actionMode = null
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Action mode for multiple selection.
|
|
|
+ */
|
|
|
+ private var actionMode: ActionMode? = null
|
|
|
+
|
|
|
/**
|
|
|
* Adapter containing the recent chapters.
|
|
|
*/
|
|
@@ -46,7 +101,6 @@ class RecentChaptersFragment : BaseRxFragment<RecentChaptersPresenter>(), Flexib
|
|
|
|
|
|
/**
|
|
|
* Called when view gets created
|
|
|
- *
|
|
|
* @param inflater layout inflater
|
|
|
* @param container view group
|
|
|
* @param savedState status of saved state
|
|
@@ -58,7 +112,6 @@ class RecentChaptersFragment : BaseRxFragment<RecentChaptersPresenter>(), Flexib
|
|
|
|
|
|
/**
|
|
|
* Called when view is created
|
|
|
- *
|
|
|
* @param view created view
|
|
|
* @param savedInstanceState status of saved sate
|
|
|
*/
|
|
@@ -70,60 +123,108 @@ class RecentChaptersFragment : BaseRxFragment<RecentChaptersPresenter>(), Flexib
|
|
|
adapter = RecentChaptersAdapter(this)
|
|
|
recycler.adapter = adapter
|
|
|
|
|
|
+ // Set swipe refresh listener
|
|
|
+ swipe_refresh.setOnRefreshListener { fetchChapters() }
|
|
|
+
|
|
|
// Update toolbar text
|
|
|
setToolbarTitle(R.string.label_recent_updates)
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Returns selected chapters
|
|
|
+ * @return list of [MangaChapter]s
|
|
|
+ */
|
|
|
+ fun getSelectedChapters(): List<MangaChapter> {
|
|
|
+ return adapter.selectedItems.map { adapter.getItem(it) as? MangaChapter }.filterNotNull()
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Called when item in list is clicked
|
|
|
- *
|
|
|
* @param position position of clicked item
|
|
|
*/
|
|
|
override fun onListItemClick(position: Int): Boolean {
|
|
|
// Get item from position
|
|
|
val item = adapter.getItem(position)
|
|
|
if (item is MangaChapter) {
|
|
|
- // Open chapter in reader
|
|
|
- openChapter(item)
|
|
|
+ if (actionMode != null && adapter.mode == FlexibleAdapter.MODE_MULTI) {
|
|
|
+ toggleSelection(position)
|
|
|
+ return true
|
|
|
+ } else {
|
|
|
+ openChapter(item)
|
|
|
+ return false
|
|
|
+ }
|
|
|
}
|
|
|
return false
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Called when item in list is long clicked
|
|
|
- *
|
|
|
* @param position position of clicked item
|
|
|
*/
|
|
|
override fun onListItemLongClick(position: Int) {
|
|
|
- // Empty function
|
|
|
+ if (actionMode == null)
|
|
|
+ actionMode = activity.startSupportActionMode(this)
|
|
|
+
|
|
|
+ toggleSelection(position)
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Called to toggle selection
|
|
|
+ * @param position position of selected item
|
|
|
+ */
|
|
|
+ private fun toggleSelection(position: Int) {
|
|
|
+ adapter.toggleSelection(position, false)
|
|
|
+
|
|
|
+ val count = adapter.selectedItemCount
|
|
|
+ if (count == 0) {
|
|
|
+ actionMode?.finish()
|
|
|
+ } else {
|
|
|
+ setContextTitle(count)
|
|
|
+ actionMode?.invalidate()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Set the context title
|
|
|
+ * @param count count of selected items
|
|
|
+ */
|
|
|
+ private fun setContextTitle(count: Int) {
|
|
|
+ actionMode?.title = getString(R.string.label_selected, count)
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Open chapter in reader
|
|
|
- *
|
|
|
- * @param chapter selected chapter
|
|
|
+ * @param mangaChapter selected [MangaChapter]
|
|
|
*/
|
|
|
- private fun openChapter(chapter: MangaChapter) {
|
|
|
- val intent = ReaderActivity.newIntent(activity, chapter.manga, chapter.chapter)
|
|
|
+ private fun openChapter(mangaChapter: MangaChapter) {
|
|
|
+ val intent = ReaderActivity.newIntent(activity, mangaChapter.manga, mangaChapter.chapter)
|
|
|
startActivity(intent)
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Download selected items
|
|
|
+ * @param mangaChapters list of selected [MangaChapter]s
|
|
|
+ */
|
|
|
+ fun downloadChapters(mangaChapters: List<MangaChapter>) {
|
|
|
+ destroyActionModeIfNeeded()
|
|
|
+ presenter.downloadChapters(mangaChapters)
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Populate adapter with chapters
|
|
|
- *
|
|
|
- * @param chapters list of chapters
|
|
|
+ * @param chapters list of [Any]
|
|
|
*/
|
|
|
fun onNextMangaChapters(chapters: List<Any>) {
|
|
|
(activity as MainActivity).updateEmptyView(chapters.isEmpty(),
|
|
|
R.string.information_no_recent, R.drawable.ic_history_black_128dp)
|
|
|
|
|
|
+ destroyActionModeIfNeeded()
|
|
|
adapter.setItems(chapters)
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Update download status of chapter
|
|
|
-
|
|
|
- * @param download download object containing download progress.
|
|
|
+ * @param download [Download] object containing download progress.
|
|
|
*/
|
|
|
fun onChapterStatusChange(download: Download) {
|
|
|
getHolder(download)?.onStatusChange(download.status)
|
|
@@ -132,8 +233,7 @@ class RecentChaptersFragment : BaseRxFragment<RecentChaptersPresenter>(), Flexib
|
|
|
|
|
|
/**
|
|
|
* 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? {
|
|
|
return recycler.findViewHolderForItemId(download.chapter.id) as? RecentChaptersHolder
|
|
@@ -141,28 +241,42 @@ class RecentChaptersFragment : BaseRxFragment<RecentChaptersPresenter>(), Flexib
|
|
|
|
|
|
/**
|
|
|
* Mark chapter as read
|
|
|
- *
|
|
|
- * @param item selected chapter with manga
|
|
|
+ * @param mangaChapters list of [MangaChapter] objects
|
|
|
*/
|
|
|
- fun markAsRead(item: MangaChapter) {
|
|
|
- presenter.markChapterRead(item.chapter, true)
|
|
|
+ fun markAsRead(mangaChapters: List<MangaChapter>) {
|
|
|
+ presenter.markChapterRead(mangaChapters, true)
|
|
|
if (presenter.preferences.removeAfterMarkedAsRead()) {
|
|
|
- deleteChapter(item)
|
|
|
+ deleteChapters(mangaChapters)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Delete selected chapters
|
|
|
+ * @param mangaChapters list of [MangaChapter] objects
|
|
|
+ */
|
|
|
+ fun deleteChapters(mangaChapters: List<MangaChapter>) {
|
|
|
+ destroyActionModeIfNeeded()
|
|
|
+ DeletingChaptersDialog().show(childFragmentManager, DeletingChaptersDialog.TAG)
|
|
|
+ presenter.deleteChapters(mangaChapters)
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Destory [ActionMode] if it's shown
|
|
|
+ */
|
|
|
+ fun destroyActionModeIfNeeded() {
|
|
|
+ actionMode?.finish()
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Mark chapter as unread
|
|
|
- *
|
|
|
- * @param item selected chapter with manga
|
|
|
+ * @param mangaChapters list of selected [MangaChapter]
|
|
|
*/
|
|
|
- fun markAsUnread(item: MangaChapter) {
|
|
|
- presenter.markChapterRead(item.chapter, false)
|
|
|
+ fun markAsUnread(mangaChapters: List<MangaChapter>) {
|
|
|
+ presenter.markChapterRead(mangaChapters, false)
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Start downloading chapter
|
|
|
- *
|
|
|
* @param item selected chapter with manga
|
|
|
*/
|
|
|
fun downloadChapter(item: MangaChapter) {
|
|
@@ -171,7 +285,6 @@ class RecentChaptersFragment : BaseRxFragment<RecentChaptersPresenter>(), Flexib
|
|
|
|
|
|
/**
|
|
|
* Start deleting chapter
|
|
|
- *
|
|
|
* @param item selected chapter with manga
|
|
|
*/
|
|
|
fun deleteChapter(item: MangaChapter) {
|
|
@@ -179,18 +292,52 @@ class RecentChaptersFragment : BaseRxFragment<RecentChaptersPresenter>(), Flexib
|
|
|
presenter.deleteChapter(item)
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Called when chapters are deleted
|
|
|
+ */
|
|
|
fun onChaptersDeleted() {
|
|
|
dismissDeletingDialog()
|
|
|
adapter.notifyDataSetChanged()
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Called when error while deleting
|
|
|
+ * @param error error message
|
|
|
+ */
|
|
|
fun onChaptersDeletedError(error: Throwable) {
|
|
|
dismissDeletingDialog()
|
|
|
Timber.e(error, error.message)
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Called to dismiss deleting dialog
|
|
|
+ */
|
|
|
fun dismissDeletingDialog() {
|
|
|
(childFragmentManager.findFragmentByTag(DeletingChaptersDialog.TAG) as? DialogFragment)?.dismiss()
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Called when swipe refresh activated.
|
|
|
+ */
|
|
|
+ fun fetchChapters() {
|
|
|
+ swipe_refresh.isRefreshing = true
|
|
|
+ presenter.fetchChaptersFromSource()
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Called after refresh is completed
|
|
|
+ */
|
|
|
+ fun onFetchChaptersDone() {
|
|
|
+ swipe_refresh.isRefreshing = false
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Called when something went wrong while refreshing
|
|
|
+ * @param error information on what went wrong
|
|
|
+ */
|
|
|
+ fun onFetchChaptersError(error: Throwable) {
|
|
|
+ swipe_refresh.isRefreshing = false
|
|
|
+ context.toast(error.message)
|
|
|
+ }
|
|
|
+
|
|
|
}
|