Added new Manga controller to library

pull/3117/head
Jay 5 years ago
parent 2d66185a02
commit c2b1c3f63f

@ -119,6 +119,7 @@ dependencies {
implementation 'androidx.annotation:annotation:1.1.0'
implementation 'androidx.browser:browser:1.2.0'
implementation 'androidx.biometric:biometric:1.0.1'
implementation 'androidx.palette:palette:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'

@ -0,0 +1,77 @@
package eu.kanade.tachiyomi.ui.download
import android.content.Context
import android.graphics.Color
import android.util.AttributeSet
import android.widget.FrameLayout
import androidx.core.content.ContextCompat
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.view.gone
import eu.kanade.tachiyomi.util.view.visible
import kotlinx.android.synthetic.main.download_button.view.*
class DownloadButton @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null)
: FrameLayout(context, attrs) {
private val activeColor = context.getResourceColor(R.attr.colorAccent)
private val disabledColor = ContextCompat.getColor(context,
R.color.material_on_surface_disabled)
private val downloadedColor = ContextCompat.getColor(context,
R.color.material_green_800)
private val errorColor = ContextCompat.getColor(context,
R.color.red_error)
private val filledCircle = ContextCompat.getDrawable(context,
R.drawable.filled_circle)?.mutate()
private val borderCircle = ContextCompat.getDrawable(context,
R.drawable.border_circle)?.mutate()
fun setDownoadStatus(state: Int, progress: Int = 0) {
when (state) {
Download.NOT_DOWNLOADED -> {
download_border.visible()
download_progress.gone()
download_progress_indeterminate.gone()
download_border.setImageDrawable(borderCircle)
download_border.drawable.setTint(activeColor)
download_icon.drawable.setTint(activeColor)
}
Download.QUEUE -> {
download_border.gone()
download_progress.gone()
download_progress_indeterminate.visible()
download_progress.isIndeterminate = true
download_icon.drawable.setTint(disabledColor)
}
Download.DOWNLOADING -> {
download_border.visible()
download_progress.visible()
download_progress_indeterminate.gone()
download_border.setImageDrawable(borderCircle)
download_progress.isIndeterminate = false
download_progress.progress = progress
download_border.drawable.setTint(disabledColor)
download_progress.progressDrawable?.setTint(downloadedColor)
download_icon.drawable.setTint(disabledColor)
}
Download.DOWNLOADED -> {
download_progress.gone()
download_border.visible()
download_progress_indeterminate.gone()
download_border.setImageDrawable(filledCircle)
download_border.drawable.setTint(downloadedColor)
download_icon.drawable.setTint(Color.WHITE)
}
Download.ERROR -> {
download_progress.gone()
download_border.visible()
download_progress_indeterminate.gone()
download_border.setImageDrawable(borderCircle)
download_border.drawable.setTint(errorColor)
download_icon.drawable.setTint(errorColor)
}
}
}
}

@ -43,7 +43,7 @@ import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.download.DownloadController
import eu.kanade.tachiyomi.ui.library.filter.SortFilterBottomSheet
import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.ui.manga.MangaChaptersController
import eu.kanade.tachiyomi.ui.migration.MigrationInterface
import eu.kanade.tachiyomi.ui.migration.manga.design.PreMigrationController
import eu.kanade.tachiyomi.ui.migration.manga.process.MigrationListController
@ -628,7 +628,8 @@ open class LibraryController(
}
fun openManga(manga: Manga, startY: Float?) {
router.pushController(MangaController(manga, startY).withFadeTransaction())
router.pushController(MangaChaptersController(manga).withFadeTransaction())
// router.pushController(MangaController(manga, startY).withFadeTransaction())
}
/**

@ -0,0 +1,20 @@
package eu.kanade.tachiyomi.ui.manga
import android.view.View
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
import eu.kanade.tachiyomi.ui.manga.chapter.ChapterItem
import eu.kanade.tachiyomi.ui.manga.chapter.ChaptersAdapter
abstract class MangaChapterHolder(
private val view: View,
private val adapter: ChaptersAdapter
) : BaseFlexibleViewHolder(view, adapter) {
/**
* Method called from [ChaptersAdapter.onBindViewHolder]. It updates the data for this
* holder with the given manga.
*
* @param item the manga item to bind.
*/
abstract fun bind(item: ChapterItem, manga: Manga)
}

@ -0,0 +1,363 @@
package eu.kanade.tachiyomi.ui.manga
import android.animation.ValueAnimator
import android.content.Intent
import android.content.res.Configuration
import android.graphics.Color
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.view.ActionMode
import androidx.core.graphics.ColorUtils
import androidx.palette.graphics.Palette
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.vectordrawable.graphics.drawable.ArgbEvaluator
import com.bluelinelabs.conductor.ControllerChangeHandler
import com.bluelinelabs.conductor.ControllerChangeType
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition
import com.bumptech.glide.signature.ObjectKey
import com.google.android.material.snackbar.BaseTransientBottomBar
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.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaImpl
import eu.kanade.tachiyomi.data.glide.GlideApp
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
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.main.MainActivity
import eu.kanade.tachiyomi.ui.main.SearchActivity
import eu.kanade.tachiyomi.ui.manga.chapter.ChapterItem
import eu.kanade.tachiyomi.ui.manga.chapter.ChaptersAdapter
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.view.getText
import eu.kanade.tachiyomi.util.view.snack
import kotlinx.android.synthetic.main.big_manga_controller.*
import kotlinx.android.synthetic.main.main_activity.*
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class MangaChaptersController : BaseController,
ActionMode.Callback,
FlexibleAdapter.OnItemClickListener,
ChaptersAdapter.MangaHeaderInterface {
constructor(manga: Manga?,
fromCatalogue: Boolean = false,
smartSearchConfig: CatalogueController.SmartSearchConfig? = null,
update: Boolean = false) : super(Bundle().apply {
putLong(MangaController.MANGA_EXTRA, manga?.id ?: 0)
putBoolean(MangaController.FROM_CATALOGUE_EXTRA, fromCatalogue)
putParcelable(MangaController.SMART_SEARCH_CONFIG_EXTRA, smartSearchConfig)
putBoolean(MangaController.UPDATE_EXTRA, update)
}) {
this.manga = manga
if (manga != null) {
source = Injekt.get<SourceManager>().getOrStub(manga.source)
}
}
constructor(mangaId: Long) : this(
Injekt.get<DatabaseHelper>().getManga(mangaId).executeAsBlocking())
constructor(bundle: Bundle) : this(bundle.getLong(MangaController.MANGA_EXTRA)) {
val notificationId = bundle.getInt("notificationId", -1)
val context = applicationContext ?: return
if (notificationId > -1) NotificationReceiver.dismissNotification(
context, notificationId, bundle.getInt("groupId", 0)
)
}
private var manga: Manga? = null
private var source: Source? = null
var colorAnimator:ValueAnimator? = null
lateinit var presenter:MangaPresenter
var coverColor:Int? = null
var toolbarIsColored = false
private var snack: Snackbar? = null
/**
* Adapter containing a list of chapters.
*/
private var adapter: ChaptersAdapter? = null
init {
setHasOptionsMenu(true)
}
override fun getTitle(): String? {
return if (toolbarIsColored) manga?.currentTitle() else null
}
override fun onViewCreated(view: View) {
super.onViewCreated(view)
coverColor = null
if (!::presenter.isInitialized) presenter = MangaPresenter(this, manga!!, source!!)
// Init RecyclerView and adapter
adapter = ChaptersAdapter(this, view.context)
//setReadingDrawable()
recycler.adapter = adapter
recycler.layoutManager = LinearLayoutManager(view.context)
recycler.addItemDecoration(
DividerItemDecoration(
view.context,
DividerItemDecoration.VERTICAL
)
)
recycler.setHasFixedSize(true)
presenter.onCreate()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
recycler.setOnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY ->
val atTop =
((recycler.layoutManager as LinearLayoutManager).findFirstCompletelyVisibleItemPosition() == 0)
if ((!atTop && !toolbarIsColored) || (atTop && toolbarIsColored)) {
toolbarIsColored = !atTop
colorAnimator?.cancel()
val color =
coverColor ?: activity!!.getResourceColor(android.R.attr.colorPrimary)
val colorFrom = ColorUtils.setAlphaComponent(
color, if (toolbarIsColored) 0 else 255
)
val colorTo = ColorUtils.setAlphaComponent(
color, if (toolbarIsColored) 255 else 0
)
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()
val isCurrentController = router?.backstack?.lastOrNull()?.controller() == this
if (isCurrentController) setTitle()
}
}
}
GlideApp.with(view.context).load(manga)
.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
.signature(ObjectKey(MangaImpl.getLastCoverFetch(manga!!.id!!).toString()))
.into(object : CustomTarget<Drawable>() {
override fun onResourceReady(resource: Drawable,
transition: Transition<in Drawable>?
) {
Palette.from(
(resource as BitmapDrawable).bitmap).generate {
if (recycler == null) return@generate
val currentNightMode =
recycler.resources!!.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
val colorBack = view.context.getResourceColor(
android.R.attr.colorBackground
)
val backDropColor =
(if (currentNightMode == Configuration.UI_MODE_NIGHT_NO) it?.getLightMutedColor(
colorBack
)
else it?.getDarkMutedColor(colorBack)) ?: colorBack
onCoverLoaded(backDropColor)
(recycler.findViewHolderForItemId(-1) as? MangaHeaderHolder)
?.setBackDrop(backDropColor)
if (toolbarIsColored)
(activity as MainActivity).toolbar.setBackgroundColor(backDropColor)
}
}
override fun onLoadCleared(placeholder: Drawable?) { }
})
//adapter?.fastScroller = fast_scroller
}
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)
/* 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()
(activity as MainActivity).toolbar.setBackgroundColor(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()
}
}
fun updateChapters(chapters: List<ChapterItem>) {
if (presenter.chapters.isEmpty()) {
//initialFetchChapters()
}
adapter?.updateDataSet(listOf(ChapterItem(Chapter.create(), manga!!)) + chapters)
}
override fun onItemClick(view: View?, position: Int): Boolean {
val adapter = adapter ?: return false
val chapter = adapter.getItem(position)?.chapter ?: return false
if (!chapter.isRecognizedNumber) return false
/*if (actionMode != null && adapter.mode == SelectableAdapter.Mode.MULTI) {
lastClickPosition = position
toggleSelection(position)
return true
} else {*/
openChapter(chapter)
return false
//}
}
fun openChapter(chapter: Chapter, hasAnimation: Boolean = false) {
val activity = activity ?: return
val intent = ReaderActivity.newIntent(activity, manga!!, chapter)
if (hasAnimation) {
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
}
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 onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.chapters, menu)
}
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
return inflater.inflate(R.layout.big_manga_controller, container, false)
}
override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {
return true
}
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
return true
}
override fun onDestroyActionMode(mode: ActionMode?) {
}
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
return true
}
fun onCoverLoaded(color: Int) {
if (view == null) return
coverColor = color
activity?.window?.statusBarColor = color
}
override fun coverColor(): Int? = coverColor
override fun nextChapter(): Chapter? {
return presenter.getNextUnreadChapter()
}
override fun readNextChapter() {
if (activity is SearchActivity && presenter.isLockedFromSearch) {
SecureActivityDelegate.promptLockIfNeeded(activity)
return
}
val item = presenter.getNextUnreadChapter()
if (item != null) {
openChapter(item.chapter)
} else if (snack == null || snack?.getText() != view?.context?.getString(
R.string.no_next_chapter)) {
snack = view?.snack(R.string.no_next_chapter, Snackbar.LENGTH_LONG) {
addCallback(object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
super.onDismissed(transientBottomBar, event)
if (snack == transientBottomBar) snack = null
}
})
}
}
}
override fun downloadChapter(position: Int) {
val adapter = adapter ?: return
val chapter = adapter.getItem(position) ?: return
if (!chapter.isRecognizedNumber) return
if (chapter.isDownloaded) {
presenter.deleteChapters(listOf(chapter))
}
else presenter.downloadChapters(listOf(chapter))
}
}

@ -0,0 +1,93 @@
package eu.kanade.tachiyomi.ui.manga
import android.content.res.ColorStateList
import android.graphics.Color
import android.view.View
import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.signature.ObjectKey
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaImpl
import eu.kanade.tachiyomi.data.glide.GlideApp
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.visibleIf
import kotlinx.android.synthetic.main.manga_header_item.*
class MangaHeaderHolder(
private val view: View,
private val adapter: ChaptersAdapter
) : MangaChapterHolder(view, adapter) {
init {
start_reading_button.setOnClickListener { adapter.coverListener?.readNextChapter() }
}
override fun bind(item: ChapterItem, manga: Manga) {
manga_title.text = manga.currentTitle()
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_label.text = "About this ${if (manga.mangaType() == Manga.TYPE_MANGA) "Manga"
else "Manhwa"}"
with(favorite_button) {
icon = ContextCompat.getDrawable(
itemView.context, when {
item.isLocked -> R.drawable.ic_lock_white_24dp
manga.favorite -> R.drawable.ic_bookmark_white_24dp
else -> R.drawable.ic_add_to_library_24dp
}
)
text = itemView.resources.getString(
when {
item.isLocked -> R.string.unlock
manga.favorite -> R.string.in_library
else -> R.string.add_to_library
}
)
backgroundTintList =
ContextCompat.getColorStateList(context, android.R.color.transparent)
if (!item.isLocked && manga.favorite) {
backgroundTintList =
ColorStateList.valueOf(
ColorUtils.setAlphaComponent(
context.getResourceColor(R.attr.colorAccent), 75))
strokeColor = ColorStateList.valueOf(Color.TRANSPARENT)
}
}
true_backdrop.setBackgroundColor(adapter.coverListener?.coverColor() ?:
itemView.context.getResourceColor(android.R.attr.colorBackground))
with(start_reading_button) {
val nextChapter = adapter.coverListener?.nextChapter()
visibleIf(nextChapter != null && !item.isLocked)
if (nextChapter != null) {
val number = adapter.decimalFormat.format(nextChapter.chapter_number.toDouble())
text = resources.getString(if (nextChapter.last_page_read > 0)
R.string.continue_reader_chapter
else R.string.start_reader_chapter, number)
}
}
GlideApp.with(view.context).load(manga)
.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
.signature(ObjectKey(MangaImpl.getLastCoverFetch(manga.id!!).toString()))
.into(manga_cover)
GlideApp.with(view.context).load(manga)
.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
.signature(ObjectKey(MangaImpl.getLastCoverFetch(manga.id!!).toString()))
.centerCrop()
.into(backdrop)
}
fun setBackDrop(color: Int) {
true_backdrop.setBackgroundColor(color)
}
}

@ -0,0 +1,258 @@
package eu.kanade.tachiyomi.ui.manga
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.ui.manga.chapter.ChapterItem
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class MangaPresenter(private val controller: MangaChaptersController,
val manga: Manga,
val source: Source,
val preferences: PreferencesHelper = Injekt.get(),
private val db: DatabaseHelper = Injekt.get(),
private val downloadManager: DownloadManager = Injekt.get()) {
var isLockedFromSearch = false
var chapters:List<ChapterItem> = emptyList()
private set
fun onCreate() {
isLockedFromSearch = SecureActivityDelegate.shouldBeLocked()
val chapters = db.getChapters(manga).executeAsBlocking().map { it.toModel() }
// Store the last emission
this.chapters = applyChapterFilters(chapters)
// Find downloaded chapters
setDownloadedChapters(chapters)
controller.updateChapters(this.chapters)
// Listen for download status changes
//observeDownloads()
// 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)
)*/
/* // Prepare the relay.
chaptersRelay.flatMap { applyChapterFilters(it) }
.observeOn(AndroidSchedulers.mainThread())
.subscribeLatestCache(
ChaptersController::onNextChapters
) { _, error -> Timber.e(error) }
// Add the subscription that retrieves the chapters from the database, keeps subscribed to
// changes, and sends the list of chapters to the relay.
add(db.getChapters(manga).asRxObservable()
.map { chapters ->
// Convert every chapter to a model.
chapters.map { it.toModel() }
}
.doOnNext { chapters ->
// Find downloaded chapters
setDownloadedChapters(chapters)
// Store the last emission
this.chapters = chapters
// Listen for download status changes
observeDownloads()
// 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)
)
}
.subscribe { chaptersRelay.call(it) })*/
}
/*private fun observeDownloads() {
observeDownloadsSubscription?.let { remove(it) }
observeDownloadsSubscription = downloadManager.queue.getStatusObservable()
.observeOn(AndroidSchedulers.mainThread())
.filter { download -> download.manga.id == manga.id }
.doOnNext { onDownloadStatusChange(it) }
.subscribeLatestCache(ChaptersController::onChapterStatusChange) {
_, error -> Timber.e(error)
}
}*/
/**
* Finds and assigns the list of downloaded chapters.
*
* @param chapters the list of chapter from the database.
*/
private fun setDownloadedChapters(chapters: List<ChapterItem>) {
for (chapter in chapters) {
if (downloadManager.isChapterDownloaded(chapter, manga)) {
chapter.status = Download.DOWNLOADED
}
}
}
/**
* Converts a chapter from the database to an extended model, allowing to store new fields.
*/
private fun Chapter.toModel(): ChapterItem {
// Create the model object.
val model = ChapterItem(this, manga)
model.isLocked = isLockedFromSearch
// Find an active download for this chapter.
val download = downloadManager.queue.find { it.chapter.id == id }
if (download != null) {
// If there's an active download, assign it.
model.download = download
}
return model
}
/**
* Sets the active display mode.
* @param mode the mode to set.
*/
fun setDisplayMode(mode: Int) {
manga.displayMode = mode
db.updateFlags(manga).executeAsBlocking()
}
/**
* Sets the sorting method and requests an UI update.
* @param sort the sorting mode.
*/
fun setSorting(sort: Int) {
manga.sorting = sort
db.updateFlags(manga).executeAsBlocking()
// refreshChapters()
}
/**
* Whether the display only downloaded filter is enabled.
*/
fun onlyDownloaded(): Boolean {
return manga.downloadedFilter == Manga.SHOW_DOWNLOADED
}
/**
* Whether the display only downloaded filter is enabled.
*/
fun onlyBookmarked(): Boolean {
return manga.bookmarkedFilter == Manga.SHOW_BOOKMARKED
}
/**
* Whether the display only unread filter is enabled.
*/
fun onlyUnread(): Boolean {
return manga.readFilter == Manga.SHOW_UNREAD
}
/**
* Whether the display only read filter is enabled.
*/
fun onlyRead(): Boolean {
return manga.readFilter == Manga.SHOW_READ
}
/**
* Whether the sorting method is descending or ascending.
*/
fun sortDescending(): Boolean {
return manga.sortDescending()
}
/**
* Applies the view filters to the list of chapters obtained from the database.
* @param chapterList the list of chapters from the database
* @return an observable of the list of chapters filtered and sorted.
*/
private fun applyChapterFilters(chapterList: List<ChapterItem>): List<ChapterItem> {
var chapters = chapterList
if (onlyUnread()) {
chapters = chapters.filter { !it.read }
} else if (onlyRead()) {
chapters = chapters.filter { it.read }
}
if (onlyDownloaded()) {
chapters = chapters.filter { it.isDownloaded || it.manga.source == LocalSource.ID }
}
if (onlyBookmarked()) {
chapters = chapters.filter { it.bookmark }
}
val sortFunction: (Chapter, Chapter) -> Int = when (manga.sorting) {
Manga.SORTING_SOURCE -> when (sortDescending()) {
true -> { c1, c2 -> c1.source_order.compareTo(c2.source_order) }
false -> { c1, c2 -> c2.source_order.compareTo(c1.source_order) }
}
Manga.SORTING_NUMBER -> when (sortDescending()) {
true -> { c1, c2 -> c2.chapter_number.compareTo(c1.chapter_number) }
false -> { c1, c2 -> c1.chapter_number.compareTo(c2.chapter_number) }
}
else -> throw NotImplementedError("Unimplemented sorting method")
}
chapters = chapters.sortedWith(Comparator(sortFunction))
//if (sortDescending())
// chapters = chapters.reversed()
return chapters
}
/**
* Returns the next unread chapter or null if everything is read.
*/
fun getNextUnreadChapter(): ChapterItem? {
return chapters.sortedByDescending { it.source_order }.find { !it.read }
}
/**
* Downloads the given list of chapters with the manager.
* @param chapters the list of chapters to download.
*/
fun downloadChapters(chapters: List<ChapterItem>) {
downloadManager.downloadChapters(manga, chapters)
}
/**
* Deletes the given list of chapter.
* @param chapters the list of chapters to delete.
*/
fun deleteChapters(chapters: List<ChapterItem>) {
deleteChaptersInternal(chapters)
setDownloadedChapters(chapters)
controller.updateChapters(this.chapters)
// if (onlyDownloaded()) refreshChapters() }
}
/**
* Deletes a list of chapters from disk. This method is called in a background thread.
* @param chapters the chapters to delete.
*/
private fun deleteChaptersInternal(chapters: List<ChapterItem>) {
downloadManager.deleteChapters(chapters, manga, source)
chapters.forEach {
it.status = Download.NOT_DOWNLOADED
it.download = null
}
}
}

@ -27,7 +27,7 @@ class ChapterHolder(
}
fun bind(item: ChapterItem, manga: Manga) {
val chapter = item.chapter
val chapter = item.chapter ?: return
val isLocked = item.isLocked
chapter_title.text = when (manga.displayMode) {
Manga.DISPLAY_NUMBER -> {
@ -90,6 +90,7 @@ class ChapterHolder(
private fun showPopupMenu(view: View) {
val item = adapter.getItem(adapterPosition) ?: return
val chapter = item.chapter ?: return
if (item.isLocked) {
adapter.unlock()
@ -101,7 +102,6 @@ class ChapterHolder(
// Inflate our menu resource into the PopupMenu's Menu
popup.menuInflater.inflate(R.menu.chapter_single, popup.menu)
val chapter = item.chapter
// Hide download and show delete if the chapter is downloaded
if (item.isDownloaded) {
@ -125,7 +125,7 @@ class ChapterHolder(
// Set a listener so we are notified if a menu item is clicked
popup.setOnMenuItemClickListener { menuItem ->
adapter.menuItemListener.onMenuItemClick(adapterPosition, menuItem)
adapter.menuItemListener?.onMenuItemClick(adapterPosition, menuItem)
true
}

@ -9,11 +9,21 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.ui.manga.MangaChapterHolder
import eu.kanade.tachiyomi.ui.manga.MangaHeaderHolder
class ChapterItem(val chapter: Chapter, val manga: Manga) : AbstractFlexibleItem<ChapterHolder>(),
Chapter by chapter {
class ChapterItem(val chapter: Chapter, val manga: Manga) :
AbstractFlexibleItem<MangaChapterHolder>(),
Chapter by chapter {
private var _status: Int = 0
val progress: Int
get() {
val pages = download?.pages ?: return 0
return pages.map(Page::progress).average().toInt()
}
var isLocked = false
var status: Int
@ -26,31 +36,36 @@ class ChapterItem(val chapter: Chapter, val manga: Manga) : AbstractFlexibleItem
get() = status == Download.DOWNLOADED
override fun getLayoutRes(): Int {
return R.layout.chapters_item
return if (!chapter.isRecognizedNumber) R.layout.manga_header_item
else R.layout.chapters_mat_item
}
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): ChapterHolder {
return ChapterHolder(view, adapter as ChaptersAdapter)
override fun isSelectable(): Boolean {
return chapter.isRecognizedNumber
}
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): MangaChapterHolder {
return if (!chapter.isRecognizedNumber) MangaHeaderHolder(view, adapter as ChaptersAdapter)
else ChapterMatHolder(view, adapter as ChaptersAdapter)
}
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>,
holder: ChapterHolder,
holder: MangaChapterHolder,
position: Int,
payloads: MutableList<Any?>?) {
holder.bind(this, manga)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other is ChapterItem) {
return chapter.id!! == other.chapter.id!!
return chapter.id ?: -1 == other.chapter.id ?: -1
}
return false
}
override fun hashCode(): Int {
return chapter.id!!.hashCode()
return chapter.id?.hashCode() ?: -1
}
}

@ -0,0 +1,116 @@
package eu.kanade.tachiyomi.ui.manga.chapter
import android.view.View
import androidx.appcompat.widget.PopupMenu
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.ui.manga.MangaChapterHolder
import eu.kanade.tachiyomi.util.view.gone
import eu.kanade.tachiyomi.util.view.invisible
import eu.kanade.tachiyomi.util.view.visible
import kotlinx.android.synthetic.main.chapters_mat_item.*
import kotlinx.android.synthetic.main.download_button.*
class ChapterMatHolder(
private val view: View,
private val adapter: ChaptersAdapter
) : MangaChapterHolder(view, adapter) {
init {
// We need to post a Runnable to show the popup to make sure that the PopupMenu is
// correctly positioned. The reason being that the view may change position before the
// PopupMenu is shown.
//chapter_menu.setOnClickListener { it.post { showPopupMenu(it) } }
download_button.setOnClickListener { downloadOrRemoveMenu() }
}
private fun downloadOrRemoveMenu() {
val chapter = adapter.getItem(adapterPosition) ?: return
if (chapter.status != Download.NOT_DOWNLOADED) {
download_button.post {
// Create a PopupMenu, giving it the clicked view for an anchor
val popup = PopupMenu(download_button.context, download_button)
// Inflate our menu resource into the PopupMenu's Menu
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(
R.string.action_cancel
)
// Set a listener so we are notified if a menu item is clicked
popup.setOnMenuItemClickListener { _ ->
adapter.coverListener?.downloadChapter(adapterPosition)
true
}
// Finally show the PopupMenu
popup.show()
}
}
else {
adapter.coverListener?.downloadChapter(adapterPosition)
}
}
override fun bind(item: ChapterItem, manga: Manga) {
val chapter = item.chapter
val isLocked = item.isLocked
chapter_title.text = when (manga.displayMode) {
Manga.DISPLAY_NUMBER -> {
val number = adapter.decimalFormat.format(chapter.chapter_number.toDouble())
itemView.context.getString(R.string.display_mode_chapter, number)
}
else -> chapter.name
}
//chapter_menu.visible()
// Set the correct drawable for dropdown and update the tint to match theme.
//chapter_menu.setVectorCompat(R.drawable.ic_more_vert_black_24dp, view.context
// .getResourceColor(R.attr.icon_color))
if (isLocked) download_button.invisible()
// Set correct text color
chapter_title.setTextColor(if (chapter.read && !isLocked)
adapter.readColor else adapter.unreadColor)
if (chapter.bookmark && !isLocked) chapter_title.setTextColor(adapter.bookmarkedColor)
/*if (chapter.date_upload > 0) {
chapter_date.text = adapter.dateFormat.format(Date(chapter.date_upload))
chapter_date.setTextColor(if (chapter.read) adapter.readColor else adapter.unreadColor)
} else {
chapter_date.text = ""
}*/
//add scanlator if exists
chapter_scanlator.text = chapter.scanlator ?: " "
//allow longer titles if there is no scanlator (most sources)
/*if (chapter_scanlator.text.isNullOrBlank()) {
chapter_title.maxLines = 2
//chapter_scanlator.gone()
} else {
chapter_title.maxLines = 1
}*/
/* chapter_pages.text = if (!chapter.read && chapter.last_page_read > 0 && !isLocked) {
itemView.context.getString(R.string.chapter_progress, chapter.last_page_read + 1)
} else {
""
}*/
notifyStatus(item.status, item.isLocked, item.progress)
}
fun notifyStatus(status: Int, locked: Boolean, progress: Int) = with(download_button) {
if (locked) {
gone()
return
}
visible()
setDownoadStatus(status, progress)
}
}

@ -5,8 +5,10 @@ import android.view.MenuItem
import androidx.fragment.app.FragmentActivity
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.ui.base.controller.BaseController
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
import eu.kanade.tachiyomi.util.system.getResourceColor
import uy.kohesive.injekt.injectLazy
@ -15,7 +17,7 @@ import java.text.DecimalFormat
import java.text.DecimalFormatSymbols
class ChaptersAdapter(
val controller: ChaptersController,
val controller: BaseController,
context: Context
) : FlexibleAdapter<ChapterItem>(null, controller, true) {
@ -23,7 +25,8 @@ class ChaptersAdapter(
var items: List<ChapterItem> = emptyList()
val menuItemListener: OnMenuItemClickListener = controller
val menuItemListener: OnMenuItemClickListener? = controller as? OnMenuItemClickListener
val coverListener: MangaHeaderInterface? = controller as? MangaHeaderInterface
val readColor = context.getResourceColor(android.R.attr.textColorHint)
@ -53,4 +56,11 @@ class ChaptersAdapter(
interface OnMenuItemClickListener {
fun onMenuItemClick(position: Int, item: MenuItem)
}
interface MangaHeaderInterface {
fun coverColor(): Int?
fun nextChapter(): Chapter?
fun readNextChapter()
fun downloadChapter(position: Int)
}
}

@ -2,22 +2,22 @@ package eu.kanade.tachiyomi.ui.setting
import android.content.Context
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceController
import androidx.preference.PreferenceScreen
import android.util.TypedValue
import android.view.ContextThemeWrapper
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceController
import androidx.preference.PreferenceScreen
import com.bluelinelabs.conductor.ControllerChangeHandler
import com.bluelinelabs.conductor.ControllerChangeType
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.ui.base.controller.BaseController
import eu.kanade.tachiyomi.util.view.RecyclerWindowInsetsListener
import eu.kanade.tachiyomi.util.view.doOnApplyWindowInsets
import eu.kanade.tachiyomi.util.view.updatePaddingRelative
import eu.kanade.tachiyomi.util.view.updateLayoutParams
import rx.Observable
import rx.Subscription
import rx.subscriptions.CompositeSubscription
@ -36,6 +36,12 @@ abstract class SettingsController : PreferenceController() {
untilDestroySubscriptions = CompositeSubscription()
}
val view = super.onCreateView(inflater, container, savedInstanceState)
view.updateLayoutParams<FrameLayout.LayoutParams> {
val attrsArray = intArrayOf(android.R.attr.actionBarSize)
val array = view.context.obtainStyledAttributes(attrsArray)
topMargin = array.getDimensionPixelSize(0, 0)
array.recycle()
}
listView.setOnApplyWindowInsetsListener(RecyclerWindowInsetsListener)
return view
}

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval"
android:thicknessRatio="2">
<solid android:color="@android:color/transparent" />
<size
android:height="25dp"
android:width="25dp" />
<stroke
android:width="2dp"
android:color="?colorAccent" />
</shape>

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:id="@android:id/progress">
<rotate
android:fromDegrees="270"
android:toDegrees="270"
android:pivotX="50%"
android:pivotY="50%" >
<shape
android:shape="ring"
android:thickness="2dp">
<gradient
android:centerColor="@color/gray_button"
android:endColor="@color/gray_button"
android:startColor="@color/gray_button"
android:type="sweep" />
</shape>
</rotate>
</item>
</layer-list>

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval"
android:thicknessRatio="2">
<solid android:color="?colorAccent" />
<size
android:height="25dp"
android:width="25dp" />
<stroke
android:width="2dp"
android:color="?colorAccent" />
</shape>

@ -4,7 +4,7 @@
<gradient
android:angle="90"
android:startColor="#cc000000"
android:startColor="#ff000000"
android:centerColor="#00000000"
android:endColor="#00ffffff"/>

@ -0,0 +1,9 @@
<!-- drawable/earth.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?actionBarTintColor">
<path android:fillColor="#000" android:pathData="M17.9,17.39C17.64,16.59 16.89,16 16,16H15V13A1,1 0 0,0 14,12H8V10H10A1,1 0 0,0 11,9V7H13A2,2 0 0,0 15,5V4.59C17.93,5.77 20,8.64 20,12C20,14.08 19.2,15.97 17.9,17.39M11,19.93C7.05,19.44 4,16.08 4,12C4,11.38 4.08,10.78 4.21,10.21L9,15V16A2,2 0 0,0 11,18M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" />
</vector>

@ -3,7 +3,7 @@
android:color="@color/gray_button">
<item android:id="@android:id/mask">
<shape android:shape="oval">
<solid android:color="@color/gray_button" />
<solid android:color="@color/fullRippleColor" />
</shape>
</item>
</ripple>

@ -1,320 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/manga_info_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@id/swipe_refresh"
android:layout_width="match_parent"
android:background="?android:colorBackground"
android:fitsSystemWindows="true"
android:layout_height="match_parent"
tools:context="eu.kanade.tachiyomi.ui.catalogue.browse.BrowseCatalogueController">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/manga_info_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="@+id/guideline"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="32dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/backdrop" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.35" />
<ImageView
android:id="@+id/backdrop"
android:layout_width="0dp"
android:layout_height="0dp"
android:alpha="0.2"
android:translationY="-40dp"
app:layout_constraintBottom_toBottomOf="@id/manga_cover"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:background="@color/material_red_400" />
<View
android:layout_width="match_parent"
android:layout_height="50dp"
android:alpha="0.25"
android:background="@drawable/gradient_shape"
android:backgroundTint="@color/material_red_400"
android:scaleY="-1"
android:translationY="-40dp"
app:layout_constraintTop_toBottomOf="@+id/backdrop" />
<ImageView
android:id="@+id/manga_cover"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:adjustViewBounds="true"
android:contentDescription="@string/description_cover"
android:src="@mipmap/ic_launcher"
app:layout_constraintBottom_toBottomOf="@id/guideline"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:background="@color/material_grey_700" />
<TextView
android:id="@+id/manga_full_title"
style="@style/TextAppearance.MaterialComponents.Headline5"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_marginStart="12dp"
android:maxLines="2"
android:text="@string/manga_info_full_title_label"
android:textIsSelectable="false"
android:textSize="20sp"
app:autoSizeTextType="uniform"
app:layout_constraintStart_toEndOf="@id/manga_cover"
app:layout_constraintTop_toTopOf="@id/manga_cover"
tools:text="Title Example" />
<TextView
android:id="@+id/manga_author_label"
style="@style/TextAppearance.Medium.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/manga_info_author_label"
android:textIsSelectable="false"
app:layout_constraintStart_toStartOf="@id/manga_full_title"
app:layout_constraintTop_toBottomOf="@+id/manga_full_title" />
<TextView
android:id="@+id/manga_author"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textIsSelectable="false"
app:layout_constraintBaseline_toBaselineOf="@+id/manga_author_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/manga_author_label" />
<TextView
android:id="@+id/manga_last_update_label"
style="@style/TextAppearance.Medium.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/manga_info_latest_data_label"
android:textIsSelectable="false"
app:layout_constraintStart_toStartOf="@id/manga_full_title"
app:layout_constraintTop_toBottomOf="@+id/manga_author_label" />
<TextView
android:id="@+id/manga_last_update"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textIsSelectable="false"
app:layout_constraintBaseline_toBaselineOf="@+id/manga_last_update_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/manga_last_update_label" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/manga_status_source"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textIsSelectable="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/manga_full_title"
app:layout_constraintTop_toBottomOf="@id/manga_last_update_label"
tools:text="Completed • Mangadex (EN)" />
<LinearLayout
android:id="@+id/button_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="14dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/manga_cover"
app:layout_constraintBottom_toTopOf="@id/start_reading_button">
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:tint="@color/gray_button"
android:src="@drawable/ic_add_to_library_24dp"/>
<View
android:layout_width="1dp"
android:layout_height="30dp"
android:layout_gravity="center"
android:background="@color/gray_button"/>
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:tint="@color/gray_button"
android:src="@drawable/ic_add_to_library_24dp"/>
<View
android:layout_width="1dp"
android:layout_height="30dp"
android:layout_gravity="center"
android:background="@color/gray_button"/>
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:tint="@color/gray_button"
android:src="@drawable/ic_sync_black_24dp"/>
</LinearLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/start_reading_button"
android:layout_width="0dp"
app:backgroundTint="?colorAccent"
android:layout_height="wrap_content"
android:textAllCaps="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button_layout"
app:layout_constraintBottom_toTopOf="@id/manga_summary_label"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:text="Start Reading"/>
<com.google.android.material.textview.MaterialTextView
android:id="@+id/manga_summary_label"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:text="@string/description"
android:textIsSelectable="false"
style="@style/TextAppearance.MaterialComponents.Headline6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:textSize="17sp"
tools:text="About this manga"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/start_reading_button" />
<TextView
android:id="@+id/manga_summary"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="match_parent"
android:layout_height="55dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:textIsSelectable="false"
app:layout_constraintBottom_toTopOf="@id/manga_genres_tags"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/manga_summary_label"
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:layout_height="20dp"
android:layout_marginEnd="30dp"
android:background="@drawable/full_gradient"
android:backgroundTint="@color/md_white_1000"
app:layout_constraintBottom_toBottomOf="@id/manga_summary"
app:layout_constraintEnd_toEndOf="@id/more_button" />
<View
android:layout_width="0dp"
android:layout_height="20dp"
android:background="@color/md_white_1000"
android:layout_marginStart="20dp"
app:layout_constraintStart_toStartOf="@id/more_button"
app:layout_constraintBottom_toBottomOf="@id/manga_summary"
app:layout_constraintEnd_toEndOf="@id/more_button" />
<com.google.android.material.button.MaterialButton
android:id="@+id/more_button"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20sp"
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" />
<me.gujun.android.taggroup.TagGroup
android:id="@+id/manga_genres_tags"
style="@style/TagGroup"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
app:atg_backgroundColor="@android:color/transparent"
app:atg_borderColor="@color/md_blue_A400"
app:atg_borderStrokeWidth="1dp"
app:atg_textColor="@color/md_blue_A400"
app:layout_constrainedHeight="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/manga_summary" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/chapters_title"
style="@style/TextAppearance.MaterialComponents.Headline6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/chapters"
android:textSize="17sp"
app:layout_constraintBottom_toTopOf="@id/recycler"
app:layout_constraintStart_toStartOf="@id/manga_summary_label"
app:layout_constraintTop_toBottomOf="@id/manga_genres_tags" />
<com.google.android.material.button.MaterialButton
android:id="@+id/show_all_button"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Show All"
android:textAllCaps="false"
android:textColor="?colorAccent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/chapters_title"
app:layout_constraintBottom_toBottomOf="@id/chapters_title"
app:rippleColor="@color/gray_button" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="0dp"
android:layout_height="0dp"
android:clipToPadding="false"
android:descendantFocusability="blocksDescendants"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/chapters_title"
tools:listitem="@layout/chapters_item">
</androidx.recyclerview.widget.RecyclerView>
</androidx.constraintlayout.widget.ConstraintLayout>
android:layout_height="match_parent"
android:clipToPadding="false"
android:descendantFocusability="blocksDescendants"
android:nestedScrollingEnabled="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/chapters_title"
tools:listitem="@layout/chapters_mat_item">
</androidx.recyclerview.widget.RecyclerView>
<View
android:id="@+id/full_backdrop"
@ -337,6 +49,5 @@
android:layout_marginBottom="16dp"
android:contentDescription="@string/description_cover"
android:visibility="invisible" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

@ -4,6 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:id="@+id/catalouge_layout"
android:layout_marginTop="?attr/actionBarSize"
android:layout_height="match_parent">
<LinearLayout

@ -2,6 +2,7 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_marginTop="?attr/actionBarSize"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView

@ -76,6 +76,7 @@
android:layout_width="match_parent"
android:layout_height="150dp"
android:layout_gravity="bottom"
android:alpha="0.75"
android:background="@drawable/gradient_shape" />
<com.google.android.material.textview.MaterialTextView

@ -3,6 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_marginTop="?attr/actionBarSize"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView

@ -1,11 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<eu.kanade.tachiyomi.widget.AutofitRecyclerView
android:id="@+id/catalogue_recycler"
<eu.kanade.tachiyomi.widget.AutofitRecyclerView android:id="@+id/catalogue_recycler"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
style="@style/Theme.Widget.GridView.Catalogue"
android:layout_width="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:layout_height="match_parent"
android:columnWidth="120dp"
android:clipToPadding="false"
tools:listitem="@layout/catalogue_grid_item" />
tools:listitem="@layout/catalogue_grid_item"
xmlns:app="http://schemas.android.com/apk/res-auto" />

@ -4,6 +4,7 @@
android:id="@+id/cat_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"
android:clipToPadding="false"
android:orientation="vertical">

@ -5,6 +5,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"
android:clipToPadding="false"
android:orientation="vertical">

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="?attr/selectable_list_drawable">
<TextView
android:id="@+id/chapter_title"
style="@style/TextAppearance.Regular.Body1"
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.Regular.Caption.Hint"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginBottom="12dp"
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_height="0dp"
android:layout_width="50dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<eu.kanade.tachiyomi.ui.download.DownloadButton xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/download_button"
android:clickable="true"
android:focusable="true"
android:layout_width="30dp"
android:layout_height="30dp">
<ImageView
android:id="@+id/download_border"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center"
android:src="@drawable/border_circle"
android:padding="3dp"/>
<ProgressBar
android:id="@+id/download_progress_indeterminate"
android:layout_width="30dp"
android:layout_height="30dp"
android:visibility="gone"
android:layout_gravity="center"
android:indeterminate="true"
android:indeterminateTint="@color/material_on_surface_disabled"/>
<ProgressBar
android:id="@+id/download_progress"
android:layout_width="30dp"
android:layout_height="30dp"
android:visibility="gone"
tools:visibility="visible"
style="?android:attr/progressBarStyleHorizontal"
android:progressDrawable="@drawable/circle_progress"
android:layout_gravity="center"
android:progressTint="@color/material_green_800"
android:progressBackgroundTint="@color/material_on_surface_disabled"/>
<ImageView
android:id="@+id/download_icon"
android:tint="?colorAccent"
android:layout_gravity="center"
android:layout_width="30dp"
android:layout_height="30dp"
android:background="@drawable/round_ripple"
android:padding="5dp"
android:src="@drawable/ic_arrow_down_white_24dp" />
</eu.kanade.tachiyomi.ui.download.DownloadButton>

@ -3,6 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_marginTop="?attr/actionBarSize"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView

@ -4,6 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"
android:id="@+id/ext_swipe_refresh">
<androidx.recyclerview.widget.RecyclerView

@ -3,6 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_marginTop="?attr/actionBarSize"
android:layout_height="match_parent">
<ImageView

@ -4,6 +4,7 @@
android:layout_width="match_parent"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/library_layout"
android:layout_marginTop="?attr/actionBarSize"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/recycler_layout"

@ -29,8 +29,10 @@
<eu.kanade.tachiyomi.widget.ElevationAppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:translationZ="0.1dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:stateListAnimator="@null"
app:layout_constraintRight_toRightOf="parent"
android:layout_height="wrap_content">
@ -65,7 +67,7 @@
app:layout_constraintBottom_toTopOf="@+id/navigationView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appbar">
app:layout_constraintTop_toTopOf="parent">
</com.bluelinelabs.conductor.ChangeHandlerFrameLayout>
@ -85,7 +87,6 @@
app:tabBackground="@color/rippleColor"
app:tabRippleColor="@color/rippleColor"
app:tabTextColor="?attr/tabBarIconColor" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.drawerlayout.widget.DrawerLayout>

@ -0,0 +1,348 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/manga_header_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<View
android:id="@+id/guideline"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="52dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/true_backdrop" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.35" />
<View
android:id="@+id/true_backdrop"
android:layout_width="0dp"
android:layout_height="200dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:background="@color/material_red_400" />
<ImageView
android:id="@+id/backdrop"
android:layout_width="0dp"
android:layout_height="200dp"
android:alpha="0.1"
android:scaleType="centerCrop"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@mipmap/ic_launcher" />
<View
android:id="@+id/backdrop_gradient"
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="@drawable/gradient_shape"
android:backgroundTint="?android:attr/colorBackground"
app:layout_constraintBottom_toBottomOf="@+id/true_backdrop" />
<View
android:id="@+id/top_view"
android:layout_width="match_parent"
android:layout_height="30dp"
app:layout_constraintBottom_toTopOf="@id/top_line"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/top_line"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:barrierDirection="top"
app:constraint_referenced_ids="cover_card,manga_layout" />
<FrameLayout
android:id="@+id/manga_layout"
android:layout_width="100dp"
android:layout_height="0dp"
android:layout_marginStart="16dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toBottomOf="@id/guideline"
app:layout_constraintDimensionRatio="h,7:10"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/top_line">
</FrameLayout>
<com.google.android.material.card.MaterialCardView
android:id="@+id/cover_card"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
app:layout_constraintBottom_toBottomOf="@id/manga_layout"
app:layout_constraintEnd_toEndOf="@id/manga_layout"
app:layout_constraintStart_toStartOf="@id/manga_layout"
app:layout_constraintTop_toTopOf="@id/top_line"
app:layout_constraintVertical_bias="1.0">
<ImageView
android:id="@+id/manga_cover"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:contentDescription="@string/description_cover"
android:maxHeight="300dp"
tools:background="@color/material_grey_700"
tools:src="@mipmap/ic_launcher" />
</com.google.android.material.card.MaterialCardView>
<TextView
android:id="@+id/manga_title"
style="@style/TextAppearance.MaterialComponents.Headline5"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginEnd="16dp"
android:ellipsize="end"
android:maxLines="4"
android:text="@string/manga_info_full_title_label"
android:textIsSelectable="false"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/manga_layout"
app:layout_constraintTop_toTopOf="@id/cover_card"
tools:text="Title Example" />
<TextView
android:id="@+id/manga_author"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/manga_info_author_label"
android:textIsSelectable="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/manga_title"
app:layout_constraintTop_toBottomOf="@+id/manga_title" />
<TextView
android:id="@+id/manga_last_update_label"
style="@style/TextAppearance.Medium.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/manga_info_latest_data_label"
android:textIsSelectable="false"
app:layout_constraintStart_toStartOf="@id/manga_title"
app:layout_constraintTop_toBottomOf="@+id/manga_author" />
<TextView
android:id="@+id/manga_last_update"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textIsSelectable="false"
app:layout_constraintBaseline_toBaselineOf="@+id/manga_last_update_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/manga_last_update_label" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/manga_status_source"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textIsSelectable="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/manga_title"
app:layout_constraintTop_toBottomOf="@id/manga_last_update_label"
tools:text="Completed • Mangadex (EN)" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/bottom_line"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="6dp"
android:orientation="horizontal"
app:barrierDirection="bottom"
app:constraint_referenced_ids="manga_status_source,manga_layout" />
<LinearLayout
android:id="@+id/button_layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="14dp"
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"
android:text="@string/add_to_library"
app:icon="@drawable/ic_add_to_library_24dp" />
<com.google.android.material.button.MaterialButton
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"
style="@style/Theme.Widget.CustomImageButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="6dp"
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" />
</LinearLayout>
<com.google.android.material.textview.MaterialTextView
android:id="@+id/manga_summary_label"
style="@style/TextAppearance.MaterialComponents.Headline6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="6dp"
android:layout_marginEnd="16dp"
android:text="@string/description"
android:textIsSelectable="false"
android:textSize="17sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/button_layout"
tools:text="About this manga" />
<TextView
android:id="@+id/manga_summary"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:maxLines="3"
android:textIsSelectable="false"
app:layout_constraintBottom_toTopOf="@id/start_reading_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/manga_summary_label"
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:layout_height="20dp"
android:layout_marginEnd="30dp"
android:background="@drawable/full_gradient"
android:backgroundTint="?android:attr/colorBackground"
app:layout_constraintBottom_toBottomOf="@id/manga_summary"
app:layout_constraintEnd_toEndOf="@id/more_button" />
<View
android:layout_width="0dp"
android:layout_height="20dp"
android:layout_marginStart="20dp"
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" />
<com.google.android.material.button.MaterialButton
android:id="@+id/more_button"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20sp"
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" />
<me.gujun.android.taggroup.TagGroup
android:id="@+id/manga_genres_tags"
style="@style/TagGroup"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:visibility="gone"
app:atg_backgroundColor="@android:color/transparent"
app:atg_borderColor="@color/md_blue_A400"
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_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/manga_summary" />
<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_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"
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_height="wrap_content"
android:layout_marginTop="18dp"
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" />
<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:padding="5dp"
android:src="@drawable/ic_filter_list_white_24dp"
app:layout_constraintBottom_toBottomOf="@id/chapters_title"
app:layout_constraintEnd_toStartOf="@id/sort_button" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -87,7 +87,7 @@
android:layout_height="match_parent">
<TextView
android:id="@+id/manga_full_title"
android:id="@+id/manga_title"
style="@style/TextAppearance.Medium.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -110,7 +110,7 @@
android:textIsSelectable="false"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_full_title" />
app:layout_constraintTop_toBottomOf="@+id/manga_title" />
<TextView
android:id="@+id/manga_author"

@ -4,4 +4,5 @@
android:id="@+id/migration_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"
android:clipToPadding="false"/>

@ -4,6 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"
android:animateLayoutChanges="true">
<androidx.recyclerview.widget.RecyclerView

@ -4,6 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"
android:animateLayoutChanges="true">
<androidx.recyclerview.widget.RecyclerView

@ -5,6 +5,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:layout_marginTop="?attr/actionBarSize"
android:orientation="vertical">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh"

@ -4,6 +4,7 @@
android:id="@+id/frame_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/action_delete"
android:title="@string/action_remove_download"
android:icon="@drawable/ic_delete_white_24dp"/>
</menu>

@ -23,6 +23,7 @@
<string name="label_extensions">Extensions</string>
<string name="label_extension_info">Extension info</string>
<string name="label_help">Help</string>
<string name="unlock">Unlock</string>
<string name="unlock_library">Unlock to access Library</string>
<plurals name="extensions_updates_available">
<item quantity="one">Update available</item>
@ -71,6 +72,7 @@
<string name="action_bookmark">Bookmark</string>
<string name="action_remove_bookmark">Remove bookmark</string>
<string name="action_delete">Delete</string>
<string name="action_remove_download">Remove download</string>
<string name="action_update_library">Update library</string>
<string name="action_edit">Edit</string>
<string name="action_add">Add</string>
@ -476,6 +478,7 @@
<string name="licensed">Licensed</string>
<string name="manga_info_full_title_label">Title</string>
<string name="manga_added_library">Added to library</string>
<string name="add_to_library">Add to Library</string>
<string name="manga_removed_library">Removed from library</string>
<string name="manga_info_author_label">Author</string>
<string name="manga_info_artist_label">Artist</string>
@ -494,6 +497,8 @@
<string name="source_not_installed">Source not installed: %1$s</string>
<!-- Manga chapters fragment -->
<string name="start_reader_chapter">Start Reading Chapter %1$s</string>
<string name="continue_reader_chapter">Continue Reading Chapter %1$s</string>
<string name="manga_chapters_tab">Chapters</string>
<string name="display_mode_chapter">Chapter %1$s</string>
<string name="chapter_downloaded">Downloaded</string>

@ -234,6 +234,31 @@
<item name="android:minWidth">48dip</item>
</style>
<style name="Theme.Widget.Button.RounededOutline" parent="Widget.MaterialComponents.Button.OutlinedButton">
<item name="android:layout_width">wrap_content</item>
<item name="android:textAllCaps">false</item>
<item name="android:letterSpacing">0.0</item>
<item name="android:layout_height">40dp</item>
<item name="iconTint">?colorAccent</item>
<item name="rippleColor">@color/fullRippleColor</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="cornerRadius">15dp</item>
</style>
<style name="Theme.Widget.Button.Primary" parent="Widget.MaterialComponents.Button">
<item name="android:textAllCaps">false</item>
<item name="backgroundTint">?colorAccent</item>
<item name="android:textColor">@android:color/white</item>
<item name="android:letterSpacing">0.0</item>
</style>
<style name="Theme.Widget.CustomImageButton">
<item name="android:background">@drawable/round_ripple</item>
<item name="android:clickable">true</item>
<item name="android:focusable">true</item>
<item name="android:tint">?colorAccent</item>
</style>
<!--===-->
<!--OLD-->
<!--===-->

Loading…
Cancel
Save