Globalize the use of caching for main sections data, improved handling of loading more pages.

This commit is contained in:
Antoine POPINEAU 2020-07-10 20:28:44 +02:00
parent 57692f2e42
commit bc1e911b41
No known key found for this signature in database
GPG Key ID: A78AC64694F84063
6 changed files with 33 additions and 34 deletions

View File

@ -19,6 +19,7 @@ class AlbumsGridFragment : FunkwhaleFragment<Album, AlbumsGridAdapter>() {
override val viewRes = R.layout.fragment_albums_grid override val viewRes = R.layout.fragment_albums_grid
override val recycler: RecyclerView get() = albums override val recycler: RecyclerView get() = albums
override val layoutManager get() = GridLayoutManager(context, 3) override val layoutManager get() = GridLayoutManager(context, 3)
override val alwaysRefresh = false
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)

View File

@ -10,10 +10,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.github.apognu.otter.repositories.HttpUpstream import com.github.apognu.otter.repositories.HttpUpstream
import com.github.apognu.otter.repositories.Repository import com.github.apognu.otter.repositories.Repository
import com.github.apognu.otter.utils.Cache import com.github.apognu.otter.utils.*
import com.github.apognu.otter.utils.Event
import com.github.apognu.otter.utils.EventBus
import com.github.apognu.otter.utils.untilNetwork
import com.google.gson.Gson import com.google.gson.Gson
import kotlinx.android.synthetic.main.fragment_artists.* import kotlinx.android.synthetic.main.fragment_artists.*
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
@ -28,8 +25,7 @@ abstract class FunkwhaleAdapter<D, VH : RecyclerView.ViewHolder> : RecyclerView.
} }
abstract class FunkwhaleFragment<D : Any, A : FunkwhaleAdapter<D, *>> : Fragment() { abstract class FunkwhaleFragment<D : Any, A : FunkwhaleAdapter<D, *>> : Fragment() {
val INITIAL_PAGES = 5 val OFFSCREEN_PAGES = 20
val OFFSCREEN_PAGES = 10
abstract val viewRes: Int abstract val viewRes: Int
abstract val recycler: RecyclerView abstract val recycler: RecyclerView
@ -39,7 +35,6 @@ abstract class FunkwhaleFragment<D : Any, A : FunkwhaleAdapter<D, *>> : Fragment
lateinit var repository: Repository<D, *> lateinit var repository: Repository<D, *>
lateinit var adapter: A lateinit var adapter: A
private var initialFetched = false
private var moreLoading = false private var moreLoading = false
private var listener: Job? = null private var listener: Job? = null
@ -57,10 +52,10 @@ abstract class FunkwhaleFragment<D : Any, A : FunkwhaleAdapter<D, *>> : Fragment
if (upstream.behavior == HttpUpstream.Behavior.Progressive) { if (upstream.behavior == HttpUpstream.Behavior.Progressive) {
recycler.setOnScrollChangeListener { _, _, _, _, _ -> recycler.setOnScrollChangeListener { _, _, _, _, _ ->
val offset = recycler.computeVerticalScrollOffset() val offset = recycler.computeVerticalScrollOffset()
val left = recycler.computeVerticalScrollRange() - recycler.height - offset
if (initialFetched && !moreLoading && offset > 0 && left < (recycler.height * OFFSCREEN_PAGES)) { if (!moreLoading && offset > 0 && needsMoreOffscreenPages()) {
moreLoading = true moreLoading = true
fetch(Repository.Origin.Network.origin, adapter.data.size) fetch(Repository.Origin.Network.origin, adapter.data.size)
} }
} }
@ -106,13 +101,17 @@ abstract class FunkwhaleFragment<D : Any, A : FunkwhaleAdapter<D, *>> : Fragment
} }
} }
repository.fetch(upstreams, size).untilNetwork(lifecycleScope, IO) { data, isCache, page, hasMore -> moreLoading = true
repository.fetch(upstreams, size).untilNetwork(lifecycleScope, IO) { data, isCache, _, hasMore ->
if (isCache && data.isEmpty()) { if (isCache && data.isEmpty()) {
return@untilNetwork fetch(Repository.Origin.Network.origin) return@untilNetwork fetch(Repository.Origin.Network.origin)
} }
lifecycleScope.launch(Main) { lifecycleScope.launch(Main) {
if (isCache) { if (isCache) {
moreLoading = false
adapter.data = data.toMutableList() adapter.data = data.toMutableList()
adapter.notifyDataSetChanged() adapter.notifyDataSetChanged()
@ -127,14 +126,6 @@ abstract class FunkwhaleFragment<D : Any, A : FunkwhaleAdapter<D, *>> : Fragment
adapter.data.addAll(data) adapter.data.addAll(data)
(repository.upstream as? HttpUpstream<*, *>)?.let { upstream ->
when (upstream.behavior) {
HttpUpstream.Behavior.Progressive -> if (!hasMore || page >= INITIAL_PAGES) swiper?.isRefreshing = false
HttpUpstream.Behavior.AtOnce -> if (!hasMore) swiper?.isRefreshing = false
HttpUpstream.Behavior.Single -> if (!hasMore) swiper?.isRefreshing = false
}
}
withContext(IO) { withContext(IO) {
if (adapter.data.isNotEmpty()) { if (adapter.data.isNotEmpty()) {
try { try {
@ -151,20 +142,27 @@ abstract class FunkwhaleFragment<D : Any, A : FunkwhaleAdapter<D, *>> : Fragment
} }
if (hasMore) { if (hasMore) {
moreLoading = false
(repository.upstream as? HttpUpstream<*, *>)?.let { upstream -> (repository.upstream as? HttpUpstream<*, *>)?.let { upstream ->
if (!isCache && upstream.behavior == HttpUpstream.Behavior.Progressive) { if (!isCache && upstream.behavior == HttpUpstream.Behavior.Progressive) {
if (page < INITIAL_PAGES) { if (first || needsMoreOffscreenPages()) {
moreLoading = true
fetch(Repository.Origin.Network.origin, adapter.data.size) fetch(Repository.Origin.Network.origin, adapter.data.size)
} else { } else {
initialFetched = true moreLoading = false
} }
} else {
moreLoading = false
} }
} }
} }
(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
}
}
when (first) { when (first) {
true -> { true -> {
adapter.notifyDataSetChanged() adapter.notifyDataSetChanged()
@ -176,4 +174,11 @@ abstract class FunkwhaleFragment<D : Any, A : FunkwhaleAdapter<D, *>> : Fragment
} }
} }
} }
private fun needsMoreOffscreenPages(): Boolean {
val offset = recycler.computeVerticalScrollOffset()
val left = recycler.computeVerticalScrollRange() - recycler.height - offset
return left < (recycler.height * OFFSCREEN_PAGES)
}
} }

View File

@ -17,6 +17,7 @@ import kotlinx.android.synthetic.main.fragment_playlists.*
class PlaylistsFragment : FunkwhaleFragment<Playlist, PlaylistsAdapter>() { class PlaylistsFragment : FunkwhaleFragment<Playlist, PlaylistsAdapter>() {
override val viewRes = R.layout.fragment_playlists override val viewRes = R.layout.fragment_playlists
override val recycler: RecyclerView get() = playlists override val recycler: RecyclerView get() = playlists
override val alwaysRefresh = false
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)

View File

@ -16,6 +16,7 @@ import kotlinx.coroutines.launch
class RadiosFragment : FunkwhaleFragment<Radio, RadiosAdapter>() { class RadiosFragment : FunkwhaleFragment<Radio, RadiosAdapter>() {
override val viewRes = R.layout.fragment_radios override val viewRes = R.layout.fragment_radios
override val recycler: RecyclerView get() = radios override val recycler: RecyclerView get() = radios
override val alwaysRefresh = false
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)

View File

@ -35,11 +35,6 @@ class RadioPlayer(val context: Context, val scope: CoroutineScope) {
private val favoritedRepository = FavoritedRepository(context) private val favoritedRepository = FavoritedRepository(context)
init { init {
Cache.get(context, "radio_type")?.readLine()?.log()
Cache.get(context, "radio_id")?.readLine()?.log()
Cache.get(context, "radio_session")?.readLine()?.log()
Cache.get(context, "radio_cookie")?.readLine()?.log()
Cache.get(context, "radio_type")?.readLine()?.let { radio_type -> Cache.get(context, "radio_type")?.readLine()?.let { radio_type ->
Cache.get(context, "radio_id")?.readLine()?.toInt()?.let { radio_id -> Cache.get(context, "radio_id")?.readLine()?.toInt()?.let { radio_id ->
Cache.get(context, "radio_session")?.readLine()?.toInt()?.let { radio_session -> Cache.get(context, "radio_session")?.readLine()?.toInt()?.let { radio_session ->

View File

@ -12,12 +12,8 @@ fun Context?.toast(message: String, length: Int = Toast.LENGTH_SHORT) {
} }
} }
fun Any.log(message: String) { fun Any.log(message: Any) {
Log.d("FUNKWHALE", "${this.javaClass.simpleName}: $message") Log.d("OTTER", "${javaClass.simpleName}: $message")
}
fun Any.log() {
Log.d("FUNKWHALE", this.toString())
} }
fun maybeNormalizeUrl(rawUrl: String?): String? { fun maybeNormalizeUrl(rawUrl: String?): String? {