Otter-App-Android-Funkwhale/app/src/main/java/com/github/apognu/otter/fragments/LiveOtterFragment.kt

155 lines
4.6 KiB
Kotlin
Raw Normal View History

2019-08-19 16:50:33 +02:00
package com.github.apognu.otter.fragments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
2020-07-13 23:32:42 +02:00
import androidx.lifecycle.LiveData
import androidx.lifecycle.lifecycleScope
2020-07-13 23:32:42 +02:00
import androidx.lifecycle.observe
2019-08-19 16:50:33 +02:00
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.SimpleItemAnimator
import com.github.apognu.otter.repositories.HttpUpstream
2019-08-19 16:50:33 +02:00
import com.github.apognu.otter.repositories.Repository
2020-07-13 23:32:42 +02:00
import com.github.apognu.otter.utils.Event
import com.github.apognu.otter.utils.EventBus
import com.github.apognu.otter.utils.untilNetwork
2019-08-19 16:50:33 +02:00
import kotlinx.android.synthetic.main.fragment_artists.*
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
2019-08-19 16:50:33 +02:00
abstract class OtterAdapter<D, VH : RecyclerView.ViewHolder> : RecyclerView.Adapter<VH>() {
2019-08-19 16:50:33 +02:00
var data: MutableList<D> = mutableListOf()
init {
super.setHasStableIds(true)
}
abstract override fun getItemId(position: Int): Long
2019-08-19 16:50:33 +02:00
}
2020-07-13 23:32:42 +02:00
abstract class LiveOtterFragment<D : Any, DAO : Any, A : OtterAdapter<DAO, *>> : Fragment() {
companion object {
const val OFFSCREEN_PAGES = 20
}
2020-07-13 23:32:42 +02:00
abstract val liveData: LiveData<List<DAO>>
2019-08-19 16:50:33 +02:00
abstract val viewRes: Int
abstract val recycler: RecyclerView
open val layoutManager: RecyclerView.LayoutManager get() = LinearLayoutManager(context)
open val alwaysRefresh = true
2019-08-19 16:50:33 +02:00
2020-07-13 23:32:42 +02:00
lateinit var repository: Repository<D>
2019-08-19 16:50:33 +02:00
lateinit var adapter: A
private var moreLoading = false
private var listener: Job? = null
2019-08-19 16:50:33 +02:00
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(viewRes, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
recycler.layoutManager = layoutManager
(recycler.itemAnimator as? SimpleItemAnimator)?.supportsChangeAnimations = false
2019-08-19 16:50:33 +02:00
recycler.adapter = adapter
2020-07-13 23:32:42 +02:00
(repository.upstream as? HttpUpstream<*>)?.let { upstream ->
if (upstream.behavior == HttpUpstream.Behavior.Progressive) {
recycler.setOnScrollChangeListener { _, _, _, _, _ ->
val offset = recycler.computeVerticalScrollOffset()
if (!moreLoading && offset > 0 && needsMoreOffscreenPages()) {
moreLoading = true
2020-07-13 23:32:42 +02:00
fetch(adapter.data.size)
}
2019-08-19 16:50:33 +02:00
}
}
}
if (listener == null) {
listener = lifecycleScope.launch(IO) {
EventBus.get().collect { event ->
if (event is Event.ListingsChanged) {
withContext(Main) {
swiper?.isRefreshing = true
2020-07-13 23:32:42 +02:00
fetch()
}
}
}
}
}
2020-07-13 23:32:42 +02:00
fetch()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
2020-07-13 23:32:42 +02:00
liveData.observe(this) {
adapter.data = it.toMutableList()
adapter.notifyDataSetChanged()
}
2019-08-19 16:50:33 +02:00
}
override fun onResume() {
super.onResume()
swiper?.setOnRefreshListener {
2020-07-13 23:32:42 +02:00
fetch()
2019-08-19 16:50:33 +02:00
}
}
open fun onDataFetched(data: List<D>) {}
2020-07-13 23:32:42 +02:00
private fun fetch(size: Int = 0) {
moreLoading = true
2020-07-13 23:32:42 +02:00
repository.fetch(size).untilNetwork(lifecycleScope, IO) { data, _, hasMore ->
lifecycleScope.launch(Main) {
onDataFetched(data)
2020-07-08 23:21:47 +02:00
if (hasMore) {
2020-07-13 23:32:42 +02:00
(repository.upstream as? HttpUpstream<*>)?.let { upstream ->
if (upstream.behavior == HttpUpstream.Behavior.Progressive) {
if (size == 0 || needsMoreOffscreenPages()) {
fetch(size + data.size)
2020-07-08 23:21:47 +02:00
} else {
moreLoading = false
}
}
}
}
2020-07-13 23:32:42 +02:00
(repository.upstream as? HttpUpstream<*>)?.let { upstream ->
when (upstream.behavior) {
HttpUpstream.Behavior.Progressive -> if (!hasMore || !moreLoading) swiper?.isRefreshing = false
HttpUpstream.Behavior.AtOnce -> if (!hasMore) swiper?.isRefreshing = false
HttpUpstream.Behavior.Single -> if (!hasMore) swiper?.isRefreshing = false
}
}
}
}
}
private fun needsMoreOffscreenPages(): Boolean {
view?.let {
val offset = recycler.computeVerticalScrollOffset()
val left = recycler.computeVerticalScrollRange() - recycler.height - offset
return left < (recycler.height * OFFSCREEN_PAGES)
}
return false
}
2019-08-19 16:50:33 +02:00
}