Attempting making library as one big list

pull/3117/head
Jay 5 years ago
parent fc0ab3e878
commit 83441a5ebd

@ -113,6 +113,8 @@ object PreferenceKeys {
const val libraryLayout = "pref_display_library_layout" const val libraryLayout = "pref_display_library_layout"
const val libraryUsingPager = "library_using_pager"
const val lang = "app_language" const val lang = "app_language"
const val dateFormat = "app_date_format" const val dateFormat = "app_date_format"

@ -11,9 +11,9 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import java.io.File import java.io.File
import java.util.Locale
import java.text.DateFormat import java.text.DateFormat
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
fun <T> Preference<T>.getOrDefault(): T = get() ?: defaultValue()!! fun <T> Preference<T>.getOrDefault(): T = get() ?: defaultValue()!!
@ -173,6 +173,8 @@ class PreferencesHelper(val context: Context) {
fun libraryLayout() = rxPrefs.getInteger(Keys.libraryLayout, 1) fun libraryLayout() = rxPrefs.getInteger(Keys.libraryLayout, 1)
fun libraryUsingPager() = rxPrefs.getBoolean(Keys.libraryUsingPager, false)
fun downloadBadge() = rxPrefs.getBoolean(Keys.downloadBadge, false) fun downloadBadge() = rxPrefs.getBoolean(Keys.downloadBadge, false)
fun filterDownloaded() = rxPrefs.getInteger(Keys.filterDownloaded, 0) fun filterDownloaded() = rxPrefs.getInteger(Keys.filterDownloaded, 0)

@ -6,11 +6,10 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.ui.category.CategoryAdapter
import eu.kanade.tachiyomi.util.lang.chop import eu.kanade.tachiyomi.util.lang.chop
import eu.kanade.tachiyomi.util.lang.removeArticles import eu.kanade.tachiyomi.util.lang.removeArticles
import eu.kanade.tachiyomi.util.system.launchUI import uy.kohesive.injekt.Injekt
import kotlinx.coroutines.delay import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Calendar import java.util.Calendar
@ -22,19 +21,18 @@ import java.util.Locale
* *
* @param view the fragment containing this adapter. * @param view the fragment containing this adapter.
*/ */
class LibraryCategoryAdapter(val view: LibraryCategoryView) : class LibraryCategoryAdapter(val libraryListener: LibraryListener) :
FlexibleAdapter<LibraryItem>(null, view, true) { FlexibleAdapter<IFlexible<*>>(null, libraryListener, true) {
init {
setDisplayHeadersAtStartUp(!Injekt.get<PreferencesHelper>().libraryUsingPager()
.getOrDefault())
}
/** /**
* The list of manga in this category. * The list of manga in this category.
*/ */
private var mangas: List<LibraryItem> = emptyList() private var mangas: List<LibraryItem> = emptyList()
/**
* Listener called when an item of the list press start reading.
*/
val libraryListener: LibraryListener = view
/** /**
* Sets a list of manga in the adapter. * Sets a list of manga in the adapter.
* *
@ -47,13 +45,26 @@ class LibraryCategoryAdapter(val view: LibraryCategoryView) :
performFilter() performFilter()
} }
/**
* Returns the position in the adapter for the given manga.
*
* @param manga the manga to find.
*/
fun indexOf(categoryOrder: Int): Int {
return currentItems.indexOfFirst {
if (it is LibraryHeaderItem) it.category.order == categoryOrder
else false }
}
/** /**
* Returns the position in the adapter for the given manga. * Returns the position in the adapter for the given manga.
* *
* @param manga the manga to find. * @param manga the manga to find.
*/ */
fun indexOf(manga: Manga): Int { fun indexOf(manga: Manga): Int {
return currentItems.indexOfFirst { it.manga.id == manga.id } return currentItems.indexOfFirst {
if (it is LibraryItem) it.manga.id == manga.id
else false }
} }
fun performFilter() { fun performFilter() {
@ -64,7 +75,7 @@ class LibraryCategoryAdapter(val view: LibraryCategoryView) :
else { else {
updateDataSet(mangas.filter { it.filter(s) }) updateDataSet(mangas.filter { it.filter(s) })
} }
isLongPressDragEnabled = view.canDrag() && s.isNullOrBlank() isLongPressDragEnabled = libraryListener.canDrag() && s.isNullOrBlank()
} }
override fun onCreateBubbleText(position: Int):String { override fun onCreateBubbleText(position: Int):String {
@ -74,6 +85,13 @@ class LibraryCategoryAdapter(val view: LibraryCategoryView) :
"Bottom" "Bottom"
} else { // Get and show the first character } else { // Get and show the first character
val iFlexible: IFlexible<*>? = getItem(position) val iFlexible: IFlexible<*>? = getItem(position)
return if (iFlexible is LibraryHeaderItem) {
iFlexible.category.name
} else {
val db:DatabaseHelper by injectLazy()
val category = db.getCategoriesForManga((iFlexible as LibraryItem).manga).executeAsBlocking().firstOrNull()?.name
category?.chop(10) ?: "Default"
}
val preferences:PreferencesHelper by injectLazy() val preferences:PreferencesHelper by injectLazy()
when (preferences.librarySortingMode().getOrDefault()) { when (preferences.librarySortingMode().getOrDefault()) {
LibrarySort.DRAG_AND_DROP -> { LibrarySort.DRAG_AND_DROP -> {
@ -145,5 +163,6 @@ class LibraryCategoryAdapter(val view: LibraryCategoryView) :
*/ */
fun startReading(position: Int) fun startReading(position: Int)
fun onItemReleased(position: Int) fun onItemReleased(position: Int)
fun canDrag(): Boolean
} }
} }

@ -79,6 +79,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
fun onCreate(controller: LibraryController) { fun onCreate(controller: LibraryController) {
this.controller = controller this.controller = controller
adapter = LibraryCategoryAdapter(this)
recycler = if (preferences.libraryLayout().getOrDefault() == 0) { recycler = if (preferences.libraryLayout().getOrDefault() == 0) {
(swipe_refresh.inflate(R.layout.library_list_recycler) as RecyclerView).apply { (swipe_refresh.inflate(R.layout.library_list_recycler) as RecyclerView).apply {
layoutManager = LinearLayoutManager(context) layoutManager = LinearLayoutManager(context)
@ -89,7 +90,6 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
} }
} }
adapter = LibraryCategoryAdapter(this)
recycler.setHasFixedSize(true) recycler.setHasFixedSize(true)
recycler.adapter = adapter recycler.adapter = adapter
@ -155,7 +155,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
.subscribe { .subscribe {
if (it == category.id) { if (it == category.id) {
adapter.currentItems.forEach { item -> adapter.currentItems.forEach { item ->
controller.setSelection(item.manga, true) controller.setSelection((item as LibraryItem).manga, true)
} }
controller.invalidateActionMode() controller.invalidateActionMode()
} }
@ -167,11 +167,12 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
if (it.second in -2..-1) { if (it.second in -2..-1) {
val items = adapter.currentItems.toMutableList() val items = adapter.currentItems.toMutableList()
val mangas = controller.selectedMangas val mangas = controller.selectedMangas
val selectedManga = items.filter { item -> item.manga in mangas } val selectedManga = items.filter { item -> (item as LibraryItem).manga in
mangas }
items.removeAll(selectedManga) items.removeAll(selectedManga)
if (it.second == -1) items.addAll(0, selectedManga) if (it.second == -1) items.addAll(0, selectedManga)
else items.addAll(selectedManga) else items.addAll(selectedManga)
adapter.setItems(items) adapter.setItems(items.filterIsInstance<LibraryItem>())
adapter.notifyDataSetChanged() adapter.notifyDataSetChanged()
saveDragSort() saveDragSort()
} }
@ -179,7 +180,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
} }
} }
fun canDrag(): Boolean { override fun canDrag(): Boolean {
val sortingMode = preferences.librarySortingMode().getOrDefault() val sortingMode = preferences.librarySortingMode().getOrDefault()
val filterOff = preferences.filterCompleted().getOrDefault() + val filterOff = preferences.filterCompleted().getOrDefault() +
preferences.filterTracked().getOrDefault() + preferences.filterTracked().getOrDefault() +
@ -212,6 +213,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
val mangaForCategory = event.getMangaForCategory(category).orEmpty() val mangaForCategory = event.getMangaForCategory(category).orEmpty()
adapter.setItems(mangaForCategory) adapter.setItems(mangaForCategory)
adapter.hideAllHeaders()
swipe_refresh.isEnabled = !preferences.hideCategories().getOrDefault() swipe_refresh.isEnabled = !preferences.hideCategories().getOrDefault()
@ -283,7 +285,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
*/ */
override fun onItemClick(view: View?, position: Int): Boolean { override fun onItemClick(view: View?, position: Int): Boolean {
// If the action mode is created and the position is valid, toggle the selection. // If the action mode is created and the position is valid, toggle the selection.
val item = adapter.getItem(position) ?: return false val item = adapter.getItem(position) as? LibraryItem ?: return false
return if (adapter.mode == SelectableAdapter.Mode.MULTI) { return if (adapter.mode == SelectableAdapter.Mode.MULTI) {
lastClickPosition = position lastClickPosition = position
toggleSelection(position) toggleSelection(position)
@ -328,13 +330,13 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
} }
override fun startReading(position: Int) { override fun startReading(position: Int) {
val manga = adapter.getItem(position)?.manga ?: return val manga = (adapter.getItem(position) as? LibraryItem)?.manga ?: return
if (adapter.mode == SelectableAdapter.Mode.MULTI) toggleSelection(position) if (adapter.mode == SelectableAdapter.Mode.MULTI) toggleSelection(position)
else controller.startReading(manga) else controller.startReading(manga)
} }
private fun saveDragSort() { private fun saveDragSort() {
val mangaIds = adapter.currentItems.mapNotNull { it.manga.id } val mangaIds = adapter.currentItems.mapNotNull { (it as? LibraryItem)?.manga?.id }
category.mangaSort = null category.mangaSort = null
category.mangaOrder = mangaIds category.mangaOrder = mangaIds
if (category.id == 0) if (category.id == 0)
@ -373,7 +375,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
private fun toggleSelection(position: Int) { private fun toggleSelection(position: Int) {
val item = adapter.getItem(position) ?: return val item = adapter.getItem(position) ?: return
controller.setSelection(item.manga, !adapter.isSelected(position)) controller.setSelection((item as LibraryItem).manga, !adapter.isSelected(position))
controller.invalidateActionMode() controller.invalidateActionMode()
} }
@ -386,7 +388,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
private fun setSelection(position: Int) { private fun setSelection(position: Int) {
val item = adapter.getItem(position) ?: return val item = adapter.getItem(position) ?: return
controller.setSelection(item.manga, true) controller.setSelection((item as LibraryItem).manga, true)
controller.invalidateActionMode() controller.invalidateActionMode()
} }

@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.ui.library
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
@ -14,10 +13,18 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.WindowInsets import android.view.WindowInsets
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.ArrayAdapter
import android.widget.Spinner
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode import androidx.appcompat.view.ActionMode
import androidx.appcompat.widget.AppCompatSpinner
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.graphics.drawable.DrawableCompat import androidx.core.graphics.drawable.DrawableCompat
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager.widget.ViewPager import androidx.viewpager.widget.ViewPager
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeHandler
@ -26,9 +33,10 @@ import com.f2prateek.rx.preferences.Preference
import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.snackbar.BaseTransientBottomBar import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.google.android.material.tabs.TabLayout
import com.jakewharton.rxrelay.BehaviorRelay import com.jakewharton.rxrelay.BehaviorRelay
import com.jakewharton.rxrelay.PublishRelay import com.jakewharton.rxrelay.PublishRelay
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.SelectableAdapter
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Category import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.LibraryManga import eu.kanade.tachiyomi.data.database.models.LibraryManga
@ -40,7 +48,6 @@ import eu.kanade.tachiyomi.data.library.LibraryUpdateService
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.ui.base.controller.BaseController import eu.kanade.tachiyomi.ui.base.controller.BaseController
import eu.kanade.tachiyomi.ui.base.controller.TabbedController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.category.CategoryController import eu.kanade.tachiyomi.ui.category.CategoryController
import eu.kanade.tachiyomi.ui.download.DownloadController import eu.kanade.tachiyomi.ui.download.DownloadController
@ -53,14 +60,20 @@ 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.MigrationListController
import eu.kanade.tachiyomi.ui.migration.manga.process.MigrationProcedureConfig import eu.kanade.tachiyomi.ui.migration.manga.process.MigrationProcedureConfig
import eu.kanade.tachiyomi.ui.reader.ReaderActivity import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.system.launchUI import eu.kanade.tachiyomi.util.system.launchUI
import eu.kanade.tachiyomi.util.view.gone
import eu.kanade.tachiyomi.util.view.inflate
import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener
import eu.kanade.tachiyomi.util.view.snack import eu.kanade.tachiyomi.util.view.snack
import eu.kanade.tachiyomi.util.view.updateLayoutParams
import eu.kanade.tachiyomi.util.view.updatePaddingRelative
import eu.kanade.tachiyomi.util.view.visible
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
import eu.kanade.tachiyomi.widget.IgnoreFirstSpinnerListener
import kotlinx.android.synthetic.main.filter_bottom_sheet.* import kotlinx.android.synthetic.main.filter_bottom_sheet.*
import kotlinx.android.synthetic.main.library_controller.* import kotlinx.android.synthetic.main.library_controller.*
import kotlinx.android.synthetic.main.main_activity.*
import rx.Subscription
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -68,12 +81,16 @@ class LibraryController(
bundle: Bundle? = null, bundle: Bundle? = null,
private val preferences: PreferencesHelper = Injekt.get() private val preferences: PreferencesHelper = Injekt.get()
) : BaseController(bundle), ) : BaseController(bundle),
TabbedController, //TabbedController,
ActionMode.Callback, ActionMode.Callback,
ChangeMangaCategoriesDialog.Listener, ChangeMangaCategoriesDialog.Listener,
MigrationInterface, MigrationInterface,
DownloadServiceListener, DownloadServiceListener,
LibraryServiceListener { LibraryServiceListener,
FlexibleAdapter.OnItemClickListener,
FlexibleAdapter.OnItemLongClickListener,
FlexibleAdapter.OnItemMoveListener,
LibraryCategoryAdapter.LibraryListener{
/** /**
* Position of the active category. * Position of the active category.
@ -135,14 +152,19 @@ class LibraryController(
/** /**
* Adapter of the view pager. * Adapter of the view pager.
*/ */
private var adapter: LibraryAdapter? = null private var pagerAdapter: LibraryAdapter? = null
private lateinit var adapter: LibraryCategoryAdapter
private lateinit var spinner: Spinner
private var lastClickPosition = -1
/** /**
* Drawer listener to allow swipe only for closing the drawer. * Drawer listener to allow swipe only for closing the drawer.
*/ */
private var tabsVisibilityRelay: BehaviorRelay<Boolean> = BehaviorRelay.create(false) // private var tabsVisibilityRelay: BehaviorRelay<Boolean> = BehaviorRelay.create(false)
private var tabsVisibilitySubscription: Subscription? = null // private var tabsVisibilitySubscription: Subscription? = null
private var observeLater:Boolean = false private var observeLater:Boolean = false
@ -153,29 +175,76 @@ class LibraryController(
private var justStarted = true private var justStarted = true
private var updateScroll = true
private var spinnerAdapter: SpinnerAdapter? = null
var scrollLister = object : RecyclerView.OnScrollListener () {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val position =
(recycler.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
val order = when (val item = adapter.getItem(position)) {
is LibraryHeaderItem -> item.category.order
is LibraryItem -> presenter.categories.find { it.id == item.manga.category }?.order
else -> null
}
if (order != null && order != activeCategory) {
preferences.lastUsedCategory().set(order)
activeCategory = order
val category = presenter.categories.find { it.order == order }
bottom_sheet.lastCategory = category
if (preferences.librarySortingMode().getOrDefault() == LibrarySort.DRAG_AND_DROP)
bottom_sheet.updateTitle()
// spinner.onItemSelectedListener = null
spinnerAdapter?.setCustomText(category?.name)
//spinner.view
//spinner.post { spinner.onItemSelectedListener = listener }
}
}
}
/**
* Recycler view of the list of manga.
*/
private lateinit var recycler: RecyclerView
var usePager = preferences.libraryUsingPager().getOrDefault()
init { init {
setHasOptionsMenu(true) setHasOptionsMenu(true)
retainViewMode = RetainViewMode.RETAIN_DETACH retainViewMode = RetainViewMode.RETAIN_DETACH
} }
override fun getTitle(): String? { override fun getTitle(): String? {
return resources?.getString(R.string.label_library) return null//if (title != null) null else resources?.getString(R.string.label_library)
}
private var title: String? = null
set(value) {
field = value
setTitle()
} }
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
return inflater.inflate(R.layout.library_controller, container, false) return inflater.inflate(R.layout.library_controller, container, false)
} }
override fun onViewCreated(view: View) { override fun onViewCreated(view: View) {
super.onViewCreated(view) super.onViewCreated(view)
mangaPerRow = getColumnsPreferenceForCurrentOrientation().getOrDefault()
adapter = LibraryAdapter(this) if (usePager) {
library_pager.adapter = adapter pager_layout.visible()
fast_scroller.gone()
pagerAdapter = LibraryAdapter(this)
library_pager.adapter = pagerAdapter
library_pager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { library_pager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageSelected(position: Int) { override fun onPageSelected(position: Int) {
preferences.lastUsedCategory().set(position) preferences.lastUsedCategory().set(position)
activeCategory = position activeCategory = position
bottom_sheet.lastCategory = adapter?.categories?.getOrNull(position) bottom_sheet.lastCategory = pagerAdapter?.categories?.getOrNull(position)
if (preferences.librarySortingMode().getOrDefault() == LibrarySort.DRAG_AND_DROP) bottom_sheet.updateTitle() if (preferences.librarySortingMode().getOrDefault() == LibrarySort.DRAG_AND_DROP) bottom_sheet.updateTitle()
} }
@ -186,14 +255,51 @@ class LibraryController(
override fun onPageScrollStateChanged(state: Int) {} override fun onPageScrollStateChanged(state: Int) {}
}) })
}
else {
adapter = LibraryCategoryAdapter(this)
recycler = if (preferences.libraryLayout().getOrDefault() == 0) {
(swipe_refresh.inflate(R.layout.library_list_recycler) as RecyclerView).apply {
layoutManager = LinearLayoutManager(context)
}
} else {
(swipe_refresh.inflate(R.layout.library_grid_recycler) as AutofitRecyclerView).apply {
spanCount = mangaPerRow
manager.spanSizeLookup = (object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
val item = this@LibraryController.adapter.getItem(position)
return if (item is LibraryHeaderItem)
manager.spanCount else 1
}
})
}
}
recycler.setHasFixedSize(true)
recycler.adapter = adapter
//adapter.setStickyHeaders(true)
recycler_layout.addView(recycler)
adapter.fastScroller = fast_scroller
recycler.addOnScrollListener(scrollLister)
spinner = swipe_refresh.inflate(R.layout.library_spinner) as AppCompatSpinner
(activity as MainActivity).supportActionBar?.setDisplayShowCustomEnabled(true)
(activity as MainActivity).supportActionBar?.customView = spinner
spinnerAdapter = SpinnerAdapter(view.context, R.layout.library_spinner_textview,
arrayOf(resources!!.getString(R.string.label_library)))
spinner.adapter = spinnerAdapter
spinnerAdapter?.setCustomText(resources?.getString(R.string.label_library))
}
mangaPerRow = getColumnsPreferenceForCurrentOrientation().getOrDefault()
if (selectedMangas.isNotEmpty()) { if (selectedMangas.isNotEmpty()) {
createActionModeIfNeeded() createActionModeIfNeeded()
} }
bottom_sheet.onCreate(pager_layout)
//bottom_sheet.onCreate(pager_layout)
bottom_sheet.onCreate(if (usePager) pager_layout else swipe_refresh)
bottom_sheet.onGroupClicked = { bottom_sheet.onGroupClicked = {
when (it) { when (it) {
@ -211,6 +317,28 @@ class LibraryController(
router.pushController(DownloadController().withFadeTransaction()) router.pushController(DownloadController().withFadeTransaction())
} }
// spinner.onItemSelectedListener = listener
/*spinner.onItemSelectedListener = IgnoreFirstSpinnerListener { position ->
if (!updateScroll) return@IgnoreFirstSpinnerListener
val headerPosition = adapter.indexOf(position) + 1
if (headerPosition > -1) (recycler.layoutManager as LinearLayoutManager)
.scrollToPositionWithOffset(position, 0)
}*/
val config = resources?.configuration
val phoneLandscape = (config?.orientation == Configuration.ORIENTATION_LANDSCAPE &&
(config.screenLayout.and(Configuration.SCREENLAYOUT_SIZE_MASK)) <
Configuration.SCREENLAYOUT_SIZE_LARGE)
// pad the recycler if the filter bottom sheet is visible
if (!usePager && !phoneLandscape) {
val height = view.context.resources.getDimensionPixelSize(R.dimen.rounder_radius) + 5.dpToPx
recycler.updatePaddingRelative(bottom = height)
fast_scroller.updateLayoutParams<CoordinatorLayout.LayoutParams> {
bottomMargin = height
}
}
if (presenter.isDownloading()) { if (presenter.isDownloading()) {
fab.scaleY = 1f fab.scaleY = 1f
fab.scaleX = 1f fab.scaleX = 1f
@ -219,9 +347,10 @@ class LibraryController(
} }
presenter.onRestore() presenter.onRestore()
val library = presenter.getAllManga() val library = presenter.getAllManga()
if (library != null) onNextLibraryUpdate(presenter.categories, library) if (library != null) presenter.updateViewBlocking() //onNextLibraryUpdate(presenter.categories, library)
else { else {
library_pager.alpha = 0f library_pager.alpha = 0f
swipe_refresh.alpha = 0f
presenter.getLibraryBlocking() presenter.getLibraryBlocking()
} }
} }
@ -229,12 +358,17 @@ class LibraryController(
override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) { override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) {
super.onChangeStarted(handler, type) super.onChangeStarted(handler, type)
if (type.isEnter) { if (type.isEnter) {
activity?.tabs?.setupWithViewPager(library_pager) if (usePager)
(activity as MainActivity).supportActionBar?.setDisplayShowCustomEnabled(true)
//activity?.tabs?.setupWithViewPager(library_pager)
presenter.getLibrary() presenter.getLibrary()
DownloadService.addListener(this) DownloadService.addListener(this)
DownloadService.callListeners() DownloadService.callListeners()
LibraryUpdateService.setListener(this) LibraryUpdateService.setListener(this)
} }
else if (type == ControllerChangeType.PUSH_EXIT) {
(activity as MainActivity).supportActionBar?.setDisplayShowCustomEnabled(false)
}
} }
override fun onActivityResumed(activity: Activity) { override fun onActivityResumed(activity: Activity) {
@ -251,18 +385,19 @@ class LibraryController(
} }
override fun onDestroy() { override fun onDestroy() {
(activity as MainActivity).supportActionBar?.setDisplayShowCustomEnabled(false)
presenter.onDestroy() presenter.onDestroy()
super.onDestroy() super.onDestroy()
} }
override fun onDestroyView(view: View) { override fun onDestroyView(view: View) {
adapter?.onDestroy() //adapter.onDestroy()
DownloadService.removeListener(this) DownloadService.removeListener(this)
LibraryUpdateService.removeListener() LibraryUpdateService.removeListener()
adapter = null //adapter = null
actionMode = null actionMode = null
tabsVisibilitySubscription?.unsubscribe() //tabsVisibilitySubscription?.unsubscribe()
tabsVisibilitySubscription = null //tabsVisibilitySubscription = null
super.onDestroyView(view) super.onDestroyView(view)
} }
@ -288,7 +423,7 @@ class LibraryController(
super.onDetach(view) super.onDetach(view)
} }
override fun configureTabs(tabs: TabLayout) { /*override fun configureTabs(tabs: TabLayout) {
with(tabs) { with(tabs) {
tabGravity = TabLayout.GRAVITY_CENTER tabGravity = TabLayout.GRAVITY_CENTER
tabMode = TabLayout.MODE_SCROLLABLE tabMode = TabLayout.MODE_SCROLLABLE
@ -307,12 +442,53 @@ class LibraryController(
override fun cleanupTabs(tabs: TabLayout) { override fun cleanupTabs(tabs: TabLayout) {
tabsVisibilitySubscription?.unsubscribe() tabsVisibilitySubscription?.unsubscribe()
tabsVisibilitySubscription = null tabsVisibilitySubscription = null
}*/
fun onNextLibraryUpdate(mangaMap: List<LibraryItem>, freshStart: Boolean = false) {
if (mangaMap.isNotEmpty()) {
empty_view.hide()
} else {
empty_view.show(R.drawable.ic_book_black_128dp, R.string.information_empty_library)
}
adapter.setItems(mangaMap)
val position = if (freshStart) adapter.indexOf(activeCategory) + 1 else null
spinner.onItemSelectedListener = null
spinnerAdapter = SpinnerAdapter(view!!.context, R.layout.library_spinner_textview,
presenter.categories.map { it.name }.toTypedArray())
spinner.adapter = spinnerAdapter
spinnerAdapter?.setCustomText(presenter.categories.find { it.order == activeCategory
}?.name ?: resources?.getString(R.string.label_library))
if (!freshStart) {
spinnerAdapter?.setCustomText(presenter.categories.find { it.order == activeCategory
}?.name ?: resources?.getString(R.string.label_library))
justStarted = false
if (swipe_refresh.alpha == 0f)
swipe_refresh.animate().alpha(1f).setDuration(500).start()
}else {
if (position != null)
(recycler.layoutManager as LinearLayoutManager)
.scrollToPositionWithOffset(position, 0)
}
spinner.onItemSelectedListener = IgnoreFirstSpinnerListener { pos ->
val headerPosition = adapter.indexOf(pos - 1) + 1
if (headerPosition > -1) {
(recycler.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(
headerPosition, 0
)
}
}
} }
fun onNextLibraryUpdate(categories: List<Category>, mangaMap: Map<Int, List<LibraryItem>>, fun onNextLibraryUpdate(categories: List<Category>, mangaMap: Map<Int, List<LibraryItem>>,
freshStart: Boolean = false) { freshStart: Boolean = false) {
val view = view ?: return val view = view ?: return
val adapter = adapter ?: return val adapter = pagerAdapter ?: return
// Show empty view if needed // Show empty view if needed
if (mangaMap.isNotEmpty()) { if (mangaMap.isNotEmpty()) {
@ -342,13 +518,13 @@ class LibraryController(
bottom_sheet.lastCategory = adapter.categories.getOrNull(activeCat) bottom_sheet.lastCategory = adapter.categories.getOrNull(activeCat)
bottom_sheet.updateTitle() bottom_sheet.updateTitle()
tabsVisibilityRelay.call(categories.size > 1) //tabsVisibilityRelay.call(categories.size > 1)
if (freshStart || !justStarted) { if (freshStart || !justStarted) {
// Delay the scroll position to allow the view to be properly measured. // Delay the scroll position to allow the view to be properly measured.
view.post { view.post {
if (isAttached) { if (isAttached) {
activity?.tabs?.setScrollPosition(library_pager.currentItem, 0f, true) //activity?.tabs?.setScrollPosition(library_pager.currentItem, 0f, true)
} }
} }
@ -398,22 +574,50 @@ class LibraryController(
} }
fun onCatSortChanged(id: Int? = null) { fun onCatSortChanged(id: Int? = null) {
val catId = id ?: adapter?.categories?.getOrNull(library_pager.currentItem)?.id ?: return val catId = id ?: presenter.categories.find { it.order == activeCategory }?.id ?: return
presenter.requestCatSortUpdate(catId) presenter.requestCatSortUpdate(catId)
//val catId = id ?: adapter?.categories?.getOrNull(library_pager.currentItem)?.id ?: return
// presenter.requestCatSortUpdate(catId)
} }
/** /**
* Reattaches the adapter to the view pager to recreate fragments * Reattaches the adapter to the view pager to recreate fragments
*/ */
private fun reattachAdapter() { private fun reattachAdapter() {
val adapter = adapter ?: return val position = (recycler.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
if (recycler is AutofitRecyclerView && preferences.libraryLayout().getOrDefault() == 0 ||
recycler !is AutofitRecyclerView && preferences.libraryLayout().getOrDefault() > 0) {
recycler_layout.removeView(recycler)
recycler = if (preferences.libraryLayout().getOrDefault() == 0) {
(swipe_refresh.inflate(R.layout.library_list_recycler) as RecyclerView).apply {
layoutManager = LinearLayoutManager(context)
}
} else {
(swipe_refresh.inflate(R.layout.library_grid_recycler) as AutofitRecyclerView).apply {
spanCount = mangaPerRow
manager.spanSizeLookup = (object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
val item = this@LibraryController.adapter.getItem(position)
return if (item is LibraryHeaderItem)
manager.spanCount else 1
}
})
}
}
recycler.setHasFixedSize(true)
recycler.addOnScrollListener(scrollLister)
recycler_layout.addView(recycler)
}
recycler.adapter = adapter
(recycler.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(position, 0)
//val adapter = adapter ?: return
val position = library_pager.currentItem /*val position = library_pager.currentItem
adapter.recycle = false adapter.recycle = false
library_pager.adapter = adapter library_pager.adapter = adapter
library_pager.currentItem = position library_pager.currentItem = position
adapter.recycle = true adapter.recycle = true*/
} }
/** /**
@ -533,8 +737,8 @@ class LibraryController(
mode.title = resources?.getString(R.string.label_selected, count) mode.title = resources?.getString(R.string.label_selected, count)
if (preferences.librarySortingMode().getOrDefault() == LibrarySort.DRAG_AND_DROP) { if (preferences.librarySortingMode().getOrDefault() == LibrarySort.DRAG_AND_DROP) {
val catId = (selectedMangas.first() as? LibraryManga)?.category val catId = (selectedMangas.first() as? LibraryManga)?.category
val sameCat = (adapter?.categories?.getOrNull(library_pager.currentItem)?.id val sameCat = /*(adapter?.categories?.getOrNull(library_pager.currentItem)?.id
== catId) && selectedMangas.all { (it as? LibraryManga)?.category == catId } == catId) &&*/ selectedMangas.all { (it as? LibraryManga)?.category == catId }
menu.findItem(R.id.action_move_manga).isVisible = sameCat menu.findItem(R.id.action_move_manga).isVisible = sameCat
} }
else menu.findItem(R.id.action_move_manga).isVisible = false else menu.findItem(R.id.action_move_manga).isVisible = false
@ -554,11 +758,11 @@ class LibraryController(
.negativeButton(android.R.string.no) .negativeButton(android.R.string.no)
.show() .show()
} }
R.id.action_select_all -> { /*R.id.action_select_all -> {
adapter?.categories?.getOrNull(library_pager.currentItem)?.id?.let { adapter?.categories?.getOrNull(library_pager.currentItem)?.id?.let {
selectAllRelay.call(it) selectAllRelay.call(it)
} }
} }*/
R.id.action_migrate -> { R.id.action_migrate -> {
router.pushController( router.pushController(
if (preferences.skipPreMigration().getOrDefault()) { if (preferences.skipPreMigration().getOrDefault()) {
@ -573,12 +777,12 @@ class LibraryController(
.withFadeTransaction()) .withFadeTransaction())
destroyActionModeIfNeeded() destroyActionModeIfNeeded()
} }
R.id.action_to_top, R.id.action_to_bottom -> { /*R.id.action_to_top, R.id.action_to_bottom -> {
adapter?.categories?.getOrNull(library_pager.currentItem)?.id?.let { adapter?.categories?.getOrNull(library_pager.currentItem)?.id?.let {
reorganizeRelay.call(it to if (item.itemId == R.id.action_to_top) -1 else -2) reorganizeRelay.call(it to if (item.itemId == R.id.action_to_top) -1 else -2)
} }
destroyActionModeIfNeeded() destroyActionModeIfNeeded()
} }*/
else -> return false else -> return false
} }
return true return true
@ -669,6 +873,19 @@ class LibraryController(
destroyActionModeIfNeeded() destroyActionModeIfNeeded()
} }
override fun startReading(position: Int) {
val activity = activity ?: return
val manga = (adapter.getItem(position) as? LibraryItem)?.manga ?: return
val chapter = presenter.getFirstUnread(manga) ?: return
val intent = ReaderActivity.newIntent(activity, manga, chapter)
destroyActionModeIfNeeded()
startActivity(intent)
}
override fun onItemReleased(position: Int) {
}
fun startReading(manga: Manga) { fun startReading(manga: Manga) {
val activity = activity ?: return val activity = activity ?: return
val chapter = presenter.getFirstUnread(manga) ?: return val chapter = presenter.getFirstUnread(manga) ?: return
@ -676,6 +893,94 @@ class LibraryController(
destroyActionModeIfNeeded() destroyActionModeIfNeeded()
startActivity(intent) startActivity(intent)
} }
override fun canDrag(): Boolean {
val sortingMode = preferences.librarySortingMode().getOrDefault()
val filterOff = preferences.filterCompleted().getOrDefault() +
preferences.filterTracked().getOrDefault() +
preferences.filterUnread().getOrDefault() +
preferences.filterCompleted().getOrDefault() == 0 &&
!preferences.hideCategories().getOrDefault()
return sortingMode == LibrarySort.DRAG_AND_DROP && filterOff &&
adapter.mode != SelectableAdapter.Mode.MULTI
}
/**
* Called when a manga is clicked.
*
* @param position the position of the element clicked.
* @return true if the item should be selected, false otherwise.
*/
override fun onItemClick(view: View?, position: Int): Boolean {
// If the action mode is created and the position is valid, toggle the selection.
val item = adapter.getItem(position) as? LibraryItem ?: return false
return if (adapter.mode == SelectableAdapter.Mode.MULTI) {
lastClickPosition = position
toggleSelection(position)
true
} else {
openManga(item.manga, 0f)
false
}
}
/**
* Called when a manga is long clicked.
*
* @param position the position of the element clicked.
*/
override fun onItemLongClick(position: Int) {
createActionModeIfNeeded()
when {
lastClickPosition == -1 -> setSelection(position)
lastClickPosition > position -> for (i in position until lastClickPosition)
setSelection(i)
lastClickPosition < position -> for (i in lastClickPosition + 1..position)
setSelection(i)
else -> setSelection(position)
}
lastClickPosition = position
}
override fun onActionStateChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
val position = viewHolder?.adapterPosition ?: return
if (actionState == 2) onItemLongClick(position)
}
/**
* Tells the presenter to toggle the selection for the given position.
*
* @param position the position to toggle.
*/
private fun toggleSelection(position: Int) {
val item = adapter.getItem(position) ?: return
setSelection((item as LibraryItem).manga, !adapter.isSelected(position))
invalidateActionMode()
}
/**
* Tells the presenter to set the selection for the given position.
*
* @param position the position to toggle.
*/
private fun setSelection(position: Int) {
val item = adapter.getItem(position) ?: return
setSelection((item as LibraryItem).manga, true)
invalidateActionMode()
}
override fun onItemMove(fromPosition: Int, toPosition: Int) { }
override fun shouldMoveItem(fromPosition: Int, toPosition: Int): Boolean {
if (adapter.selectedItemCount > 1)
return false
if (adapter.isSelected(fromPosition))
toggleSelection(fromPosition)
return true
}
} }
object HeightTopWindowInsetsListener : View.OnApplyWindowInsetsListener { object HeightTopWindowInsetsListener : View.OnApplyWindowInsetsListener {
@ -689,3 +994,29 @@ object HeightTopWindowInsetsListener : View.OnApplyWindowInsetsListener {
return insets return insets
} }
} }
class SpinnerAdapter(context: Context, layoutId: Int, val array: Array<String>) :
ArrayAdapter<String>
(context, layoutId, array) {
private var mCustomText = ""
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = super.getView(position, convertView, parent)
val tv: TextView = view as TextView
tv.text = mCustomText
return view
}
fun setCustomText(customText: String?) {
// Call to set the text that must be shown in the spinner for the custom option.
val text = customText ?: return
mCustomText = text
notifyDataSetChanged()
}
override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = parent.inflate(R.layout.library_spinner_entry_text) as TextView
view.text = array[position]
return view
}
}

@ -7,13 +7,11 @@ import android.widget.FrameLayout
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.signature.ObjectKey import com.bumptech.glide.signature.ObjectKey
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.MangaImpl import eu.kanade.tachiyomi.data.database.models.MangaImpl
import eu.kanade.tachiyomi.data.glide.GlideApp import eu.kanade.tachiyomi.data.glide.GlideApp
import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.util.system.dpToPx import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.view.gone import eu.kanade.tachiyomi.util.view.gone
import eu.kanade.tachiyomi.util.view.updatePaddingRelative
import eu.kanade.tachiyomi.util.view.visible import eu.kanade.tachiyomi.util.view.visible
import kotlinx.android.synthetic.main.catalogue_grid_item.* import kotlinx.android.synthetic.main.catalogue_grid_item.*
import kotlinx.android.synthetic.main.unread_download_badge.* import kotlinx.android.synthetic.main.unread_download_badge.*

@ -0,0 +1,64 @@
package eu.kanade.tachiyomi.ui.library
import android.view.View
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractHeaderItem
import eu.davidea.flexibleadapter.items.IFlexible
import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Category
class LibraryHeaderItem(val category: Category) : AbstractHeaderItem<LibraryHeaderItem.Holder>() {
override fun getLayoutRes(): Int {
return R.layout.library_category_header_item
}
override fun createViewHolder(
view: View,
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>
): Holder {
return Holder(view, adapter)
}
override fun bindViewHolder(
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>,
holder: Holder,
position: Int,
payloads: MutableList<Any?>?
) {
holder.bind(this)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other is LibraryHeaderItem) {
return category.id == other.category.id
}
return false
}
override fun isDraggable(): Boolean {
return false
}
override fun isSelectable(): Boolean {
return false
}
override fun hashCode(): Int {
return category.id!!
}
class Holder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>) :
FlexibleViewHolder(view, adapter, true) {
private val sectionText: TextView = view.findViewById(R.id.category_title)
fun bind(item: LibraryHeaderItem) {
sectionText.text = item.category.name
}
}
}

@ -37,8 +37,4 @@ abstract class LibraryHolder(
super.onItemReleased(position) super.onItemReleased(position)
(adapter as? LibraryCategoryAdapter)?.libraryListener?.onItemReleased(position) (adapter as? LibraryCategoryAdapter)?.libraryListener?.onItemReleased(position)
} }
protected fun convertColor(color: Int):String {
return Integer.toHexString(color and 0x00ffffff)
}
} }

@ -9,7 +9,7 @@ import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.f2prateek.rx.preferences.Preference import com.f2prateek.rx.preferences.Preference
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import eu.davidea.flexibleadapter.items.AbstractSectionableItem
import eu.davidea.flexibleadapter.items.IFilterable import eu.davidea.flexibleadapter.items.IFilterable
import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
@ -21,8 +21,10 @@ import eu.kanade.tachiyomi.widget.AutofitRecyclerView
import kotlinx.android.synthetic.main.catalogue_grid_item.view.* import kotlinx.android.synthetic.main.catalogue_grid_item.view.*
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
class LibraryItem(val manga: LibraryManga, private val libraryLayout: Preference<Int>) : class LibraryItem(val manga: LibraryManga,
AbstractFlexibleItem<LibraryHolder>(), IFilterable<String> { private val libraryLayout: Preference<Int>,
header: LibraryHeaderItem) :
AbstractSectionableItem<LibraryHolder, LibraryHeaderItem>(header), IFilterable<String> {
var downloadCount = -1 var downloadCount = -1
var unreadType = 1 var unreadType = 1
@ -51,7 +53,8 @@ class LibraryItem(val manga: LibraryManga, private val libraryLayout: Preference
cover_thumbnail.maxHeight = Integer.MAX_VALUE cover_thumbnail.maxHeight = Integer.MAX_VALUE
constraint_layout.minHeight = 0 constraint_layout.minHeight = 0
cover_thumbnail.adjustViewBounds = false cover_thumbnail.adjustViewBounds = false
cover_thumbnail.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, coverHeight) cover_thumbnail.layoutParams =
FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, coverHeight)
} }
else { else {
constraint_layout.minHeight = coverHeight constraint_layout.minHeight = coverHeight

@ -17,7 +17,6 @@ import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource 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.ui.migration.MigrationFlags
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import eu.kanade.tachiyomi.util.lang.removeArticles import eu.kanade.tachiyomi.util.lang.removeArticles
@ -29,20 +28,16 @@ import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.Comp
import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import rx.Observable import rx.Observable
import rx.Subscription
import rx.android.schedulers.AndroidSchedulers import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers import rx.schedulers.Schedulers
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.util.ArrayList import java.util.ArrayList
import java.util.Calendar
import java.util.Collections import java.util.Collections
import java.util.Comparator import java.util.Comparator
import java.util.Date
/** /**
* Class containing library information. * Class containing library information.
@ -109,7 +104,7 @@ class LibraryPresenter(
mangaMap mangaMap
} }
currentMangaMap = mangaMap currentMangaMap = mangaMap
view.onNextLibraryUpdate(categories, mangaMap) updateView(categories, mangaMap)
} }
} }
@ -124,7 +119,9 @@ class LibraryPresenter(
mangaMap mangaMap
}() }()
currentMangaMap = mangaMap currentMangaMap = mangaMap
view.onNextLibraryUpdate(categories, mangaMap, true) launchUI {
updateView(categories, mangaMap, true)
}
} }
fun getAllManga(): LibraryMap? { fun getAllManga(): LibraryMap? {
@ -314,7 +311,6 @@ class LibraryPresenter(
} }
val catListing by lazy { val catListing by lazy {
val default = createDefaultCategory() val default = createDefaultCategory()
default.order = -1
listOf(default) + db.getCategories().executeAsBlocking() listOf(default) + db.getCategories().executeAsBlocking()
} }
@ -423,10 +419,37 @@ class LibraryPresenter(
var libraryManga = db.getLibraryMangas().executeAsBlocking() var libraryManga = db.getLibraryMangas().executeAsBlocking()
if (!showCategories) if (!showCategories)
libraryManga = libraryManga.distinctBy { it.id } libraryManga = libraryManga.distinctBy { it.id }
val libraryMap = libraryManga.map { manga -> /*val libraryMap = libraryManga.map { manga ->
LibraryItem(manga, libraryLayout).apply { unreadType = unreadBadgeType } LibraryItem(manga, libraryLayout).apply { unreadType = unreadBadgeType }
}.groupBy { }.groupBy {
if (showCategories) it.manga.category else 0 if (showCategories) it.manga.category else 0
}*/
val catItemMain = LibraryHeaderItem(categories.firstOrNull() ?: createDefaultCategory())
val libraryMap =
if (preferences.libraryUsingPager().getOrDefault()) {
libraryManga.map { manga ->
LibraryItem(manga, libraryLayout, catItemMain).apply { unreadType = unreadBadgeType }
}.groupBy {
if (showCategories) it.manga.category else 0
}
}
else {
libraryManga.groupBy { manga ->
if (showCategories) manga.category else 0
//LibraryItem(manga, libraryLayout).apply { unreadType = unreadBadgeType }
}.map { entry ->
val categoryItem = LibraryHeaderItem(categories.find { entry.key == it.id }
?: createDefaultCategory())
entry.value.map {
LibraryItem(
it, libraryLayout, categoryItem
).apply { unreadType = unreadBadgeType }
}
}.map {
val cat = if (showCategories) it.firstOrNull()?.manga?.category ?: 0 else 0
cat to it
//LibraryItem(manga, libraryLayout).apply { unreadType = unreadBadgeType }
}.toMap()
} }
if (libraryMap.containsKey(0)) if (libraryMap.containsKey(0))
categories.add(0, createDefaultCategory()) categories.add(0, createDefaultCategory())
@ -441,6 +464,7 @@ class LibraryPresenter(
private fun createDefaultCategory(): Category { private fun createDefaultCategory(): Category {
val default = Category.createDefault(context) val default = Category.createDefault(context)
default.order = -1
val defOrder = preferences.defaultMangaOrder().getOrDefault() val defOrder = preferences.defaultMangaOrder().getOrDefault()
if (defOrder.firstOrNull()?.isLetter() == true) default.mangaSort = defOrder.first() if (defOrder.firstOrNull()?.isLetter() == true) default.mangaSort = defOrder.first()
else default.mangaOrder = defOrder.split("/").mapNotNull { it.toLongOrNull() } else default.mangaOrder = defOrder.split("/").mapNotNull { it.toLongOrNull() }
@ -456,8 +480,59 @@ class LibraryPresenter(
mangaMap = withContext(Dispatchers.IO) { applyFilters(mangaMap) } mangaMap = withContext(Dispatchers.IO) { applyFilters(mangaMap) }
mangaMap = withContext(Dispatchers.IO) { applySort(mangaMap) } mangaMap = withContext(Dispatchers.IO) { applySort(mangaMap) }
currentMangaMap = mangaMap currentMangaMap = mangaMap
view.onNextLibraryUpdate(categories, mangaMap) updateView(categories, mangaMap)
}
}
suspend fun updateView(categories: List<Category>, mangaMap: LibraryMap, freshStart:Boolean
= false) {
/* val list = withContext(Dispatchers.IO) {
val showCategories = !preferences.hideCategories().getOrDefault()
val current = mangaMap.values.first()
current.groupBy {
if (showCategories) it.manga.category else 0
}.flatMap { it.value }
}*/
if (preferences.libraryUsingPager().getOrDefault()) {
view.onNextLibraryUpdate(categories, mangaMap, true)
}
else {
val mangaList = withContext(Dispatchers.IO) {
val list = mutableListOf<LibraryItem>()
val many = categories.size > 1
for (element in mangaMap.toSortedMap(compareBy { entry ->
categories.find { it.id == entry }?.order ?: -1
})) {
list.addAll(element.value)
}
list
} }
view.onNextLibraryUpdate(mangaList, freshStart)
}
}
fun updateViewBlocking() {
/* val list = withContext(Dispatchers.IO) {
val showCategories = !preferences.hideCategories().getOrDefault()
val current = mangaMap.values.first()
current.groupBy {
if (showCategories) it.manga.category else 0
}.flatMap { it.value }
}*/
val mangaMap = currentMangaMap ?: return
if (preferences.libraryUsingPager().getOrDefault()) {
view.onNextLibraryUpdate(categories, mangaMap, true)
}
else {
val list = mutableListOf<LibraryItem>()
for (element in mangaMap?.toSortedMap(compareBy { entry ->
categories.find { it.id == entry }?.order ?: -1
})) {
list.addAll(element.value)
}
view.onNextLibraryUpdate(list, true)
}
} }
/** /**
@ -471,7 +546,7 @@ class LibraryPresenter(
val current = currentMangaMap ?: return@launchUI val current = currentMangaMap ?: return@launchUI
withContext(Dispatchers.IO) { setDownloadCount(current) } withContext(Dispatchers.IO) { setDownloadCount(current) }
currentMangaMap = current currentMangaMap = current
view.onNextLibraryUpdate(categories, current) updateView(categories, current)
} }
} }
@ -487,7 +562,7 @@ class LibraryPresenter(
val current = currentMangaMap ?: return@launchUI val current = currentMangaMap ?: return@launchUI
withContext(Dispatchers.IO) { setUnreadBadge(current) } withContext(Dispatchers.IO) { setUnreadBadge(current) }
currentMangaMap = current currentMangaMap = current
view.onNextLibraryUpdate(categories, current) updateView(categories, current)
} }
} }
@ -499,7 +574,7 @@ class LibraryPresenter(
var mangaMap = currentMangaMap ?: return@launchUI var mangaMap = currentMangaMap ?: return@launchUI
mangaMap = withContext(Dispatchers.IO) { applySort(mangaMap) } mangaMap = withContext(Dispatchers.IO) { applySort(mangaMap) }
currentMangaMap = mangaMap currentMangaMap = mangaMap
view.onNextLibraryUpdate(categories, mangaMap) updateView(categories, mangaMap)
} }
} }
@ -508,7 +583,7 @@ class LibraryPresenter(
var mangaMap = currentMangaMap ?: return@launchUI var mangaMap = currentMangaMap ?: return@launchUI
mangaMap = withContext(Dispatchers.IO) { applyCatSort(mangaMap, catId) } mangaMap = withContext(Dispatchers.IO) { applyCatSort(mangaMap, catId) }
currentMangaMap = mangaMap currentMangaMap = mangaMap
view.onNextLibraryUpdate(categories, mangaMap) updateView(categories, mangaMap)
} }
} }
@ -698,5 +773,6 @@ class LibraryPresenter(
private companion object { private companion object {
var currentLibrary:Library? = null var currentLibrary:Library? = null
var currentList:List<LibraryItem>? = null
} }
} }

@ -1,7 +1,6 @@
package eu.kanade.tachiyomi.ui.main package eu.kanade.tachiyomi.ui.main
import android.app.SearchManager import android.app.SearchManager
import android.content.ComponentCallbacks2
import android.content.Intent import android.content.Intent
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.Color import android.graphics.Color
@ -48,7 +47,6 @@ import eu.kanade.tachiyomi.ui.catalogue.global_search.CatalogueSearchController
import eu.kanade.tachiyomi.ui.download.DownloadController import eu.kanade.tachiyomi.ui.download.DownloadController
import eu.kanade.tachiyomi.ui.extension.ExtensionController import eu.kanade.tachiyomi.ui.extension.ExtensionController
import eu.kanade.tachiyomi.ui.library.LibraryController import eu.kanade.tachiyomi.ui.library.LibraryController
import eu.kanade.tachiyomi.ui.library.LibraryPresenter
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.ui.recent_updates.RecentChaptersController import eu.kanade.tachiyomi.ui.recent_updates.RecentChaptersController
import eu.kanade.tachiyomi.ui.recently_read.RecentlyReadController import eu.kanade.tachiyomi.ui.recently_read.RecentlyReadController
@ -58,7 +56,6 @@ import eu.kanade.tachiyomi.util.system.launchUI
import eu.kanade.tachiyomi.util.view.gone import eu.kanade.tachiyomi.util.view.gone
import eu.kanade.tachiyomi.util.view.updateLayoutParams import eu.kanade.tachiyomi.util.view.updateLayoutParams
import eu.kanade.tachiyomi.util.view.updatePadding import eu.kanade.tachiyomi.util.view.updatePadding
import eu.kanade.tachiyomi.util.view.updatePaddingRelative
import eu.kanade.tachiyomi.util.view.visible import eu.kanade.tachiyomi.util.view.visible
import kotlinx.android.synthetic.main.main_activity.* import kotlinx.android.synthetic.main.main_activity.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -186,6 +183,8 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
} }
} }
supportActionBar?.setDisplayShowCustomEnabled(true)
content.setOnApplyWindowInsetsListener { v, insets -> content.setOnApplyWindowInsetsListener { v, insets ->
// if device doesn't support light nav bar // if device doesn't support light nav bar
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
@ -279,6 +278,10 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
}) })
if (router.backstackSize <= 1) {
tabAnimator.hide()
}
syncActivityViewWithController(router.backstack.lastOrNull()?.controller()) syncActivityViewWithController(router.backstack.lastOrNull()?.controller())
if (savedInstanceState == null) { if (savedInstanceState == null) {

@ -1,9 +1,9 @@
package eu.kanade.tachiyomi.ui.main package eu.kanade.tachiyomi.ui.main
import android.animation.ObjectAnimator import android.animation.ObjectAnimator
import com.google.android.material.tabs.TabLayout
import android.view.ViewTreeObserver import android.view.ViewTreeObserver
import android.view.animation.DecelerateInterpolator import android.view.animation.DecelerateInterpolator
import com.google.android.material.tabs.TabLayout
class TabsAnimator(val tabs: TabLayout) { class TabsAnimator(val tabs: TabLayout) {
@ -97,6 +97,11 @@ class TabsAnimator(val tabs: TabLayout) {
isLastStateShown = false isLastStateShown = false
} }
fun hide() {
setHeight(0)
isLastStateShown = false
}
/** /**
* Returns whether the tab layout has a known height. * Returns whether the tab layout has a known height.
*/ */

@ -52,6 +52,13 @@ class SettingsLibraryController : SettingsController() {
} }
} }
switchPreference {
key = Keys.libraryUsingPager
titleRes = R.string.pref_remove_articles
summaryRes = R.string.pref_remove_articles_summary
defaultValue = false
}
switchPreference { switchPreference {
key = Keys.removeArticles key = Keys.removeArticles
titleRes = R.string.pref_remove_articles titleRes = R.string.pref_remove_articles

@ -3,13 +3,12 @@ package eu.kanade.tachiyomi.widget
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlin.math.max import kotlin.math.max
class AutofitRecyclerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : class AutofitRecyclerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
androidx.recyclerview.widget.RecyclerView(context, attrs) { androidx.recyclerview.widget.RecyclerView(context, attrs) {
private val manager = GridLayoutManager(context, 1) val manager = GridLayoutManager(context, 1)
private var columnWidth = -1 private var columnWidth = -1

@ -57,8 +57,8 @@
android:layout_height="30dp" android:layout_height="30dp"
android:layout_gravity="end" android:layout_gravity="end"
android:layout_marginTop="6dp" android:layout_marginTop="6dp"
android:layout_marginBottom="6dp"
android:layout_marginEnd="6dp" android:layout_marginEnd="6dp"
android:layout_marginBottom="6dp"
android:background="@drawable/round_play_background" android:background="@drawable/round_play_background"
android:contentDescription="@string/start_reading" android:contentDescription="@string/start_reading"
android:padding="6dp" android:padding="6dp"
@ -107,13 +107,14 @@
android:id="@+id/badge_guide" android:id="@+id/badge_guide"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="10dp" android:layout_height="10dp"
app:layout_constraintTop_toTopOf="@+id/card"/> app:layout_constraintTop_toTopOf="@+id/card" />
<include layout="@layout/unread_download_badge" <include
layout="@layout/unread_download_badge"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintBottom_toBottomOf="@id/badge_guide"
app:layout_constraintBottom_toBottomOf="@id/badge_guide"/> app:layout_constraintStart_toStartOf="parent" />
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/title" android:id="@+id/title"
@ -138,9 +139,9 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginTop="-1dp"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
android:layout_marginTop="-1dp"
android:ellipsize="end" android:ellipsize="end"
android:singleLine="true" android:singleLine="true"
android:textColor="?android:attr/textColorSecondary" android:textColor="?android:attr/textColorSecondary"
@ -150,4 +151,4 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
tools:text="Sample artist" /> tools:text="Sample artist" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="15dp"
android:paddingBottom="5dp"
android:gravity="center_vertical">
<TextView
android:id="@+id/category_title"
style="@style/TextAppearance.MaterialComponents.Headline5"
android:layout_width="0dp"
android:layout_height="match_parent"
android:gravity="center|start"
android:inputType="none"
android:layout_marginStart="10dp"
android:maxLines="1"
android:textColor="?attr/actionBarTintColor"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Title" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -2,11 +2,24 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout 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:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/library_layout" android:id="@+id/library_layout"
android:layout_height="match_parent"> android:layout_height="match_parent">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/recycler_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<androidx.coordinatorlayout.widget.CoordinatorLayout <androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/pager_layout" android:id="@+id/pager_layout"
android:visibility="gone"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@ -16,6 +29,14 @@
android:layout_height="match_parent" /> android:layout_height="match_parent" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
<eu.davidea.fastscroller.FastScroller
android:id="@+id/fast_scroller"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="end"
app:fastScrollerBubbleEnabled="true"
tools:visibility="visible" />
<androidx.coordinatorlayout.widget.CoordinatorLayout <androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/snackbar_layout" android:id="@+id/snackbar_layout"
android:layout_width="match_parent" android:layout_width="match_parent"

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.AppCompatSpinner xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:entries="@array/color_filter_modes" />

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
style="@style/TextAppearance.AppCompat.Widget.DropDownItem"
android:textAllCaps="false"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
android:textSize="16sp"
android:padding="10dip"
tools:text="Title"
tools:background="?attr/colorPrimary"/>

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
android:textSize="20sp"
tools:text="Title"
tools:background="?attr/colorPrimary"/>
Loading…
Cancel
Save