fix: scroll state not preserved after rotation change at some places

Affected fragments: SubscriptionsFragment, WatchHistoryFragment, ChannelFragment, PlaylistFragment
This commit is contained in:
Bnyro 2024-05-01 17:26:36 +02:00
parent 7868695987
commit d40e4eb9fa
5 changed files with 83 additions and 2 deletions

View File

@ -1,6 +1,8 @@
package com.github.libretube.ui.fragments
import android.content.res.Configuration
import android.os.Bundle
import android.os.Parcelable
import android.util.Log
import android.view.LayoutInflater
import android.view.View
@ -12,6 +14,7 @@ import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.R
import com.github.libretube.api.RetrofitInstance
import com.github.libretube.api.obj.ChannelTab
@ -61,6 +64,7 @@ class ChannelFragment : DynamicLayoutManagerFragment() {
private var searchChannelAdapter: SearchChannelAdapter? = null
private var isAppBarFullyExpanded: Boolean = true
private var recyclerViewState: Parcelable? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -109,6 +113,13 @@ class ChannelFragment : DynamicLayoutManagerFragment() {
loadNextPage()
}
binding.channelRecView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
recyclerViewState = binding.channelRecView.layoutManager?.onSaveInstanceState()
}
})
fetchChannel()
}
@ -338,4 +349,11 @@ class ChannelFragment : DynamicLayoutManagerFragment() {
return newContent.nextpage
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
// manually restore the recyclerview state due to https://github.com/material-components/material-components-android/issues/3473
binding.channelRecView.layoutManager?.onRestoreInstanceState(recyclerViewState)
}
}

View File

@ -1,7 +1,9 @@
package com.github.libretube.ui.fragments
import android.annotation.SuppressLint
import android.content.res.Configuration
import android.os.Bundle
import android.os.Parcelable
import android.util.Log
import android.view.LayoutInflater
import android.view.View
@ -72,6 +74,7 @@ class PlaylistFragment : DynamicLayoutManagerFragment() {
field = value
}
private val sortOptions by lazy { resources.getStringArray(R.array.playlistSortOptions) }
private var recyclerViewState: Parcelable? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -106,6 +109,14 @@ class PlaylistFragment : DynamicLayoutManagerFragment() {
binding.playlistRecView.updatePadding(bottom = if (it) 64f.dpToPx() else 0)
}
// manually restore the recyclerview state due to https://github.com/material-components/material-components-android/issues/3473
binding.playlistRecView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
recyclerViewState = binding.playlistRecView.layoutManager?.onSaveInstanceState()
}
})
fetchPlaylist()
}
@ -402,4 +413,10 @@ class PlaylistFragment : DynamicLayoutManagerFragment() {
isLoading = false
}
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
// manually restore the recyclerview state due to https://github.com/material-components/material-components-android/issues/3473
binding.playlistRecView.layoutManager?.onRestoreInstanceState(recyclerViewState)
}
}

View File

@ -1,7 +1,9 @@
package com.github.libretube.ui.fragments
import android.annotation.SuppressLint
import android.content.res.Configuration
import android.os.Bundle
import android.os.Parcelable
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -16,6 +18,7 @@ import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.R
import com.github.libretube.api.obj.StreamItem
import com.github.libretube.api.obj.Subscription
@ -72,6 +75,9 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment() {
field = value
}
private var subChannelsRecyclerViewState: Parcelable? = null
private var subFeedRecyclerViewState: Parcelable? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@ -177,6 +183,21 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment() {
ChannelGroupsSheet().show(childFragmentManager, null)
}
// manually restore the recyclerview state due to https://github.com/material-components/material-components-android/issues/3473
binding.subChannels.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
subChannelsRecyclerViewState = binding.subChannels.layoutManager?.onSaveInstanceState()
}
})
binding.subFeed.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
subFeedRecyclerViewState = binding.subFeed.layoutManager?.onSaveInstanceState()
}
})
lifecycleScope.launch(Dispatchers.IO) {
val groups = DatabaseHolder.Database.subscriptionGroupsDao().getAll()
.sortedBy { it.index }
@ -379,4 +400,11 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment() {
val subCount = subscriptions.size.toLong().formatShort()
binding.toggleSubs.text = "${getString(R.string.subscriptions)} ($subCount)"
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
// manually restore the recyclerview state due to https://github.com/material-components/material-components-android/issues/3473
binding.subChannels.layoutManager?.onRestoreInstanceState(subChannelsRecyclerViewState)
binding.subFeed.layoutManager?.onRestoreInstanceState(subFeedRecyclerViewState)
}
}

View File

@ -1,8 +1,11 @@
package com.github.libretube.ui.fragments
import android.content.res.Configuration
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.Parcelable
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -46,6 +49,7 @@ class WatchHistoryFragment : DynamicLayoutManagerFragment() {
private val handler = Handler(Looper.getMainLooper())
private val playerViewModel: PlayerViewModel by activityViewModels()
private var isLoading = false
private var recyclerViewState: Parcelable? = null
private var selectedStatusFilter = PreferenceHelper.getInt(
PreferenceKeys.SELECTED_HISTORY_STATUS_FILTER,
@ -143,6 +147,14 @@ class WatchHistoryFragment : DynamicLayoutManagerFragment() {
}.show(childFragmentManager)
}
// manually restore the recyclerview state due to https://github.com/material-components/material-components-android/issues/3473
binding.watchHistoryRecView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
recyclerViewState = binding.watchHistoryRecView.layoutManager?.onSaveInstanceState()
}
})
showWatchHistory(allHistory)
}
@ -256,6 +268,12 @@ class WatchHistoryFragment : DynamicLayoutManagerFragment() {
}
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
// manually restore the recyclerview state due to https://github.com/material-components/material-components-android/issues/3473
binding.watchHistoryRecView.layoutManager?.onRestoreInstanceState(recyclerViewState)
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null

View File

@ -36,12 +36,12 @@
android:visibility="gone">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/playlist_app_bar"
android:id="@+id/watch_history_app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/playlist_collapsing_tb"
android:id="@+id/watch_history_collapsing_tb"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll"