Library loading fixes

This commit is contained in:
Blatzar 2023-01-27 22:26:24 +01:00
parent 1f67644290
commit 7512dbcbf8
14 changed files with 254 additions and 121 deletions

View file

@ -569,7 +569,7 @@ class HomeFragment : Fragment() {
val mutableListOfResponse = mutableListOf<SearchResponse>() val mutableListOfResponse = mutableListOf<SearchResponse>()
listHomepageItems.clear() listHomepageItems.clear()
(home_master_recycler?.adapter as? ParentItemAdapter?)?.updateList( (home_master_recycler?.adapter as? ParentItemAdapter)?.updateList(
d.values.toMutableList(), d.values.toMutableList(),
home_master_recycler home_master_recycler
) )
@ -621,7 +621,7 @@ class HomeFragment : Fragment() {
//home_loaded?.isVisible = false //home_loaded?.isVisible = false
} }
is Resource.Loading -> { 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_shimmer?.startShimmer()
home_loading?.isVisible = true home_loading?.isVisible = true
home_loading_error?.isVisible = false home_loading_error?.isVisible = false

View file

@ -2,13 +2,18 @@ package com.lagradost.cloudstream3.ui.library
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.os.Looper
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ArrayAdapter
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.core.os.postDelayed
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import com.google.android.material.tabs.TabLayoutMediator 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.R
import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.debugAssert import com.lagradost.cloudstream3.mvvm.debugAssert
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.syncproviders.SyncAPI import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.syncproviders.SyncIdName 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.AppUtils.reduceDragSensitivity
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount
import kotlinx.android.synthetic.main.fragment_library.* import kotlinx.android.synthetic.main.fragment_library.*
import kotlinx.coroutines.delay
const val LIBRARY_FOLDER = "library_folder" const val LIBRARY_FOLDER = "library_folder"
@ -285,9 +293,27 @@ class LibraryFragment : Fragment() {
viewpager?.offscreenPageLimit = 2 viewpager?.offscreenPageLimit = 2
viewpager?.reduceDragSensitivity() 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 -> observe(libraryViewModel.pages) { resource ->
when (resource) { when (resource) {
is Resource.Success -> { is Resource.Success -> {
handler.removeCallbacks(startLoading)
val pages = resource.value val pages = resource.value
val showNotice = pages.all { it.items.isEmpty() } val showNotice = pages.all { it.items.isEmpty() }
empty_list_textview?.isVisible = showNotice empty_list_textview?.isVisible = showNotice
@ -303,6 +329,11 @@ class LibraryFragment : Fragment() {
// Using notifyItemRangeChanged keeps the animations when sorting // Using notifyItemRangeChanged keeps the animations when sorting
viewpager.adapter?.notifyItemRangeChanged(0, viewpager.adapter?.itemCount ?: 0) 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 -> savedInstanceState?.getInt(VIEWPAGER_ITEM_KEY)?.let { currentPos ->
viewpager?.setCurrentItem(currentPos, false) viewpager?.setCurrentItem(currentPos, false)
savedInstanceState.remove(VIEWPAGER_ITEM_KEY) savedInstanceState.remove(VIEWPAGER_ITEM_KEY)
@ -314,20 +345,23 @@ class LibraryFragment : Fragment() {
) { tab, position -> ) { tab, position ->
tab.text = pages.getOrNull(position)?.title?.asStringNull(context) tab.text = pages.getOrNull(position)?.title?.asStringNull(context)
}.attach() }.attach()
loading_indicator?.hide()
} }
is Resource.Loading -> { is Resource.Loading -> {
loading_indicator?.show() // Only start loading after 200ms to prevent loading cached lists
empty_list_textview?.isVisible = false handler.postDelayed(startLoading, 200)
} }
is Resource.Failure -> { is Resource.Failure -> {
stopLoading.run()
// No user indication it failed :( // No user indication it failed :(
// TODO // TODO
loading_indicator?.hide()
} }
} }
} }
} }
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
}
} }
class MenuSearchView(context: Context) : SearchView(context) { class MenuSearchView(context: Context) : SearchView(context) {

View file

@ -11,6 +11,7 @@ import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.SyncApis import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.SyncApis
import com.lagradost.cloudstream3.syncproviders.SyncAPI import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import kotlinx.coroutines.delay
enum class ListSorting(@StringRes val stringRes: Int) { enum class ListSorting(@StringRes val stringRes: Int) {
Query(R.string.none), Query(R.string.none),
@ -96,6 +97,7 @@ class LibraryViewModel : ViewModel() {
) )
} }
delay(5000)
_pages.postValue(Resource.Success(pages)) _pages.postValue(Resource.Success(pages))
} }
} }

View file

@ -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)
}
}

View file

@ -220,7 +220,7 @@ class QuickSearchFragment : Fragment() {
when (it) { when (it) {
is Resource.Success -> { is Resource.Success -> {
it.value.let { data -> it.value.let { data ->
(quick_search_autofit_results?.adapter as? SearchAdapter?)?.updateList( (quick_search_autofit_results?.adapter as? SearchAdapter)?.updateList(
context?.filterSearchResultByFilmQuality(data) ?: data context?.filterSearchResultByFilmQuality(data) ?: data
) )
} }

View file

@ -277,7 +277,7 @@ open class ResultFragment : ResultTrailerPlayer() {
private var downloadButton: EasyDownloadButton? = null private var downloadButton: EasyDownloadButton? = null
override fun onDestroyView() { override fun onDestroyView() {
updateUIListener = null updateUIListener = null
(result_episodes?.adapter as EpisodeAdapter?)?.killAdapter() (result_episodes?.adapter as? EpisodeAdapter)?.killAdapter()
downloadButton?.dispose() downloadButton?.dispose()
super.onDestroyView() super.onDestroyView()
@ -458,7 +458,7 @@ open class ResultFragment : ResultTrailerPlayer() {
temporary_no_focus?.requestFocus() temporary_no_focus?.requestFocus()
} }
(result_episodes?.adapter as? EpisodeAdapter?)?.updateList(episodes.value) (result_episodes?.adapter as? EpisodeAdapter)?.updateList(episodes.value)
if (isTv && hasEpisodes) main { if (isTv && hasEpisodes) main {
delay(500) delay(500)
@ -687,7 +687,7 @@ open class ResultFragment : ResultTrailerPlayer() {
val newList = list.filter { it.isSynced && it.hasAccount } val newList = list.filter { it.isSynced && it.hasAccount }
result_mini_sync?.isVisible = newList.isNotEmpty() 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 var currentSyncProgress = 0
@ -900,7 +900,7 @@ open class ResultFragment : ResultTrailerPlayer() {
result_cast_items?.isVisible = d.actors != null 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()) updateList(d.actors ?: emptyList())
} }

View file

@ -485,7 +485,7 @@ class ResultFragmentPhone : ResultFragment() {
result_recommendations?.post { result_recommendations?.post {
rec?.let { list -> 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 })
} }
} }
} }

View file

@ -107,7 +107,7 @@ class ResultFragmentTv : ResultFragment() {
result_recommendations?.isGone = isInvalid result_recommendations?.isGone = isInvalid
result_recommendations_holder?.isGone = isInvalid result_recommendations_holder?.isGone = isInvalid
val matchAgainst = validApiName ?: rec?.firstOrNull()?.apiName 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()) ?: emptyList())
rec?.map { it.apiName }?.distinct()?.let { apiNames -> rec?.map { it.apiName }?.distinct()?.let { apiNames ->

View file

@ -420,7 +420,7 @@ class SearchFragment : Fragment() {
is Resource.Success -> { is Resource.Success -> {
it.value.let { data -> it.value.let { data ->
if (data.isNotEmpty()) { if (data.isNotEmpty()) {
(search_autofit_results?.adapter as SearchAdapter?)?.updateList(data) (search_autofit_results?.adapter as? SearchAdapter)?.updateList(data)
} }
} }
searchExitIcon.alpha = 1f searchExitIcon.alpha = 1f

View file

@ -143,7 +143,7 @@ class PluginsFragment : Fragment() {
} }
observe(pluginViewModel.filteredPlugins) { (scrollToTop, list) -> observe(pluginViewModel.filteredPlugins) { (scrollToTop, list) ->
(plugin_recycler_view?.adapter as? PluginAdapter?)?.updateList(list) (plugin_recycler_view?.adapter as? PluginAdapter)?.updateList(list)
if (scrollToTop) if (scrollToTop)
plugin_recycler_view?.scrollToPosition(0) plugin_recycler_view?.scrollToPosition(0)

View file

@ -89,12 +89,10 @@
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<!-- <RelativeLayout--> <FrameLayout
<!-- android:layout_width="match_parent"--> android:layout_width="match_parent"
<!-- android:layout_height="match_parent"--> android:layout_height="match_parent"
<!-- android:nestedScrollingEnabled="true"--> app:layout_behavior="@string/appbar_scrolling_view_behavior">
<!-- android:orientation="vertical"-->
<!-- app:layout_behavior="@string/appbar_scrolling_view_behavior">-->
<androidx.viewpager2.widget.ViewPager2 <androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewpager" android:id="@+id/viewpager"
@ -102,10 +100,42 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:clipToPadding="false" android:clipToPadding="false"
android:paddingBottom="40dp" 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 <FrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -115,28 +145,12 @@
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/sort_fab" android:id="@+id/sort_fab"
style="@style/ExtendedFloatingActionButton" style="@style/ExtendedFloatingActionButton"
android:text="Sort" android:text="@string/sort"
android:textColor="?attr/textColor" android:textColor="?attr/textColor"
app:icon="@drawable/ic_baseline_sort_24" app:icon="@drawable/ic_baseline_sort_24"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
</FrameLayout> </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 <com.google.android.material.tabs.TabLayout
android:id="@+id/library_tab_layout" android:id="@+id/library_tab_layout"
style="@style/Theme.Widget.Tabs" style="@style/Theme.Widget.Tabs"
@ -157,6 +171,5 @@
app:tabSelectedTextColor="@color/lightTextColor" app:tabSelectedTextColor="@color/lightTextColor"
app:tabTextAppearance="@style/TabNoCaps" app:tabTextAppearance="@style/TabNoCaps"
app:tabTextColor="@color/textColor" /> app:tabTextColor="@color/textColor" />
<!-- </RelativeLayout>-->
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View 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>

View file

@ -10,15 +10,23 @@
android:foreground="@drawable/outline_drawable" android:foreground="@drawable/outline_drawable"
android:orientation="vertical"> android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
android:id="@+id/background_card" android:id="@+id/background_card"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="0dp"
android:layout_margin="2dp" android:layout_margin="2dp"
android:layout_marginBottom="2dp" android:layout_marginBottom="2dp"
android:elevation="10dp" android:elevation="10dp"
app:cardBackgroundColor="?attr/primaryGrayBackground" 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 <ImageView
android:id="@+id/imageView" android:id="@+id/imageView"
@ -54,21 +62,21 @@
android:id="@+id/text_rating_holder" android:id="@+id/text_rating_holder"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:cardCornerRadius="@dimen/rounded_image_radius"
android:layout_gravity="end" android:layout_gravity="end"
android:visibility="gone"
tools:visibility="visible"
android:layout_margin="2dp" android:layout_margin="2dp"
android:backgroundTint="@color/ratingColorBg"
android:elevation="0dp" android:elevation="0dp"
android:visibility="gone"
app:cardCornerRadius="@dimen/rounded_image_radius"
app:cardElevation="0dp" app:cardElevation="0dp"
android:backgroundTint="@color/ratingColorBg"> tools:visibility="visible">
<TextView <TextView
style="@style/SearchBox"
android:minWidth="40dp"
android:layout_margin="0dp"
android:textColor="@color/ratingColor"
android:id="@+id/text_rating" android:id="@+id/text_rating"
style="@style/SearchBox"
android:layout_margin="0dp"
android:minWidth="40dp"
android:textColor="@color/ratingColor"
tools:text="★ 7.7" /> tools:text="★ 7.7" />
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
@ -99,6 +107,7 @@
tools:progress="50" tools:progress="50"
tools:visibility="visible" /> tools:visibility="visible" />
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView <TextView
android:id="@+id/imageText" android:id="@+id/imageText"

View file

@ -619,6 +619,7 @@
<string name="apk_installer_package_installer">PackageInstaller</string> <string name="apk_installer_package_installer">PackageInstaller</string>
<string name="delayed_update_notice">App will be updated upon exit</string> <string name="delayed_update_notice">App will be updated upon exit</string>
<string name="sort_by">Sort by</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_desc">Rating (High to Low)</string>
<string name="sort_rating_asc">Rating (Low to High)</string> <string name="sort_rating_asc">Rating (Low to High)</string>
<string name="sort_updated_new">Updated (New to Old)</string> <string name="sort_updated_new">Updated (New to Old)</string>