Library views recycling

This commit is contained in:
len 2016-08-22 12:54:16 +02:00
parent 97454ca162
commit dfb2487640
9 changed files with 295 additions and 236 deletions

View file

@ -27,7 +27,7 @@ abstract class FlexibleViewHolder(view: View,
return true
}
protected fun toggleActivation() {
fun toggleActivation() {
itemView.isActivated = adapter.isSelected(adapterPosition)
}

View file

@ -1,22 +1,23 @@
package eu.kanade.tachiyomi.ui.library
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentManager
import android.view.View
import android.view.ViewGroup
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.ui.base.adapter.SmartFragmentStatePagerAdapter
import eu.kanade.tachiyomi.util.inflate
import eu.kanade.tachiyomi.widget.RecyclerViewPagerAdapter
/**
* This adapter stores the categories from the library, used with a ViewPager.
*
* @param fm the fragment manager.
* @constructor creates an instance of the adapter.
*/
class LibraryAdapter(fm: FragmentManager) : SmartFragmentStatePagerAdapter(fm) {
class LibraryAdapter(private val fragment: LibraryFragment) : RecyclerViewPagerAdapter() {
/**
* The categories to bind in the adapter.
*/
var categories: List<Category>? = null
var categories: List<Category> = emptyList()
// This setter helps to not refresh the adapter if the reference to the list doesn't change.
set(value) {
if (field !== value) {
@ -26,13 +27,34 @@ class LibraryAdapter(fm: FragmentManager) : SmartFragmentStatePagerAdapter(fm) {
}
/**
* Creates a new fragment for the given position when it's called.
* Creates a new view for this adapter.
*
* @param position the position to instantiate.
* @return a fragment for the given position.
* @return a new view.
*/
override fun getItem(position: Int): Fragment {
return LibraryCategoryFragment.newInstance(position)
override fun createView(container: ViewGroup): View {
val view = container.inflate(R.layout.item_library_category) as LibraryCategoryFragment
view.onCreate(fragment)
return view
}
/**
* Binds a view with a position.
*
* @param view the view to bind.
* @param position the position in the adapter.
*/
override fun bindView(view: View, position: Int) {
(view as LibraryCategoryFragment).onBind(categories[position])
}
/**
* Recycles a view.
*
* @param view the view to recycle.
* @param position the position in the adapter.
*/
override fun recycleView(view: View, position: Int) {
(view as LibraryCategoryFragment).onRecycle()
}
/**
@ -41,7 +63,7 @@ class LibraryAdapter(fm: FragmentManager) : SmartFragmentStatePagerAdapter(fm) {
* @return the number of categories or 0 if the list is null.
*/
override fun getCount(): Int {
return categories?.size ?: 0
return categories.size
}
/**
@ -51,28 +73,7 @@ class LibraryAdapter(fm: FragmentManager) : SmartFragmentStatePagerAdapter(fm) {
* @return the title to display.
*/
override fun getPageTitle(position: Int): CharSequence {
return categories!![position].name
}
/**
* Method to enable or disable the action mode (multiple selection) for all the instantiated
* fragments.
*
* @param mode the mode to set.
*/
fun setSelectionMode(mode: Int) {
for (fragment in getRegisteredFragments()) {
(fragment as LibraryCategoryFragment).setSelectionMode(mode)
}
}
/**
* Notifies the adapters in all the registered fragments to refresh their content.
*/
fun refreshRegisteredAdapters() {
for (fragment in getRegisteredFragments()) {
(fragment as LibraryCategoryFragment).adapter.notifyDataSetChanged()
}
return categories[position].name
}
}

View file

@ -84,7 +84,7 @@ class LibraryCategoryAdapter(val fragment: LibraryCategoryFragment) :
* @return a new view holder for a manga.
*/
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LibraryHolder {
//depending on preferences, display a list or display a grid
// Depending on preferences, display a list or display a grid
if (parent is AutofitRecyclerView) {
val view = parent.inflate(R.layout.item_catalogue_grid).apply {
val coverHeight = parent.itemWidth / 3 * 4
@ -96,7 +96,6 @@ class LibraryCategoryAdapter(val fragment: LibraryCategoryFragment) :
val view = parent.inflate(R.layout.item_library_list)
return LibraryListHolder(view, this, fragment)
}
}
/**
@ -113,4 +112,13 @@ class LibraryCategoryAdapter(val fragment: LibraryCategoryFragment) :
holder.itemView.isActivated = isSelected(position)
}
/**
* Returns the position in the adapter for the given manga.
*
* @param manga the manga to find.
*/
fun indexOf(manga: Manga): Int {
return mangas.orEmpty().indexOfFirst { it.id == manga.id }
}
}

View file

@ -1,24 +1,23 @@
package eu.kanade.tachiyomi.ui.library
import android.os.Bundle
import android.content.Context
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.util.AttributeSet
import android.widget.FrameLayout
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder
import eu.kanade.tachiyomi.ui.base.fragment.BaseFragment
import eu.kanade.tachiyomi.ui.manga.MangaActivity
import eu.kanade.tachiyomi.util.inflate
import eu.kanade.tachiyomi.util.toast
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
import kotlinx.android.synthetic.main.fragment_library_category.*
import kotlinx.android.synthetic.main.item_library_category.view.*
import rx.Subscription
import uy.kohesive.injekt.injectLazy
@ -26,23 +25,33 @@ import uy.kohesive.injekt.injectLazy
* Fragment containing the library manga for a certain category.
* Uses R.layout.fragment_library_category.
*/
class LibraryCategoryFragment : BaseFragment(), FlexibleViewHolder.OnListItemClickListener {
class LibraryCategoryFragment @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null)
: FrameLayout(context, attrs), FlexibleViewHolder.OnListItemClickListener {
/**
* Preferences.
*/
val preferences: PreferencesHelper by injectLazy()
private val preferences: PreferencesHelper by injectLazy()
/**
* The fragment containing this view.
*/
private lateinit var fragment: LibraryFragment
/**
* Category for this view.
*/
private lateinit var category: Category
/**
* Recycler view of the list of manga.
*/
private lateinit var recycler: RecyclerView
/**
* Adapter to hold the manga in this category.
*/
lateinit var adapter: LibraryCategoryAdapter
private set
/**
* Position in the adapter from [LibraryAdapter].
*/
private var position: Int = 0
private lateinit var adapter: LibraryCategoryAdapter
/**
* Subscription for the library manga.
@ -54,69 +63,30 @@ class LibraryCategoryFragment : BaseFragment(), FlexibleViewHolder.OnListItemCli
*/
private var searchSubscription: Subscription? = null
companion object {
/**
* Key to save and restore [position] from a [Bundle].
* Subscription of the library selections.
*/
const val POSITION_KEY = "position_key"
private var selectionSubscription: Subscription? = null
/**
* Creates a new instance of this class.
*
* @param position the position in the adapter from [LibraryAdapter].
* @return a new instance of [LibraryCategoryFragment].
*/
fun newInstance(position: Int): LibraryCategoryFragment {
val fragment = LibraryCategoryFragment()
fragment.position = position
fun onCreate(fragment: LibraryFragment) {
this.fragment = fragment
return fragment
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_library_category, container, false)
}
override fun onViewCreated(view: View, savedState: Bundle?) {
adapter = LibraryCategoryAdapter(this)
val recycler = if (preferences.libraryAsList().getOrDefault()) {
recycler = if (preferences.libraryAsList().getOrDefault()) {
(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 = libraryFragment.mangaPerRow
spanCount = fragment.mangaPerRow
}
}
// This crashes when opening a manga after changing categories, but then viewholders aren't
// recycled between pages. It may be fixed if this fragment is replaced with a custom view.
//(recycler.layoutManager as LinearLayoutManager).recycleChildrenOnDetach = true
//recycler.recycledViewPool = libraryFragment.pool
adapter = LibraryCategoryAdapter(this)
recycler.setHasFixedSize(true)
recycler.adapter = adapter
swipe_refresh.addView(recycler)
if (libraryFragment.actionMode != null) {
setSelectionMode(FlexibleAdapter.MODE_MULTI)
}
searchSubscription = libraryPresenter.searchSubject.subscribe { text ->
adapter.searchText = text
adapter.updateDataSet()
}
if (savedState != null) {
position = savedState.getInt(POSITION_KEY)
adapter.onRestoreInstanceState(savedState)
if (adapter.mode == FlexibleAdapter.MODE_SINGLE) {
adapter.clearSelection()
}
}
recycler.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recycler: RecyclerView, newState: Int) {
// Disable swipe refresh when view is not at the top
@ -130,36 +100,47 @@ class LibraryCategoryFragment : BaseFragment(), FlexibleViewHolder.OnListItemCli
swipe_refresh.setDistanceToTriggerSync((2 * 64 * resources.displayMetrics.density).toInt())
swipe_refresh.setOnRefreshListener {
if (!LibraryUpdateService.isRunning(context)) {
libraryPresenter.categories.getOrNull(position)?.let {
LibraryUpdateService.start(context, true, it)
LibraryUpdateService.start(context, true, category)
context.toast(R.string.updating_category)
}
}
// It can be a very long operation, so we disable swipe refresh and show a toast.
swipe_refresh.isRefreshing = false
}
}
override fun onDestroyView() {
searchSubscription?.unsubscribe()
super.onDestroyView()
fun onBind(category: Category) {
this.category = category
val presenter = fragment.presenter
searchSubscription = presenter.searchSubject.subscribe { text ->
adapter.searchText = text
adapter.updateDataSet()
}
override fun onResume() {
super.onResume()
libraryMangaSubscription = libraryPresenter.libraryMangaSubject
adapter.mode = if (presenter.selectedMangas.isNotEmpty()) {
FlexibleAdapter.MODE_MULTI
} else {
FlexibleAdapter.MODE_SINGLE
}
libraryMangaSubscription = presenter.libraryMangaSubject
.subscribe { onNextLibraryManga(it) }
selectionSubscription = presenter.selectionSubject
.subscribe { onSelectionChanged(it) }
}
override fun onPause() {
fun onRecycle() {
adapter.setItems(emptyList())
adapter.clearSelection()
}
override fun onDetachedFromWindow() {
searchSubscription?.unsubscribe()
libraryMangaSubscription?.unsubscribe()
super.onPause()
}
override fun onSaveInstanceState(outState: Bundle) {
outState.putInt(POSITION_KEY, position)
adapter.onSaveInstanceState(outState)
super.onSaveInstanceState(outState)
selectionSubscription?.unsubscribe()
super.onDetachedFromWindow()
}
/**
@ -169,17 +150,61 @@ class LibraryCategoryFragment : BaseFragment(), FlexibleViewHolder.OnListItemCli
* @param event the event received.
*/
fun onNextLibraryManga(event: LibraryMangaEvent) {
// Get the categories from the parent fragment.
val categories = libraryFragment.adapter.categories ?: return
// When a category is deleted, the index can be greater than the number of categories.
if (position >= categories.size) return
// Get the manga list for this category.
val mangaForCategory = event.getMangaForCategory(categories[position]) ?: emptyList()
val mangaForCategory = event.getMangaForCategory(category).orEmpty()
// Update the category with its manga.
adapter.setItems(mangaForCategory)
if (adapter.mode == FlexibleAdapter.MODE_MULTI) {
fragment.presenter.selectedMangas.forEach { manga ->
val position = adapter.indexOf(manga)
if (position != -1 && !adapter.isSelected(position)) {
adapter.toggleSelection(position)
(recycler.findViewHolderForItemId(manga.id!!) as? LibraryHolder)?.toggleActivation()
}
}
}
}
/**
* Subscribe to [LibrarySelectionEvent]. When an event is received, it updates the selection
* depending on the type of event received.
*
* @param event the selection event received.
*/
private fun onSelectionChanged(event: LibrarySelectionEvent) {
when (event) {
is LibrarySelectionEvent.Selected -> {
if (adapter.mode != FlexibleAdapter.MODE_MULTI) {
adapter.mode = FlexibleAdapter.MODE_MULTI
}
findAndToggleSelection(event.manga)
}
is LibrarySelectionEvent.Unselected -> {
findAndToggleSelection(event.manga)
if (fragment.presenter.selectedMangas.isEmpty()) {
adapter.mode = FlexibleAdapter.MODE_SINGLE
}
}
is LibrarySelectionEvent.Cleared -> {
adapter.mode = FlexibleAdapter.MODE_SINGLE
adapter.clearSelection()
}
}
}
/**
* Toggles the selection for the given manga and updates the view if needed.
*
* @param manga the manga to toggle.
*/
private fun findAndToggleSelection(manga: Manga) {
val position = adapter.indexOf(manga)
if (position != -1) {
adapter.toggleSelection(position)
(recycler.findViewHolderForItemId(manga.id!!) as? LibraryHolder)?.toggleActivation()
}
}
/**
@ -191,7 +216,7 @@ class LibraryCategoryFragment : BaseFragment(), FlexibleViewHolder.OnListItemCli
override fun onListItemClick(position: Int): Boolean {
// If the action mode is created and the position is valid, toggle the selection.
val item = adapter.getItem(position) ?: return false
if (libraryFragment.actionMode != null) {
if (adapter.mode == FlexibleAdapter.MODE_MULTI) {
toggleSelection(position)
return true
} else {
@ -206,7 +231,7 @@ class LibraryCategoryFragment : BaseFragment(), FlexibleViewHolder.OnListItemCli
* @param position the position of the element clicked.
*/
override fun onListItemLongClick(position: Int) {
libraryFragment.createActionModeIfNeeded()
fragment.createActionModeIfNeeded()
toggleSelection(position)
}
@ -217,63 +242,24 @@ class LibraryCategoryFragment : BaseFragment(), FlexibleViewHolder.OnListItemCli
*/
private fun openManga(manga: Manga) {
// Notify the presenter a manga is being opened.
libraryPresenter.onOpenManga()
fragment.presenter.onOpenManga()
// Create a new activity with the manga.
val intent = MangaActivity.newIntent(context, manga)
startActivity(intent)
fragment.startActivity(intent)
}
/**
* Toggles the selection for a manga.
* Tells the presenter to toggle the selection for the given position.
*
* @param position the position to toggle.
*/
private fun toggleSelection(position: Int) {
val library = libraryFragment
val manga = adapter.getItem(position) ?: return
// Toggle the selection.
adapter.toggleSelection(position, false)
// Notify the selection to the presenter.
library.presenter.setSelection(adapter.getItem(position), adapter.isSelected(position))
// Get the selected count.
val count = library.presenter.selectedMangas.size
if (count == 0) {
// Destroy action mode if there are no items selected.
library.destroyActionModeIfNeeded()
} else {
// Update action mode with the new selection.
library.setContextTitle(count)
library.setVisibilityOfCoverEdit(count)
library.invalidateActionMode()
}
fragment.presenter.setSelection(manga, !adapter.isSelected(position))
fragment.invalidateActionMode()
}
/**
* Sets the mode for the adapter.
*
* @param mode the mode to set. It should be MODE_SINGLE or MODE_MULTI.
*/
fun setSelectionMode(mode: Int) {
adapter.mode = mode
if (mode == FlexibleAdapter.MODE_SINGLE) {
adapter.clearSelection()
}
}
/**
* Property to get the library fragment.
*/
private val libraryFragment: LibraryFragment
get() = parentFragment as LibraryFragment
/**
* Property to get the library presenter.
*/
private val libraryPresenter: LibraryPresenter
get() = libraryFragment.presenter
}

View file

@ -11,7 +11,6 @@ import android.support.v7.widget.SearchView
import android.view.*
import com.afollestad.materialdialogs.MaterialDialog
import com.f2prateek.rx.preferences.Preference
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.Manga
@ -26,6 +25,7 @@ import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.fragment_library.*
import nucleus.factory.RequiresPresenter
import rx.Subscription
import timber.log.Timber
import uy.kohesive.injekt.injectLazy
import java.io.IOException
@ -66,8 +66,7 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
/**
* Action mode for manga selection.
*/
var actionMode: ActionMode? = null
private set
private var actionMode: ActionMode? = null
/**
* Selected manga for editing its cover.
@ -91,14 +90,8 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
private set
/**
* A pool to share view holders between all the registered categories (fragments).
* Subscription for the number of manga per row.
*/
// TODO find out why this breaks sometimes
// var pool = RecyclerView.RecycledViewPool().apply { setMaxRecycledViews(0, 20) }
// private set(value) {
// field = value.apply { setMaxRecycledViews(0, 20) }
// }
private var numColumnsSubscription: Subscription? = null
companion object {
@ -141,7 +134,7 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
override fun onViewCreated(view: View, savedState: Bundle?) {
setToolbarTitle(getString(R.string.label_library))
adapter = LibraryAdapter(childFragmentManager)
adapter = LibraryAdapter(this)
view_pager.adapter = adapter
view_pager.addOnPageChangeListener(object : ViewPager.SimpleOnPageChangeListener() {
override fun onPageSelected(position: Int) {
@ -154,6 +147,9 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
activeCategory = savedState.getInt(CATEGORY_KEY)
query = savedState.getString(QUERY_KEY)
presenter.searchSubject.onNext(query)
if (presenter.selectedMangas.isNotEmpty()) {
createActionModeIfNeeded()
}
} else {
activeCategory = preferences.lastUsedCategory().getOrDefault()
}
@ -261,8 +257,7 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
* Applies filter change
*/
private fun onFilterCheckboxChanged() {
presenter.updateLibrary()
adapter.refreshRegisteredAdapters()
presenter.resubscribeLibrary()
activity.supportInvalidateOptionsMenu()
}
@ -278,11 +273,11 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
* Reattaches the adapter to the view pager to recreate fragments
*/
private fun reattachAdapter() {
// pool.clear()
// pool = RecyclerView.RecycledViewPool()
val position = view_pager.currentItem
adapter.recycle = false
view_pager.adapter = adapter
view_pager.currentItem = position
adapter.recycle = true
}
/**
@ -323,7 +318,7 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
R.string.information_empty_library, R.drawable.ic_book_black_128dp)
// Get the current active category.
val activeCat = if (adapter.categories != null) view_pager.currentItem else activeCategory
val activeCat = if (adapter.categories.isNotEmpty()) view_pager.currentItem else activeCategory
// Set the categories
adapter.categories = categories
@ -339,31 +334,42 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
}
/**
* Sets the title of the action mode.
*
* @param count the number of items selected.
* Creates the action mode if it's not created already.
*/
fun setContextTitle(count: Int) {
actionMode?.title = getString(R.string.label_selected, count)
fun createActionModeIfNeeded() {
if (actionMode == null) {
actionMode = activity.startSupportActionMode(this)
}
}
/**
* Sets the visibility of the edit cover item.
*
* @param count the number of items selected.
* Destroys the action mode.
*/
fun setVisibilityOfCoverEdit(count: Int) {
// If count = 1 display edit button
actionMode?.menu?.findItem(R.id.action_edit_cover)?.isVisible = count == 1
fun destroyActionModeIfNeeded() {
actionMode?.finish()
}
/**
* Invalidates the action mode, forcing it to refresh its content.
*/
fun invalidateActionMode() {
actionMode?.invalidate()
}
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
mode.menuInflater.inflate(R.menu.library_selection, menu)
adapter.setSelectionMode(FlexibleAdapter.MODE_MULTI)
return true
}
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
val count = presenter.selectedMangas.size
if (count == 0) {
// Destroy action mode if there are no items selected.
destroyActionModeIfNeeded()
} else {
mode.title = getString(R.string.label_selected, count)
menu.findItem(R.id.action_edit_cover)?.isVisible = count == 1
}
return false
}
@ -381,18 +387,10 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
}
override fun onDestroyActionMode(mode: ActionMode) {
adapter.setSelectionMode(FlexibleAdapter.MODE_SINGLE)
presenter.selectedMangas.clear()
presenter.clearSelections()
actionMode = null
}
/**
* Destroys the action mode.
*/
fun destroyActionModeIfNeeded() {
actionMode?.finish()
}
/**
* Changes the cover for the selected manga.
*
@ -422,14 +420,14 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
context.contentResolver.openInputStream(data.data).use {
// Update cover to selected file, show error if something went wrong
if (presenter.editCoverWithStream(it, manga)) {
adapter.refreshRegisteredAdapters()
// TODO refresh cover
} else {
context.toast(R.string.notification_manga_update_failed)
}
}
} catch (e: IOException) {
} catch (error: IOException) {
context.toast(R.string.notification_manga_update_failed)
e.printStackTrace()
Timber.e(error, error.message)
}
}
@ -476,20 +474,4 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
.show()
}
/**
* Creates the action mode if it's not created already.
*/
fun createActionModeIfNeeded() {
if (actionMode == null) {
actionMode = activity.startSupportActionMode(this)
}
}
/**
* Invalidates the action mode, forcing it to refresh its content.
*/
fun invalidateActionMode() {
actionMode?.invalidate()
}
}

View file

@ -16,6 +16,7 @@ import rx.Observable
import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers
import rx.subjects.BehaviorSubject
import rx.subjects.PublishSubject
import uy.kohesive.injekt.injectLazy
import java.io.IOException
import java.io.InputStream
@ -29,22 +30,27 @@ class LibraryPresenter : BasePresenter<LibraryFragment>() {
/**
* Categories of the library.
*/
lateinit var categories: List<Category>
var categories: List<Category> = emptyList()
/**
* Currently selected manga.
*/
var selectedMangas = mutableListOf<Manga>()
val selectedMangas = mutableListOf<Manga>()
/**
* Search query of the library.
*/
val searchSubject: BehaviorSubject<String> = BehaviorSubject.create<String>()
val searchSubject: BehaviorSubject<String> = BehaviorSubject.create()
/**
* Subject to notify the library's viewpager for updates.
*/
val libraryMangaSubject: BehaviorSubject<LibraryMangaEvent> = BehaviorSubject.create<LibraryMangaEvent>()
val libraryMangaSubject: BehaviorSubject<LibraryMangaEvent> = BehaviorSubject.create()
/**
* Subject to notify the UI of selection updates.
*/
val selectionSubject: PublishSubject<LibrarySelectionEvent> = PublishSubject.create()
/**
* Database.
@ -149,7 +155,7 @@ class LibraryPresenter : BasePresenter<LibraryFragment>() {
/**
* Resubscribes to library.
*/
fun updateLibrary() {
fun resubscribeLibrary() {
start(GET_LIBRARY)
}
@ -219,11 +225,21 @@ class LibraryPresenter : BasePresenter<LibraryFragment>() {
fun setSelection(manga: Manga, selected: Boolean) {
if (selected) {
selectedMangas.add(manga)
selectionSubject.onNext(LibrarySelectionEvent.Selected(manga))
} else {
selectedMangas.remove(manga)
selectionSubject.onNext(LibrarySelectionEvent.Unselected(manga))
}
}
/**
* Clears all the manga selections and notifies the UI.
*/
fun clearSelections() {
selectedMangas.clear()
selectionSubject.onNext(LibrarySelectionEvent.Cleared())
}
/**
* Returns the common categories for the given list of manga.
*

View file

@ -0,0 +1,10 @@
package eu.kanade.tachiyomi.ui.library
import eu.kanade.tachiyomi.data.database.models.Manga
sealed class LibrarySelectionEvent {
class Selected(val manga: Manga) : LibrarySelectionEvent()
class Unselected(val manga: Manga) : LibrarySelectionEvent()
class Cleared() : LibrarySelectionEvent()
}

View file

@ -0,0 +1,42 @@
package eu.kanade.tachiyomi.widget
import android.support.v4.view.PagerAdapter
import android.view.View
import android.view.ViewGroup
import java.util.*
abstract class RecyclerViewPagerAdapter : PagerAdapter() {
private val pool = Stack<View>()
var recycle = true
set(value) {
if (!value) pool.clear()
field = value
}
protected abstract fun createView(container: ViewGroup): View
protected abstract fun bindView(view: View, position: Int)
protected open fun recycleView(view: View, position: Int) {}
override fun instantiateItem(container: ViewGroup, position: Int): Any {
val view = if (pool.isNotEmpty()) pool.pop() else createView(container)
bindView(view, position)
container.addView(view)
return view
}
override fun destroyItem(container: ViewGroup, position: Int, obj: Any) {
val view = obj as View
recycleView(view, position)
container.removeView(view)
if (recycle) pool.push(view)
}
override fun isViewFromObject(view: View, obj: Any): Boolean {
return view === obj
}
}

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<eu.kanade.tachiyomi.ui.library.LibraryCategoryFragment
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v4.widget.SwipeRefreshLayout>
</eu.kanade.tachiyomi.ui.library.LibraryCategoryFragment>