mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-12-23 20:21:49 +01:00
Added material library grid
Like the old grid but using a card view + play button Increased memory size of glide + remove cross fade being on by default
This commit is contained in:
parent
489ef7a5f8
commit
a07de130a9
@ -9,6 +9,7 @@ import com.bumptech.glide.annotation.GlideModule
|
||||
import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader
|
||||
import com.bumptech.glide.load.DecodeFormat
|
||||
import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory
|
||||
import com.bumptech.glide.load.engine.cache.LruResourceCache
|
||||
import com.bumptech.glide.load.model.GlideUrl
|
||||
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
|
||||
import com.bumptech.glide.module.AppGlideModule
|
||||
@ -28,8 +29,12 @@ class TachiGlideModule : AppGlideModule() {
|
||||
override fun applyOptions(context: Context, builder: GlideBuilder) {
|
||||
builder.setDiskCache(InternalCacheDiskCacheFactory(context, 50 * 1024 * 1024))
|
||||
builder.setDefaultRequestOptions(RequestOptions().format(DecodeFormat.PREFER_RGB_565))
|
||||
builder.setDefaultTransitionOptions(Drawable::class.java,
|
||||
DrawableTransitionOptions.withCrossFade())
|
||||
val memoryCacheSizeBytes = 1024 * 1024 * 100 // 1000mb
|
||||
builder.setMemoryCache(LruResourceCache(memoryCacheSizeBytes.toLong()))
|
||||
|
||||
/* builder.setDefaultTransitionOptions(
|
||||
Drawable::class.java,
|
||||
DrawableTransitionOptions.withCrossFade())*/
|
||||
}
|
||||
|
||||
override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
|
||||
|
@ -106,7 +106,7 @@ open class BrowseCatalogueController(bundle: Bundle) :
|
||||
/**
|
||||
* Recycler view with the list of results.
|
||||
*/
|
||||
private var recycler: androidx.recyclerview.widget.RecyclerView? = null
|
||||
private var recycler: RecyclerView? = null
|
||||
|
||||
/**
|
||||
* Subscription for the search view.
|
||||
@ -212,9 +212,9 @@ open class BrowseCatalogueController(bundle: Bundle) :
|
||||
private fun setupRecycler(view: View) {
|
||||
numColumnsSubscription?.unsubscribe()
|
||||
|
||||
var oldPosition = androidx.recyclerview.widget.RecyclerView.NO_POSITION
|
||||
var oldPosition = RecyclerView.NO_POSITION
|
||||
val oldRecycler = catalogue_view?.getChildAt(1)
|
||||
if (oldRecycler is androidx.recyclerview.widget.RecyclerView) {
|
||||
if (oldRecycler is RecyclerView) {
|
||||
oldPosition = (oldRecycler.layoutManager as androidx.recyclerview.widget.LinearLayoutManager).findFirstVisibleItemPosition()
|
||||
oldRecycler.adapter = null
|
||||
|
||||
@ -239,7 +239,7 @@ open class BrowseCatalogueController(bundle: Bundle) :
|
||||
(layoutManager as androidx.recyclerview.widget.GridLayoutManager).spanSizeLookup = object : androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup() {
|
||||
override fun getSpanSize(position: Int): Int {
|
||||
return when (adapter?.getItemViewType(position)) {
|
||||
R.layout.catalogue_grid_item, null -> 1
|
||||
R.layout.catalogue_mat_grid_item, null -> 1
|
||||
else -> spanCount
|
||||
}
|
||||
}
|
||||
@ -251,7 +251,7 @@ open class BrowseCatalogueController(bundle: Bundle) :
|
||||
|
||||
catalogue_view.addView(recycler, 1)
|
||||
recycler.setOnApplyWindowInsetsListener(RecyclerWindowInsetsListener)
|
||||
if (oldPosition != androidx.recyclerview.widget.RecyclerView.NO_POSITION) {
|
||||
if (oldPosition != RecyclerView.NO_POSITION) {
|
||||
recycler.layoutManager?.scrollToPosition(oldPosition)
|
||||
}
|
||||
this.recycler = recycler
|
||||
|
@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.catalogue.browse
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
import android.widget.FrameLayout
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.f2prateek.rx.preferences.Preference
|
||||
@ -15,6 +16,7 @@ import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
|
||||
import kotlinx.android.synthetic.main.catalogue_grid_item.view.*
|
||||
import kotlinx.android.synthetic.main.catalogue_mat_grid_item.view.*
|
||||
|
||||
class CatalogueItem(val manga: Manga, private val catalogueAsList: Preference<Boolean>) :
|
||||
AbstractFlexibleItem<CatalogueHolder>() {
|
||||
@ -23,19 +25,19 @@ class CatalogueItem(val manga: Manga, private val catalogueAsList: Preference<Bo
|
||||
return if (catalogueAsList.getOrDefault())
|
||||
R.layout.catalogue_list_item
|
||||
else
|
||||
R.layout.catalogue_grid_item
|
||||
R.layout.catalogue_mat_grid_item
|
||||
}
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): CatalogueHolder {
|
||||
val parent = adapter.recyclerView
|
||||
return if (parent is AutofitRecyclerView) {
|
||||
view.apply {
|
||||
card.layoutParams = FrameLayout.LayoutParams(
|
||||
MATCH_PARENT, parent.itemWidth / 3 * 4)
|
||||
gradient.layoutParams = FrameLayout.LayoutParams(
|
||||
MATCH_PARENT, parent.itemWidth / 3 * 4 / 2, Gravity.BOTTOM)
|
||||
val coverHeight = (parent.itemWidth / 3 * 4f).toInt()
|
||||
constraint_layout.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT)
|
||||
cover_thumbnail.adjustViewBounds = false
|
||||
cover_thumbnail.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, coverHeight)
|
||||
}
|
||||
CatalogueGridHolder(view, adapter)
|
||||
CatalogueMatGridHolder(view, adapter)
|
||||
} else {
|
||||
CatalogueListHolder(view, adapter)
|
||||
}
|
||||
|
@ -0,0 +1,63 @@
|
||||
package eu.kanade.tachiyomi.ui.catalogue.browse
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
|
||||
import com.bumptech.glide.signature.ObjectKey
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
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.library.LibraryCategoryAdapter
|
||||
import eu.kanade.tachiyomi.util.view.gone
|
||||
import eu.kanade.tachiyomi.widget.StateImageViewTarget
|
||||
import kotlinx.android.synthetic.main.catalogue_mat_grid_item.*
|
||||
|
||||
/**
|
||||
* Class used to hold the displayed data of a manga in the library, like the cover or the title.
|
||||
* All the elements from the layout file "item_catalogue_grid" are available in this class.
|
||||
*
|
||||
* @param view the inflated view for this holder.
|
||||
* @param adapter the adapter handling this holder.
|
||||
* @param listener a listener to react to single tap and long tap events.
|
||||
* @constructor creates a new library holder.
|
||||
*/
|
||||
class CatalogueMatGridHolder(
|
||||
private val view: View,
|
||||
private val adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>) :
|
||||
CatalogueHolder(view, adapter) {
|
||||
|
||||
/**
|
||||
* Method called from [LibraryCategoryAdapter.onBindViewHolder]. It updates the data for this
|
||||
* holder with the given manga.
|
||||
*
|
||||
* @param manga the manga item to bind.
|
||||
*/
|
||||
override fun onSetValues(manga: Manga) {
|
||||
// Update the title of the manga.
|
||||
title.text = manga.currentTitle()
|
||||
subtitle.gone()
|
||||
|
||||
bookmark_text.visibility = if (manga.favorite) View.VISIBLE else View.GONE
|
||||
|
||||
// Update the cover.
|
||||
setImage(manga)
|
||||
}
|
||||
|
||||
override fun setImage(manga: Manga) {
|
||||
if (manga.thumbnail_url == null)
|
||||
Glide.with(view.context).clear(cover_thumbnail)
|
||||
else {
|
||||
GlideApp.with(view.context)
|
||||
.load(manga)
|
||||
.diskCacheStrategy(DiskCacheStrategy.DATA)
|
||||
.centerCrop()
|
||||
.placeholder(android.R.color.transparent)
|
||||
.transition(DrawableTransitionOptions.withCrossFade())
|
||||
.into(StateImageViewTarget(cover_thumbnail, progress))
|
||||
}
|
||||
}
|
||||
}
|
@ -11,8 +11,9 @@ import eu.kanade.tachiyomi.util.lang.chop
|
||||
import eu.kanade.tachiyomi.util.lang.removeArticles
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
import java.util.Calendar
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Adapter storing a list of manga in a certain category.
|
||||
@ -29,6 +30,12 @@ class LibraryCategoryAdapter(val view: LibraryCategoryView) :
|
||||
|
||||
val onItemReleaseListener: CategoryAdapter.OnItemReleaseListener = view
|
||||
|
||||
|
||||
/**
|
||||
* Listener called when an item of the list press start reading.
|
||||
*/
|
||||
val libraryListener: LibraryListener = view
|
||||
|
||||
/**
|
||||
* Sets a list of manga in the adapter.
|
||||
*
|
||||
@ -133,4 +140,10 @@ class LibraryCategoryAdapter(val view: LibraryCategoryView) :
|
||||
SimpleDateFormat("yyyy", Locale.getDefault()).format(date)
|
||||
}
|
||||
|
||||
interface LibraryListener {
|
||||
/**
|
||||
* Called when an item of the list is released.
|
||||
*/
|
||||
fun startReading(position: Int)
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
||||
FlexibleAdapter.OnItemClickListener,
|
||||
FlexibleAdapter.OnItemLongClickListener,
|
||||
FlexibleAdapter.OnItemMoveListener,
|
||||
LibraryCategoryAdapter.LibraryListener,
|
||||
CategoryAdapter.OnItemReleaseListener {
|
||||
|
||||
/**
|
||||
@ -96,6 +97,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
||||
recycler.adapter = adapter
|
||||
swipe_refresh.addView(recycler)
|
||||
adapter.fastScroller = fast_scroller
|
||||
// recycler.addOnScrollListener(adapter.preloader())
|
||||
|
||||
if (::category.isInitialized) {
|
||||
val mangaForCategory = controller.presenter.getMangaInCategory(category.id)
|
||||
@ -332,6 +334,11 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
||||
if (adapter.selectedItemCount == 0) saveDragSort()
|
||||
}
|
||||
|
||||
override fun startReading(position: Int) {
|
||||
val manga = adapter.getItem(position)?.manga ?: return
|
||||
controller.startReading(manga)
|
||||
}
|
||||
|
||||
private fun saveDragSort() {
|
||||
val mangaIds = adapter.currentItems.mapNotNull { it.manga.id }
|
||||
category.mangaSort = null
|
||||
|
@ -1,6 +1,8 @@
|
||||
package eu.kanade.tachiyomi.ui.library
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
@ -50,6 +52,7 @@ 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
|
||||
import eu.kanade.tachiyomi.ui.migration.manga.process.MigrationProcedureConfig
|
||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
import eu.kanade.tachiyomi.util.system.launchUI
|
||||
import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener
|
||||
@ -141,6 +144,8 @@ class LibraryController(
|
||||
|
||||
private var tabsVisibilitySubscription: Subscription? = null
|
||||
|
||||
private var observeLater:Boolean = false
|
||||
|
||||
var snack: Snackbar? = null
|
||||
|
||||
var presenter = LibraryPresenter(this)
|
||||
@ -165,22 +170,11 @@ class LibraryController(
|
||||
|
||||
adapter = LibraryAdapter(this)
|
||||
library_pager.adapter = adapter
|
||||
|
||||
library_pager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
|
||||
override fun onPageSelected(position: Int) {
|
||||
preferences.lastUsedCategory().set(position)
|
||||
activeCategory = position
|
||||
}
|
||||
|
||||
override fun onPageScrollStateChanged(state: Int) {}
|
||||
|
||||
override fun onPageScrolled(
|
||||
position: Int, positionOffset: Float, positionOffsetPixels: Int
|
||||
) {
|
||||
}
|
||||
})
|
||||
|
||||
library_pager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
|
||||
override fun onPageSelected(position: Int) {
|
||||
bottom_sheet.lastCategory = adapter?.categories?.getOrNull(position)
|
||||
if (preferences.librarySortingMode().getOrDefault() == LibrarySort.DRAG_AND_DROP) bottom_sheet.updateTitle()
|
||||
}
|
||||
@ -227,6 +221,7 @@ class LibraryController(
|
||||
val library = presenter.getAllManga()
|
||||
if (library != null) onNextLibraryUpdate(presenter.categories, library)
|
||||
else {
|
||||
library_pager.alpha = 0f
|
||||
presenter.getLibraryBlocking()
|
||||
}
|
||||
}
|
||||
@ -242,6 +237,14 @@ class LibraryController(
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResumed(activity: Activity) {
|
||||
super.onActivityResumed(activity)
|
||||
if (observeLater) {
|
||||
presenter.getLibrary()
|
||||
observeLater = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
presenter.onDestroy()
|
||||
super.onDestroy()
|
||||
@ -345,6 +348,8 @@ class LibraryController(
|
||||
}
|
||||
else if (!freshStart) {
|
||||
justStarted = false
|
||||
if (library_pager.alpha == 0f)
|
||||
library_pager.animate().alpha(1f).setDuration(500).start()
|
||||
}
|
||||
}
|
||||
|
||||
@ -666,6 +671,14 @@ class LibraryController(
|
||||
presenter.moveMangasToCategories(categories, mangas)
|
||||
destroyActionModeIfNeeded()
|
||||
}
|
||||
|
||||
fun startReading(manga: Manga) {
|
||||
val activity = activity ?: return
|
||||
val chapter = presenter.getFirstUnread(manga) ?: return
|
||||
val intent = ReaderActivity.newIntent(activity, manga, chapter)
|
||||
observeLater = true
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
object HeightTopWindowInsetsListener : View.OnApplyWindowInsetsListener {
|
||||
|
@ -25,7 +25,7 @@ import kotlinx.android.synthetic.main.catalogue_grid_item.*
|
||||
*/
|
||||
class LibraryGridHolder(
|
||||
private val view: View,
|
||||
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>
|
||||
adapter: LibraryCategoryAdapter
|
||||
|
||||
) : LibraryHolder(view, adapter) {
|
||||
|
||||
|
@ -15,7 +15,7 @@ import eu.davidea.flexibleadapter.items.IFlexible
|
||||
|
||||
abstract class LibraryHolder(
|
||||
view: View,
|
||||
val adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>
|
||||
val adapter: LibraryCategoryAdapter
|
||||
) : BaseFlexibleViewHolder(view, adapter) {
|
||||
|
||||
/**
|
||||
|
@ -1,10 +1,7 @@
|
||||
package eu.kanade.tachiyomi.ui.library
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||
import android.widget.FrameLayout
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.f2prateek.rx.preferences.Preference
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
@ -15,10 +12,10 @@ import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
|
||||
import kotlinx.android.synthetic.main.catalogue_grid_item.view.*
|
||||
import kotlinx.android.synthetic.main.catalogue_mat_grid_item.view.*
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.io.Serializable
|
||||
|
||||
class LibraryItem(val manga: LibraryManga, private val libraryAsList: Preference<Boolean>) :
|
||||
AbstractFlexibleItem<LibraryHolder>(), IFilterable<String> {
|
||||
@ -30,21 +27,21 @@ class LibraryItem(val manga: LibraryManga, private val libraryAsList: Preference
|
||||
return if (libraryAsList.getOrDefault())
|
||||
R.layout.catalogue_list_item
|
||||
else
|
||||
R.layout.catalogue_grid_item
|
||||
R.layout.catalogue_mat_grid_item
|
||||
}
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): LibraryHolder {
|
||||
val parent = adapter.recyclerView
|
||||
return if (parent is AutofitRecyclerView) {
|
||||
view.apply {
|
||||
val coverHeight = parent.itemWidth / 3 * 4
|
||||
card.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, coverHeight)
|
||||
gradient.layoutParams = FrameLayout.LayoutParams(
|
||||
MATCH_PARENT, coverHeight / 2, Gravity.BOTTOM)
|
||||
val coverHeight = (parent.itemWidth / 3 * 4f).toInt()
|
||||
constraint_layout.minHeight = coverHeight
|
||||
}
|
||||
LibraryGridHolder(view, adapter)
|
||||
LibraryMatGridHolder(view, adapter as LibraryCategoryAdapter, parent.itemWidth - 22.dpToPx, parent
|
||||
.spanCount)
|
||||
|
||||
} else {
|
||||
LibraryListHolder(view, adapter)
|
||||
LibraryListHolder(view, adapter as LibraryCategoryAdapter)
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,7 +49,6 @@ class LibraryItem(val manga: LibraryManga, private val libraryAsList: Preference
|
||||
holder: LibraryHolder,
|
||||
position: Int,
|
||||
payloads: MutableList<Any?>?) {
|
||||
|
||||
holder.onSetValues(this)
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ import kotlinx.android.synthetic.main.catalogue_list_item.unread_text
|
||||
|
||||
class LibraryListHolder(
|
||||
private val view: View,
|
||||
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>
|
||||
adapter: LibraryCategoryAdapter
|
||||
) : LibraryHolder(view, adapter) {
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,99 @@
|
||||
package eu.kanade.tachiyomi.ui.library
|
||||
|
||||
import android.view.View
|
||||
import com.bumptech.glide.Glide
|
||||
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.MangaImpl
|
||||
import eu.kanade.tachiyomi.data.glide.GlideApp
|
||||
import eu.kanade.tachiyomi.source.LocalSource
|
||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
import kotlinx.android.synthetic.main.catalogue_mat_grid_item.*
|
||||
import kotlinx.android.synthetic.main.catalogue_mat_grid_item.view.*
|
||||
|
||||
/**
|
||||
* Class used to hold the displayed data of a manga in the library, like the cover or the title.
|
||||
* All the elements from the layout file "item_catalogue_grid" are available in this class.
|
||||
*
|
||||
* @param view the inflated view for this holder.
|
||||
* @param adapter the adapter handling this holder.
|
||||
* @param listener a listener to react to single tap and long tap events.
|
||||
* @constructor creates a new library holder.
|
||||
*/
|
||||
class LibraryMatGridHolder(
|
||||
private val view: View,
|
||||
adapter: LibraryCategoryAdapter,
|
||||
var width:Int,
|
||||
var rowCount: Int
|
||||
) : LibraryHolder(view, adapter) {
|
||||
|
||||
/**
|
||||
* Method called from [LibraryCategoryAdapter.onBindViewHolder]. It updates the data for this
|
||||
* holder with the given manga.
|
||||
*
|
||||
* @param item the manga item to bind.
|
||||
*/
|
||||
override fun onSetValues(item: LibraryItem) {
|
||||
// Update the title of the manga.
|
||||
title.text = item.manga.currentTitle()
|
||||
|
||||
// Update the unread count and its visibility.
|
||||
val unread = item.manga.unread
|
||||
|
||||
// Update the subtitle of the manga with artist or the unread count
|
||||
with(subtitle) {
|
||||
text = when {
|
||||
item.manga.unread > 0 -> when (item.unreadType) {
|
||||
1 -> view.resources.getQuantityString(R.plurals.unread_count, unread, unread)
|
||||
0 -> view.resources.getString(R.string.new_chapter)
|
||||
else -> item.manga.originalAuthor()
|
||||
}
|
||||
else -> item.manga.originalAuthor()
|
||||
}
|
||||
setTextColor(
|
||||
view.context.getResourceColor(
|
||||
if (item.manga.unread > 0 && item.unreadType > -1) android.R.attr.colorAccent
|
||||
else android.R.attr.textColorSecondary
|
||||
)
|
||||
)
|
||||
}
|
||||
play_button.visibility = if (unread > 0) View.VISIBLE else View.GONE
|
||||
play_button.setOnClickListener { playButtonClicked() }
|
||||
|
||||
// Update the download count and its visibility.
|
||||
with(download_text) {
|
||||
visibility = if (item.downloadCount > 0) View.VISIBLE else View.GONE
|
||||
text = item.downloadCount.toString()
|
||||
}
|
||||
// Set local visibility if its local manga
|
||||
local_text.visibility = if (item.manga.source == LocalSource.ID) View.VISIBLE else View.GONE
|
||||
|
||||
// Update the cover.
|
||||
if (item.manga.thumbnail_url == null) Glide.with(view.context).clear(cover_thumbnail)
|
||||
else {
|
||||
val id = item.manga.id ?: return
|
||||
GlideApp.with(view.context).load(item.manga)
|
||||
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
|
||||
.signature(ObjectKey(MangaImpl.getLastCoverFetch(id).toString()))
|
||||
.into(cover_thumbnail)
|
||||
}
|
||||
}
|
||||
|
||||
private fun playButtonClicked() {
|
||||
adapter.libraryListener.startReading(adapterPosition)
|
||||
}
|
||||
|
||||
override fun onActionStateChanged(position: Int, actionState: Int) {
|
||||
super.onActionStateChanged(position, actionState)
|
||||
if (actionState == 2) {
|
||||
view.card.isDragged = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onItemReleased(position: Int) {
|
||||
super.onItemReleased(position)
|
||||
view.card.isDragged = false
|
||||
}
|
||||
|
||||
}
|
@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.library
|
||||
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.LibraryManga
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaCategory
|
||||
@ -16,6 +17,7 @@ import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.ui.manga.chapter.ChapterItem
|
||||
import eu.kanade.tachiyomi.ui.migration.MigrationFlags
|
||||
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
||||
import eu.kanade.tachiyomi.util.lang.removeArticles
|
||||
@ -27,16 +29,20 @@ import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.Comp
|
||||
import kotlinx.coroutines.CoroutineStart
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import rx.Observable
|
||||
import rx.Subscription
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import rx.schedulers.Schedulers
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.util.ArrayList
|
||||
import java.util.Calendar
|
||||
import java.util.Collections
|
||||
import java.util.Comparator
|
||||
import java.util.Date
|
||||
|
||||
/**
|
||||
* Class containing library information.
|
||||
@ -78,6 +84,8 @@ class LibraryPresenter(
|
||||
|
||||
private var currentMangaMap:LibraryMap? = null
|
||||
|
||||
private var readerSubscription: Subscription? = null
|
||||
|
||||
fun isDownloading() = downloadManager.hasQueue()
|
||||
|
||||
fun onDestroy() {
|
||||
@ -684,6 +692,12 @@ class LibraryPresenter(
|
||||
}
|
||||
}
|
||||
|
||||
fun getFirstUnread(manga: Manga): Chapter? {
|
||||
val chapters = db.getChapters(manga).executeAsBlocking()
|
||||
return chapters.sortedByDescending { it.source_order }.find { !it.read }
|
||||
|
||||
}
|
||||
|
||||
private companion object {
|
||||
var currentLibrary:Library? = null
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.afollestad.materialdialogs.list.listItemsSingleChoice
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
|
||||
import com.bumptech.glide.request.target.CustomTarget
|
||||
import com.bumptech.glide.request.transition.Transition
|
||||
import com.bumptech.glide.signature.ObjectKey
|
||||
@ -333,6 +334,7 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
|
||||
.load(manga)
|
||||
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
|
||||
.signature(ObjectKey(MangaImpl.getLastCoverFetch(manga.id!!).toString()))
|
||||
.transition(DrawableTransitionOptions.withCrossFade())
|
||||
//.centerCrop()
|
||||
.into(manga_cover)
|
||||
if (manga_cover_full != null) {
|
||||
@ -356,6 +358,7 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
|
||||
.load(manga)
|
||||
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
|
||||
.signature(ObjectKey(MangaImpl.getLastCoverFetch(manga.id!!).toString()))
|
||||
.transition(DrawableTransitionOptions.withCrossFade())
|
||||
.centerCrop()
|
||||
.into(backdrop)
|
||||
}
|
||||
|
@ -119,20 +119,6 @@ class ReaderPresenter(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the presenter is destroyed. It saves the current progress and cleans up
|
||||
* references on the currently active chapters.
|
||||
*/
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
val currentChapters = viewerChaptersRelay.value
|
||||
if (currentChapters != null) {
|
||||
currentChapters.unref()
|
||||
saveChapterProgress(currentChapters.currChapter)
|
||||
saveChapterHistory(currentChapters.currChapter)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the presenter instance is being saved. It saves the currently active chapter
|
||||
* id and the last page read.
|
||||
@ -152,6 +138,12 @@ class ReaderPresenter(
|
||||
*/
|
||||
fun onBackPressed() {
|
||||
deletePendingChapters()
|
||||
val currentChapters = viewerChaptersRelay.value
|
||||
if (currentChapters != null) {
|
||||
currentChapters.unref()
|
||||
saveChapterProgress(currentChapters.currChapter)
|
||||
saveChapterHistory(currentChapters.currChapter)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -364,10 +356,7 @@ class ReaderPresenter(
|
||||
*/
|
||||
private fun saveChapterHistory(chapter: ReaderChapter) {
|
||||
val history = History.create(chapter.chapter).apply { last_read = Date().time }
|
||||
db.updateHistoryLastRead(history).asRxCompletable()
|
||||
.onErrorComplete()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe()
|
||||
db.updateHistoryLastRead(history).executeAsBlocking()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -9,7 +9,7 @@ import kotlin.math.max
|
||||
class AutofitRecyclerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
androidx.recyclerview.widget.RecyclerView(context, attrs) {
|
||||
|
||||
private val manager = androidx.recyclerview.widget.GridLayoutManager(context, 1)
|
||||
private val manager = GridLayoutManager(context, 1)
|
||||
|
||||
private var columnWidth = -1
|
||||
|
||||
|
35
app/src/main/res/drawable/library_item_selector.xml
Normal file
35
app/src/main/res/drawable/library_item_selector.xml
Normal file
@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/selectorColor">
|
||||
<item android:id="@android:id/mask"
|
||||
android:top="0dp"
|
||||
android:bottom="12dp"
|
||||
android:left="4dp"
|
||||
android:right="4dp">
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="4dp" />
|
||||
<solid android:color="@color/selectorColor" />
|
||||
</shape>
|
||||
</item>
|
||||
<item
|
||||
android:top="0dp"
|
||||
android:bottom="12dp"
|
||||
android:left="4dp"
|
||||
android:right="4dp">
|
||||
<selector>
|
||||
<item android:state_selected="true">
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="4dp" />
|
||||
<solid android:color="@color/selectorColor" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item android:state_activated="true">
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="4dp" />
|
||||
<solid android:color="@color/selectorColor" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
</item>
|
||||
</ripple>
|
24
app/src/main/res/drawable/round_play_background.xml
Normal file
24
app/src/main/res/drawable/round_play_background.xml
Normal file
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:background="#FFFF00"
|
||||
android:color="?android:attr/colorAccent">
|
||||
<item android:id="@android:id/mask">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="?android:attr/colorAccent" />
|
||||
<corners android:radius="13dp" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item android:id="@android:id/background">
|
||||
<shape
|
||||
android:shape="rectangle">
|
||||
<corners android:radius="16dp"/>
|
||||
<size
|
||||
android:height="32dp"
|
||||
android:width="32dp" />
|
||||
<solid android:color="#AD212121"/>
|
||||
<stroke android:width="0.1dp" android:color="#EDEDED" />
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
@ -3,5 +3,5 @@
|
||||
<gradient
|
||||
android:angle="90"
|
||||
android:endColor="@android:color/transparent"
|
||||
android:startColor="?android:attr/textColorSecondary"/>
|
||||
android:startColor="@color/md_black_1000_54"/>
|
||||
</shape>
|
187
app/src/main/res/layout/catalogue_mat_grid_item.xml
Normal file
187
app/src/main/res/layout/catalogue_mat_grid_item.xml
Normal file
@ -0,0 +1,187 @@
|
||||
<?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"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/manga_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/constraint_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:background="@drawable/library_item_selector"
|
||||
android:minHeight="200dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_marginStart="6dp"
|
||||
android:layout_marginEnd="6dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/title"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="1.0">
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/cover_thumbnail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:adjustViewBounds="true"
|
||||
android:background="?android:attr/colorBackground"
|
||||
android:maxHeight="250dp"
|
||||
app:layout_constrainedHeight="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:background="?android:attr/colorBackground"
|
||||
tools:ignore="ContentDescription"
|
||||
tools:src="@mipmap/ic_launcher" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/badge_layout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginTop="4dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/local_text"
|
||||
style="@style/TextAppearance.Regular.Caption.Light"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/md_teal_500"
|
||||
android:paddingStart="3dp"
|
||||
android:paddingTop="1dp"
|
||||
android:paddingEnd="3dp"
|
||||
android:paddingBottom="1dp"
|
||||
android:text="@string/local_source_badge"
|
||||
android:textColor="@color/md_white_1000"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:visibility="visible" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/download_text"
|
||||
style="@style/TextAppearance.Regular.Caption.Light"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="-10dp"
|
||||
android:layout_marginTop="-10dp"
|
||||
android:background="@drawable/dialog_rounded_background"
|
||||
android:backgroundTint="@color/md_red_500"
|
||||
android:gravity="start|center"
|
||||
android:paddingStart="14dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingEnd="5dp"
|
||||
android:paddingBottom="3dp"
|
||||
android:textColor="@color/md_white_1000"
|
||||
android:textSize="13sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/local_text"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="1" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bookmark_text"
|
||||
style="@style/TextAppearance.Regular.Caption.Light"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="-10dp"
|
||||
android:layout_marginTop="-10dp"
|
||||
android:background="@drawable/dialog_rounded_background"
|
||||
android:backgroundTint="@color/md_blue_A400_87"
|
||||
android:gravity="start|center"
|
||||
android:paddingStart="14dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingEnd="5dp"
|
||||
android:paddingBottom="3dp"
|
||||
android:textColor="@color/md_white_1000"
|
||||
android:textSize="13sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/local_text"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:text="@string/in_library"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/play_button"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_gravity="end|bottom"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:background="@drawable/round_play_background"
|
||||
android:contentDescription="@string/start_reading"
|
||||
android:src="@drawable/ic_play_arrow_white_24dp"
|
||||
android:tint="@android:color/white"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress"
|
||||
style="?android:attr/progressBarStyleSmall"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/title"
|
||||
style="@style/TextAppearance.Regular.Body1.Light"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="6dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:ellipsize="end"
|
||||
android:lineSpacingExtra="-4dp"
|
||||
android:singleLine="true"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/subtitle"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:text="Sample name\nsdf" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/subtitle"
|
||||
style="@style/TextAppearance.Regular.Body1.Light"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="14dp"
|
||||
android:ellipsize="end"
|
||||
android:lineSpacingExtra="-4dp"
|
||||
android:paddingStart="6dp"
|
||||
android:paddingEnd="6dp"
|
||||
android:singleLine="true"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:text="Sample artist" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</FrameLayout>
|
@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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"
|
||||
@ -7,4 +8,4 @@
|
||||
android:layout_height="match_parent"
|
||||
android:columnWidth="140dp"
|
||||
android:clipToPadding="false"
|
||||
tools:listitem="@layout/catalogue_grid_item" />
|
||||
tools:listitem="@layout/catalogue_mat_grid_item" />
|
@ -33,10 +33,10 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="20dp"
|
||||
android:background="@drawable/shape_gradient_top_shadow"
|
||||
android:backgroundTint="@color/md_black_1000_54"
|
||||
android:paddingBottom="10dp"
|
||||
app:layout_anchorGravity="top"
|
||||
app:layout_anchor="@id/bottom_sheet" />
|
||||
|
||||
<!-- Adding bottom sheet after main content -->
|
||||
<include layout="@layout/filter_bottom_sheet"/>
|
||||
|
||||
|
@ -49,6 +49,7 @@
|
||||
|
||||
<!-- Text Colors -->
|
||||
<color name="md_black_1000_87">#DE000000</color>
|
||||
<color name="md_black_1000_70">#B3000000</color>
|
||||
<color name="md_black_1000_54">#8A000000</color>
|
||||
<color name="md_black_1000_38">#61000000</color>
|
||||
<color name="md_black_1000_12">#1F000000</color>
|
||||
@ -71,10 +72,13 @@
|
||||
<color name="md_blue_A200_50">#80448AFF</color>
|
||||
|
||||
<color name="md_blue_A400">#2979FF</color>
|
||||
<color name="md_blue_A400_87">#DE2979FF</color>
|
||||
<color name="md_blue_A400_38">#612979FF</color>
|
||||
|
||||
<color name="md_red_500">#F44336</color>
|
||||
|
||||
<color name="md_red_500_87">#DEF44336</color>
|
||||
|
||||
<color name="md_teal_500">#009688</color>
|
||||
|
||||
</resources>
|
||||
|
@ -409,6 +409,12 @@
|
||||
<string name="category_already_in_queue">%1$s is already in queue</string>
|
||||
<string name="local_source_badge">Local</string>
|
||||
<string name="confirm_manga_deletion">Remove from library?</string>
|
||||
<plurals name="unread_count">
|
||||
<item quantity="one">New chapter</item>
|
||||
<item quantity="other">%d Unread</item>
|
||||
</plurals>
|
||||
<string name="new_chapter">New</string>
|
||||
<string name="start_reading">Start Reading</string>
|
||||
|
||||
<!-- Catalogue fragment -->
|
||||
<string name="source_search_options">Search filters</string>
|
||||
@ -419,6 +425,7 @@
|
||||
<string name="action_global_search_hint">Global search…</string>
|
||||
<string name="latest">Latest</string>
|
||||
<string name="browse">Browse</string>
|
||||
<string name="in_library">In Library</string>
|
||||
|
||||
<!-- Manga activity -->
|
||||
<string name="manga_not_in_db">This manga has been removed from the database.</string>
|
||||
|
Loading…
Reference in New Issue
Block a user