Made chapters swipeable to change read/bookmark status

This commit is contained in:
Jay 2020-03-22 04:10:27 -04:00
parent 87e86f81cf
commit b331e12fde
23 changed files with 352 additions and 90 deletions

View File

@ -40,7 +40,7 @@ class Download(val source: HttpSource, val manga: Manga, val chapter: Chapter) {
} }
companion object { companion object {
const val CHECKED = -1
const val NOT_DOWNLOADED = 0 const val NOT_DOWNLOADED = 0
const val QUEUE = 1 const val QUEUE = 1
const val DOWNLOADING = 2 const val DOWNLOADING = 2

View File

@ -29,6 +29,10 @@ class DownloadButton @JvmOverloads constructor(context: Context, attrs: Attribut
R.drawable.filled_circle)?.mutate() R.drawable.filled_circle)?.mutate()
private val borderCircle = ContextCompat.getDrawable(context, private val borderCircle = ContextCompat.getDrawable(context,
R.drawable.border_circle)?.mutate() R.drawable.border_circle)?.mutate()
private val downloadDrawable = ContextCompat.getDrawable(context,
R.drawable.ic_arrow_down_white_24dp)?.mutate()
private val checkDrawable = ContextCompat.getDrawable(context,
R.drawable.ic_check_white_24dp)?.mutate()
private var isAnimating = false private var isAnimating = false
private var iconAnimation: ObjectAnimator? = null private var iconAnimation: ObjectAnimator? = null
@ -38,7 +42,17 @@ class DownloadButton @JvmOverloads constructor(context: Context, attrs: Attribut
download_icon.alpha = 1f download_icon.alpha = 1f
isAnimating = false isAnimating = false
} }
download_icon.setImageDrawable(if (state == Download.CHECKED)
checkDrawable else downloadDrawable)
when (state) { when (state) {
Download.CHECKED -> {
download_progress.gone()
download_border.visible()
download_progress_indeterminate.gone()
download_border.setImageDrawable(filledCircle)
download_border.drawable.setTint(activeColor)
download_icon.drawable.setTint(Color.WHITE)
}
Download.NOT_DOWNLOADED -> { Download.NOT_DOWNLOADED -> {
download_border.visible() download_border.visible()
download_progress.gone() download_progress.gone()

View File

@ -441,7 +441,7 @@ open class LibraryController(
val searchItem = menu.findItem(R.id.action_search) val searchItem = menu.findItem(R.id.action_search)
val searchView = searchItem.actionView as SearchView val searchView = searchItem.actionView as SearchView
searchView.queryHint = resources?.getString(R.string.search_hint) searchView.queryHint = resources?.getString(R.string.library_search_hint)
searchItem.collapseActionView() searchItem.collapseActionView()
if (query.isNotEmpty()) { if (query.isNotEmpty()) {

View File

@ -59,10 +59,8 @@ import eu.kanade.tachiyomi.ui.setting.SettingsController
import eu.kanade.tachiyomi.ui.setting.SettingsMainController import eu.kanade.tachiyomi.ui.setting.SettingsMainController
import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.system.launchUI import eu.kanade.tachiyomi.util.system.launchUI
import eu.kanade.tachiyomi.util.view.gone
import eu.kanade.tachiyomi.util.view.updateLayoutParams import eu.kanade.tachiyomi.util.view.updateLayoutParams
import eu.kanade.tachiyomi.util.view.updatePadding import eu.kanade.tachiyomi.util.view.updatePadding
import eu.kanade.tachiyomi.util.view.visible
import java.util.Date import java.util.Date
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.math.abs import kotlin.math.abs
@ -197,19 +195,6 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
container.systemUiVisibility = container.systemUiVisibility =
View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
updateRecentsIcon() updateRecentsIcon()
content.viewTreeObserver.addOnGlobalLayoutListener {
val heightDiff: Int = content.rootView.height - content.height
if (heightDiff > 200 && window.attributes.softInputMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) {
// keyboard is open, hide layout
bottom_nav.gone()
} else if (bottom_nav.visibility == View.GONE && window.attributes.softInputMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) {
// keyboard is hidden, show layout
// use coroutine to delay so the bottom bar doesn't flash on top of the keyboard
launchUI {
bottom_nav.visible()
}
}
}
supportActionBar?.setDisplayShowCustomEnabled(true) supportActionBar?.setDisplayShowCustomEnabled(true)

View File

@ -19,7 +19,6 @@ import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.Gravity
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.Menu import android.view.Menu
import android.view.MenuInflater import android.view.MenuInflater
@ -31,12 +30,14 @@ import android.view.inputmethod.InputMethodManager
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode import androidx.appcompat.view.ActionMode
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.appcompat.widget.SearchView
import androidx.core.content.pm.ShortcutInfoCompat import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.ColorUtils import androidx.core.graphics.ColorUtils
import androidx.core.graphics.drawable.IconCompat import androidx.core.graphics.drawable.IconCompat
import androidx.palette.graphics.Palette import androidx.palette.graphics.Palette
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.transition.ChangeBounds import androidx.transition.ChangeBounds
@ -95,6 +96,7 @@ import eu.kanade.tachiyomi.util.system.launchUI
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.view.doOnApplyWindowInsets import eu.kanade.tachiyomi.util.view.doOnApplyWindowInsets
import eu.kanade.tachiyomi.util.view.getText import eu.kanade.tachiyomi.util.view.getText
import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener
import eu.kanade.tachiyomi.util.view.snack import eu.kanade.tachiyomi.util.view.snack
import eu.kanade.tachiyomi.util.view.updateLayoutParams import eu.kanade.tachiyomi.util.view.updateLayoutParams
import eu.kanade.tachiyomi.util.view.updatePaddingRelative import eu.kanade.tachiyomi.util.view.updatePaddingRelative
@ -114,6 +116,7 @@ class MangaDetailsController : BaseController,
FlexibleAdapter.OnItemLongClickListener, FlexibleAdapter.OnItemLongClickListener,
ActionMode.Callback, ActionMode.Callback,
ChaptersAdapter.MangaHeaderInterface, ChaptersAdapter.MangaHeaderInterface,
FlexibleAdapter.OnItemMoveListener,
ChangeMangaCategoriesDialog.Listener { ChangeMangaCategoriesDialog.Listener {
constructor( constructor(
@ -159,6 +162,11 @@ class MangaDetailsController : BaseController,
private var editMangaDialog: EditMangaDialog? = null private var editMangaDialog: EditMangaDialog? = null
var refreshTracker: Int? = null var refreshTracker: Int? = null
/**
* Library search query.
*/
private var query = ""
/** /**
* Adapter containing a list of chapters. * Adapter containing a list of chapters.
*/ */
@ -190,6 +198,7 @@ class MangaDetailsController : BaseController,
adapter = ChaptersAdapter(this, view.context) adapter = ChaptersAdapter(this, view.context)
recycler.adapter = adapter recycler.adapter = adapter
adapter?.isSwipeEnabled = true
recycler.layoutManager = LinearLayoutManager(view.context) recycler.layoutManager = LinearLayoutManager(view.context)
recycler.addItemDecoration( recycler.addItemDecoration(
DividerItemDecoration( DividerItemDecoration(
@ -369,6 +378,13 @@ class MangaDetailsController : BaseController,
presenter.preferences.theme() presenter.preferences.theme()
) )
) { ) {
if (forThis)
(activity as MainActivity).appbar.context.setTheme(R.style
.ThemeOverlay_AppCompat_DayNight_ActionBar)
else
(activity as MainActivity).appbar.context.setTheme(R.style
.Theme_ActionBar_Dark_DayNight)
val iconPrimary = view?.context?.getResourceColor( val iconPrimary = view?.context?.getResourceColor(
if (forThis) android.R.attr.textColorPrimary if (forThis) android.R.attr.textColorPrimary
else R.attr.actionBarTintColor else R.attr.actionBarTintColor
@ -418,6 +434,8 @@ class MangaDetailsController : BaseController,
adapter?.addSelection(position) adapter?.addSelection(position)
(recycler.findViewHolderForAdapterPosition(position) as? BaseFlexibleViewHolder) (recycler.findViewHolderForAdapterPosition(position) as? BaseFlexibleViewHolder)
?.toggleActivation() ?.toggleActivation()
(recycler.findViewHolderForAdapterPosition(position) as? ChapterMatHolder)
?.notifyStatus(Download.CHECKED, false, 0)
startingDLChapterPos = position startingDLChapterPos = position
actionMode?.invalidate() actionMode?.invalidate()
} else { } else {
@ -446,24 +464,24 @@ class MangaDetailsController : BaseController,
val adapter = adapter ?: return val adapter = adapter ?: return
val item = (adapter.getItem(position) as? ChapterItem) ?: return val item = (adapter.getItem(position) as? ChapterItem) ?: return
val itemView = getHolder(item)?.itemView ?: return val itemView = getHolder(item)?.itemView ?: return
val popup = PopupMenu(itemView.context, itemView, Gravity.END) val popup = PopupMenu(itemView.context, itemView)
// Inflate our menu resource into the PopupMenu's Menu // Inflate our menu resource into the PopupMenu's Menu
popup.menuInflater.inflate(R.menu.chapters_mat_single, popup.menu) popup.menuInflater.inflate(R.menu.chapters_mat_single, popup.menu)
// Hide bookmark if bookmark // Hide bookmark if bookmark
popup.menu.findItem(R.id.action_bookmark).isVisible = !item.bookmark popup.menu.findItem(R.id.action_bookmark).isVisible = false // !item.bookmark
popup.menu.findItem(R.id.action_remove_bookmark).isVisible = item.bookmark popup.menu.findItem(R.id.action_remove_bookmark).isVisible = false // item.bookmark
// Hide mark as unread when the chapter is unread // Hide mark as unread when the chapter is unread
if (!item.read && item.last_page_read == 0) { // if (!item.read && item.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 (item.read) { // if (item.read) {
popup.menu.findItem(R.id.action_mark_as_read).isVisible = false popup.menu.findItem(R.id.action_mark_as_read).isVisible = false
} // }
// Set a listener so we are notified if a menu item is clicked // Set a listener so we are notified if a menu item is clicked
popup.setOnMenuItemClickListener { menuItem -> popup.setOnMenuItemClickListener { menuItem ->
@ -491,19 +509,30 @@ class MangaDetailsController : BaseController,
} }
} }
fun bookmarkChapter(position: Int) {
val item = adapter?.getItem(position) as? ChapterItem ?: return
bookmarkChapters(listOf(item), !item.bookmark)
}
fun toggleReadChapter(position: Int) {
val item = adapter?.getItem(position) as? ChapterItem ?: return
if (!item.read) markAsRead(listOf(item), false)
else markAsUnread(listOf(item), false)
}
private fun bookmarkChapters(chapters: List<ChapterItem>, bookmarked: Boolean) { private fun bookmarkChapters(chapters: List<ChapterItem>, bookmarked: Boolean) {
presenter.bookmarkChapters(chapters, bookmarked) presenter.bookmarkChapters(chapters, bookmarked)
} }
private fun markAsRead(chapters: List<ChapterItem>) { private fun markAsRead(chapters: List<ChapterItem>, refresh: Boolean = true) {
presenter.markChaptersRead(chapters, true) presenter.markChaptersRead(chapters, read = true)
if (presenter.preferences.removeAfterMarkedAsRead()) { if (presenter.preferences.removeAfterMarkedAsRead()) {
presenter.deleteChapters(chapters) presenter.deleteChapters(chapters)
} }
} }
private fun markAsUnread(chapters: List<ChapterItem>) { private fun markAsUnread(chapters: List<ChapterItem>, refresh: Boolean = true) {
presenter.markChaptersRead(chapters, false) presenter.markChaptersRead(chapters, read = false)
} }
private fun openChapter(chapter: Chapter) { private fun openChapter(chapter: Chapter) {
@ -535,6 +564,25 @@ class MangaDetailsController : BaseController,
?: Color.BLACK ?: Color.BLACK
menu.findItem(R.id.action_download).icon?.mutate()?.setTint(iconPrimary) menu.findItem(R.id.action_download).icon?.mutate()?.setTint(iconPrimary)
editItem.icon?.mutate()?.setTint(iconPrimary) editItem.icon?.mutate()?.setTint(iconPrimary)
val searchItem = menu.findItem(R.id.action_search)
val searchView = searchItem.actionView as SearchView
searchView.queryHint = resources?.getString(R.string.chapter_search_hint)
searchItem.icon?.mutate()?.setTint(iconPrimary)
searchItem.collapseActionView()
if (query.isNotEmpty()) {
searchItem.expandActionView()
searchView.setQuery(query, true)
searchView.clearFocus()
}
setOnQueryTextChangeListener(searchView) {
query = it ?: ""
adapter?.setFilter(query)
adapter?.performFilter()
true
}
searchItem.fixExpand(onExpand = { invalidateMenuOnExpand() })
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
@ -1033,6 +1081,12 @@ class MangaDetailsController : BaseController,
override fun onDestroyActionMode(mode: ActionMode?) { override fun onDestroyActionMode(mode: ActionMode?) {
actionMode = null actionMode = null
setStatusBarAndToolbar() setStatusBarAndToolbar()
if (startingDLChapterPos != null) {
val item = adapter?.getItem(startingDLChapterPos!!) as? ChapterItem
(recycler.findViewHolderForAdapterPosition(startingDLChapterPos!!) as? ChapterMatHolder)?.notifyStatus(
item?.status ?: Download.NOT_DOWNLOADED, false, 0
)
}
startingDLChapterPos = null startingDLChapterPos = null
adapter?.mode = SelectableAdapter.Mode.IDLE adapter?.mode = SelectableAdapter.Mode.IDLE
adapter?.clearSelection() adapter?.clearSelection()
@ -1096,6 +1150,17 @@ class MangaDetailsController : BaseController,
} }
} }
override fun onActionStateChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
swipe_refresh.isEnabled = actionState != ItemTouchHelper.ACTION_STATE_SWIPE
}
override fun onItemMove(fromPosition: Int, toPosition: Int) {
}
override fun shouldMoveItem(fromPosition: Int, toPosition: Int): Boolean {
return true
}
override fun zoomImageFromThumb(thumbView: View) { override fun zoomImageFromThumb(thumbView: View) {
// If there's an animation in progress, cancel it immediately and proceed with this one. // If there's an animation in progress, cancel it immediately and proceed with this one.
currentAnimator?.cancel() currentAnimator?.cancel()

View File

@ -22,6 +22,10 @@ class MangaHeaderItem(val manga: Manga, private val startExpanded: Boolean) :
return false return false
} }
override fun isSwipeable(): Boolean {
return false
}
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): MangaHeaderHolder { override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): MangaHeaderHolder {
return MangaHeaderHolder(view, adapter as ChaptersAdapter, startExpanded) return MangaHeaderHolder(view, adapter as ChaptersAdapter, startExpanded)
} }

View File

@ -41,6 +41,10 @@ class ChapterItem(val chapter: Chapter, val manga: Manga) :
return true return true
} }
override fun isSwipeable(): Boolean {
return !isLocked
}
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): ChapterMatHolder { override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): ChapterMatHolder {
return ChapterMatHolder(view, adapter as ChaptersAdapter) return ChapterMatHolder(view, adapter as ChaptersAdapter)
} }

View File

@ -1,8 +1,10 @@
package eu.kanade.tachiyomi.ui.manga.chapter package eu.kanade.tachiyomi.ui.manga.chapter
import android.animation.Animator
import android.text.format.DateUtils import android.text.format.DateUtils
import android.view.View import android.view.View
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.core.content.ContextCompat
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.download.model.Download
@ -10,9 +12,9 @@ import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
import eu.kanade.tachiyomi.util.view.gone import eu.kanade.tachiyomi.util.view.gone
import eu.kanade.tachiyomi.util.view.visibleIf import eu.kanade.tachiyomi.util.view.visibleIf
import java.util.Date
import kotlinx.android.synthetic.main.chapters_mat_item.* import kotlinx.android.synthetic.main.chapters_mat_item.*
import kotlinx.android.synthetic.main.download_button.* import kotlinx.android.synthetic.main.download_button.*
import java.util.Date
class ChapterMatHolder( class ChapterMatHolder(
private val view: View, private val view: View,
@ -74,32 +76,93 @@ class ChapterMatHolder(
if (isLocked) download_button.gone() if (isLocked) download_button.gone()
// Set correct text color // Set correct text color
chapter_title.setTextColor(if (chapter.read && !isLocked) chapter_title.setTextColor(
adapter.readColor else adapter.unreadColor) if (chapter.read && !isLocked) adapter.readColor else adapter.unreadColor
)
if (chapter.bookmark && !isLocked) chapter_title.setTextColor(adapter.bookmarkedColor) if (chapter.bookmark && !isLocked) chapter_title.setTextColor(adapter.bookmarkedColor)
val statuses = mutableListOf<String>() val statuses = mutableListOf<String>()
if (chapter.date_upload > 0) { if (chapter.date_upload > 0) {
statuses.add(DateUtils.getRelativeTimeSpanString(chapter.date_upload, statuses.add(
Date().time, DateUtils.HOUR_IN_MILLIS).toString()) DateUtils.getRelativeTimeSpanString(
chapter.date_upload, Date().time, DateUtils.HOUR_IN_MILLIS
).toString()
)
} }
if (!chapter.read && chapter.last_page_read > 0 && chapter.pages_left > 0 && !isLocked) { if (!chapter.read && chapter.last_page_read > 0 && chapter.pages_left > 0 && !isLocked) {
statuses.add(itemView.resources.getQuantityString(R.plurals.pages_left, chapter statuses.add(
.pages_left, chapter.pages_left)) itemView.resources.getQuantityString(
R.plurals.pages_left, chapter.pages_left, chapter.pages_left
)
)
} else if (!chapter.read && chapter.last_page_read > 0 && !isLocked) { } else if (!chapter.read && chapter.last_page_read > 0 && !isLocked) {
statuses.add(itemView.context.getString(R.string.chapter_progress, chapter statuses.add(
.last_page_read + 1)) itemView.context.getString(
R.string.chapter_progress, chapter.last_page_read + 1
)
)
} }
if (!chapter.scanlator.isNullOrBlank()) { if (!chapter.scanlator.isNullOrBlank()) {
statuses.add(chapter.scanlator!!) statuses.add(chapter.scanlator!!)
} }
read.setImageDrawable(
ContextCompat.getDrawable(
read.context, if (item.read) R.drawable.eye_off
else R.drawable.eye
)
)
bookmark.setImageDrawable(
ContextCompat.getDrawable(
read.context, if (item.bookmark) R.drawable.star_off
else R.drawable.star
)
)
chapter_scanlator.setTextColor(if (chapter.read) adapter.readColor else adapter.unreadColor) chapter_scanlator.setTextColor(if (chapter.read) adapter.readColor else adapter.unreadColor)
chapter_scanlator.text = statuses.joinToString("") chapter_scanlator.text = statuses.joinToString("")
notifyStatus(item.status, item.isLocked, item.progress) notifyStatus(
if (adapter.isSelected(adapterPosition)) Download.CHECKED else item.status,
item.isLocked,
item.progress
)
resetFrontView()
}
override fun getFrontView(): View {
return front_view
}
override fun getRearRightView(): View {
return right_view
}
override fun getRearLeftView(): View {
return left_view
}
private fun resetFrontView() {
if (front_view.translationX == 0f) return
itemView.post {
adapter.recyclerView.itemAnimator?.changeDuration = 0
val anim = front_view.animate().translationX(0f).setDuration(250)
anim.setListener(object : Animator.AnimatorListener {
override fun onAnimationEnd(animation: Animator?) {
adapter.itemTouchHelper.startSwipe(this@ChapterMatHolder)
adapter.notifyItemChanged(adapterPosition)
}
override fun onAnimationCancel(animation: Animator?) {}
override fun onAnimationRepeat(animation: Animator?) {}
override fun onAnimationStart(animation: Animator?) {}
})
anim.start()
}
} }
fun notifyStatus(status: Int, locked: Boolean, progress: Int) = with(download_button) { fun notifyStatus(status: Int, locked: Boolean, progress: Int) = with(download_button) {

View File

@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.manga.chapter
import android.content.Context import android.content.Context
import android.view.View import android.view.View
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.recyclerview.widget.ItemTouchHelper
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
@ -37,7 +38,7 @@ class ChaptersAdapter(
fun setChapters(items: List<ChapterItem>?) { fun setChapters(items: List<ChapterItem>?) {
this.items = items ?: emptyList() this.items = items ?: emptyList()
super.updateDataSet(items) performFilter()
} }
fun indexOf(item: ChapterItem): Int { fun indexOf(item: ChapterItem): Int {
@ -49,6 +50,24 @@ class ChaptersAdapter(
SecureActivityDelegate.promptLockIfNeeded(activity) SecureActivityDelegate.promptLockIfNeeded(activity)
} }
fun performFilter() {
val s = getFilter(String::class.java)
if (s.isNullOrBlank()) {
updateDataSet(items)
} else {
updateDataSet(items.filter { it.name.contains(s, true) ||
it.scanlator?.contains(s, true) == true })
}
}
override fun onItemSwiped(position: Int, direction: Int) {
super.onItemSwiped(position, direction)
when (direction) {
ItemTouchHelper.RIGHT -> controller.bookmarkChapter(position)
ItemTouchHelper.LEFT -> controller.toggleReadChapter(position)
}
}
interface MangaHeaderInterface { interface MangaHeaderInterface {
fun coverColor(): Int? fun coverColor(): Int?
fun mangaPresenter(): MangaDetailsPresenter fun mangaPresenter(): MangaDetailsPresenter

View File

@ -36,6 +36,8 @@ class TrackHolder(view: View, adapter: TrackAdapter) : BaseViewHolder(view) {
track_title.text = track.title track_title.text = track.title
with(track_chapters) { with(track_chapters) {
text = when { text = when {
track.total_chapters > 0 && track.last_chapter_read == track.total_chapters ->
context.getString(R.string.all_chapters_read)
track.total_chapters > 0 -> context.getString( track.total_chapters > 0 -> context.getString(
R.string.chapter_x_of_y, track.last_chapter_read, track.total_chapters R.string.chapter_x_of_y, track.last_chapter_read, track.total_chapters
) )

View File

@ -36,7 +36,7 @@ class SettingsMainController : SettingsController() {
onClick { navigateTo(SettingsLibraryController()) } onClick { navigateTo(SettingsLibraryController()) }
} }
preference { preference {
iconRes = R.drawable.ic_chrome_reader_mode_black_24dp iconRes = R.drawable.ic_read_24dp
iconTint = tintColor iconTint = tintColor
titleRes = R.string.pref_category_reader titleRes = R.string.pref_category_reader
onClick { navigateTo(SettingsReaderController()) } onClick { navigateTo(SettingsReaderController()) }

View File

@ -14,6 +14,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.ViewTreeObserver import android.view.ViewTreeObserver
import android.view.WindowInsets import android.view.WindowInsets
import android.view.inputmethod.InputMethodManager
import android.widget.Button import android.widget.Button
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.ImageView import android.widget.ImageView
@ -310,6 +311,10 @@ fun Controller.setOnQueryTextChangeListener(
override fun onQueryTextSubmit(query: String?): Boolean { override fun onQueryTextSubmit(query: String?): Boolean {
if (router.backstack.lastOrNull()?.controller() == this@setOnQueryTextChangeListener) { if (router.backstack.lastOrNull()?.controller() == this@setOnQueryTextChangeListener) {
val imm =
activity?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
?: return f(query)
imm.hideSoftInputFromWindow(searchView.windowToken, 0)
return f(query) return f(query)
} }
return true return true
@ -320,6 +325,7 @@ fun Controller.setOnQueryTextChangeListener(
fun Controller.scrollViewWith( fun Controller.scrollViewWith(
recycler: RecyclerView, recycler: RecyclerView,
padBottom: Boolean = false, padBottom: Boolean = false,
customPadding: Boolean = false,
swipeRefreshLayout: SwipeRefreshLayout? = null, swipeRefreshLayout: SwipeRefreshLayout? = null,
afterInsets: ((WindowInsets) -> Unit)? = null afterInsets: ((WindowInsets) -> Unit)? = null
) { ) {
@ -331,7 +337,7 @@ fun Controller.scrollViewWith(
array.recycle() array.recycle()
recycler.doOnApplyWindowInsets { view, insets, _ -> recycler.doOnApplyWindowInsets { view, insets, _ ->
val headerHeight = insets.systemWindowInsetTop + appBarHeight val headerHeight = insets.systemWindowInsetTop + appBarHeight
view.updatePaddingRelative( if (!customPadding) view.updatePaddingRelative(
top = headerHeight, top = headerHeight,
bottom = if (padBottom) insets.systemWindowInsetBottom else view.paddingBottom bottom = if (padBottom) insets.systemWindowInsetBottom else view.paddingBottom
) )
@ -365,8 +371,10 @@ fun Controller.scrollViewWith(
android.R.integer.config_shortAnimTime android.R.integer.config_shortAnimTime
) ?: 0 ) ?: 0
val closerToTop = abs(activity!!.appbar.y) - halfWay > 0 val closerToTop = abs(activity!!.appbar.y) - halfWay > 0
val atTop = val atTop = (!customPadding &&
(recycler.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() < 2 (recycler.layoutManager as LinearLayoutManager)
.findFirstVisibleItemPosition() < 2) ||
!recycler.canScrollVertically(-1)
activity!!.appbar.animate().y( activity!!.appbar.animate().y(
if (closerToTop && !atTop) (-activity!!.appbar.height.toFloat()) if (closerToTop && !atTop) (-activity!!.appbar.height.toFloat())
else 0f else 0f

View File

@ -0,0 +1,8 @@
<!-- drawable/eye.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M12,9A3,3 0 0,0 9,12A3,3 0 0,0 12,15A3,3 0 0,0 15,12A3,3 0 0,0 12,9M12,17A5,5 0 0,1 7,12A5,5 0 0,1 12,7A5,5 0 0,1 17,12A5,5 0 0,1 12,17M12,4.5C7,4.5 2.73,7.61 1,12C2.73,16.39 7,19.5 12,19.5C17,19.5 21.27,16.39 23,12C21.27,7.61 17,4.5 12,4.5Z" />
</vector>

View File

@ -0,0 +1,8 @@
<!-- drawable/eye_off.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M11.83,9L15,12.16C15,12.11 15,12.05 15,12A3,3 0 0,0 12,9C11.94,9 11.89,9 11.83,9M7.53,9.8L9.08,11.35C9.03,11.56 9,11.77 9,12A3,3 0 0,0 12,15C12.22,15 12.44,14.97 12.65,14.92L14.2,16.47C13.53,16.8 12.79,17 12,17A5,5 0 0,1 7,12C7,11.21 7.2,10.47 7.53,9.8M2,4.27L4.28,6.55L4.73,7C3.08,8.3 1.78,10 1,12C2.73,16.39 7,19.5 12,19.5C13.55,19.5 15.03,19.2 16.38,18.66L16.81,19.08L19.73,22L21,20.73L3.27,3M12,7A5,5 0 0,1 17,12C17,12.64 16.87,13.26 16.64,13.82L19.57,16.75C21.07,15.5 22.27,13.86 23,12C21.27,7.61 17,4.5 12,4.5C10.6,4.5 9.26,4.75 8,5.2L10.17,7.35C10.74,7.13 11.35,7 12,7Z" />
</vector>

View File

@ -0,0 +1,8 @@
<!-- drawable/star.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M12,17.27L18.18,21L16.54,13.97L22,9.24L14.81,8.62L12,2L9.19,8.62L2,9.24L7.45,13.97L5.82,21L12,17.27Z" />
</vector>

View File

@ -0,0 +1,8 @@
<!-- drawable/star_off.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M2,5.27L3.28,4L20,20.72L18.73,22L17.05,20.31L12,17.27L5.82,21L7.45,13.97L2,9.24L5.66,8.93L2,5.27M12,2L14.81,8.62L22,9.24L16.54,13.97L16.77,14.95L9.56,7.74L12,2Z" />
</vector>

View File

@ -1,48 +1,103 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/chapter_layout" android:id="@+id/chapter_layout"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?attr/selectable_list_drawable"> android:background="?android:attr/colorBackground">
<TextView <FrameLayout
android:id="@+id/chapter_title" android:id="@+id/right_view"
style="@style/TextAppearance.MaterialComponents.Body2" android:layout_width="match_parent"
android:layout_width="0dp" android:layout_height="match_parent"
android:layout_height="wrap_content" android:visibility="gone"
android:layout_marginStart="16dp" android:background="@color/material_green_800"
android:layout_marginTop="12dp"
android:ellipsize="end"
android:maxLines="2"
app:layout_constraintBottom_toTopOf="@id/chapter_scanlator"
app:layout_constraintEnd_toStartOf="@id/download_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Chapter 123 - The Real One" />
<TextView
android:id="@+id/chapter_scanlator"
style="@style/TextAppearance.MaterialComponents.Caption"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginBottom="12dp"
android:ellipsize="end"
android:maxLines="1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/download_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/chapter_title"
tools:text="3 days ago • On page 45 • Scanlator" />
<include
layout="@layout/download_button"
android:layout_width="50dp"
android:layout_height="0dp"
android:layout_marginEnd="8dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintStart_toStartOf="parent"
</androidx.constraintlayout.widget.ConstraintLayout> app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/read"
android:layout_width="24dp"
android:layout_height="24dp"
android:tint="@color/md_white_1000"
android:layout_gravity="end|center"
android:layout_marginEnd="21dp"
android:src="@drawable/ic_read_24dp" />
</FrameLayout>
<FrameLayout
android:id="@+id/left_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
android:background="@color/colorAccent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/bookmark"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="start|center"
android:layout_marginStart="21dp"
android:tint="@color/md_white_1000"
android:src="@drawable/ic_bookmark_white_24dp" />
</FrameLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/front_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
android:background="?attr/selectable_list_drawable"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/chapter_title"
style="@style/TextAppearance.MaterialComponents.Body2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="12dp"
android:ellipsize="end"
android:maxLines="2"
app:layout_constraintBottom_toTopOf="@id/chapter_scanlator"
app:layout_constraintEnd_toStartOf="@id/download_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Chapter 123 - The Real One" />
<TextView
android:id="@+id/chapter_scanlator"
style="@style/TextAppearance.MaterialComponents.Caption"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginBottom="12dp"
android:ellipsize="end"
android:maxLines="1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/download_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/chapter_title"
tools:text="3 days ago • On page 45 • Scanlator" />
<include
layout="@layout/download_button"
android:layout_width="50dp"
android:layout_height="0dp"
android:layout_marginEnd="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>

View File

@ -3,10 +3,11 @@
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item <item
android:id="@+id/action_edit" android:id="@+id/action_search"
android:icon="@drawable/ic_edit_white_24dp" android:icon="@drawable/ic_search_white_24dp"
android:title="@string/action_edit" android:title="@string/action_search"
app:showAsAction="ifRoom" /> app:actionViewClass="androidx.appcompat.widget.SearchView"
app:showAsAction="collapseActionView|ifRoom" />
<item <item
android:id="@+id/action_download" android:id="@+id/action_download"
@ -44,6 +45,12 @@
android:title="@string/action_mark_all_as_unread" android:title="@string/action_mark_all_as_unread"
app:showAsAction="never" /> app:showAsAction="never" />
<item
android:id="@+id/action_edit"
android:icon="@drawable/ic_edit_white_24dp"
android:title="@string/action_edit"
app:showAsAction="never" />
<item <item
android:id="@+id/action_add_to_home_screen" android:id="@+id/action_add_to_home_screen"
android:title="@string/action_add_to_home_screen" android:title="@string/action_add_to_home_screen"

View File

@ -41,6 +41,9 @@
</style> </style>
<style name="Theme.Splash" parent="Theme.Tachiyomi"> <style name="Theme.Splash" parent="Theme.Tachiyomi">
<item name="android:windowBackground">@drawable/splash_background</item>
<item name="android:statusBarColor">@color/splashBackground</item>
<item name="android:navigationBarColor">@color/splashBackground</item>
<item name="android:windowLightStatusBar">false</item> <item name="android:windowLightStatusBar">false</item>
</style> </style>
</resources> </resources>

View File

@ -183,7 +183,7 @@
<item quantity="many">Po %1$s minutach</item> <item quantity="many">Po %1$s minutach</item>
<item quantity="other">Po %1$s minutach</item> <item quantity="other">Po %1$s minutach</item>
</plurals> </plurals>
<string name="search_hint">Szukaj tytułu, tagów, źródła…</string> <string name="library_search_hint">Szukaj tytułu, tagów, źródła…</string>
<string name="pref_refresh_covers_too">Automatycznie odśwież okładki</string> <string name="pref_refresh_covers_too">Automatycznie odśwież okładki</string>
<string name="pref_refresh_covers_too_summary">Odśwież okładki w bibliotece podczas odświeżania biblioteki (nadpisuje lokalne okładki)</string> <string name="pref_refresh_covers_too_summary">Odśwież okładki w bibliotece podczas odświeżania biblioteki (nadpisuje lokalne okładki)</string>

View File

@ -520,7 +520,7 @@
<string name="score">Оценка</string> <string name="score">Оценка</string>
<string name="search_hint">Название, теги, источник</string> <string name="library_search_hint">Название, теги, источник</string>
<string name="search_parameter">Параметр поиска (например: language:english)</string> <string name="search_parameter">Параметр поиска (например: language:english)</string>
<string name="second_to_last">Предпоследняя</string> <string name="second_to_last">Предпоследняя</string>

View File

@ -244,7 +244,7 @@
<item quantity="one">After %1$s minute</item> <item quantity="one">After %1$s minute</item>
<item quantity="other">After %1$s minutes</item> <item quantity="other">After %1$s minutes</item>
</plurals> </plurals>
<string name="search_hint">Search title, tags, source</string> <string name="library_search_hint">Search title, tags, source</string>
<string name="pref_refresh_covers_too">Automatically refresh covers</string> <string name="pref_refresh_covers_too">Automatically refresh covers</string>
<string name="pref_refresh_covers_too_summary">Refresh covers in library as well <string name="pref_refresh_covers_too_summary">Refresh covers in library as well
when updating library (overwrites local covers)</string> when updating library (overwrites local covers)</string>
@ -531,6 +531,7 @@
<string name="remove_from_library">Remove from library</string> <string name="remove_from_library">Remove from library</string>
<string name="select_start_chapter">Select starting chapter</string> <string name="select_start_chapter">Select starting chapter</string>
<string name="select_end_chapter">Select ending chapter</string> <string name="select_end_chapter">Select ending chapter</string>
<string name="chapter_search_hint">Search chapters</string>
<!-- Manga chapters fragment --> <!-- Manga chapters fragment -->
<string name="start_reading">Start reading</string> <string name="start_reading">Start reading</string>