mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-11-09 19:55:11 +01:00
More work on new manga controller
Synced download badge for chapters items with downloads Download arrow now pulses while it's downloading Started work on the filter/sort bottom sheet for chapters Expanding description
This commit is contained in:
parent
c3620b74f1
commit
d58923acbf
@ -294,4 +294,6 @@ class DownloadManager(val context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
fun addListener(listener: DownloadQueue.DownloadListener) = queue.addListener(listener)
|
||||
fun removeListener(listener: DownloadQueue.DownloadListener) = queue.removeListener(listener)
|
||||
}
|
||||
|
@ -18,14 +18,27 @@ class Download(val source: HttpSource, val manga: Manga, val chapter: Chapter) {
|
||||
set(status) {
|
||||
field = status
|
||||
statusSubject?.onNext(this)
|
||||
statusCallback?.invoke(this)
|
||||
}
|
||||
|
||||
@Transient private var statusSubject: PublishSubject<Download>? = null
|
||||
|
||||
@Transient private var statusCallback: ((Download) -> Unit)? = null
|
||||
|
||||
val progress: Int
|
||||
get() {
|
||||
val pages = pages ?: return 0
|
||||
return pages.map(Page::progress).average().toInt()
|
||||
}
|
||||
|
||||
fun setStatusSubject(subject: PublishSubject<Download>?) {
|
||||
statusSubject = subject
|
||||
}
|
||||
|
||||
fun setStatusCallback(f: ((Download) -> Unit)?) {
|
||||
statusCallback = f
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val NOT_DOWNLOADED = 0
|
||||
|
@ -18,9 +18,12 @@ class DownloadQueue(
|
||||
|
||||
private val updatedRelay = PublishRelay.create<Unit>()
|
||||
|
||||
private val downloadListeners = mutableListOf<DownloadListener>()
|
||||
|
||||
fun addAll(downloads: List<Download>) {
|
||||
downloads.forEach { download ->
|
||||
download.setStatusSubject(statusSubject)
|
||||
download.setStatusCallback(::setPagesFor)
|
||||
download.status = Download.QUEUE
|
||||
}
|
||||
queue.addAll(downloads)
|
||||
@ -32,6 +35,10 @@ class DownloadQueue(
|
||||
val removed = queue.remove(download)
|
||||
store.remove(download)
|
||||
download.setStatusSubject(null)
|
||||
download.setStatusCallback(null)
|
||||
if (download.status == Download.DOWNLOADING || download.status == Download.QUEUE)
|
||||
download.status = Download.NOT_DOWNLOADED
|
||||
downloadListeners.forEach { it.updateDownload(download) }
|
||||
if (removed) {
|
||||
updatedRelay.call(Unit)
|
||||
}
|
||||
@ -52,6 +59,10 @@ class DownloadQueue(
|
||||
fun clear() {
|
||||
queue.forEach { download ->
|
||||
download.setStatusSubject(null)
|
||||
download.setStatusCallback(null)
|
||||
if (download.status == Download.DOWNLOADING || download.status == Download.QUEUE)
|
||||
download.status = Download.NOT_DOWNLOADED
|
||||
downloadListeners.forEach { it.updateDownload(download) }
|
||||
}
|
||||
queue.clear()
|
||||
store.clear()
|
||||
@ -67,6 +78,27 @@ class DownloadQueue(
|
||||
.startWith(Unit)
|
||||
.map { this }
|
||||
|
||||
private fun setPagesFor(download: Download) {
|
||||
if (download.status == Download.DOWNLOADING) {
|
||||
if (download.pages != null)
|
||||
for (page in download.pages!!)
|
||||
page.setStatusCallback {
|
||||
callListeners(download)
|
||||
}
|
||||
downloadListeners.forEach { it.updateDownload(download) }
|
||||
} else if (download.status == Download.DOWNLOADED || download.status == Download.ERROR) {
|
||||
setPagesSubject(download.pages, null)
|
||||
downloadListeners.forEach { it.updateDownload(download) }
|
||||
}
|
||||
else {
|
||||
downloadListeners.forEach { it.updateDownload(download) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun callListeners(download: Download) {
|
||||
downloadListeners.forEach { it.updateDownload(download) }
|
||||
}
|
||||
|
||||
fun getProgressObservable(): Observable<Download> {
|
||||
return statusSubject.onBackpressureBuffer()
|
||||
.startWith(getActiveDownloads())
|
||||
@ -74,6 +106,7 @@ class DownloadQueue(
|
||||
if (download.status == Download.DOWNLOADING) {
|
||||
val pageStatusSubject = PublishSubject.create<Int>()
|
||||
setPagesSubject(download.pages, pageStatusSubject)
|
||||
downloadListeners.forEach { it.updateDownload(download) }
|
||||
return@flatMap pageStatusSubject
|
||||
.onBackpressureBuffer()
|
||||
.filter { it == Page.READY }
|
||||
@ -81,6 +114,7 @@ class DownloadQueue(
|
||||
|
||||
} else if (download.status == Download.DOWNLOADED || download.status == Download.ERROR) {
|
||||
setPagesSubject(download.pages, null)
|
||||
downloadListeners.forEach { it.updateDownload(download) }
|
||||
}
|
||||
Observable.just(download)
|
||||
}
|
||||
@ -95,4 +129,16 @@ class DownloadQueue(
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
fun addListener(listener: DownloadListener) {
|
||||
downloadListeners.add(listener)
|
||||
}
|
||||
|
||||
fun removeListener(listener: DownloadListener) {
|
||||
downloadListeners.remove(listener)
|
||||
}
|
||||
|
||||
interface DownloadListener {
|
||||
fun updateDownload(download: Download)
|
||||
}
|
||||
|
||||
}
|
@ -18,12 +18,19 @@ open class Page(
|
||||
set(value) {
|
||||
field = value
|
||||
statusSubject?.onNext(value)
|
||||
statusCallback?.invoke(this)
|
||||
}
|
||||
|
||||
@Transient @Volatile var progress: Int = 0
|
||||
set(value) {
|
||||
field = value
|
||||
statusCallback?.invoke(this)
|
||||
}
|
||||
|
||||
@Transient private var statusSubject: Subject<Int, Int>? = null
|
||||
|
||||
@Transient private var statusCallback: ((Page) -> Unit)? = null
|
||||
|
||||
override fun update(bytesRead: Long, contentLength: Long, done: Boolean) {
|
||||
progress = if (contentLength > 0) {
|
||||
(100 * bytesRead / contentLength).toInt()
|
||||
@ -36,6 +43,10 @@ open class Page(
|
||||
this.statusSubject = subject
|
||||
}
|
||||
|
||||
fun setStatusCallback(f: ((Page) -> Unit)?) {
|
||||
statusCallback = f
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val QUEUE = 0
|
||||
const val LOAD_PAGE = 1
|
||||
|
@ -1,5 +1,6 @@
|
||||
package eu.kanade.tachiyomi.ui.download
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.util.AttributeSet
|
||||
@ -26,9 +27,16 @@ class DownloadButton @JvmOverloads constructor(context: Context, attrs: Attribut
|
||||
R.drawable.filled_circle)?.mutate()
|
||||
private val borderCircle = ContextCompat.getDrawable(context,
|
||||
R.drawable.border_circle)?.mutate()
|
||||
private var isAnimating = false
|
||||
private var iconAnimation:ObjectAnimator? = null
|
||||
|
||||
|
||||
fun setDownoadStatus(state: Int, progress: Int = 0) {
|
||||
fun setDownloadStatus(state: Int, progress: Int = 0) {
|
||||
if (state != Download.DOWNLOADING) {
|
||||
iconAnimation?.cancel()
|
||||
download_icon.alpha = 1f
|
||||
isAnimating = false
|
||||
}
|
||||
when (state) {
|
||||
Download.NOT_DOWNLOADED -> {
|
||||
download_border.visible()
|
||||
@ -55,6 +63,15 @@ class DownloadButton @JvmOverloads constructor(context: Context, attrs: Attribut
|
||||
download_border.drawable.setTint(disabledColor)
|
||||
download_progress.progressDrawable?.setTint(downloadedColor)
|
||||
download_icon.drawable.setTint(disabledColor)
|
||||
if (!isAnimating) {
|
||||
iconAnimation = ObjectAnimator.ofFloat(download_icon, "alpha", 1f, 0f).apply {
|
||||
duration = 1000
|
||||
repeatCount = ObjectAnimator.INFINITE
|
||||
repeatMode = ObjectAnimator.REVERSE
|
||||
}
|
||||
iconAnimation?.start()
|
||||
isAnimating = true
|
||||
}
|
||||
}
|
||||
Download.DOWNLOADED -> {
|
||||
download_progress.gone()
|
||||
|
@ -86,6 +86,8 @@ open class LibraryController(
|
||||
*/
|
||||
protected var query = ""
|
||||
|
||||
var customQuery = ""
|
||||
|
||||
/**
|
||||
* Currently selected mangas.
|
||||
*/
|
||||
@ -256,6 +258,24 @@ open class LibraryController(
|
||||
}
|
||||
}
|
||||
|
||||
override fun onChangeEnded(
|
||||
changeHandler: ControllerChangeHandler,
|
||||
changeType: ControllerChangeType
|
||||
) {
|
||||
super.onChangeEnded(changeHandler, changeType)
|
||||
if (changeType.isEnter) {
|
||||
if (customQuery.isNotEmpty()) {
|
||||
query = customQuery
|
||||
((activity as MainActivity).toolbar.menu.findItem(
|
||||
R.id.action_search
|
||||
)?.actionView as? SearchView)?.setQuery(
|
||||
customQuery, true
|
||||
)
|
||||
}
|
||||
customQuery = ""
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResumed(activity: Activity) {
|
||||
super.onActivityResumed(activity)
|
||||
if (observeLater && ::presenter.isInitialized) {
|
||||
@ -477,15 +497,19 @@ open class LibraryController(
|
||||
menu.findItem(R.id.action_library_filter).icon.mutate()
|
||||
|
||||
setOnQueryTextChangeListener(searchView) {
|
||||
query = it ?: ""
|
||||
searchRelay.call(query)
|
||||
true
|
||||
onSearch(it)
|
||||
}
|
||||
searchItem.fixExpand(onExpand = { invalidateMenuOnExpand() })
|
||||
}
|
||||
|
||||
fun search(query:String) {
|
||||
this.query = query
|
||||
open fun onSearch(query: String?): Boolean {
|
||||
this.query = query ?: ""
|
||||
searchRelay.call(query)
|
||||
return true
|
||||
}
|
||||
|
||||
open fun search(query: String) {
|
||||
this.customQuery = query
|
||||
}
|
||||
|
||||
override fun handleRootBack(): Boolean {
|
||||
|
@ -5,15 +5,12 @@ import android.os.Bundle
|
||||
import android.util.TypedValue
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.math.MathUtils.clamp
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
@ -36,10 +33,8 @@ import eu.kanade.tachiyomi.ui.main.SwipeGestureInterface
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import eu.kanade.tachiyomi.util.system.launchUI
|
||||
import eu.kanade.tachiyomi.util.view.inflate
|
||||
import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener
|
||||
import eu.kanade.tachiyomi.util.view.snack
|
||||
import eu.kanade.tachiyomi.util.view.updatePaddingRelative
|
||||
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
|
||||
import kotlinx.android.synthetic.main.filter_bottom_sheet.*
|
||||
import kotlinx.android.synthetic.main.library_grid_recycler.*
|
||||
import kotlinx.android.synthetic.main.library_list_controller.*
|
||||
@ -302,27 +297,21 @@ class LibraryListController(bundle: Bundle? = null) : LibraryController(bundle),
|
||||
}
|
||||
|
||||
override fun reattachAdapter() {
|
||||
if (libraryLayout == 0)recycler.spanCount = 1
|
||||
if (libraryLayout == 0) recycler.spanCount = 1
|
||||
else recycler.columnWidth = (90 + (preferences.gridSize().getOrDefault() * 30)).dpToPx
|
||||
val position =
|
||||
(recycler.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
|
||||
libraryLayout = preferences.libraryLayout().getOrDefault()
|
||||
recycler.adapter = adapter
|
||||
(recycler as? AutofitRecyclerView)?.spanCount = if (libraryLayout == 0) 1 else mangaPerRow
|
||||
|
||||
(recycler.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(position, 0)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater)
|
||||
val searchItem = menu.findItem(R.id.action_search)
|
||||
val searchView = searchItem.actionView as SearchView
|
||||
setOnQueryTextChangeListener(searchView) {
|
||||
query = it ?: ""
|
||||
adapter.setFilter(it)
|
||||
adapter.performFilter()
|
||||
true
|
||||
}
|
||||
override fun onSearch(query: String?): Boolean {
|
||||
this.query = query ?: ""
|
||||
adapter.setFilter(query)
|
||||
adapter.performFilter()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDestroyActionMode(mode: ActionMode?) {
|
||||
|
@ -12,6 +12,7 @@ import android.view.GestureDetector
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import android.webkit.WebView
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.appcompat.graphics.drawable.DrawerArrowDrawable
|
||||
@ -54,8 +55,10 @@ import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsMainController
|
||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
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.updatePadding
|
||||
import eu.kanade.tachiyomi.util.view.visible
|
||||
import kotlinx.android.synthetic.main.main_activity.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
@ -198,18 +201,19 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION*/
|
||||
updateRecentsIcon()
|
||||
content.viewTreeObserver.addOnGlobalLayoutListener {
|
||||
/*val heightDiff: Int = content.rootView.height - content.height
|
||||
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
|
||||
navigationView.gone()
|
||||
} else if (navigationView.visibility == View.GONE) {
|
||||
} else if (navigationView.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 {
|
||||
navigationView.visible()
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
supportActionBar?.setDisplayShowCustomEnabled(true)
|
||||
|
@ -0,0 +1,157 @@
|
||||
package eu.kanade.tachiyomi.ui.manga
|
||||
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.CompoundButton
|
||||
import android.widget.RadioButton
|
||||
import android.widget.RadioGroup
|
||||
import com.f2prateek.rx.preferences.Preference
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import eu.kanade.tachiyomi.util.view.setBottomEdge
|
||||
import eu.kanade.tachiyomi.util.view.setEdgeToEdge
|
||||
import eu.kanade.tachiyomi.util.view.visibleIf
|
||||
import kotlinx.android.synthetic.main.chapter_sort_bottom_sheet.*
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class ChaptersSortBottomSheet(private val controller: MangaChaptersController) : BottomSheetDialog
|
||||
(controller.activity!!, R.style.BottomSheetDialogTheme) {
|
||||
|
||||
val activity = controller.activity!!
|
||||
|
||||
/**
|
||||
* Preferences helper.
|
||||
*/
|
||||
private val preferences by injectLazy<PreferencesHelper>()
|
||||
|
||||
private var sheetBehavior: BottomSheetBehavior<*>
|
||||
|
||||
|
||||
init {
|
||||
// Use activity theme for this layout
|
||||
val view = activity.layoutInflater.inflate(R.layout.chapter_sort_bottom_sheet, null)
|
||||
setContentView(view)
|
||||
|
||||
sheetBehavior = BottomSheetBehavior.from(view.parent as ViewGroup)
|
||||
setEdgeToEdge(activity, bottom_sheet, view, false)
|
||||
val height = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
activity.window.decorView.rootWindowInsets.systemWindowInsetBottom
|
||||
} else 0
|
||||
sheetBehavior.peekHeight = 220.dpToPx + height
|
||||
|
||||
sheetBehavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
|
||||
override fun onSlide(bottomSheet: View, progress: Float) { }
|
||||
|
||||
override fun onStateChanged(p0: View, state: Int) {
|
||||
if (state == BottomSheetBehavior.STATE_EXPANDED) {
|
||||
sheetBehavior.skipCollapsed = true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
sheetBehavior.skipCollapsed = true
|
||||
sheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the sheet is created. It initializes the listeners and values of the preferences.
|
||||
*/
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
initGeneralPreferences()
|
||||
setBottomEdge(show_bookmark, activity)
|
||||
close_button.setOnClickListener {
|
||||
dismiss()
|
||||
true
|
||||
}
|
||||
settings_scroll_view.viewTreeObserver.addOnGlobalLayoutListener {
|
||||
val isScrollable =
|
||||
settings_scroll_view!!.height < bottom_sheet.height +
|
||||
settings_scroll_view.paddingTop + settings_scroll_view.paddingBottom
|
||||
close_button.visibleIf(isScrollable)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun initGeneralPreferences() {
|
||||
|
||||
show_read.isChecked = controller.presenter.onlyRead()
|
||||
show_unread.isChecked = controller.presenter.onlyUnread()
|
||||
show_download.isChecked = controller.presenter.onlyDownloaded()
|
||||
show_bookmark.isChecked = controller.presenter.onlyBookmarked()
|
||||
|
||||
show_all.isChecked = !(show_read.isChecked || show_unread.isChecked ||
|
||||
show_download.isChecked || show_bookmark.isChecked)
|
||||
|
||||
if (controller.presenter.onlyRead())
|
||||
//Disable unread filter option if read filter is enabled.
|
||||
show_unread.isEnabled = false
|
||||
if (controller.presenter.onlyUnread())
|
||||
//Disable read filter option if unread filter is enabled.
|
||||
show_read.isEnabled = false
|
||||
|
||||
sort_group.check(if (controller.presenter.manga.sortDescending()) R.id.sort_newest else
|
||||
R.id.sort_oldest)
|
||||
|
||||
show_titles.isChecked = controller.presenter.manga.displayMode == Manga.DISPLAY_NAME
|
||||
sort_by_source.isChecked = controller.presenter.manga.sorting == Manga.SORTING_SOURCE
|
||||
|
||||
sort_group.setOnCheckedChangeListener { _, checkedId ->
|
||||
controller.presenter.setSortOrder(checkedId == R.id.sort_oldest)
|
||||
dismiss()
|
||||
}
|
||||
|
||||
/*sort_group.bindToPreference(preferences.libraryLayout()) {
|
||||
controller.reattachAdapter()
|
||||
if (sheetBehavior.state == BottomSheetBehavior.STATE_COLLAPSED)
|
||||
dismiss()
|
||||
}
|
||||
uniform_grid.bindToPreference(preferences.uniformGrid()) {
|
||||
controller.reattachAdapter()
|
||||
}
|
||||
grid_size_toggle_group.bindToPreference(preferences.gridSize()) {
|
||||
controller.reattachAdapter()
|
||||
}
|
||||
download_badge.bindToPreference(preferences.downloadBadge()) {
|
||||
controller.presenter.requestDownloadBadgesUpdate()
|
||||
}
|
||||
unread_badge_group.bindToPreference(preferences.unreadBadgeType()) {
|
||||
controller.presenter.requestUnreadBadgesUpdate()
|
||||
}*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a checkbox or switch view with a boolean preference.
|
||||
*/
|
||||
private fun CompoundButton.bindToPreference(pref: Preference<Boolean>, block: () -> Unit) {
|
||||
isChecked = pref.getOrDefault()
|
||||
setOnCheckedChangeListener { _, isChecked ->
|
||||
pref.set(isChecked)
|
||||
block()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a radio group with a int preference.
|
||||
*/
|
||||
private fun RadioGroup.bindToPreference(pref: Preference<Int>, block: () -> Unit) {
|
||||
(getChildAt(pref.getOrDefault()) as RadioButton).isChecked = true
|
||||
setOnCheckedChangeListener { _, checkedId ->
|
||||
val index = indexOfChild(findViewById(checkedId))
|
||||
pref.set(index)
|
||||
block()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package eu.kanade.tachiyomi.ui.manga
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Color
|
||||
@ -8,6 +9,7 @@ import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
@ -15,6 +17,7 @@ import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.palette.graphics.Palette
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
@ -31,6 +34,7 @@ import com.google.android.material.snackbar.Snackbar
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaImpl
|
||||
@ -41,10 +45,13 @@ import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.ui.base.controller.BaseController
|
||||
import eu.kanade.tachiyomi.ui.catalogue.CatalogueController
|
||||
import eu.kanade.tachiyomi.ui.library.ChangeMangaCategoriesDialog
|
||||
import eu.kanade.tachiyomi.ui.library.LibraryController
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.ui.main.SearchActivity
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaController.Companion.FROM_CATALOGUE_EXTRA
|
||||
import eu.kanade.tachiyomi.ui.manga.chapter.ChapterItem
|
||||
import eu.kanade.tachiyomi.ui.manga.chapter.ChapterMatHolder
|
||||
import eu.kanade.tachiyomi.ui.manga.chapter.ChaptersAdapter
|
||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
|
||||
@ -56,14 +63,18 @@ import eu.kanade.tachiyomi.util.view.snack
|
||||
import eu.kanade.tachiyomi.util.view.updateLayoutParams
|
||||
import eu.kanade.tachiyomi.util.view.updatePaddingRelative
|
||||
import kotlinx.android.synthetic.main.big_manga_controller.*
|
||||
import kotlinx.android.synthetic.main.big_manga_controller.swipe_refresh
|
||||
import kotlinx.android.synthetic.main.main_activity.*
|
||||
import kotlinx.android.synthetic.main.manga_info_controller.*
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class MangaChaptersController : BaseController,
|
||||
ActionMode.Callback,
|
||||
FlexibleAdapter.OnItemClickListener,
|
||||
ChaptersAdapter.MangaHeaderInterface {
|
||||
FlexibleAdapter.OnItemLongClickListener,
|
||||
ChaptersAdapter.MangaHeaderInterface,
|
||||
ChangeMangaCategoriesDialog.Listener {
|
||||
|
||||
constructor(manga: Manga?,
|
||||
fromCatalogue: Boolean = false,
|
||||
@ -121,7 +132,6 @@ class MangaChaptersController : BaseController,
|
||||
|
||||
// Init RecyclerView and adapter
|
||||
adapter = ChaptersAdapter(this, view.context)
|
||||
//setReadingDrawable()
|
||||
|
||||
recycler.adapter = adapter
|
||||
recycler.layoutManager = LinearLayoutManager(view.context)
|
||||
@ -133,9 +143,6 @@ class MangaChaptersController : BaseController,
|
||||
)
|
||||
recycler.setHasFixedSize(true)
|
||||
adapter?.fastScroller = fast_scroller
|
||||
/*activity?.controller_container?.updateLayoutParams<ConstraintLayout.LayoutParams> {
|
||||
topMargin = 0
|
||||
}*/
|
||||
val attrsArray = intArrayOf(android.R.attr.actionBarSize)
|
||||
val array = view.context.obtainStyledAttributes(attrsArray)
|
||||
val appbarHeight = array.getDimensionPixelSize(0, 0)
|
||||
@ -144,6 +151,7 @@ class MangaChaptersController : BaseController,
|
||||
|
||||
recycler.doOnApplyWindowInsets { v, insets, _ ->
|
||||
headerHeight = appbarHeight + insets.systemWindowInsetTop + offset
|
||||
swipe_refresh.setProgressViewOffset(false, (-40).dpToPx, headerHeight)
|
||||
(recycler.findViewHolderForAdapterPosition(0) as? MangaHeaderHolder)
|
||||
?.setTopHeight(headerHeight)
|
||||
fast_scroller?.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
@ -156,7 +164,7 @@ class MangaChaptersController : BaseController,
|
||||
|
||||
presenter.onCreate()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
recycler.setOnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY ->
|
||||
recycler.setOnScrollChangeListener { _, _, _, _, _ ->
|
||||
val atTop = !recycler.canScrollVertically(-1)
|
||||
if ((!atTop && !toolbarIsColored) || (atTop && toolbarIsColored)) {
|
||||
toolbarIsColored = !atTop
|
||||
@ -173,7 +181,6 @@ class MangaChaptersController : BaseController,
|
||||
ArgbEvaluator(), colorFrom, colorTo
|
||||
)
|
||||
colorAnimator?.duration = 250 // milliseconds
|
||||
//colorAnimation.startDelay = 150
|
||||
colorAnimator?.addUpdateListener { animator ->
|
||||
(activity as MainActivity).toolbar.setBackgroundColor(animator.animatedValue as Int)
|
||||
activity?.window?.statusBarColor = (animator.animatedValue as Int)
|
||||
@ -184,8 +191,6 @@ class MangaChaptersController : BaseController,
|
||||
}
|
||||
}
|
||||
}
|
||||
// recycler.setOnApplyWindowInsetsListener(RecyclerWindowInsetsListener)
|
||||
// recycler.requestApplyInsetsWhenAttached()
|
||||
GlideApp.with(view.context).load(manga)
|
||||
.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
|
||||
.signature(ObjectKey(MangaImpl.getLastCoverFetch(manga!!.id!!).toString()))
|
||||
@ -222,7 +227,11 @@ class MangaChaptersController : BaseController,
|
||||
swipe_refresh.setOnRefreshListener {
|
||||
presenter.refreshAll()
|
||||
}
|
||||
//adapter?.fastScroller = fast_scroller
|
||||
}
|
||||
|
||||
override fun onActivityResumed(activity: Activity) {
|
||||
super.onActivityResumed(activity)
|
||||
presenter.fetchChapters()
|
||||
}
|
||||
|
||||
fun showError(message: String) {
|
||||
@ -230,43 +239,21 @@ class MangaChaptersController : BaseController,
|
||||
view?.snack(message)
|
||||
}
|
||||
|
||||
fun updateChapterDownload(download: Download) {
|
||||
getHolder(download.chapter)?.notifyStatus(download.status, presenter.isLockedFromSearch,
|
||||
download.progress)
|
||||
}
|
||||
|
||||
private fun getHolder(chapter: Chapter): ChapterMatHolder? {
|
||||
return recycler?.findViewHolderForItemId(chapter.id!!) as? ChapterMatHolder
|
||||
}
|
||||
|
||||
override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) {
|
||||
super.onChangeStarted(handler, type)
|
||||
if (type == ControllerChangeType.PUSH_ENTER || type == ControllerChangeType.POP_ENTER) {
|
||||
(activity as MainActivity).appbar.setBackgroundColor(Color.TRANSPARENT)
|
||||
(activity as MainActivity).toolbar.setBackgroundColor(Color.TRANSPARENT)
|
||||
activity?.window?.statusBarColor = Color.TRANSPARENT
|
||||
/* val colorFrom = ((activity as MainActivity).toolbar.background as ColorDrawable).color
|
||||
val colorTo = Color.TRANSPARENT
|
||||
colorAnimator = ValueAnimator.ofObject(
|
||||
ArgbEvaluator(), colorFrom, colorTo)
|
||||
colorAnimator?.duration = 250 // milliseconds
|
||||
//colorAnimation.startDelay = 150
|
||||
colorAnimator?.addUpdateListener { animator ->
|
||||
(activity as MainActivity).toolbar.setBackgroundColor(animator.animatedValue as Int)
|
||||
//activity?.window?.statusBarColor = (animator.animatedValue as Int)
|
||||
}
|
||||
colorAnimator?.start()*/
|
||||
|
||||
/*activity!!.window.setFlags(
|
||||
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
|
||||
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
val insetTop = activity!!.window.decorView.rootWindowInsets.systemWindowInsetTop
|
||||
val insetBottom = activity!!.window.decorView.rootWindowInsets.stableInsetBottom
|
||||
(activity)?.appbar?.updateLayoutParams<ConstraintLayout.LayoutParams> {
|
||||
topMargin = insetTop
|
||||
}
|
||||
|
||||
(activity)?.navigationView?.updateLayoutParams<ConstraintLayout.LayoutParams> {
|
||||
bottomMargin = insetBottom
|
||||
}
|
||||
}*/
|
||||
|
||||
//
|
||||
//(activity as MainActivity).toolbar.setBackgroundColor(Color.TRANSPARENT)
|
||||
//(activity as MainActivity).appbar.gone()
|
||||
}
|
||||
else if (type == ControllerChangeType.PUSH_EXIT || type == ControllerChangeType.POP_EXIT) {
|
||||
colorAnimator?.cancel()
|
||||
@ -278,22 +265,6 @@ class MangaChaptersController : BaseController,
|
||||
activity?.window?.statusBarColor = activity?.getResourceColor(
|
||||
android.R.attr.colorPrimary
|
||||
) ?: Color.BLACK
|
||||
// activity!!.window.clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)
|
||||
|
||||
/* activity?.window?.statusBarColor = activity?.getResourceColor(
|
||||
android.R.attr.colorPrimary
|
||||
) ?: Color.BLACK*/
|
||||
/*(activity as MainActivity).appbar.updateLayoutParams<ConstraintLayout.LayoutParams> {
|
||||
topMargin = 0
|
||||
}
|
||||
(activity as MainActivity).navigationView.updateLayoutParams<ConstraintLayout
|
||||
.LayoutParams> {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
bottomMargin = 0
|
||||
}
|
||||
}*/
|
||||
//(activity as MainActivity).appbar.background = null
|
||||
// (activity as AppCompatActivity).supportActionBar?.show()
|
||||
}
|
||||
}
|
||||
|
||||
@ -305,9 +276,12 @@ class MangaChaptersController : BaseController,
|
||||
if (presenter.chapters.isEmpty()) {
|
||||
adapter?.updateDataSet(listOf(ChapterItem(Chapter.createH(), presenter.manga)))
|
||||
}
|
||||
else
|
||||
adapter?.updateDataSet(listOf(ChapterItem(Chapter.createH(), presenter.manga))
|
||||
+ presenter.chapters)
|
||||
else {
|
||||
swipe_refresh.isRefreshing = false
|
||||
adapter?.updateDataSet(
|
||||
listOf(ChapterItem(Chapter.createH(), presenter.manga)) + presenter.chapters
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -333,7 +307,62 @@ class MangaChaptersController : BaseController,
|
||||
//}
|
||||
}
|
||||
|
||||
fun openChapter(chapter: Chapter, hasAnimation: Boolean = false) {
|
||||
override fun onItemLongClick(position: Int) {
|
||||
val adapter = adapter ?: return
|
||||
val item = adapter.getItem(position) ?: return
|
||||
val itemView = getHolder(item)?.itemView ?: return
|
||||
val popup = PopupMenu(itemView.context, itemView, Gravity.END)
|
||||
|
||||
// Inflate our menu resource into the PopupMenu's Menu
|
||||
popup.menuInflater.inflate(R.menu.chapters_mat_single, popup.menu)
|
||||
|
||||
// Hide bookmark if bookmark
|
||||
popup.menu.findItem(R.id.action_bookmark).isVisible = !item.bookmark
|
||||
popup.menu.findItem(R.id.action_remove_bookmark).isVisible = item.bookmark
|
||||
|
||||
// Hide mark as unread when the chapter is unread
|
||||
if (!item.read && item.last_page_read == 0) {
|
||||
popup.menu.findItem(R.id.action_mark_as_unread).isVisible = false
|
||||
}
|
||||
|
||||
// Hide mark as read when the chapter is read
|
||||
if (item.read) {
|
||||
popup.menu.findItem(R.id.action_mark_as_read).isVisible = false
|
||||
}
|
||||
|
||||
// Set a listener so we are notified if a menu item is clicked
|
||||
popup.setOnMenuItemClickListener { menuItem ->
|
||||
val chapters = listOf(item)
|
||||
when (menuItem.itemId) {
|
||||
R.id.action_bookmark -> bookmarkChapters(chapters, true)
|
||||
R.id.action_remove_bookmark -> bookmarkChapters(chapters, false)
|
||||
R.id.action_mark_as_read -> markAsRead(chapters)
|
||||
R.id.action_mark_as_unread -> markAsUnread(chapters)
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
// Finally show the PopupMenu
|
||||
popup.show()
|
||||
}
|
||||
|
||||
private fun bookmarkChapters(chapters: List<ChapterItem>, bookmarked: Boolean) {
|
||||
//destroyActionModeIfNeeded()
|
||||
presenter.bookmarkChapters(chapters, bookmarked)
|
||||
}
|
||||
|
||||
private fun markAsRead(chapters: List<ChapterItem>) {
|
||||
presenter.markChaptersRead(chapters, true)
|
||||
if (presenter.preferences.removeAfterMarkedAsRead()) {
|
||||
presenter.deleteChapters(chapters)
|
||||
}
|
||||
}
|
||||
|
||||
private fun markAsUnread(chapters: List<ChapterItem>) {
|
||||
presenter.markChaptersRead(chapters, false)
|
||||
}
|
||||
|
||||
private fun openChapter(chapter: Chapter, hasAnimation: Boolean = false) {
|
||||
val activity = activity ?: return
|
||||
val intent = ReaderActivity.newIntent(activity, manga!!, chapter)
|
||||
if (hasAnimation) {
|
||||
@ -342,13 +371,10 @@ class MangaChaptersController : BaseController,
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
fun getStatusBarHeight(): Int {
|
||||
var result = 0
|
||||
val resourceId = resources!!.getIdentifier("status_bar_height", "dimen", "android")
|
||||
if (resourceId > 0) {
|
||||
result = resources!!.getDimensionPixelSize(resourceId)
|
||||
}
|
||||
return result
|
||||
override fun onDestroyView(view: View) {
|
||||
snack?.dismiss()
|
||||
presenter.onDestroy()
|
||||
super.onDestroyView(view)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
@ -387,8 +413,6 @@ class MangaChaptersController : BaseController,
|
||||
override fun topCoverHeight(): Int = headerHeight
|
||||
|
||||
override fun nextChapter(): Chapter? = presenter.getNextUnreadChapter()
|
||||
override fun newestChapterDate(): Long? = presenter.getNewestChapterTime()
|
||||
override fun lastChapter(): Float? = presenter.getLatestChapter()
|
||||
override fun mangaSource(): Source = presenter.source
|
||||
|
||||
override fun readNextChapter() {
|
||||
@ -421,4 +445,100 @@ class MangaChaptersController : BaseController,
|
||||
}
|
||||
else presenter.downloadChapters(listOf(chapter))
|
||||
}
|
||||
|
||||
override fun tagClicked(text: String) {
|
||||
val firstController = router.backstack.first()?.controller()
|
||||
if (firstController is LibraryController && router.backstack.size == 2) {
|
||||
router.handleBack()
|
||||
firstController.search(text)
|
||||
}
|
||||
}
|
||||
|
||||
override fun showChapterFilter() {
|
||||
ChaptersSortBottomSheet(this).show()
|
||||
}
|
||||
|
||||
override fun chapterCount():Int = presenter.chapters.size
|
||||
|
||||
override fun favoriteManga(longPress: Boolean) {
|
||||
val manga = presenter.manga
|
||||
if (longPress) {
|
||||
if (!manga.favorite) {
|
||||
presenter.toggleFavorite()
|
||||
showAddedSnack()
|
||||
}
|
||||
val categories = presenter.getCategories()
|
||||
if (categories.isEmpty()) {
|
||||
// no categories exist, display a message about adding categories
|
||||
snack = view?.snack(R.string.action_add_category)
|
||||
} else {
|
||||
val ids = presenter.getMangaCategoryIds(manga)
|
||||
val preselected = ids.mapNotNull { id ->
|
||||
categories.indexOfFirst { it.id == id }.takeIf { it != -1 }
|
||||
}.toTypedArray()
|
||||
|
||||
ChangeMangaCategoriesDialog(this, listOf(manga), categories, preselected)
|
||||
.showDialog(router)
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (presenter.toggleFavorite()) {
|
||||
val categories = presenter.getCategories()
|
||||
val defaultCategoryId = presenter.preferences.defaultCategory()
|
||||
val defaultCategory = categories.find { it.id == defaultCategoryId }
|
||||
when {
|
||||
defaultCategory != null -> presenter.moveMangaToCategory(manga, defaultCategory)
|
||||
defaultCategoryId == 0 || categories.isEmpty() -> // 'Default' or no category
|
||||
presenter.moveMangaToCategory(manga, null)
|
||||
else -> {
|
||||
val ids = presenter.getMangaCategoryIds(manga)
|
||||
val preselected = ids.mapNotNull { id ->
|
||||
categories.indexOfFirst { it.id == id }.takeIf { it != -1 }
|
||||
}.toTypedArray()
|
||||
|
||||
ChangeMangaCategoriesDialog(
|
||||
this,
|
||||
listOf(manga),
|
||||
categories,
|
||||
preselected
|
||||
).showDialog(router)
|
||||
}
|
||||
}
|
||||
showAddedSnack()
|
||||
} else {
|
||||
showRemovedSnack()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showAddedSnack() {
|
||||
val view = view ?: return
|
||||
snack?.dismiss()
|
||||
snack = view.snack(view.context.getString(R.string.manga_added_library))
|
||||
}
|
||||
|
||||
private fun showRemovedSnack() {
|
||||
val view = view ?: return
|
||||
snack?.dismiss()
|
||||
snack = view.snack(
|
||||
view.context.getString(R.string.manga_removed_library),
|
||||
Snackbar.LENGTH_INDEFINITE
|
||||
) {
|
||||
setAction(R.string.action_undo) {
|
||||
presenter.setFavorite(true)
|
||||
}
|
||||
addCallback(object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
|
||||
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
|
||||
super.onDismissed(transientBottomBar, event)
|
||||
if (!presenter.manga.favorite) presenter.confirmDeletion()
|
||||
}
|
||||
})
|
||||
}
|
||||
(activity as? MainActivity)?.setUndoSnackBar(snack, fab_favorite)
|
||||
}
|
||||
|
||||
override fun updateCategoriesForMangas(mangas: List<Manga>, categories: List<Category>) {
|
||||
val manga = mangas.firstOrNull() ?: return
|
||||
presenter.moveMangaToCategories(manga, categories)
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.ui.manga
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.text.format.DateUtils
|
||||
import android.view.View
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
@ -17,10 +16,11 @@ import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.ui.manga.chapter.ChapterItem
|
||||
import eu.kanade.tachiyomi.ui.manga.chapter.ChaptersAdapter
|
||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
import eu.kanade.tachiyomi.util.view.gone
|
||||
import eu.kanade.tachiyomi.util.view.updateLayoutParams
|
||||
import eu.kanade.tachiyomi.util.view.visible
|
||||
import eu.kanade.tachiyomi.util.view.visibleIf
|
||||
import kotlinx.android.synthetic.main.manga_header_item.*
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
class MangaHeaderHolder(
|
||||
@ -33,17 +33,60 @@ class MangaHeaderHolder(
|
||||
top_view.updateLayoutParams<ConstraintLayout.LayoutParams> {
|
||||
topMargin = adapter.coverListener?.topCoverHeight() ?: 0
|
||||
}
|
||||
more_button.setOnClickListener { expandDesc() }
|
||||
manga_summary.setOnClickListener { expandDesc() }
|
||||
less_button.setOnClickListener {
|
||||
manga_summary.maxLines = 3
|
||||
manga_genres_tags.gone()
|
||||
less_button.gone()
|
||||
more_button_group.visible()
|
||||
}
|
||||
manga_genres_tags.setOnTagClickListener {
|
||||
adapter.coverListener?.tagClicked(it)
|
||||
}
|
||||
filter_button.setOnClickListener {
|
||||
adapter.coverListener?.showChapterFilter()
|
||||
}
|
||||
favorite_button.setOnClickListener {
|
||||
adapter.coverListener?.favoriteManga(false)
|
||||
}
|
||||
favorite_button.setOnLongClickListener {
|
||||
adapter.coverListener?.favoriteManga(true)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private fun expandDesc() {
|
||||
if (more_button.visibility == View.VISIBLE) {
|
||||
manga_summary.maxLines = Integer.MAX_VALUE
|
||||
manga_genres_tags.visible()
|
||||
less_button.visible()
|
||||
more_button_group.gone()
|
||||
}
|
||||
}
|
||||
|
||||
override fun bind(item: ChapterItem, manga: Manga) {
|
||||
manga_full_title.text = manga.currentTitle()
|
||||
|
||||
if (manga.currentGenres().isNullOrBlank().not())
|
||||
manga_genres_tags.setTags(manga.currentGenres()?.split(", ")?.map(String::trim))
|
||||
else
|
||||
manga_genres_tags.setTags(emptyList())
|
||||
|
||||
if (manga.currentAuthor() == manga.currentArtist() ||
|
||||
manga.currentArtist().isNullOrBlank())
|
||||
manga_author.text = manga.currentAuthor()
|
||||
else {
|
||||
manga_author.text = "${manga.currentAuthor()?.trim()}, ${manga.currentArtist()}"
|
||||
}
|
||||
manga_summary.text = manga.currentDesc()
|
||||
manga_summary.text = manga.currentDesc() ?: itemView.context.getString(R.string
|
||||
.no_description)
|
||||
|
||||
manga_summary.post {
|
||||
if (manga_summary.lineCount < 3 && manga.currentGenres().isNullOrBlank()) {
|
||||
more_button_group.gone()
|
||||
}
|
||||
}
|
||||
manga_summary_label.text = itemView.context.getString(R.string.about_this,
|
||||
itemView.context.getString(
|
||||
when {
|
||||
@ -77,6 +120,10 @@ class MangaHeaderHolder(
|
||||
context.getResourceColor(R.attr.colorAccent), 75))
|
||||
strokeColor = ColorStateList.valueOf(Color.TRANSPARENT)
|
||||
}
|
||||
else strokeColor = ColorStateList.valueOf(
|
||||
ColorUtils.setAlphaComponent(
|
||||
itemView.context.getResourceColor(R.attr
|
||||
.colorOnSurface), 31))
|
||||
}
|
||||
true_backdrop.setBackgroundColor(adapter.coverListener?.coverColor() ?:
|
||||
itemView.context.getResourceColor(android.R.attr.colorBackground))
|
||||
@ -98,31 +145,20 @@ class MangaHeaderHolder(
|
||||
}
|
||||
}
|
||||
|
||||
val count = adapter.coverListener?.chapterCount() ?: 0
|
||||
chapters_title.text = itemView.resources.getQuantityString(R.plurals.chapters, count, count)
|
||||
|
||||
top_view.updateLayoutParams<ConstraintLayout.LayoutParams> {
|
||||
topMargin = adapter.coverListener?.topCoverHeight() ?: 0
|
||||
}
|
||||
val lastUpdated = adapter.coverListener?.newestChapterDate()
|
||||
if (lastUpdated != null) {
|
||||
manga_last_update.text = itemView.context.getString(
|
||||
R.string.last_updated, DateUtils.getRelativeTimeSpanString(
|
||||
lastUpdated, Date().time, DateUtils.HOUR_IN_MILLIS
|
||||
).toString()
|
||||
)
|
||||
}
|
||||
else {
|
||||
manga_last_update.text = itemView.context.getString(R.string.last_update_unknown)
|
||||
}
|
||||
|
||||
val sourceAndStatus = mutableListOf<String>()
|
||||
sourceAndStatus.add(itemView.context.getString( when (manga.status) {
|
||||
manga_status.text = (itemView.context.getString( when (manga.status) {
|
||||
SManga.ONGOING -> R.string.ongoing
|
||||
SManga.COMPLETED -> R.string.completed
|
||||
SManga.LICENSED -> R.string.licensed
|
||||
else -> R.string.unknown_status
|
||||
}))
|
||||
val sourceName = adapter.coverListener?.mangaSource()?.toString()
|
||||
if (sourceName != null) sourceAndStatus.add(sourceName)
|
||||
manga_status_source.text = sourceAndStatus.joinToString(" • ")
|
||||
manga_source.text = adapter.coverListener?.mangaSource()?.toString()
|
||||
|
||||
GlideApp.with(view.context).load(manga)
|
||||
.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
|
||||
|
@ -1,46 +1,65 @@
|
||||
package eu.kanade.tachiyomi.ui.manga
|
||||
|
||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaCategory
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaImpl
|
||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||
import eu.kanade.tachiyomi.data.download.model.Download
|
||||
import eu.kanade.tachiyomi.data.download.model.DownloadQueue
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.source.LocalSource
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.ui.manga.chapter.ChapterItem
|
||||
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
|
||||
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.util.Date
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class MangaPresenter(private val controller: MangaChaptersController,
|
||||
val manga: Manga,
|
||||
val source: Source,
|
||||
val preferences: PreferencesHelper = Injekt.get(),
|
||||
private val coverCache: CoverCache = Injekt.get(),
|
||||
private val db: DatabaseHelper = Injekt.get(),
|
||||
private val downloadManager: DownloadManager = Injekt.get()) {
|
||||
private val downloadManager: DownloadManager = Injekt.get()):
|
||||
CoroutineScope,
|
||||
DownloadQueue.DownloadListener {
|
||||
|
||||
override var coroutineContext:CoroutineContext = Job() + Dispatchers.Default
|
||||
|
||||
var isLockedFromSearch = false
|
||||
var hasRequested = false
|
||||
|
||||
var chapters:List<ChapterItem> = emptyList()
|
||||
private set
|
||||
|
||||
fun onCreate() {
|
||||
isLockedFromSearch = SecureActivityDelegate.shouldBeLocked()
|
||||
downloadManager.addListener(this)
|
||||
if (!manga.initialized)
|
||||
fetchMangaFromSource()
|
||||
updateChapters()
|
||||
controller.updateChapters(this.chapters)
|
||||
}
|
||||
|
||||
fun onDestroy() {
|
||||
downloadManager.removeListener(this)
|
||||
}
|
||||
|
||||
fun fetchMangaFromSource() {
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
withContext(Dispatchers.Main) {
|
||||
@ -66,6 +85,24 @@ class MangaPresenter(private val controller: MangaChaptersController,
|
||||
}
|
||||
}
|
||||
|
||||
fun fetchChapters() {
|
||||
launch {
|
||||
getChapters()
|
||||
withContext(Dispatchers.Main) { controller.updateChapters(chapters) }
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getChapters() {
|
||||
val chapters = withContext(Dispatchers.IO) {
|
||||
db.getChapters(manga).executeAsBlocking().map { it.toModel() }
|
||||
}
|
||||
// Store the last emission
|
||||
this.chapters = applyChapterFilters(chapters)
|
||||
|
||||
// Find downloaded chapters
|
||||
setDownloadedChapters(chapters)
|
||||
}
|
||||
|
||||
private fun updateChapters(fetchedChapters: List<Chapter>? = null) {
|
||||
val chapters = (fetchedChapters ?:
|
||||
db.getChapters(manga).executeAsBlocking()).map { it.toModel() }
|
||||
@ -75,17 +112,6 @@ class MangaPresenter(private val controller: MangaChaptersController,
|
||||
|
||||
// Find downloaded chapters
|
||||
setDownloadedChapters(chapters)
|
||||
|
||||
/*
|
||||
// Emit the number of chapters to the info tab.
|
||||
chapterCountRelay.call(chapters.maxBy { it.chapter_number }?.chapter_number
|
||||
?: 0f)
|
||||
|
||||
// Emit the upload date of the most recent chapter
|
||||
lastUpdateRelay.call(
|
||||
Date(chapters.maxBy { it.date_upload }?.date_upload
|
||||
?: 0)
|
||||
)*/
|
||||
}
|
||||
|
||||
/**
|
||||
@ -100,6 +126,14 @@ class MangaPresenter(private val controller: MangaChaptersController,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateDownload(download: Download) {
|
||||
chapters.find { it.id == download.chapter.id }?.download = download
|
||||
launch(Dispatchers.Main) {
|
||||
controller.updateChapterDownload(download)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a chapter from the database to an extended model, allowing to store new fields.
|
||||
*/
|
||||
@ -237,9 +271,11 @@ class MangaPresenter(private val controller: MangaChaptersController,
|
||||
* @param chapters the list of chapters to delete.
|
||||
*/
|
||||
fun deleteChapters(chapters: List<ChapterItem>) {
|
||||
deleteChaptersInternal(chapters)
|
||||
deleteChaptersInternal(chapters)
|
||||
|
||||
setDownloadedChapters(chapters)
|
||||
chapters.forEach { chapter ->
|
||||
this.chapters.find { it.id == chapter.id }?.download?.status = Download.NOT_DOWNLOADED
|
||||
}
|
||||
|
||||
controller.updateChapters(this.chapters)
|
||||
// if (onlyDownloaded()) refreshChapters() }
|
||||
@ -258,8 +294,46 @@ class MangaPresenter(private val controller: MangaChaptersController,
|
||||
}
|
||||
|
||||
fun refreshAll() {
|
||||
fetchMangaFromSource()
|
||||
fetchChaptersFromSource()
|
||||
launch {
|
||||
var mangaError: java.lang.Exception? = null
|
||||
var chapterError: java.lang.Exception? = null
|
||||
val chapters = async(Dispatchers.IO) {
|
||||
try {
|
||||
source.fetchChapterList(manga).toBlocking().single()
|
||||
|
||||
} catch (e: Exception) {
|
||||
chapterError = e
|
||||
emptyList<SChapter>()
|
||||
} ?: emptyList()
|
||||
}
|
||||
val thumbnailUrl = manga.thumbnail_url
|
||||
val nManga = async(Dispatchers.IO) {
|
||||
try {
|
||||
source.fetchMangaDetails(manga).toBlocking().single()
|
||||
} catch (e: java.lang.Exception) {
|
||||
mangaError = e
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
val networkManga = nManga.await()
|
||||
if (networkManga != null) {
|
||||
manga.copyFrom(networkManga)
|
||||
manga.initialized = true
|
||||
db.insertManga(manga).executeAsBlocking()
|
||||
if (thumbnailUrl != networkManga.thumbnail_url)
|
||||
MangaImpl.setLastCoverFetch(manga.id!!, Date().time)
|
||||
}
|
||||
val finChapters = chapters.await()
|
||||
if (finChapters.isNotEmpty()) {
|
||||
syncChaptersWithSource(db, finChapters, manga, source)
|
||||
withContext(Dispatchers.IO) { updateChapters() }
|
||||
}
|
||||
if (chapterError == null)
|
||||
withContext(Dispatchers.Main) { controller.updateChapters(this@MangaPresenter.chapters) }
|
||||
if (mangaError != null)
|
||||
withContext(Dispatchers.Main) { controller.showError(trimException(mangaError!!)) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -268,12 +342,12 @@ class MangaPresenter(private val controller: MangaChaptersController,
|
||||
fun fetchChaptersFromSource() {
|
||||
hasRequested = true
|
||||
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
launch(Dispatchers.IO) {
|
||||
val chapters = try {
|
||||
source.fetchChapterList(manga).toBlocking().single()
|
||||
}
|
||||
catch(e: Exception) {
|
||||
controller.showError(trimException(e))
|
||||
withContext(Dispatchers.Main) { controller.showError(trimException(e)) }
|
||||
return@launch
|
||||
} ?: listOf()
|
||||
try {
|
||||
@ -291,4 +365,108 @@ class MangaPresenter(private val controller: MangaChaptersController,
|
||||
private fun trimException(e: java.lang.Exception): String {
|
||||
return e.message?.split(": ")?.drop(1)?.joinToString(": ") ?: "Error"
|
||||
}
|
||||
|
||||
/**
|
||||
* Bookmarks the given list of chapters.
|
||||
* @param selectedChapters the list of chapters to bookmark.
|
||||
*/
|
||||
fun bookmarkChapters(selectedChapters: List<ChapterItem>, bookmarked: Boolean) {
|
||||
launch(Dispatchers.IO) {
|
||||
selectedChapters.forEach {
|
||||
it.bookmark = bookmarked
|
||||
}
|
||||
db.updateChaptersProgress(selectedChapters).executeAsBlocking()
|
||||
withContext(Dispatchers.Main) { controller.updateChapters(chapters) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the selected chapter list as read/unread.
|
||||
* @param selectedChapters the list of selected chapters.
|
||||
* @param read whether to mark chapters as read or unread.
|
||||
*/
|
||||
fun markChaptersRead(selectedChapters: List<ChapterItem>, read: Boolean) {
|
||||
launch(Dispatchers.IO) {
|
||||
selectedChapters.forEach {
|
||||
it.read = read
|
||||
if (!read) {
|
||||
it.last_page_read = 0
|
||||
it.pages_left = 0
|
||||
}
|
||||
}
|
||||
db.updateChaptersProgress(selectedChapters).executeAsBlocking()
|
||||
withContext(Dispatchers.Main) { controller.updateChapters(chapters) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverses the sorting and requests an UI update.
|
||||
*/
|
||||
fun setSortOrder(desend: Boolean) {
|
||||
manga.setChapterOrder(if (desend) Manga.SORT_ASC else Manga.SORT_DESC)
|
||||
db.updateFlags(manga).executeAsBlocking()
|
||||
updateChapters()
|
||||
controller.updateChapters(chapters)
|
||||
}
|
||||
|
||||
fun toggleFavorite(): Boolean {
|
||||
manga.favorite = !manga.favorite
|
||||
db.insertManga(manga).executeAsBlocking()
|
||||
controller.updateHeader()
|
||||
return manga.favorite
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user categories.
|
||||
*
|
||||
* @return List of categories, not including the default category
|
||||
*/
|
||||
fun getCategories(): List<Category> {
|
||||
return db.getCategories().executeAsBlocking()
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the given manga to the category.
|
||||
*
|
||||
* @param manga the manga to move.
|
||||
* @param category the selected category, or null for default category.
|
||||
*/
|
||||
fun moveMangaToCategory(manga: Manga, category: Category?) {
|
||||
moveMangaToCategories(manga, listOfNotNull(category))
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the given manga to categories.
|
||||
*
|
||||
* @param manga the manga to move.
|
||||
* @param categories the selected categories.
|
||||
*/
|
||||
fun moveMangaToCategories(manga: Manga, categories: List<Category>) {
|
||||
val mc = categories.filter { it.id != 0 }.map { MangaCategory.create(manga, it) }
|
||||
db.setMangaCategories(mc, listOf(manga))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the category id's the manga is in, if the manga is not in a category, returns the default id.
|
||||
*
|
||||
* @param manga the manga to get categories from.
|
||||
* @return Array of category ids the manga is in, if none returns default id
|
||||
*/
|
||||
fun getMangaCategoryIds(manga: Manga): Array<Int> {
|
||||
val categories = db.getCategoriesForManga(manga).executeAsBlocking()
|
||||
return categories.mapNotNull { it.id }.toTypedArray()
|
||||
}
|
||||
|
||||
fun confirmDeletion() {
|
||||
coverCache.deleteFromCache(manga.thumbnail_url)
|
||||
db.resetMangaInfo(manga).executeAsBlocking()
|
||||
downloadManager.deleteManga(manga, source)
|
||||
}
|
||||
|
||||
fun setFavorite(favorite: Boolean) {
|
||||
if (manga.favorite == favorite) {
|
||||
return
|
||||
}
|
||||
toggleFavorite()
|
||||
}
|
||||
}
|
@ -29,7 +29,9 @@ class ChapterMatHolder(
|
||||
|
||||
private fun downloadOrRemoveMenu() {
|
||||
val chapter = adapter.getItem(adapterPosition) ?: return
|
||||
if (chapter.status != Download.NOT_DOWNLOADED) {
|
||||
if (chapter.status == Download.NOT_DOWNLOADED || chapter.status == Download.ERROR) {
|
||||
adapter.coverListener?.downloadChapter(adapterPosition)
|
||||
} else {
|
||||
download_button.post {
|
||||
// Create a PopupMenu, giving it the clicked view for an anchor
|
||||
val popup = PopupMenu(download_button.context, download_button)
|
||||
@ -38,8 +40,7 @@ class ChapterMatHolder(
|
||||
popup.menuInflater.inflate(R.menu.chapter_download, popup.menu)
|
||||
|
||||
// Hide download and show delete if the chapter is downloaded
|
||||
if (chapter.status != Download.DOWNLOADED) popup.menu.findItem(R.id.action_delete)
|
||||
.title = download_button.context.getString(
|
||||
if (chapter.status != Download.DOWNLOADED) popup.menu.findItem(R.id.action_delete).title = download_button.context.getString(
|
||||
R.string.action_cancel
|
||||
)
|
||||
|
||||
@ -53,9 +54,6 @@ class ChapterMatHolder(
|
||||
popup.show()
|
||||
}
|
||||
}
|
||||
else {
|
||||
adapter.coverListener?.downloadChapter(adapterPosition)
|
||||
}
|
||||
}
|
||||
|
||||
override fun bind(item: ChapterItem, manga: Manga) {
|
||||
@ -113,6 +111,6 @@ class ChapterMatHolder(
|
||||
return
|
||||
}
|
||||
visible()
|
||||
setDownoadStatus(status, progress)
|
||||
setDownloadStatus(status, progress)
|
||||
}
|
||||
}
|
||||
|
@ -64,8 +64,10 @@ class ChaptersAdapter(
|
||||
fun readNextChapter()
|
||||
fun downloadChapter(position: Int)
|
||||
fun topCoverHeight(): Int
|
||||
fun newestChapterDate(): Long?
|
||||
fun lastChapter(): Float?
|
||||
fun chapterCount(): Int
|
||||
fun tagClicked(text: String)
|
||||
fun mangaSource(): Source
|
||||
fun showChapterFilter()
|
||||
fun favoriteManga(longPress: Boolean)
|
||||
}
|
||||
}
|
||||
|
@ -386,9 +386,9 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
|
||||
|
||||
fun setLastUpdateDate(date: Date) {
|
||||
if (date.time != 0L) {
|
||||
manga_last_update?.text = dateFormat.format(date)
|
||||
manga_status?.text = dateFormat.format(date)
|
||||
} else {
|
||||
manga_last_update?.text = resources?.getString(R.string.unknown)
|
||||
manga_status?.text = resources?.getString(R.string.unknown)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,7 +150,7 @@
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/manga_last_update"
|
||||
android:id="@+id/manga_status"
|
||||
style="@style/TextAppearance.Medium.Body2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@ -160,7 +160,7 @@
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/manga_last_update"
|
||||
android:id="@+id/manga_status"
|
||||
style="@style/TextAppearance.Regular.Body1.Secondary"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
@ -168,8 +168,8 @@
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textIsSelectable="false"
|
||||
app:layout_constraintBaseline_toBaselineOf="@+id/manga_last_update"
|
||||
app:layout_constraintStart_toEndOf="@+id/manga_last_update"
|
||||
app:layout_constraintBaseline_toBaselineOf="@+id/manga_status"
|
||||
app:layout_constraintStart_toEndOf="@+id/manga_status"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<TextView
|
||||
@ -179,7 +179,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/manga_info_status_label"
|
||||
android:textIsSelectable="false"
|
||||
app:layout_constraintTop_toBottomOf="@+id/manga_last_update"
|
||||
app:layout_constraintTop_toBottomOf="@+id/manga_status"
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
|
||||
<TextView
|
||||
|
135
app/src/main/res/layout/chapter_sort_bottom_sheet.xml
Normal file
135
app/src/main/res/layout/chapter_sort_bottom_sheet.xml
Normal file
@ -0,0 +1,135 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/diplay_bottom_sheet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/settings_scroll_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/bottom_sheet"
|
||||
style="@style/BottomSheetDialogTheme"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_bottom_sheet_dialog_fragment"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="12dp"
|
||||
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
style="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:text="@string/action_sort" />
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/sort_group"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp">
|
||||
|
||||
<com.google.android.material.radiobutton.MaterialRadioButton
|
||||
android:id="@+id/sort_newest"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Newest first" />
|
||||
|
||||
<com.google.android.material.radiobutton.MaterialRadioButton
|
||||
android:id="@+id/sort_oldest"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:text="Oldest first" />
|
||||
</RadioGroup>
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/sort_by_source"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:text="Sort by source's order" />
|
||||
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/show_titles"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:text="Hide chapter titles" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
style="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:text="@string/action_filter" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/show_all"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:text="Show All" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/show_read"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:text="Show read chapters" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/show_unread"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:text="Show unread chapters" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/show_download"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:text="Show downloaded chapters" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/show_bookmark"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:text="Show bookmarked chapters" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/close_button"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:background="@drawable/round_ripple"
|
||||
android:clickable="true"
|
||||
android:contentDescription="@string/action_close"
|
||||
android:focusable="true"
|
||||
android:src="@drawable/ic_close_white_24dp"
|
||||
android:tint="@color/gray_button" />
|
||||
</FrameLayout>
|
@ -28,6 +28,7 @@
|
||||
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"
|
||||
|
@ -25,11 +25,11 @@
|
||||
android:id="@+id/true_backdrop"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintHeight_min="200dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/bottom_line"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHeight_min="200dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="@id/bottom_line"
|
||||
app:layout_constraintVertical_bias="0.0"
|
||||
tools:background="@color/material_red_400" />
|
||||
|
||||
@ -39,10 +39,10 @@
|
||||
android:layout_height="0dp"
|
||||
android:alpha="0.1"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/true_backdrop"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/true_backdrop"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/true_backdrop"
|
||||
tools:src="@mipmap/ic_launcher" />
|
||||
|
||||
<View
|
||||
@ -76,9 +76,9 @@
|
||||
android:id="@+id/manga_layout"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_marginStart="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/guideline"
|
||||
app:layout_constraintDimensionRatio="h,7:10"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
@ -89,13 +89,13 @@
|
||||
android:id="@+id/cover_card"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/guideline"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/true_backdrop"
|
||||
app:layout_constraintEnd_toEndOf="@id/manga_layout"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/top_view"
|
||||
app:layout_constraintTop_toTopOf="@id/true_backdrop"
|
||||
app:layout_constraintVertical_bias="1.0">
|
||||
|
||||
<ImageView
|
||||
@ -112,7 +112,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/manga_full_title"
|
||||
style="@style/TextAppearance.MaterialComponents.Headline5"
|
||||
style="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
@ -129,9 +129,10 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/manga_author"
|
||||
style="@style/TextAppearance.Regular.Body1.Secondary"
|
||||
android:textAppearance="@style/TextAppearance.Regular.Body1.SemiBold"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="@string/manga_info_author_label"
|
||||
@ -141,19 +142,19 @@
|
||||
app:layout_constraintTop_toBottomOf="@+id/manga_full_title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/manga_last_update"
|
||||
android:id="@+id/manga_status"
|
||||
style="@style/TextAppearance.Regular.Body1.Secondary"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="@string/manga_info_latest_data_label"
|
||||
tools:text="Last updated 3 days ago"
|
||||
android:textIsSelectable="false"
|
||||
app:layout_constraintStart_toStartOf="@id/manga_full_title"
|
||||
app:layout_constraintTop_toBottomOf="@+id/manga_author" />
|
||||
app:layout_constraintTop_toBottomOf="@+id/manga_author"
|
||||
tools:text="Completed" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/manga_status_source"
|
||||
android:id="@+id/manga_source"
|
||||
style="@style/TextAppearance.Regular.Body1.Secondary"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
@ -162,8 +163,8 @@
|
||||
android:textIsSelectable="false"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@id/manga_full_title"
|
||||
app:layout_constraintTop_toBottomOf="@id/manga_last_update"
|
||||
tools:text="Completed • Mangadex (EN)" />
|
||||
app:layout_constraintTop_toBottomOf="@id/manga_status"
|
||||
tools:text="Mangadex (EN)" />
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/bottom_line"
|
||||
@ -172,7 +173,7 @@
|
||||
android:layout_margin="6dp"
|
||||
android:orientation="horizontal"
|
||||
app:barrierDirection="bottom"
|
||||
app:constraint_referenced_ids="manga_status_source,manga_layout,cover_card" />
|
||||
app:constraint_referenced_ids="manga_source,manga_layout,cover_card" />
|
||||
|
||||
|
||||
<LinearLayout
|
||||
@ -181,26 +182,27 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="14dp"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintBottom_toTopOf="@id/manga_summary_label"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/bottom_line">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
style="@style/Theme.Widget.Button.RounededOutline"
|
||||
android:id="@+id/favorite_button"
|
||||
style="@style/Theme.Widget.Button.RounededOutline"
|
||||
android:text="@string/add_to_library"
|
||||
app:icon="@drawable/ic_add_to_library_24dp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/track_button"
|
||||
style="@style/Theme.Widget.Button.RounededOutline"
|
||||
android:layout_marginStart="6dp"
|
||||
android:id="@+id/track_button"
|
||||
android:text="@string/manga_tracking_tab"
|
||||
app:icon="@drawable/ic_sync_black_24dp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/download_button"
|
||||
android:id="@+id/edit_button"
|
||||
style="@style/Theme.Widget.CustomImageButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@ -209,8 +211,7 @@
|
||||
android:layout_marginEnd="6dp"
|
||||
android:padding="5dp"
|
||||
android:src="@drawable/ic_edit_white_24dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/chapters_title"
|
||||
app:layout_constraintEnd_toStartOf="@id/sort_button" />
|
||||
app:layout_constraintBottom_toBottomOf="@id/chapters_title" />
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
@ -235,6 +236,8 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:maxLines="3"
|
||||
android:textIsSelectable="false"
|
||||
@ -245,7 +248,8 @@
|
||||
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." />
|
||||
|
||||
<View
|
||||
android:layout_width="75dp"
|
||||
android:id="@+id/more_bg_gradient"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginEnd="30dp"
|
||||
android:background="@drawable/full_gradient"
|
||||
@ -254,26 +258,39 @@
|
||||
app:layout_constraintEnd_toEndOf="@id/more_button" />
|
||||
|
||||
<View
|
||||
android:id="@+id/more_bg_solid"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginStart="45dp"
|
||||
android:background="?android:attr/colorBackground"
|
||||
app:layout_constraintBottom_toBottomOf="@id/manga_summary"
|
||||
app:layout_constraintEnd_toEndOf="@id/more_button"
|
||||
app:layout_constraintStart_toStartOf="@id/more_button" />
|
||||
|
||||
<View
|
||||
android:id="@+id/more_guide"
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="15sp"
|
||||
app:layout_constraintEnd_toEndOf="@id/manga_summary"
|
||||
app:layout_constraintTop_toBottomOf="@id/manga_summary"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/more_button"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
style="@style/Theme.Widget.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="19.5sp"
|
||||
android:text="More"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="?colorAccent"
|
||||
app:layout_constraintEnd_toEndOf="@id/manga_summary"
|
||||
app:layout_constraintTop_toTopOf="@id/manga_summary"
|
||||
app:rippleColor="@color/gray_button" />
|
||||
android:text="@string/more"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:textAlignment="textEnd"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="@id/more_guide"
|
||||
app:rippleColor="@null" />
|
||||
|
||||
<androidx.constraintlayout.widget.Group
|
||||
android:id="@+id/more_button_group"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:constraint_referenced_ids="more_button,more_bg_gradient,more_bg_solid" />
|
||||
|
||||
<me.gujun.android.taggroup.TagGroup
|
||||
android:id="@+id/manga_genres_tags"
|
||||
@ -289,58 +306,74 @@
|
||||
app:atg_borderStrokeWidth="1dp"
|
||||
app:atg_textColor="@color/md_blue_A400"
|
||||
app:layout_constrainedHeight="true"
|
||||
app:layout_constraintBottom_toTopOf="@id/start_reading_button"
|
||||
app:layout_constraintBottom_toTopOf="@id/less_button"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/manga_summary" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/less_button"
|
||||
style="@style/Theme.Widget.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/less"
|
||||
android:visibility="gone"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:textAlignment="textEnd"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/start_reading_button"
|
||||
app:layout_constraintTop_toBottomOf="@id/manga_genres_tags"
|
||||
app:rippleColor="@null" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/start_reading_button"
|
||||
style="@style/Theme.Widget.Button.Primary"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@string/start_reading"
|
||||
app:layout_constraintBottom_toTopOf="@id/chapters_title"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/manga_genres_tags"
|
||||
app:layout_constraintTop_toBottomOf="@+id/less_button"
|
||||
tools:text="Continue Reading Chapter 17.1" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/chapters_title"
|
||||
style="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="18dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:text="@string/chapters"
|
||||
android:textSize="17sp"
|
||||
android:layout_marginBottom="12dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@id/manga_summary_label"
|
||||
app:layout_constraintTop_toBottomOf="@id/start_reading_button" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/sort_button"
|
||||
style="@style/Theme.Widget.CustomImageButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:padding="5dp"
|
||||
android:src="@drawable/ic_swap_vert_white_24dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/chapters_title"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
app:layout_constraintTop_toBottomOf="@id/start_reading_button"
|
||||
app:layout_constraintEnd_toStartOf="@id/filters_text"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/filter_button"
|
||||
style="@style/Theme.Widget.CustomImageButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:padding="5dp"
|
||||
android:src="@drawable/ic_filter_list_white_24dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/chapters_title"
|
||||
app:layout_constraintEnd_toStartOf="@id/sort_button" />
|
||||
app:layout_constraintTop_toTopOf="@id/chapters_title"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/filters_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:padding="5dp"
|
||||
tools:text="Read"
|
||||
app:layout_constraintTop_toTopOf="@id/filter_button"
|
||||
app:layout_constraintBottom_toBottomOf="@id/filter_button"
|
||||
app:layout_constraintEnd_toStartOf="@id/filter_button" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -172,7 +172,7 @@
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/manga_last_update"
|
||||
android:id="@+id/manga_status"
|
||||
style="@style/TextAppearance.Medium.Body2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@ -182,7 +182,7 @@
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/manga_last_update"
|
||||
android:id="@+id/manga_status"
|
||||
style="@style/TextAppearance.Regular.Body1.Secondary"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
@ -190,8 +190,8 @@
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textIsSelectable="false"
|
||||
app:layout_constraintBaseline_toBaselineOf="@+id/manga_last_update"
|
||||
app:layout_constraintStart_toEndOf="@+id/manga_last_update"
|
||||
app:layout_constraintBaseline_toBaselineOf="@+id/manga_status"
|
||||
app:layout_constraintStart_toEndOf="@+id/manga_status"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
<TextView
|
||||
@ -201,7 +201,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/manga_info_status_label"
|
||||
android:textIsSelectable="false"
|
||||
app:layout_constraintTop_toBottomOf="@+id/manga_last_update"
|
||||
app:layout_constraintTop_toBottomOf="@+id/manga_status"
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
|
||||
<TextView
|
||||
|
22
app/src/main/res/menu/chapters_mat_single.xml
Normal file
22
app/src/main/res/menu/chapters_mat_single.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:id="@+id/action_bookmark"
|
||||
android:title="@string/action_bookmark"
|
||||
android:visible="true" />
|
||||
|
||||
<item android:id="@+id/action_remove_bookmark"
|
||||
android:title="@string/action_remove_bookmark"
|
||||
android:visible="true" />
|
||||
|
||||
<item android:id="@+id/action_mark_as_read"
|
||||
android:title="@string/action_mark_as_read" />
|
||||
|
||||
<item android:id="@+id/action_mark_as_unread"
|
||||
android:title="@string/action_mark_as_unread" />
|
||||
|
||||
<item android:id="@+id/action_mark_multiple"
|
||||
android:title="@string/action_mark_multiple"/>
|
||||
|
||||
</menu>
|
@ -70,6 +70,7 @@
|
||||
<string name="action_mark_as_read">Mark as read</string>
|
||||
<string name="action_mark_as_unread">Mark as unread</string>
|
||||
<string name="action_mark_previous_as_read">Mark previous as read</string>
|
||||
<string name="action_mark_multiple">Mark multiple</string>
|
||||
<string name="action_download">Download</string>
|
||||
<string name="action_bookmark">Bookmark</string>
|
||||
<string name="action_remove_bookmark">Remove bookmark</string>
|
||||
@ -224,7 +225,7 @@
|
||||
<string name="lock_always">Always</string>
|
||||
<string name="lock_never">Never</string>
|
||||
<plurals name="lock_after_mins">
|
||||
<item quantity="one">After %1$s minutes</item>
|
||||
<item quantity="one">After %1$s minute</item>
|
||||
<item quantity="other">After %1$s minutes</item>
|
||||
</plurals>
|
||||
<string name="search_hint">Search title, tags, source</string>
|
||||
@ -500,6 +501,11 @@
|
||||
<string name="copied_to_clipboard">%1$s copied to clipboard</string>
|
||||
<string name="source_not_installed">Source not installed: %1$s</string>
|
||||
<string name="about_this">About this %1$s</string>
|
||||
<plurals name="chapters">
|
||||
<item quantity="one">%1$d chapter</item>
|
||||
<item quantity="other">%1$d chapters</item>
|
||||
</plurals>
|
||||
<string name="no_description">No description</string>
|
||||
|
||||
<!-- Manga chapters fragment -->
|
||||
<string name="start_reading">Start reading</string>
|
||||
@ -697,5 +703,7 @@
|
||||
<string name="reset_tags">Reset Tags</string>
|
||||
<string name="display_as">Display as</string>
|
||||
<string name="action_auto">Auto</string>
|
||||
<string name="more">More</string>
|
||||
<string name="less">Less</string>
|
||||
|
||||
</resources>
|
||||
|
@ -252,6 +252,12 @@
|
||||
<item name="android:letterSpacing">0.0</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Widget.Button.TextButton" parent="Widget.MaterialComponents.Button.TextButton">
|
||||
<item name="android:textAllCaps">false</item>
|
||||
<item name="rippleColor">@color/fullRippleColor</item>
|
||||
<item name="android:textColor">?colorAccent</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Widget.CustomImageButton">
|
||||
<item name="android:background">@drawable/round_ripple</item>
|
||||
<item name="android:clickable">true</item>
|
||||
|
Loading…
Reference in New Issue
Block a user