bugfix: restore scroll position on Contributions page after activity recreation#6725
Open
amahuli03 wants to merge 4 commits intocommons-app:mainfrom
Open
bugfix: restore scroll position on Contributions page after activity recreation#6725amahuli03 wants to merge 4 commits intocommons-app:mainfrom
amahuli03 wants to merge 4 commits intocommons-app:mainfrom
Conversation
…ition when the theme changes, BaseActivity called recreate(). Previously, onRestoreInstanceState would discard the FragmentManager-restored fragment by calling loadFragment(newInstance()), creating a fresh instance with no saved state. Now we reuse the existing restored fragment and only update the activity's reference fields.
After recreation, the paging list observer fires fresh data into an empty adapter, overriding the restored layout manager state. Now defers scroll restoration until after submitList completes, and guards scrollToPosition(0) to only fire when items are added to an already-populated list (new uploads) rather than on initial data loads.
|
✅ Generated APK variants! |
RitikaPahwa4444
requested changes
Mar 15, 2026
| if (fragmentName == ActiveFragment.CONTRIBUTIONS.name) { | ||
| title = getString(R.string.contributions_fragment) | ||
| loadFragment(newInstance(), false) | ||
| if (existing is ContributionsFragment) { |
Collaborator
There was a problem hiding this comment.
How about simplifying this block and making it DRYer? Do we really need the fragmentName anymore?
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes loss of scroll position on the Contributions screen when MainActivity is recreated (theme/language change, rotation, etc.) by reusing FragmentManager-restored fragments and deferring RecyclerView scroll restoration until after list updates.
Changes:
MainActivity: reuse the fragment already restored intoR.id.fragmentContainerinstead of always replacing it with a new instance duringonRestoreInstanceState.ContributionsListFragment: store pending RecyclerView state and attempt to restore it aftersubmitListcompletes; tighten “scroll to top” behavior to only trigger for genuine top insertions into a non-empty list.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.kt | Avoids replacing FragmentManager-restored fragments on state restore, preserving fragment state (including scroll). |
| app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.kt | Defers RV scroll state restoration and prevents unconditional scroll-to-top on initial/reload inserts. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
477
to
482
| override fun onViewStateRestored(savedInstanceState: Bundle?) { | ||
| super.onViewStateRestored(savedInstanceState) | ||
| if (null != savedInstanceState) { | ||
| val savedRecyclerLayoutState = | ||
| pendingScrollState = | ||
| BundleCompat.getParcelable(savedInstanceState, RV_STATE, Parcelable::class.java) | ||
| rvContributionsList!!.layoutManager!!.onRestoreInstanceState(savedRecyclerLayoutState) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description (required)
Fixes #6610
According to the issue description, when the user changes the app theme in settings and returns to the contributions page, their scroll position was lost and the list reset to the top. More broadly, whenever the app's activity was recreated (due to a theme change, language change, screen rotation, process death, etc.) the contributions page lost the user's scroll position and reset it to the top.
This was caused by two bugs:
MainActivity:onRestoreInstanceStatealways called loadFragment(newInstance(), ...) discarding the fragment the FragmentManager had already restored with its saved state.ContributionsListFragment: The scroll state was restored immediately inonViewStateRestored, but the paging list observer subsequently fired fresh data into an empty adapter, overriding it. Additionally,scrollToPosition(0)inonItemRangeInsertedfired on every data load, not just genuine new uploads.What changes did you make and why?
MainActivity.kt: reuse the FragmentManager-restored fragmentBaseActivity.onResume()callsrecreate()when the theme changes. During recreation, the Android FragmentManager automatically saves and restores the active fragment, including its instance state (scroll position). However,onRestoreInstanceStatewas callingrestoreActiveFragment(), which always calledloadFragment(newInstance(), false)— replacing the restored fragment with a brand new instance that had no saved state.The fix: use
supportFragmentManager.findFragmentById()to get the fragment the system already restored. If it matches the expected type, update the activity's reference fields directly without callingloadFragment. Theelsebranch retains the originalloadFragmentcall as a fallback for cases where no fragment exists in the container.ContributionsListFragment.kt: defer scroll restoration until after data loadsEven with the fragment preserved, scroll position was still lost because of a timing issue.
onViewStateRestoredis called before the paging list observer fires, so restoring the layout manager state immediately had no effect. The observer would then calladapter.submitList()with fresh data into an empty adapter, resetting the scroll to the top.The fix stores the saved state in a
pendingScrollStatefield inonViewStateRestoredinstead of applying it immediately, then applies it inside thesubmitListcommit callback, which fires after the diff is applied and items are laid out.Additionally,
scrollToPosition(0)in theAdapterDataObserverwas firing on every data load (including the initial load after recreation), not just when a new upload appeared. This was overriding the restored scroll position on subsequent page fetches. The fix adds acountBeforeInsertioncheck soscrollToPosition(0)only fires when items are inserted into an already-populated list (which is the case for a genuine new upload) and not during initial or reload data loads.Because this fix affects
MainActivity.restoreActiveFragment, which is called after theme/language change, screen rotation, and system process death, the scroll position will be restored after all of these. These are all places we should restore the user's contributions page scroll position.Tests performed (required)
Tested betaDebug on Android 16.0 emulator with API level 36.1.
Video of feature:
I scrolled down on my contributions page, changed the theme in settings, and came back to the contributions page to the same scroll position as before. Before this fix, the page did not restore the scroll position after a theme change.
Screen_recording_20260311_180528.webm
Happy to answer any clarifying question about this or make any changes if needed!