forked from recloudstream/cloudstream
Library loading fixes
This commit is contained in:
parent
1f67644290
commit
7512dbcbf8
14 changed files with 254 additions and 121 deletions
|
@ -569,7 +569,7 @@ class HomeFragment : Fragment() {
|
|||
val mutableListOfResponse = mutableListOf<SearchResponse>()
|
||||
listHomepageItems.clear()
|
||||
|
||||
(home_master_recycler?.adapter as? ParentItemAdapter?)?.updateList(
|
||||
(home_master_recycler?.adapter as? ParentItemAdapter)?.updateList(
|
||||
d.values.toMutableList(),
|
||||
home_master_recycler
|
||||
)
|
||||
|
@ -621,7 +621,7 @@ class HomeFragment : Fragment() {
|
|||
//home_loaded?.isVisible = false
|
||||
}
|
||||
is Resource.Loading -> {
|
||||
(home_master_recycler?.adapter as? ParentItemAdapter?)?.updateList(listOf())
|
||||
(home_master_recycler?.adapter as? ParentItemAdapter)?.updateList(listOf())
|
||||
home_loading_shimmer?.startShimmer()
|
||||
home_loading?.isVisible = true
|
||||
home_loading_error?.isVisible = false
|
||||
|
|
|
@ -2,13 +2,18 @@ package com.lagradost.cloudstream3.ui.library
|
|||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import androidx.fragment.app.Fragment
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.os.postDelayed
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
|
@ -20,6 +25,7 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
|||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.mvvm.Resource
|
||||
import com.lagradost.cloudstream3.mvvm.debugAssert
|
||||
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
||||
import com.lagradost.cloudstream3.mvvm.observe
|
||||
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
||||
import com.lagradost.cloudstream3.syncproviders.SyncIdName
|
||||
|
@ -32,7 +38,9 @@ import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult
|
|||
import com.lagradost.cloudstream3.utils.AppUtils.reduceDragSensitivity
|
||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount
|
||||
import kotlinx.android.synthetic.main.fragment_library.*
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
const val LIBRARY_FOLDER = "library_folder"
|
||||
|
||||
|
@ -285,9 +293,27 @@ class LibraryFragment : Fragment() {
|
|||
viewpager?.offscreenPageLimit = 2
|
||||
viewpager?.reduceDragSensitivity()
|
||||
|
||||
val startLoading = Runnable {
|
||||
gridview?.numColumns = context?.getSpanCount() ?: 3
|
||||
gridview?.adapter =
|
||||
context?.let { LoadingPosterAdapter(it, 6 * 3) }
|
||||
library_loading_overlay?.isVisible = true
|
||||
library_loading_shimmer?.startShimmer()
|
||||
empty_list_textview?.isVisible = false
|
||||
}
|
||||
|
||||
val stopLoading = Runnable {
|
||||
gridview?.adapter = null
|
||||
library_loading_overlay?.isVisible = false
|
||||
library_loading_shimmer?.stopShimmer()
|
||||
}
|
||||
|
||||
val handler = Handler(Looper.getMainLooper())
|
||||
|
||||
observe(libraryViewModel.pages) { resource ->
|
||||
when (resource) {
|
||||
is Resource.Success -> {
|
||||
handler.removeCallbacks(startLoading)
|
||||
val pages = resource.value
|
||||
val showNotice = pages.all { it.items.isEmpty() }
|
||||
empty_list_textview?.isVisible = showNotice
|
||||
|
@ -303,6 +329,11 @@ class LibraryFragment : Fragment() {
|
|||
// Using notifyItemRangeChanged keeps the animations when sorting
|
||||
viewpager.adapter?.notifyItemRangeChanged(0, viewpager.adapter?.itemCount ?: 0)
|
||||
|
||||
// Only stop loading after 300ms to hide the fade effect the viewpager produces when updating
|
||||
// Without this there would be a flashing effect:
|
||||
// loading -> show old viewpager -> black screen -> show new viewpager
|
||||
handler.postDelayed(stopLoading, 300)
|
||||
|
||||
savedInstanceState?.getInt(VIEWPAGER_ITEM_KEY)?.let { currentPos ->
|
||||
viewpager?.setCurrentItem(currentPos, false)
|
||||
savedInstanceState.remove(VIEWPAGER_ITEM_KEY)
|
||||
|
@ -314,20 +345,23 @@ class LibraryFragment : Fragment() {
|
|||
) { tab, position ->
|
||||
tab.text = pages.getOrNull(position)?.title?.asStringNull(context)
|
||||
}.attach()
|
||||
loading_indicator?.hide()
|
||||
}
|
||||
is Resource.Loading -> {
|
||||
loading_indicator?.show()
|
||||
empty_list_textview?.isVisible = false
|
||||
// Only start loading after 200ms to prevent loading cached lists
|
||||
handler.postDelayed(startLoading, 200)
|
||||
}
|
||||
is Resource.Failure -> {
|
||||
stopLoading.run()
|
||||
// No user indication it failed :(
|
||||
// TODO
|
||||
loading_indicator?.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
}
|
||||
}
|
||||
|
||||
class MenuSearchView(context: Context) : SearchView(context) {
|
||||
|
|
|
@ -11,6 +11,7 @@ import com.lagradost.cloudstream3.mvvm.Resource
|
|||
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.SyncApis
|
||||
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
enum class ListSorting(@StringRes val stringRes: Int) {
|
||||
Query(R.string.none),
|
||||
|
@ -96,6 +97,7 @@ class LibraryViewModel : ViewModel() {
|
|||
)
|
||||
}
|
||||
|
||||
delay(5000)
|
||||
_pages.postValue(Resource.Success(pages))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package com.lagradost.cloudstream3.ui.library
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.BaseAdapter
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.ListPopupWindow.MATCH_PARENT
|
||||
import android.widget.RelativeLayout
|
||||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.toPx
|
||||
import kotlinx.android.synthetic.main.loading_poster_dynamic.view.*
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.math.sqrt
|
||||
|
||||
class LoadingPosterAdapter(context: Context, private val itemCount: Int) :
|
||||
BaseAdapter() {
|
||||
private val inflater: LayoutInflater = LayoutInflater.from(context)
|
||||
|
||||
override fun getCount(): Int {
|
||||
return itemCount
|
||||
}
|
||||
|
||||
override fun getItem(position: Int): Any? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
return position.toLong()
|
||||
}
|
||||
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
return convertView ?: inflater.inflate(R.layout.loading_poster_dynamic, parent, false)
|
||||
}
|
||||
}
|
|
@ -220,7 +220,7 @@ class QuickSearchFragment : Fragment() {
|
|||
when (it) {
|
||||
is Resource.Success -> {
|
||||
it.value.let { data ->
|
||||
(quick_search_autofit_results?.adapter as? SearchAdapter?)?.updateList(
|
||||
(quick_search_autofit_results?.adapter as? SearchAdapter)?.updateList(
|
||||
context?.filterSearchResultByFilmQuality(data) ?: data
|
||||
)
|
||||
}
|
||||
|
|
|
@ -277,7 +277,7 @@ open class ResultFragment : ResultTrailerPlayer() {
|
|||
private var downloadButton: EasyDownloadButton? = null
|
||||
override fun onDestroyView() {
|
||||
updateUIListener = null
|
||||
(result_episodes?.adapter as EpisodeAdapter?)?.killAdapter()
|
||||
(result_episodes?.adapter as? EpisodeAdapter)?.killAdapter()
|
||||
downloadButton?.dispose()
|
||||
|
||||
super.onDestroyView()
|
||||
|
@ -458,7 +458,7 @@ open class ResultFragment : ResultTrailerPlayer() {
|
|||
temporary_no_focus?.requestFocus()
|
||||
}
|
||||
|
||||
(result_episodes?.adapter as? EpisodeAdapter?)?.updateList(episodes.value)
|
||||
(result_episodes?.adapter as? EpisodeAdapter)?.updateList(episodes.value)
|
||||
|
||||
if (isTv && hasEpisodes) main {
|
||||
delay(500)
|
||||
|
@ -687,7 +687,7 @@ open class ResultFragment : ResultTrailerPlayer() {
|
|||
val newList = list.filter { it.isSynced && it.hasAccount }
|
||||
|
||||
result_mini_sync?.isVisible = newList.isNotEmpty()
|
||||
(result_mini_sync?.adapter as? ImageAdapter?)?.updateList(newList.mapNotNull { it.icon })
|
||||
(result_mini_sync?.adapter as? ImageAdapter)?.updateList(newList.mapNotNull { it.icon })
|
||||
}
|
||||
|
||||
var currentSyncProgress = 0
|
||||
|
@ -900,7 +900,7 @@ open class ResultFragment : ResultTrailerPlayer() {
|
|||
|
||||
|
||||
result_cast_items?.isVisible = d.actors != null
|
||||
(result_cast_items?.adapter as ActorAdaptor?)?.apply {
|
||||
(result_cast_items?.adapter as? ActorAdaptor)?.apply {
|
||||
updateList(d.actors ?: emptyList())
|
||||
}
|
||||
|
||||
|
|
|
@ -485,7 +485,7 @@ class ResultFragmentPhone : ResultFragment() {
|
|||
|
||||
result_recommendations?.post {
|
||||
rec?.let { list ->
|
||||
(result_recommendations?.adapter as SearchAdapter?)?.updateList(list.filter { it.apiName == matchAgainst })
|
||||
(result_recommendations?.adapter as? SearchAdapter)?.updateList(list.filter { it.apiName == matchAgainst })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,7 +107,7 @@ class ResultFragmentTv : ResultFragment() {
|
|||
result_recommendations?.isGone = isInvalid
|
||||
result_recommendations_holder?.isGone = isInvalid
|
||||
val matchAgainst = validApiName ?: rec?.firstOrNull()?.apiName
|
||||
(result_recommendations?.adapter as SearchAdapter?)?.updateList(rec?.filter { it.apiName == matchAgainst }
|
||||
(result_recommendations?.adapter as? SearchAdapter)?.updateList(rec?.filter { it.apiName == matchAgainst }
|
||||
?: emptyList())
|
||||
|
||||
rec?.map { it.apiName }?.distinct()?.let { apiNames ->
|
||||
|
|
|
@ -420,7 +420,7 @@ class SearchFragment : Fragment() {
|
|||
is Resource.Success -> {
|
||||
it.value.let { data ->
|
||||
if (data.isNotEmpty()) {
|
||||
(search_autofit_results?.adapter as SearchAdapter?)?.updateList(data)
|
||||
(search_autofit_results?.adapter as? SearchAdapter)?.updateList(data)
|
||||
}
|
||||
}
|
||||
searchExitIcon.alpha = 1f
|
||||
|
|
|
@ -143,7 +143,7 @@ class PluginsFragment : Fragment() {
|
|||
}
|
||||
|
||||
observe(pluginViewModel.filteredPlugins) { (scrollToTop, list) ->
|
||||
(plugin_recycler_view?.adapter as? PluginAdapter?)?.updateList(list)
|
||||
(plugin_recycler_view?.adapter as? PluginAdapter)?.updateList(list)
|
||||
|
||||
if (scrollToTop)
|
||||
plugin_recycler_view?.scrollToPosition(0)
|
||||
|
|
|
@ -89,12 +89,10 @@
|
|||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<!-- <RelativeLayout-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="match_parent"-->
|
||||
<!-- android:nestedScrollingEnabled="true"-->
|
||||
<!-- android:orientation="vertical"-->
|
||||
<!-- app:layout_behavior="@string/appbar_scrolling_view_behavior">-->
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/viewpager"
|
||||
|
@ -102,10 +100,42 @@
|
|||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:paddingBottom="40dp"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
tools:listitem="@layout/library_viewpager_page">
|
||||
tools:listitem="@layout/library_viewpager_page" />
|
||||
|
||||
</androidx.viewpager2.widget.ViewPager2>
|
||||
<LinearLayout
|
||||
android:visibility="gone"
|
||||
android:id="@+id/library_loading_overlay"
|
||||
android:layout_width="match_parent"
|
||||
tools:visibility="visible"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/primaryBlackBackground">
|
||||
|
||||
<com.facebook.shimmer.ShimmerFrameLayout
|
||||
android:layout_margin="2dp"
|
||||
android:id="@+id/library_loading_shimmer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:elevation="100dp"
|
||||
android:orientation="vertical"
|
||||
app:shimmer_auto_start="true"
|
||||
app:shimmer_base_alpha="0.2"
|
||||
app:shimmer_duration="@integer/loading_time"
|
||||
app:shimmer_highlight_alpha="0.3">
|
||||
|
||||
<GridView
|
||||
android:id="@+id/gridview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:horizontalSpacing="10dp"
|
||||
android:verticalSpacing="10dp"
|
||||
android:numColumns="3"
|
||||
tools:listitem="@layout/loading_poster_dynamic" />
|
||||
</com.facebook.shimmer.ShimmerFrameLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
|
@ -115,28 +145,12 @@
|
|||
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
android:id="@+id/sort_fab"
|
||||
style="@style/ExtendedFloatingActionButton"
|
||||
android:text="Sort"
|
||||
android:text="@string/sort"
|
||||
android:textColor="?attr/textColor"
|
||||
app:icon="@drawable/ic_baseline_sort_24"
|
||||
tools:ignore="ContentDescription" />
|
||||
</FrameLayout>
|
||||
|
||||
<!-- </com.google.android.material.appbar.AppBarLayout>-->
|
||||
|
||||
<androidx.core.widget.ContentLoadingProgressBar
|
||||
android:id="@+id/loading_indicator"
|
||||
style="@android:style/Widget.Material.ProgressBar.Horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_marginBottom="33dp"
|
||||
android:elevation="100dp"
|
||||
android:indeterminate="true"
|
||||
android:indeterminateTint="?attr/colorPrimary"
|
||||
android:progressTint="?attr/colorPrimary">
|
||||
|
||||
</androidx.core.widget.ContentLoadingProgressBar>
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/library_tab_layout"
|
||||
style="@style/Theme.Widget.Tabs"
|
||||
|
@ -157,6 +171,5 @@
|
|||
app:tabSelectedTextColor="@color/lightTextColor"
|
||||
app:tabTextAppearance="@style/TabNoCaps"
|
||||
app:tabTextColor="@color/textColor" />
|
||||
<!-- </RelativeLayout>-->
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
|
37
app/src/main/res/layout/loading_poster_dynamic.xml
Normal file
37
app/src/main/res/layout/loading_poster_dynamic.xml
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/card_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:background="@color/grayShimmer"
|
||||
app:cardCornerRadius="@dimen/loading_radius"
|
||||
app:layout_constraintDimensionRatio="1:1.414"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<include
|
||||
layout="@layout/loading_line_short_center"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="15dp"
|
||||
android:layout_marginHorizontal="20dp"
|
||||
android:layout_marginVertical="10dp" />
|
||||
</LinearLayout>
|
|
@ -10,15 +10,23 @@
|
|||
android:foreground="@drawable/outline_drawable"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/background_card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:layout_margin="2dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:elevation="10dp"
|
||||
app:cardBackgroundColor="?attr/primaryGrayBackground"
|
||||
app:cardCornerRadius="@dimen/rounded_image_radius">
|
||||
app:cardCornerRadius="@dimen/rounded_image_radius"
|
||||
app:layout_constraintDimensionRatio="1:1.414"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
|
@ -54,21 +62,21 @@
|
|||
android:id="@+id/text_rating_holder"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:cardCornerRadius="@dimen/rounded_image_radius"
|
||||
android:layout_gravity="end"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
android:layout_margin="2dp"
|
||||
android:backgroundTint="@color/ratingColorBg"
|
||||
android:elevation="0dp"
|
||||
android:visibility="gone"
|
||||
app:cardCornerRadius="@dimen/rounded_image_radius"
|
||||
app:cardElevation="0dp"
|
||||
android:backgroundTint="@color/ratingColorBg">
|
||||
tools:visibility="visible">
|
||||
|
||||
<TextView
|
||||
style="@style/SearchBox"
|
||||
android:minWidth="40dp"
|
||||
android:layout_margin="0dp"
|
||||
android:textColor="@color/ratingColor"
|
||||
android:id="@+id/text_rating"
|
||||
style="@style/SearchBox"
|
||||
android:layout_margin="0dp"
|
||||
android:minWidth="40dp"
|
||||
android:textColor="@color/ratingColor"
|
||||
|
||||
tools:text="★ 7.7" />
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
@ -99,6 +107,7 @@
|
|||
tools:progress="50"
|
||||
tools:visibility="visible" />
|
||||
</androidx.cardview.widget.CardView>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/imageText"
|
||||
|
|
|
@ -619,6 +619,7 @@
|
|||
<string name="apk_installer_package_installer">PackageInstaller</string>
|
||||
<string name="delayed_update_notice">App will be updated upon exit</string>
|
||||
<string name="sort_by">Sort by</string>
|
||||
<string name="sort">Sort</string>
|
||||
<string name="sort_rating_desc">Rating (High to Low)</string>
|
||||
<string name="sort_rating_asc">Rating (Low to High)</string>
|
||||
<string name="sort_updated_new">Updated (New to Old)</string>
|
||||
|
|
Loading…
Reference in a new issue